はじめに
Disney+上の字幕をダウンロードするために「Disney+ Subtitles Downloader」というTampermonkeyスクリプトを使おうとしましたが、
最近のディズニープラスの仕様変更により、そのままでは動作しなくなっていました。
本記事では、スクリプトを自力で修正して正常に動かせるようにした方法をまとめます。
問題点
スクリプトを導入しても、
- 字幕横にダウンロードボタンが出ない
- ボタンは出ても、押してもファイルがダウンロードされない
という問題が発生しました。
ブラウザの開発者ツール(Console)を確認すると、
以下のエラーが出ていました。
Uncaught TypeError: Cannot read properties of undefined (reading 'innerHTML')
原因は、
- ディズニープラス側のHTML構造が変わったため
- スクリプト内で「存在しないノード」から
innerHTML
を読もうとしてエラーになっていた
というものでした。
修正内容
主に以下の2ヶ所を修正しました。
① イベント登録対象の取得方法を変更
Before(元のコード)
// 「親要素(字幕リスト)」の全部の子ノード(子divや空白も含む)を取ってきている
// 空白を拾って、それを対象にonclick判別しようとしてエラーになる可能性有
picker[0].childNodes.forEach(function(child) {
var element = child.childNodes[0];
if (child.onclick == null) {
child.onclick = selectsub;
}
});
After(修正後のコード)
// ターゲットを「ラベルだけ」に絞るように変更
var labels = picker[0].querySelectorAll('label.picker-item');
labels.forEach(function(label) {
if (label.parentElement && label.parentElement.onclick == null) {
label.parentElement.onclick = selectsub;
}
});
解説
childNodes
を使うと、空白ノード(改行やスペース)まで拾ってしまうリスクがありました。
そのため、querySelectorAll('label.picker-item')
を使い、必要なラベル要素だけを直接取得するように変更しました。
② 字幕言語の取得方法を変更
Before(元のコード)
var lang = this.childNodes[0].childNodes[1].innerHTML;
After(修正後のコード)
var label = this.querySelector('label.picker-item');
var lang = label ? label.innerText : "";
解説
元のコードでは、固定的なchildNodes
の階層に依存して字幕名を取得していましたが、
ページ構造の変更により対象が存在しなくなっていました。
querySelector
を使い、ラベル要素を安全に取得し、そのinnerTextを読むように変更しました。
結果
上記の2点を修正したところ、
ようになりました!
まとめ
今回、ブラウザの開発者ツール(Console)を使ってエラーを確認し、
実際にHTML構造を目で見ながらコードを修正することで、ディズニープラスの仕様変更に対応できました。
今回学んだポイントは以下の通りです。
- childNodesは空白ノードも拾うため、注意が必要
- 実際のHTML構造に合わせて、querySelectorで確実にターゲットを指定するのが安全
- デバッグは「中身を可視化して、事実ベースで判断する」のが大事
同じようにDisney+ Subtitles Downloaderが動かなくて困っている方の参考になれば嬉しいです!