概要niconico(旧・ニコニコ動画)に投稿されるボーカロイドの新着動画1日分(52曲~260曲)を1曲20秒のダイジェストにして全曲を1個の動画にまとめて投稿するのをVPS下で全自動に行います。【マイリストを削除しました】きっかけツイッターのTLで紹介していたブログを読んだのがきっかけです。ブログ主が当時の記事を消したので背景が分からなくなってしまいましたね。ブロマガ当時の関連したブログがありましたので雰囲気を掴んで頂けると幸いです。週刊歌らん作者のブログべ、別に巨乳が気になってクリックした訳じゃないから!(紹介してくださってありがとうございます)★ブルマと巨乳とランキング製作メモまあ、こんな雰囲気の中、ボーカロイド楽曲の新着を1日毎にまとめた紹介動画を全自動で毎日投稿するのも力試しに面白そうだと思って作り始めました。軌道に乗るまでに2ヶ月もかかるとは思ってなかったですよ、ハイ。当時からこれまでのツイートを辿ることができます。Tabtter起動の契機どこのご家庭にもあるcrontabを使い、毎朝7時に開始します。??????????←内緒ただしcronはアカウントのhomeをカレントディレクトリにして起動するので都合が悪いです。こんなシェルスクリプトを中継してディレクトリを切り替えています。$SCRIPT_FILEDB更新チェック対象となる新着動画は運営が用意したスナップショット検索APIを使い検索します。このスナップショットは毎日朝5時のものですが概ね朝7時から公開してくれます。「概ね」というのはログから分かった実績で、APIでは正式な更新時刻を明記していません。その代わり「スナップショットの日時が分かるAPI」で日時を貰い、昨日の投稿を把握できる状態になるまで待機します。ガイド検索APIでタグ「VOCALOID」で昨日投稿した動画IDをごっそりいただきます。タグ検索でヒットする動画にボーカロイドの新着でないのが多数含まれていますので、除外する既知のタグにハイフン「-」を前置して検索キーに追加します。投稿開始から3ヶ月で除外タグは50個を越えました。それとAPIを1回呼ぶと最多で100個までの動画IDを取得できます。101個目以降は100個単位で取得できますけれど最初の100個と一部重複することがあるので重複分を除外して全ての動画IDを取得するまで検索を繰り返します。最初の100個を取得した際に総数を取得できています。これがなければ正確な全動画の一覧を取得できないところでした。検索した動画には、まだ除外するものが含まれています。無関係の動画にタグVOCALOIDを付ける投稿者が投稿した動画や、除外タグがないけれどタイトルで除外できる動画です。それとスナップショットにあってもダイジェスト作成時に削除や非公開になっている動画も除外します。大げさなものではなくて、mp4エンコード時のCRF値を決定します。この後、個々の新着動画からダイジェストを作りますが、無圧縮で保存するとVPSの容量不足になってしまいます。借りてるVPSの容量は10GBですからピーク時で5GBを越えないようにしておきます。そこで暫定的に投稿動画より若干画質が良い程度にmp4でエンコードしておきます。投稿動画は100MBが上限で、ダイジェストの個数から再生時間が分かるのでビットレートを逆算、ざっくりとした圧縮の程度をCRF値で決めて、個々のダイジェスト(暫定のほう)は、それよりも2だけ小さい値で少し高画質な程度にエンコードします。投稿動画はデフォルト19で、これが1増える毎に映像のビットレートがだいたい1dB減ります。投稿動画をエンコードした際に100MBを越えていれば全自動でCRF値を1増やして再試行するので、不正確でも大丈夫、単に再エンコードの回数が減るのを期待している程度です。新着動画を処理ここから先は、新着動画1個ずつの処理となります。新着動画の個数は日によってバラつきがあり、今のところ52個~260個の範囲です。ダウンロード普通のダウンロードで動画を丸ごとぶっこ抜きます。抜いたファイルはIDがsmで始まる動画がMP4ファイルで、nmで始まるのがSWFファイルです。ダウンロード時の応答ヘッダにファイル名があるので推定は不要です。SWF→AVI変換niconicoに投稿されているSWFファイルはffmpegで扱えないCWS形式ですから同じSWFファイルのFWS形式に変換します。それから音声と映像の第1フレーム(1コマ目)を抜き出して静止画のAVIファイルを作ります。本来、SWFファイルはFlashPlayerの再生ファイルなので、スクリプト+映像+音声で構成されていて、その内、niconicoに投稿されている物は、大抵ニコニコムービーメーカーで作成されていて、映像+音声の紙芝居的な構造になっています。スクリプトの再生は諦めて、音声はそのまま、映像は止め絵にしました。ダイジェスト編集(映像)括弧内はffmpegのフィルタ名です。新着動画の1:00~1:20を切り出してフレーム合成をします。フレームはGIMPで作ったPNGファイルです。窓を透明(αチャネル)にしてありフィルタで新着動画の上からかぶせます(overlay)。新着動画の解像度は様々なので、縦横比を変えずに縮小、中心を合わせて配置します(scale)。黒の下地に幅と高さのどちらが大きいか自動判定して収めていますので小さい方は黒の下地が見えています(pad)。合成後の状態は新着ダイジェストをご覧の通りです。字幕で埋めます(drawtext)。タイトルの字幕をプロポーショナルスペーシングにできず等幅なので、新着動画のタイトル通りではハミ出ます。フォントは、おたもんさんの源暎ゴシックを使いました。読みやすいフォントです。可能な限り半角に変換して幅を縮めています。全角の記号も代用できそうな半角へ変換しています。当初、Windows下で色々とテストしていてperlのsystem関数を使いフィルターのdrawtextもそのままパラメタで渡していました。ところがdrawtextの文字列は先頭の空白や文字列中のダブルクォーテーションのエスケープがややこしくて、更にWindowsというかMS-DOSのパラメタの切り出しやエスケープもあって降参しました。オプションのfilter_complex_scriptにフィルターを書いたテキストファイルを指定して回避しました。フィルタのテキストファイルを別に用意するにしても、これらの編集をffmpegのコマンド一発でできる事に驚きました。稀にメモリ不足が発生するケースがありrc-lookaheadを減らすと改善するため30を指定するようにしました。それでもごく稀に再発するので失敗した際は5ずつ減らして再試行、5でもダメなら諦めるようにしてあります。ダイジェスト編集(音声)ffmepgでは音声を並行して切り出す事もできますというか普通は一緒に切り出されます。元動画の映像につられた位置と長さで切り出されてしまうことがあります。それを使うとダイジェストがピッタリ20秒にはならないケースがあり、映像の1フレーム分の増減があります。この1フレーム分の約0.03秒が100個集まると3秒のズレが生じます。新着ダイジェストでは再生中の新着動画へジャンプできる「本家」ボタン、次の新着を再生する「次」ボタンをニコスクリプトで設定する都合上、1秒以上のズレはボタンが使いにくいというかほとんどバグ。音声だけを20秒切り出してから映像と合成、ピッタリ20秒になるよう工夫しています。曲間でのノイズと違和感を抑制するため、フェードイン、フェードアウトをそれぞれ1秒入れてます(afade)。新着動画は音量調整がバラバラ(笑)なので、新着ダイジェストで統一感が出るようコンプレッサーを掛けています(compand)。ボーカロイドの動画はヘッドルームがない状態(0dBFS)で投稿される傾向にあり、-80dBFSから-12dBFSへ向かう緩やかなカーブを設定しました。コンプレッサーのカーブと言ってもffmpegのフィルターは折れ線の設定です。これで爆音の動画が新着してもピークが-12dBFSの落ち着いた新着ダイジェストになります。音声のエンコードはpcm_s16le、サンプリング周波数48kHz、チャネル数2(ステレオ)です。ダイジェスト編集(合成)20秒の映像と音声を貼り合わせます。二つのファイルを入力してmapオプションで映像と音声を並べます。エンコードしないで右から左へコピーして終わりです。全ダイジェストを連結、エンコードffmpegで入力ファイルを連結するのに、オプション-iをひたすら並べていくとWindows下で100個行く前に失敗するようになりました。そこで入力ファイル名を並べたテキストファイルを用意、その名前をオプションなしで指定、-filter_complexのconcatフィルターで連結しました。この方法で260個を連結できました。音声のエンコードはlibfdk_aac、プロファイルはHE-AAC(v1)、ビットレートは48kbpsです。このffmpegではプロファイルHE-AACv2も使えますけれど、試聴で音質が良かったv1を使います。前述しました通りniconico動画の投稿サイズ100MBを越えていればCRF値を1ずつ増加させてファイルサイズが小さくなるように調整して再試行します。「次」ボタンで次の新着へスキップさせた時に曲の頭へシークできるように20秒毎にキーフレームを入れてあります。新着ダイジェストを投稿後、私が視聴して除外する場合にニコスクリプトでスキップさせます。その元ネタとなる動画ID一覧と個々の再生/スキップの状態をテキストファイルに保存しておきます。投稿直後は全て再生の状態です。その後、私の視聴で除外が決まればその動画だけスキップに変更します。新着ダイジェストを投稿します。niconicoで動画を投稿した方ならご存知のアップロード、投稿設定を行います。サムネイルは0:00固定、そのほかの設定は新着ダイジェストをご覧の通りです。投稿者コメント(ニコスクリプト)再生中の本家(新着動画)へジャンプできる「本家」ボタン、次の新着へスキップできる「次」ボタンを設定します。前述のダイジェスト情報から取得した動画IDを使い本家ボタンから本家へジャンプできるようにしてあります。スクリプトの問い合わせがありましたので追記しました。1分20秒から動画sm12345678を紹介する例を載せます。同時刻に「本家」と「次」を設定すると上下が逆転するケースがありましたので、「次」を1秒遅らせて同時に消します。4行目の右端に半角の空白が1個あります。スクリプトの上限は1000行でしたので、1000行/動画÷5行/曲=200曲/動画まで設定し201曲目からはありません。1000行を越えて送信すると全行失敗しました。プログラムがサーバーへ送信するフォーマットは投稿者のブラウザ画面で設定するものと異なります。投稿した新着ダイジェストをこのページ冒頭で紹介したマイリストへ登録します。視聴して除外を決めた場合、自分宛に除外指定をツイートします。VPSはツイートを監視、除外指定を拾い、ダイジェスト情報から新着ダイジェストの投稿者コメントを生成して上書きします。例によってcrontabの出番です。監視間隔は1分毎です。その他に、除外タグ、除外ユーザも同様に自分宛にツイートすることでVPSが更新します。時々、除外指定をツイートしていますのでどんな感じか興味がありましたらご覧ください。いやあ、ツイッターって本当に便利ですね。それに比べると除外指定の操作も、仕組みを構築するのも両方とも省力化できました。なったこのページを書いている間に自動投稿された新着ダイジェストがこちらです。このページを動画にしました。2ヶ月のボーカロイド新着を眺めていると毎日100件程度の投稿があるので1ヶ月3000件のペースで投稿されているのが分かりました。こんなに投稿されているなら、わざわざランキングに上った曲を聞かなくても良いと思う。mojolicious。