プロフィール

髭山髭人(ひげひと)

自分の書いた記事が、一人でも誰かの役に立てば...
活動信条の一つとして「貴方のメモは、誰かのヒント」というのがあります。

このサイトについて

本家HP packetroom.net から切り離した いわゆる技術メモ用のブログで、無料レンタルサーバーにて運用しています。広告表示はその義務なのでご容赦。
XREA さんには長年お世話になっています

エピソード系チェック

アイドルエピソード系

作業用Discordサーバー用として掲載しており、加筆修正の可能性があります

#モバマス再現保存部

エピソード一覧表

複数Pさんにご協力いただいて組んだものになります。

恐らくそのままブラウザで開くと文字化けするので、
右クリックから「名前を付けてリンク先を保存」などしてDLを挟んでください
idol_episode_list.txt

上記テキストは Excel , Googleスプレッドシート に直接貼り付けると管理が楽なのでドチャクソお勧めです。
( Googleドライブ上に保存されます )

上記表を自作の雑なツールで変換した .json で良ければ、以下もどうぞ。
( ※中身ちゃんと精査していない)
🙄 idol_episode_list.json 🙄


[Task-1] アイドルエピソード一覧の確認

後述のスクリプト [Script-A] を実行し、ファイルを出力。
中身を確認後、不明扱いのエピソードIDが記載(=判明)されていればそれを教えて頂きたい
...というものです。

※ 12/6 02:30 頃更新
残り 367 1点...だが、運営的欠番の可能性が高い

解放しているアイドルエピソード一覧の確認用スクリプト

  • 実行場所
    多分どこでもOK
  • 機能・効果など
    バックグラウンドでアイドルギャラリーにアクセスし、
    そのデレドルのエピソード情報(割り振られているIDや、解放の有無)を回収します
    所要時間 2分程? ( DevTools 眺めていれば、巡回ログが都度出るのでわかりやすいハズ )
    全工程終了のち、情報をまとめたテキストが 計2つ DLされます
    ( + オマケで親愛埋め状況のカウントテキストが書出 )
    Tab 記号で整形しているので、Excelや Googleスプレッドシート 等に貼り付け・管理できるようにしています。
    貼り付け→エピソードIDでソートすると楽
  • 注意点
    途中でページを移動したり閉じてしまうと処理が破棄されます
    その場合は最初からやり直してください
  • 末尾番号のみ
    末尾に番号のみで表記された各行は
    髭同様「そのアカウントで存在が取得できなかったエピソード番号」です。
[Script-A] アイドルギャラリーページ内からエピソード一覧取得 / 巡回一括DL版
※ マウスオーバーで、コード右端上に Copy ボタンが表示されます

(async()=>{

    const _galleryList = await fetch("https://mkt.packetroom.net/script/idol_gallery_list.json",{mode:"cors"}).then(_res=>_res.json()).catch(_err=>{ console.warn(`${_err} アクセス失敗`);});

    const _dlTargetDefObj = [];
    _galleryList.forEach(_e=>{
        const _nameKey = Object.keys(_e)[0];
        const _hash = _e[_nameKey];
        const _url = `https://sp.pf.mbga.jp/12008305/?guid=ON&url=http://mobamas.net/idolmaster/idol_gallery/idol_detail/${_hash}`;
        const _dlFileName = `${_nameKey}`;
        console.info(_nameKey , _hash);
        _dlTargetDefObj.push( { "idol_name" : _nameKey , "url" : _url , "optionExt" : ".json"}  );
    });
    const _resultList = [];
    const _loveResultList = [];
    const _existStoryObj = {};
    console.info("================== 巡回アクセス & DL処理を開始します ==================");
    function _fileDl(_dlInfo , _index) {
        return new Promise((resolve,reject) => {
            const _idolName = _dlInfo["idol_name"];
            const _url = _dlInfo["url"];
            setTimeout(async () => {
                const _restext = await fetch(_url,{ method: "GET"})
                .then(_res=>{
                    if(!_res.ok){
                        throw new Error(_res.statusText);
                    }else{
                        return _res.text();
                    }
                })
                .catch(_err=>{
                    console.warn(`${_idolName} アクセス失敗`);
                });
                let _sucess = false;
                // === 何らかのレス 取得成功と判断
                if(_restext){
                    const _dPsr = new DOMParser();
                    const _doc = _dPsr.parseFromString(_restext , "text/html");
                    const _scr = [..._doc.scripts].find(_e=> _e.textContent.length>6000);
                    let _idols,_strys;
                    try{
                        const _m = _scr.textContent.match(/idol\.detail_list = (\[.*);\nidol\.idol_story_list = (\[.*\]);\nidol\.images/);
                        _idols = JSON.parse(_m[1]);
                        _strys = JSON.parse(_m[2]);
                        _sucess = true;
                    }catch(_err){ console.error(_err); }
                    if(!_sucess){ console.error("パース関連の予期せぬ例外:失敗,中断"); reject(); return; }
                    let _lMax = 0;
                    let _cardCount = _idols.length;
                    const _cID = _idols[0].profile.card_id; // ソートカテゴリ用
                    const _name = _idolName;//_idols[0].profile.card_name;
                    _idols.forEach(_i=>{ (_i.is_max_love?(_lMax++):0); });
                    _strys.forEach(_s=>{
                        let _arr = [];
                        const _id = _s.story_id;
                        // 最大エピソード数3前提
                        let _epiPartIdArr = ["","",""];
                        let _epiOpenFlgArr = ["","",""];
                        // open_flag のキー名(または配列index)と、_s.flash_path のキー名は同じ長さ且つ合致した前提とする
                        let _tempIndex = 0;
                        for(let _key in _s.open_flag){
                            const _flashUrl = decodeURIComponent(_s.flash_path[_key]);
                            const _epiPartId = _flashUrl.split("/movie_play/").pop().split("/")[1];
                            _epiPartIdArr[_tempIndex] = _epiPartId;
                            _epiOpenFlgArr[_tempIndex] = _s.open_flag[_key];
                            _tempIndex++;
                        }
                        // エピID,サブパートIDx3枠,タイトル,アイドル名,ソート用アイドルID,エピ開放記録x3枠
                        _arr = [ _id , ..._epiPartIdArr , _s.story_title , _name , _cID , ..._epiOpenFlgArr ];
                        _existStoryObj[_id] = true;
                        _tempMinId = _id;
                        _resultList.push(_arr);
                    })
                    console.info(`${_name} 親愛MAX数 ${_lMax} / ${_cardCount}`);
                    _loveResultList.push( [ _name , _lMax , _cardCount , _cID , (_lMax==_cardCount?"◎":"")] );
                }
                if(_sucess != true){
                    console.warn(_idolName , "失敗判定");
                    _resultList.push("failed");
                }
                resolve();
            }, 100);    // DL間隔ミリ秒。あまり縮めないほうが良い
        });
    }
    // 実処理開始
    (async () => {
        await _dlTargetDefObj.reduce((promise, _dlInfo , _currentIndex) => {
            return promise.then(async () => {
                await _fileDl(_dlInfo , _currentIndex);
            });
        }, Promise.resolve());
        console.info("=== ギャラリーページへのアタッチ終了 ====");
        const _missingList = []; // 欠番処理
        {   
            const _objLen = Object.keys(_existStoryObj).length;
            let _lastNumKey = Object.keys(_existStoryObj)[_objLen-1];
            for(let _n=1; _lastNumKey>=_n; _n++){ if(!(_n in _existStoryObj)){ _missingList.push(_n) } };
        }
        {
            let _outStr = `アイドルストーリ解放状況 ( 結果 : およそ ${_resultList.length} 行分 )\n`
            + "※ 実装されているが、ギャラリー上に表示されないストーリーが(例え親愛埋めMAXでも)あり得るので注意\n"
            + "未参加イベント?や(上位系)特別編等がこれに相当する模様?\n"
            + "(末行の)空白行は巡回で確認できなかったエピソードです\n"
            + "タブで整形されているので、Excel,スプレッドシート等にそのまま貼り付け・ソートなどが可能です\n"
            + "p1~p3はエピソードID内の小分けパートです。前後編ならp1,p2で、中編あればp3まで\n"
            + "op1~はopen(開放)を示し、p1~同様その対応エピソードパートの開放を示します(1:開放,0:未開放)\n\n\n\n"
            + "episode_id\tp1\tp2\tp3\tstory_name\tidol_name\tprovisional_id\top1\top2\top3\n";
            _resultList.forEach((_e)=>{ _outStr += ( _e.join("\t") ) + "\n"; });
            _missingList.forEach((_e)=>{ _outStr += ( `${_e}` ) + "\n"; });
            const _dlUrl = URL.createObjectURL( new Blob([_outStr+"\n\n"] , {type: "text/plain"}) );
            const _a = document.createElement('a');
            _a.href = _dlUrl;
            _a.download = `_エピソード一覧.txt`;
            document.body.appendChild(_a); _a.click();
            console.info(`${_a.download} をDLしました`);
            document.body.removeChild(_a);
            URL.revokeObjectURL(_dlUrl);
        }
        {
            let _outStr = `親愛度MAX数 ( 結果 : およそ ${_loveResultList.length} 行分 )\n`
            + "タブで整形されているので、Excel,スプレッドシート等にそのまま貼り付け・ソートなどが可能です\n\n\n\n"
            + "idol_name\tnow\tmax\tprovisional_id\tfull\n";
            _loveResultList.forEach((_e)=>{ _outStr += ( _e.join("\t") ) + "\n"; });
            const _dlUrl = URL.createObjectURL( new Blob([_outStr+"\n\n"] , {type: "text/plain"}) );
            const _a = document.createElement('a');
            _a.href = _dlUrl;
            _a.download = `_親愛埋め数リスト.txt`;
            document.body.appendChild(_a); _a.click();
            console.info(`${_a.download} をDLしました`);
            document.body.removeChild(_a);
            URL.revokeObjectURL(_dlUrl);
        }
    })();

})();