YouTubeの字幕を動画枠外に表示する

YouTubeの字幕を枠外で見たいという話題があって、その時は Android の ccTube を使えばいいなー、と思ったのですが、もしかして、JavaScriptから字幕のデータが取得できればWebブラウザで表示できるかもしれないと思い試してみました。


いろいろ試行錯誤した結果…

いい感じにできました!

現在の字幕が赤字で表示され、自動スクロールします。また、字幕をクリックすると、動画がその時間から再生されます。

こちらの URL で公開しています。

https://shikarunochi.github.io/YouTubeClosedCaption/

引数に YouTube Video Id を付けると、自動的に入力枠に反映されます。

https://shikarunochi.github.io/YouTubeClosedCaption/index.html?eP9y_7it3ZM

自動再生はうまく動かせなかったので今のところは入力枠への反映のみです。

HTMLソースを見たい場合はこちらでどうぞ。HTMLファイル1つだけです。

https://github.com/shikarunochi/YouTubeClosedCaption


以下、(自分のメモを兼ねての)プログラム説明です。

YouTube Playerのコントロール

iframe に表示した YouTube のコントロールを行うAPIがありました。

iframe 組み込みの YouTube Player API リファレンス
https://developers.google.com/youtube/iframe_api_reference?hl=ja

現時点の再生秒数取得(getCurrentTime)と、指定秒数位置を再生(seekTo)、できます!


字幕データの取得

動画が持っている字幕の言語種類を取得して、言語指定して字幕獲得するようです。

Youtube(ユーチューブ)の字幕を抜き出す方法
https://foxorz.com/youtube-transration-caption/

今回は、字幕言語種類取得は特に行わずに、英語「en」していで取得してます。なので、動画が英語字幕を持ってない場合、字幕は表示されないです。注意なのですが、自動生成(auto-generated)の字幕は、取得対象にはならないです。

こういう感じで取得します。

https://video.google.com/timedtext?hl=en&lang=en&name=&v=eP9y_7it3ZM

字幕データはXMLで獲得されます。

表示開始時間、表示する時間、表示テキストが順に入っています。


XMLのパース

XMLのパースはこれだけでできますね。簡単。

window.DOMParser().parseFromString(str, "text/xml")

パースしたXMLから、”text”要素を取得。”start”プロパティと、textタグ内のHTML(=字幕テキスト)を連想配列の配列で保持します。

const textList = data.querySelectorAll("text")
textList.forEach(element => {
   srtList.push({ time: 0 + element.getAttribute("start"), text: element.innerHTML }); //数値として
});

作成されるデータは こういう感じですね。表示する時間情報は、今回は特に使わずです。

[ {time: "012.38", text: "Welcome back to the 8-Bit guy."}, 
  {time: "013.71", text: "So, when you watch documentaries about early↵computer innovations, particularly the late"}
  ,……]

HTMLの生成

var index = 0;
srtList.forEach(srtInfo => {
  html += `
  <p id="${index}" onclick="jumpTo(${index});">
  ${srtInfo["text"]}
  </p>
  `
  index++;
});

id で index を振って、字幕データを出力してます。クリック時に index を引数に jumpTo 関数を呼ぶようにしました。


スクロール表示

字幕データは画面内でスクロール表示するのに、<div> で overflow:auto を使っています。

最初 iframe で実装していたのですが、 iframe 内部/外部 とのやりとりが同じドメインからの HTMLでないと不可能でした。iframe 内のHTMLを動的生成した場合は元ドメインが無いので、どうにもならなそうだったので別の方法になりました。


スクロール位置の変更

0.5秒に1回、再生中の動画秒数の位置に字幕を合わせるようにしています。 var updatePosition = function () の処理です。

scrollTop に値を代入すると、対象要素のスクロールができます。字幕表示要素の表示Y座標からうまいこと計算して、ちょうど現在の字幕が真ん中あたりになるようにしています。ついでに、色も変えてます。function selectSrt(index)  の処理です。


字幕クリックで、該当位置に動画データを変更

クリックされた index に該当する字幕の時間を取得して、その時間から動画を再生しています。

player.seekTo(srtList[index]["time"]);

画面サイズに応じて字幕表示エリア調整

こちらを参考にしました。

【JavaScript】ウィンドウサイズ変更時に要素のサイズを変更する
https://max999blog.com/javascript-resize-element-size-with-window-size/


HTMLの引数でVideo Id指定

location.href に URL が入ってきますので、”?” の後に続く値を取得しています。

var param = location.href.split("?")[1];

なかなか盛りだくさんでしたが、HTML + JavaScript だけでここまでできるんですねー。