新しい「一括下書き化」機能について
最後の更新記事は、2019年9月でした。 その春に「一括下書き化」のスマホ版がリリースされ、PC版は作られないままだったので、JavaScriptで近似の機能を作ったのが「Every Page」シリーズです。
今春、PC版が漸くリリースされてテストしてみた感想は、1記事ずつ編集画面に開いては変換する「Every Page」シリーズに比べて、圧倒的に早く済むと言う事です。 そらぁ、アメーバ側の記事のデータベースを操作するだけですから、当然ですけど。
ただし、元の公開状態に一括で戻す機能がありません。 沢山の記事を「下書き」にしてしまうと、後で 1記事ずつ手作業で戻すのは大変です。 一旦「下書き」にしてしまうと、元が「アメンバー記事」「公開記事」「下書き記事」のどれだったかは、ユーザー自身が記事タイトルや投稿日付で記録していない限り、全く区別できません。
「一括公開」の機能が無いのは、ユーザーが間違って非公開記事を公開してしまうリスクを配慮した結果と思いますが、「一括下書き化」の機能の利用範囲をとても狭めていると言えます。
やっぱり使える「Every Page」シリーズ
EdgeがChromeクローンになる前に制作した JavaScriptツールですが、今回改めてテストしてみると、しっかり動作しました。 記事の読込みと保存の繰り返しのシーケンス動作は苦労した部分ですが、したたかに確実に動作します。 この JavaScriptのコードは他にも応用が出来るはずで、廃れさせるのは勿体ないので、現在の「記事の編集・削除」の画面にデザインを調整して、再リニュアルしました。
要点は、
▪ 高速の「styleタグ」書込み技法を導入。
▪ Edge が Chromeのクローンになった事で、ユーザーエージェント対応を Chrome と Firefoxに簡素化。
▪ 新しい「記事の編集・削除」に適した style指定に更新する。
▪ 現在の「Ameblo Management」のデザインに合せて、リスト幅を拡張。
▪「記事の編集・削除」のトップ画面で現在の日時を取得出来ないバグを回避。
などです。 主機能のコードは完成度が高く、殆どデザイン上の更新です。
「Every Page」シリーズの概要
「Every Page」シリーズは 3個のツールで構成されます。
①「Every Page Snap 💢」
これは、ブログ記事の「公開」「下書き」「アメンバー」の公開形式を、高速で記録するツールです。 この「Snap」情報はローカルストレージに記録し、「Every Page Opener 💢」が「下書き化」をしたブログを再公開する時に利用します。
「Snap」記録に要する時間は、1000件の記事があっても数分しかかかりません。
➁「Every Page Closer 💢」
自動でブログ記事を「下書き」に変更するツールです。 このツールは、今回のアメーバが提供した「一括下書き化」機能で、もはや必要がありません。
ただし、「Ameblo Management」を利用していない場合は、このツールが適用する大量処理に適したコンパクトデザイン下で「一括下書き化」の作業ができます。
➂「Every Page Opener 💢」
「Snap」情報に従って、「下書き化」したブログの全部または一部を、元の公開状態に戻すツールです。 ツールは「記事の編集・削除」の画面で、1記事ずつ編集画面に開いて再保存します。 今回テストすると、1ページ20件の記事を200sec弱で処理しました。 俊足ではありませんが、手作業で行う事を考えると大変に楽です。
なお、①の「Snap」データがない場合に、「一括下書き化」の処理をした記事群を全て公開に変更する処理も可能です。 元が「アメンバー」「下書き」の記事も通常の公開記事になりますが、大量の記事を自動処理で公開に戻したい場合に使えます。
幾つかの特徴
▪「記事ID+公開形式」の形て情報保存するので、「Snap」をした時点以降に、一部の記事を削除したり追加をしても、再公開時の操作に問題は生じません。
▪「Snap」情報は、長い年月の先の再公開を考慮し、ローカルストレージからダウンロードファイルの形で、PC内に保存ができる様にしています。 データを保存したファイルは大変に軽量で、別のPCにもコピーができます。
▪ ファイル化した「Snap」情報は、 「Every Page」シリーズでローカルストレージに読み込んで利用します。
▪「Snap」「Close」「Open」操作は、ブログの一期間の部分だけを処理できます。目的の範囲の「Snap」情報があれば良く、前後に余分な期間の情報が有っても無問題です。「Close」「Open」の操作は「記事の編集・削除」の20件/1ページごとの、必要な期間のみを処理できます。 もちろん、ブログ全記事の処理も可です。
「Every Page Snap 💢」の機能と操作方法
下は「Every Page Snap 💢」の処理画面の全体の様子です。
上部に「Every Page Snap 💢」のコンソールがあります。
❶「Snap」処理のスタートスイッチ
処理を開始したページから以降の新しい記事に向かって、自動的にページを開き記事の投稿形式の情報を収集します。
▪ブログ開始時の記事から処理を開始し、最近の記事までを「Snap」するのが通常の使い方ですが、特定のページから「Snap」を開始する事も可能です。
▪ブログの一部の期間を「Snap」すれば、その情報は、その期間の公開状態を復元する事に使えます。
▪「Snap」の情報は、ローカルストレージに即時に書き込まれます。 この時、以前に「Snap」した同じ記事のデータがある場合は、それを上書き更新します。 従って、ある時期以降の期間だけを追加で「Snap」する操作ができます。
▪ただし、「Snap」する範囲に「一括下書き化」をした記事群がある場合は、そこを「Snap」してしまうと、その「Snap」データでは「一括下書き化」の部分を復元できなくなります。(「一括下書き化」する以前の「Snap」データをファイル化していれば、そのデータで復元が出来ます)
▪処理の途中で停止したい場合は、非常に高速でページが送られるので、コンソール上にポインタを乗せる事で停止する様にしています。 下の様に暗い色に変化した後、次のページを開いた所で停止します。
❷ 収集してストレージに記録したデータを「初期化」します。
下段の ➎ に、収集した記事件数が表示されます。 同じ期間を複数回「Snap」しても、以前の「Snap」で収集したデータを上書きするだけで、件数は増えません。
「初期化」ボタン ❷ は、最初から「Snap」をしなおす際に使います。 収集記事数の一致を確認する時や、特定期間の「Snap」データを取得する時に必要です。
❸ ローカルストレージの「Snap」データを、ダウンロードフォルダーに保存します。 ファイル名は「blogDB(n).json」という連番形式で保存されます。
❹ ❸でファイル保存した「Snap」データを、ファイルを読み込んで確認する事ができます。 ファイルの読込みを行うと、それまでのローカルストレージの内容は失われ、ファイル上の「Snap」データが書き込まれます。(ストレージの上書き)
▪PCのローカルストレージは基本的には勝手に消えないので、ファイル化・再読込みは不要ですが、長い期間でのストレージのリセット、他のツールによるリセットが有り得るので、実運用ではファイル化して保存する事が必須で重要です。
▪「一括下書き化」の処理をした後に「Snap」データの収集を行うと、「下書き化」した記事を「下書き」記事として復元する「Snap」データになります。 そのデ-タでは、最初に「下書き化」した記事の公開処理は出来ない事に注意が要ります。
何度かに分けて「Snap」データ収集と「一括下書き化」の処理を繰り返した場合は、話が大変にややこしくなり、いつの時期にどの範囲を「Snap」したデータかを区別して、「Every Page Opener 💢」で利用する必要があります。
➎「Snap」処理で収集した記事の投稿形式のデータが、逐次表示されます。 この表示は、ローカルストレージが保持しているデータ内容の表示です。 ❷ の「初期化」を
押さない限り、最後に「Snap」の処理を行った時の情報が保持されます。
「Every Page」シリーズ利用上の注意
「Every Page」シリーズは、どれも「非常駐型」のツールです。 処理画面で同時に起動する他のスクリプトツールを停止し、拡張機能は「Tampermonkey」に限るのがベターです。 要するに処理作業のみ行う様にすれば、一番良い結果になります。
また、「Every Page Closer 💢」「Every Page Opener 💢」は、ブログ編集画面の動作を本来と異なったものに変えるので、このシリーズの目的とする処理が終了したら、忘れずに「Tampermonkey」上でツールを「OFF」にしてください。 また当然ですが、「Every Page」シリーズのツールどうしも、同時に「ON」にする事は適切ではありません。
「Every Page Snap 💢」を利用するには
このツールは Chrome / Edge / Firefox版の拡張機能「Tampermonkey」上で動作します。 以下に、このツールの導入手順を簡単に説明します。
❶「Tampermonkey」を導入します
◎ 使用しているブラウザに拡張機能「Tampermonkey」を導入する事が必要です。
既に「Tampermonkey」を導入している場合は、この手順 ❶ は不要です。
拡張機能の導入については、以下のページに簡単な説明があるので参照ください。
❷「Tampermonkey」にスクリプトを登録します
◎「Tampermonkey」の「+」マークの「新規スクリプト」タブを開きます。
◎「新規スクリプト」には、最初からテンプレートが記入されています。 これは全て削除して、完全に空白の編集枠に 下のコードをコピー&ペーストします。
〔コピー方法〕 軽量シンプルなツール「PreBox Button 」を使うと
コード枠内を「Ctrl+左Click」➔「Copy code 」を「左Click」
の操作で、掲載コードのコピーが可能になります。
◎ 最後に「ファイル」メニューの「保存」を押すと、ツールが使用可能になります。
〔 Every Page Snap 💢 〕 ver. 2.7
// ==UserScript== // @name Every Page Snap 💢 // @namespace http://tampermonkey.net/ // @version 2.7 // @description 「記事の編集・削除」ページで全記事の「公開設定」を記録する // @author Ameba Blog User // @match https://blog.ameba.jp/ucs/entry/srventrylist* // @run-at document-start // ==/UserScript== let retry=0; let interval=setInterval(wait_target, 1); function wait_target(){ retry++; if(retry>100){ // リトライ制限 100回 0.1secまで clearInterval(interval); } let target=document.documentElement; // 監視 target if(target){ clearInterval(interval); style_in(); }} function style_in(){ let style= '<style id="EPS">'+ '#globalHeader, #ucsHeader, #ucsMainLeft h1, .l-ucs-sidemenu-area, #ucsMainRight,'+ '#entryList .deleteCol, #entryList dl, .selection-bar, #footerAd, #globalFooter, '+ '.checkboxAllControl { display: none !important; } '+ '#ucsContent { width: 840px; background: #fff; box-shadow: 0 0 0 100vh #c5d8e1; '+ 'border-radius: 0; }'+ '#ucsContent::before { content: unset; } '+ '#ucsMain { margin-top: 15px; background: #fff; border-radius: 0; } '+ '#ucsMainLeft { width: 838px; padding: 0 15px; float: none; } '+ '#entryYear { margin: 0 5px; }'+ '#entryMonth { font-size: 14px; overflow: visible; margin-top: 3px }'+ '#entryMonth li, #entryMonth #nowMonthLi { font-weight: bold; padding: 2px 10px 0; }'+ '#entryMonth li a:visited { color: #3970B5; }'+ '#nowMonth { padding: 1px 6px 0; border: none; border-radius: 1px; '+ 'color: #000; box-shadow: 0 0 0 2px #0066cc; }'+ '#entryListEdit form { display: flex; flex-direction: column } #entrySort { order: -2 }'+ '.pagingArea { order: -1; padding: 4px; margin-bottom: -33px; background: #ddedf3 !important }'+ '.pagingArea a { border: 1px solid #888 } .pagingArea .active{ border: 2px solid #0066cc }'+ '.pagingArea a, .pagingArea .active, .pagingArea .disabled { font-size: 14px; line-height: 23px }'+ '#sorting { margin: 38px 0 4px; padding: 4px 10px; height: 110px; '+ 'font: 14px Meiryo; background: #ddedf3 }'+ '#sorting select, #sorting ul { display: none }'+ 'input { font-family: meiryo; font-size: 14px }'+ '#entryList li{ height: 24px; padding: 8px 5px 5px !important }'+ '#entryList li:hover{ background-color: #e2eef0 }'+ '#entryList li .entry-column { padding: 0; }'+ '#entryList .leftCol, #entryList .titleCol h2{ width: 560px; line-height: 24px; }'+ '#entryList .leftCol a { text-decoration: none !important; color: #3970b5 !important; }'+ '#entryList .rightCol { flex-direction: row; align-items: center; width: 260px !important; '+ 'min-width: unset; margin: 0; }'+ '#entryList .txtCol{ white-space: nowrap; font-size: 13px; margin-bottom: 2px; line-height: 16px }'+ '#entryList .txtCol .date{ display: inline-block; padding: 2px 4px 0; font-size: 13px; color: #000; }'+ '#entryList .status-text { display: inline-block; font-weight: normal; color: #000; '+ 'width: 64px; padding: 2px 4px 0; overflow: hidden; margin-bottom: -4px; }'+ '#entryList ul input[value="1"] + input + li .txtCol span:first-child{ display: inline-block;'+ 'text-indent: 12px; color: #fff; background: #2196f3 !important }'+ '#entryList ul input[value="2"] + input + li .txtCol span:first-child{ display: inline-block;'+ 'text-indent: 2px; color: #fff; background: #009688 !important }'+ '#div0 { color: #333; margin: 10px -10px 0 15px; }'+ '#div1 { color: #000; margin: 8px 15px; border: 1px solid #888; background: #fafcfd; }'+ '#list_snap { padding: 2px 0 0; margin: 7px 40px 7px 0; width: 210px; }'+ '#reset { padding: 2px 0 0; margin-right: 20px; width: 60px; }'+ '#export { padding: 2px 0 0; margin: 7px 10px 7px 0; width: 150px; }'+ '#import_sw { padding: 2px 0 0; margin: 7px 10px 7px 0; width: 115px; }'+ '#import_result { display: inline-flex; padding: 2px 0 0; margin: 7px 0; width: 160px; '+ 'overflow: hidden; white-space: nowrap; }'+ '#import { display: none; }'+ '#snap_result { display: inline-block; margin: 6px 12px 4px; }'+ '</style>'; if(!document.querySelector('#EPS')){ document.documentElement.insertAdjacentHTML('beforeend', style); } } // style_in() window.addEventListener('load', function(){ let drive_mode; let blogDB={}; //「アメンバー公開」の記事IDリスト let entry_id; let entry_id_DB; let publish_f; let pub_all; let pub_dra; let pub_ame; let ua=0; let agent=window.navigator.userAgent.toLowerCase(); if(agent.indexOf('firefox') > -1){ ua=1; } // Firefoxの場合のフラグ let read_json=localStorage.getItem('blogDB_back'); // ローカルストレージ 保存名 blogDB=JSON.parse(read_json); if(blogDB==null){ blogDB=[['00000000000', 's']]; } drive_mode=blogDB[0][1]; // 起動時に動作フラグを取得 if(drive_mode==0){ drive_mode='s'; } // 旧ファイルの救済 reg_set(); function reg_set(){ let k; entry_id_DB=[]; // リセット pub_all=0; pub_dra=0; pub_ame=0; for(k=0; k<blogDB.length; k++){ entry_id_DB[k]=blogDB[k][0]; // ID検索用の配列を作成 if(blogDB[k][1]=='0'){ pub_all +=1; continue; } if(blogDB[k][1]=='1'){ pub_dra +=1; continue; } if(blogDB[k][1]=='2'){ pub_ame +=1; continue; }}} control_pannel(drive_mode); function control_pannel(d){ let box=document.querySelector('#sorting'); if(box){ let insert_div= '<div id="div0">'+ '<input id="list_snap" type="submit">'+ '<input id="reset" type="submit" value="初期化">'+ '<input id="export" type="submit" value="SNAPをファイル保存">'+ '<input id="import_sw" type="submit" value="ファイル読込み">'+ '<span id="import_result"></span>'+ '<input id="import" type="file">'+ '</div>'+ '<div id="div1">'+ '<span id="snap_result"></span>'+ '</div>'; if(!box.querySelector('#div0')){ box.insertAdjacentHTML('beforeend', insert_div); } let button1=box.querySelector('#list_snap'); let button2=box.querySelector('#reset'); let button3=box.querySelector('#export'); let button4=box.querySelector('#import_sw'); let span5=box.querySelector('#import_result'); let input6=box.querySelector('#import'); let span7=box.querySelector('#snap_result'); if(d=='s'){ button1.value='公開設定のSNAP開始 ▶'; button1.onclick=function(e){ e.preventDefault(); start(); } function start(){ let conf_str=[' 🔴 このページ以降の記事に関して 「公開設定」を記録します', '\n 連続動作はコントロール部にマウスに乗せると停止します'].join(' '); let ok=confirm(conf_str); if(ok){ blogDB[0][1]='c'; // 連続動作フラグをセット let write_json=JSON.stringify(blogDB); localStorage.setItem('blogDB_back', write_json); // ローカルストレージ 保存 next(); }}} else if(d=='c'){ // 「c」は連続動作 button2.style.display='none'; // 動作モードが「c」の場合は非表示 button3.style.display='none'; // 動作モードが「c」の場合は非表示 button4.style.display='none'; // 動作モードが「c」の場合は非表示 span5.style.display='none'; // 動作モードが「c」の場合は非表示 button1.value='SNAPを停止 ❚❚'; button1.style.pointerEvents='none'; button1.style.width='760px'; box.addEventListener('mouseover', function(e){ e.preventDefault(); box.style.background='#96b6d2'; stop(); }, false); function stop(){ blogDB[0][1]='s'; // 連続動作フラグをリセット let write_json=JSON.stringify(blogDB); localStorage.setItem('blogDB_back', write_json); } // ローカルストレージ 保存 setTimeout(next, 400); } // 連続実行のぺージ遷移のタイミング 0.4sec ⭕ else if(d=='e'){ // 「e」は終了 button1.value='🔵 SNAPが終了しました'; button1.style.pointerEvents='none'; box.addEventListener('mouseover', function(e){ e.preventDefault(); box.style.background='#ddedf3'; }, false); } if(d=='s' || d=='e'){ button2.onclick=function(e){ e.preventDefault(); blogDB=[['00000000000', 's']]; let write_json=JSON.stringify(blogDB); localStorage.setItem('blogDB_back', write_json); // ローカルストレージ保存 snap_disp(); button2.value='〔 〕'; span5.textContent=''; input6.value=''; } button3.onclick=function(e){ e.preventDefault(); let write_json=JSON.stringify(blogDB); let blob=new Blob([write_json], {type: 'application/json'}); let a_elem=document.createElement('a'); a_elem.href=URL.createObjectURL(blob); a_elem.download='blogDB.json'; // 保存ファイル名 if(ua==1){ a_elem.target='_blank'; document.body.appendChild(a_elem); } a_elem.click(); if(ua==1){ document.body.removeChild(a_elem); } URL.revokeObjectURL(a_elem.href); } button4.onclick=function(e){ e.preventDefault(); input6.click(); } input6.addEventListener("change", function(){ if(!(input6.value)) return; // ファイルが選択されない場合 let file_list=input6.files; if(!file_list) return; // ファイルリストが選択されない場合 let file=file_list[0]; if(!file) return; // ファイルが無い場合 let file_reader=new FileReader(); file_reader.readAsText(file); file_reader.onload=function(){ if(file_reader.result.slice(0, 15)=='[["00000000000"'){ // blogDB.jsonの確認 let data_in=JSON.parse(file_reader.result); blogDB=data_in; // 読込み上書き処理 let write_json=JSON.stringify(blogDB); localStorage.setItem('blogDB_back', write_json); // ローカルストレージ 保存 button2.value='初期化'; // 初期化後なら読み込んだ事を示す snap_disp(); span5.textContent=file.name; } else{ alert(" ⛔ 不適合なファイルです blogDB(n).json ファイルを選択してください"); }} }); } snap_disp(); } // if(box) } // control_pannel() function snap_disp(){ reg_set(); let span7=document.querySelector('#snap_result'); span7.innerHTML=' 記録件数:<b>' + (blogDB.length -1) + '</b> 全員に公開:<b>' + pub_all + '</b> アメンバー限定公開:<b>' + pub_ame + '</b> 下書き:<b>' + pub_dra; +'</b>'; } function next(){ let win_url; let current; let pageid; let next_url; let pager; let end; entry_id=document.querySelectorAll('input[name="entry_id"]'); if(entry_id.length >0){ snap(); } // 投稿記事がある場合SNAPを実行 無ければスルーする win_url=window.location.search.substring(1,window.location.search.length); current=win_url.slice(-6); if(!current){ current=make_curr(); } if(win_url.indexOf('pageID') ==-1){ // pageIDが無い 月のトップページの場合 pager=document.querySelector('.pagingArea'); if(pager){ // ページャーが有りその末尾でなければ同月次ページへ next_url=['https://blog.ameba.jp/ucs/entry/srventrylist.do?', 'pageID=2&entry_ym=' + current].join(''); window.open( next_url, '_self'); } else{ // ページャーが無ければ次月トップページへ current=make_next(current); if(current!=0){ // 現在を越えないなら次月へ next_url=['https://blog.ameba.jp/ucs/entry/srventrylist.do?', 'entry_ym=' + current].join(''); window.open( next_url, '_self'); } else{ // 現在を越えたら0が戻り停止 when_edge(); }}} else{ // pageIDを含み 月のトップページでない場合 end=document.querySelector('.pagingArea .disabled.next'); if(!end){ // ページャーの末尾でなければ同月次ページへ pageid=parseInt(win_url.slice(7).slice(0, -16), 10) +1; next_url=['https://blog.ameba.jp/ucs/entry/srventrylist.do?', 'pageID=' + pageid + '&entry_ym=' + current].join(''); window.open( next_url, '_self'); } else{ // ページャーの末尾なら次月トップページへ current=make_next(current); if(current!=0){ // 現在を越えないなら次月へ next_url=['https://blog.ameba.jp/ucs/entry/srventrylist.do?', 'entry_ym=' + current].join(''); window.open( next_url, '_self'); } else{ // 現在を越えたら0が戻り停止 when_edge(); }}} function make_next(curr){ let ym; let y; let m; ym=parseInt(curr, 10); // 10進数値化 y=Math.floor(ym/100); // 年は100で割った商 m=ym % 100; // 月は100で割った余り if(m !=12){ ym=100*y + m +1; } else{ ym=100*y + 101; } let now=new Date(); if(ym > 100*now.getFullYear() + now.getMonth() +1){ return 0; } // 現在の月を越える場合は0を返す else{ return ym; }} // 次年月の数値を返す function make_curr(){ let now=new Date(); return 100*now.getFullYear() + now.getMonth() +1 } function when_edge(){ blogDB[0][1]='s'; // 連続動作フラグをリセット let write_json=JSON.stringify(blogDB); localStorage.setItem('blogDB_back', write_json); // ローカルストレージ保存 document.querySelector('#div0').remove(); document.querySelector('#div1').remove(); control_pannel('e'); } // SNAP終了時の表示をさせる } // next() function snap(){ // ページ内の「公開設定」をSNAPする let k; entry_id=document.querySelectorAll('input[name="entry_id"]'); publish_f=document.querySelectorAll('input[name="publish_flg"]'); for(k=0; k< entry_id.length; k++){ let index=entry_id_DB.indexOf(entry_id[k].value); if(index==-1){ // IDがblogDBに記録されていない場合 blogDB.push([entry_id[k].value, publish_f[k].value]); } // 公開設定の登録を追加 else{ // IDがblogDBに記録されていた場合 blogDB[index]=[entry_id[k].value, publish_f[k].value]; }} // 公開設定の登録を更新 setTimeout(write_local, 10); function write_local(){ let write_json=JSON.stringify(blogDB); localStorage.setItem('blogDB_back', write_json); }} // ローカルストレージ保存 });
〔追記〕2023.02.28
アメーバ管理ヘッダーの変更に対応して、スタイルコードの一部を更新しました。
上記コードは Every Page Snap ver. 2.5 ➔ ver. 2.6 としています。
〔追記〕 2020.04.30
コードをブラシュアップして更新しました。 バージョンは変更していません。
▪コンソール要素の生成コードを全面的に更新。
▪「初期化」の後に同ファイルを重ねて読込むと、無反応に見える問題を改善。
▪「ファイル読込み」のボタンデザインを変更。
〔追記〕 2023.06.10
Chromeにおける「overflow: overlay」の無効化に即し、上記コードの該当箇所を修正しました。 これにより、ver.2.6 ➔ ver.2.7 に更新しています。
「Every Page Snap 💢」最新版について
旧いバージョンの JavaScriptツールは、アメーバのページ構成の変更で動作しない場合があり、導入する場合は最新バージョンをお勧めします。
●「Every Page Snap 💢」の最新バージョンへのリンクは、以下のページのリンクリストから探せます。