初歩からの無職

放送大学の授業動画の字幕変換ツールを作った

  • JavaScript
  • Node.js

この記事は放送大学 Advent Calendar 2018の3日目の記事です。

前回の記事で放送大学の講義動画をダウンロードしましたが、字幕対応の放送授業についてはせっかくなので字幕も欲しいところです。

放送大学の字幕はMicrosoftのSAMI形式で実装されています。URLはhttps://vod.ouj.ac.jp/v1/tenants/1/vod-contents/{動画ID}/samiです(ログインしていないと表示されません)。ゴリゴリにスタイルをマークアップしていくのが特徴で2000年代のマイクロソフト感を感じさせてくれます。SAMI形式をサポートしている動画プレイヤーは少なく、ffmpegで字幕を直接焼いてみても表示されません。

より一般的なSRT形式の字幕に変換する必要がありますが、SAMIは前述のように独自にゴリゴリマークアップがあるので変換がすべてうまくいくとは限りません。ローカルで実行できる変換ツールをいくつか試しましたが、うまく変換できるものはなかったので自分で作りました。

mtane0412/ouj-smi2srt

Node.js製のCLIツールです。SRTの書式はシンプルで以下のように字幕番号とduration、字幕テキストからなるブロックを延々と追加していく感じです。

1
00:00:00,000 ‒‒> 00:00:01,000
hoge

2
00:00:01,000 ‒‒> 00:00:02,000
fuga

なので放送大学のSAMIファイルから正規表現で必要な情報だけを抜き取ってこの形式で変換しています。変換時に苦労したのは一つは時間の形式が一般的ではない形式だったのでちょっと考えまた。これはQiitaに書いています。

【JavaScript】ミリ秒をhh:mm:ss,sss形式に変換する - Qiita

もう一つはSAMIの方は開始時刻のみを指定すればよいのですが、SRTは開始時間と終了時間を指定する必要があります。行ごとにループを回す方法だとできないのですが、かといって2回ループ回すのもなんかダサい気がするのでどうすればいいんだろうと思ったのですが、reduceを使っていい感じにできました。

...
const blocks = smi.split('\n');
let srt = new String;
blocks.reduce((previous, current, index) => {
  let [startTime, preSub] = previous.split('::');
  let endTime = current.split('::')[0];
  if (endTime) {
    srt += `${index}\n${startTime} --> ${endTime}\n${preSub.replace(/:return:/g, '\n')}\n`;
  }
  return current;
})
...

実際に変換してみます。

$ npm install -g ouj-smi2srt
$ smi2srt subtitles.smi subtitles.srt
Converted: subtitles.smi --> subtitles.srt

変換したSRTファイルをVLCメディアプレイヤーなどの字幕対応プレイヤーで指定すると字幕が表示されます。ここではさらにffmpegで動画に字幕を直接焼いてみます。ffmpegで字幕を追加するにはlibassが必要なのでffmpegと一緒にインストールする必要があります。

$ brew install ffmpeg --with-libass

-vfオプションで字幕とスタイルを指定します。

ffmpeg -i input.mp4 -vf \"subtitles=subtitles.srt:force_style='FontName=ヒラギノ角ゴシック W6, BorderStyle=3\" output.mp4

再生してみます。

ouj-subtitles 認知心理学('13) 第01回 認知心理学のプロフィールより

字幕がつきました。字幕のスタイリングはffmpegの方で指定できます。

授業動画のダウンロードから字幕を焼くところまでやりましたが、受講している単位数×15回を手動でやるのはなかなか骨が折れるのでここらへんをある程度自動化するツールを現在作っています。間に合えばアドベントカレンダーネタとして投稿します。