七色七音

「いつも」と「今日」は別の日だから……

youtube-dlで動画をDLしてffmpegで字幕を付ける方法

(多分YouTube=Googleに嫌われるような事だからアフィ貼ってないこっちに書くとする)

お行儀悪い事をしてます。先の記事で書いた通り僕は消えがちなネット上での知見を確実に手元に残すためにこういう事をやってます。(これは私的複製の範囲内だと認識している)
それ以外の違法行為はやめましょう。違法DL/UPは最底辺の人未満がやる事です。(違法な物と知りながらDLする行為も同様です)

という訳で、今回の目標: youtube-dlで動画をDL、高画質を保ったままffmpegで字幕を付ける(字幕は再生ソフト側でオンオフ出来る奴、つまり焼き込みではない。その方が都合がよい)

なぜyoutube-dl+ffmpegなのかと言われたら、単にCLIで済ませたいからです。CLIという語の意味が分からない人はこの記事を読まない方が良いと思います。

mkvが使える場合

youtube-dlを使用する際に特にフォーマットの指定などをしないとmkvで手に入る事がある。もしそうならラッキー(mkvの方が字幕が扱いやすい、mkvに非対応の端末で使いたいなら後述のmp4をどうぞ)

まずは動画をDLする。--all-subsを指定して字幕もセットで入手する

youtube-dl --all-subs <URL>

恐らく字幕はvttなのでassに変換する(やらなくても良いかも)

ffmpeg -i '字幕.vtt' '字幕.ass'

ffmpegで字幕付きにする

ffmpeg -i '動画.mkv' -i '字幕.ass' -map 0 -map 1 -c copy -c:s copy out.mkv

字幕が複数あるなら-iと-mapを増やしていけば良い

ffmpeg -i '動画.mkv' -i '字幕1.ass' -i '字幕2.ass' -map 0 -map 1 -map 2 -c copy -c:s copy out.mkv

字幕の言語指定

応用編、上記の方法だと字幕のStreamが「und」になり複数字幕ある場合に言語が分かりづらい
この場合は-metadataを使って言語指定をしてやる

ffmpeg -i '動画.mkv' -i '字幕.ass' -map 0 -map 1 -c copy -c:s copy -metadata:s:2 language=jpn out.mkv

字幕が複数の場合はmetadataを増やしていく

ffmpeg -i '動画.mkv' -i '字幕1.ass' -i '字幕2.ass' -map 0 -map 1 -map 2 -c copy -c:s copy -metadata:s:2 language=jpn -metadata:s:3 language=eng out.mkv

-metadataで指定している:s:2の部分だが、これはストリーム番号の指定、左側はsで良い。
右側の2や3の部分はストリーム番号

これはffmpeg -i '動画.mkv'などとするとStream #0:0とか表示されている右側の数値(すなわち0:1なら1、0:2なら2)
例えば元動画(ストリーム0:動画、ストリーム1:音声)に字幕を2つ付け足したなら、1つめの字幕がストリーム2、2つめの字幕がストリーム3となる。

通常は0が動画、1が音声だが、場合によっては2に副音声、みたいになっている場合もあるもあるので要確認

mp4で欲しい場合

mkvに対応してなさそうな端末で使いたい、みたいな時はmp4にする必要がある。

-fでmp4を指定してmp4でDLする。

youtube-dl -f mp4 --all-subs <URL>

mkvの時同様にvttをassに変換

ffmpeg -i '字幕.vtt' '字幕.ass'

mkvとだいたい同じだが、mp4の場合は字幕形式がmov_textになる。

ffmpeg -i '動画.mp4' -i '字幕.ass' -map 0 -map 1 -c copy -c:s mov_text out.mp4

後はmkvと同様、複数字幕もmetadata指定も同じように出来る。

余談

Windowsに付属してる動画プレイヤーだとmp4の字幕を認識せず(mkvなら認識した)、VLCやMPCなら字幕認識した

だがな、残念な事にmkvだとエクスプローラーでサムネイルが表示されねぇんだわ
mp4だと標準のプレイヤーで字幕を認識せず、mkvだとサムネイルが表示されない。帯に短し襷に長しとはこの事か……

mapの指定

-mapで指定した0とか1とか、これは短縮形で、実際には-map 入力:ストリーム:出力:ストリームという指定になる。

例えば動画1(入力0)から映像(ストリーム0)を、動画2(入力1)から音声(ストリーム1)を抽出して1まとめにするにはこんな感じ

ffmpeg -i '動画1.mp4' -i '動画2.mp4' -map 0:0:0:0 map 1:1:0:1 out.mp4

入力は先に指定したものから順に0,1,2,3...となる(なので最初に指定した動画1が0で、次に指定した動画2が1)

ストリームは入力元動画によって違うが、大抵の場合映像が0で音声が1。マルチリンガルな動画だと英語が1で日本語が2で……となっているかもしれない。これはffmpeg -i '動画.mp4'などとすれば確かめられる。