HLSとは HLS(HTTP Live Streaming)とはAppleが開発したストリーミング動画配信プロトコルで大抵のブラウザが対応しています。一つの動画を複数のセグメントに分割し.ts
ファイル(セグメントファイル)として保存、そのリストを.m3u8
ファイル(インデックスファイル)に保存します。これらのファイルは通常のHTTP通信で静的配信されます。つまり、キャッシュされるということです。
準備
たまにCache-Control: no-store
になってたり、max-age
に極端に小さい値が設定されてたりするのでこの拡張機能でキャッシュ保存期間を伸ばします。
設定はこんな感じでOKでしょう。とにかくキャッシュする設定です。
1 2 3 4 5 6 Url Patterns: * Action: Modify Header Field Name: cache-control Header Field Value: max-age=1000000000 Comment: hoge Apply on: Response
キャッシュ容量変更 すべてキャッシュするようにしてもデフォルトのキャッシュ容量が1GB程度なので動画だとすぐにいっぱいになって古いのから消えます。そこでキャッシュ容量を拡張します。
FirefoxのURLバーからabout:config
にアクセスし、
browser.cache.disk.smart_size.enabled
を検索して、false
にします。
browser.cache.disk.capacity
を検索して、適当に100000000
(100GB)くらいにします。
Firefoxを再起動します。
普通に見る HLS配信されている動画をFirefoxで普通に見ます。倍速で最小化しててもOKです。
URLを調べる デベロッパーツールのネットワークタブで.m3u8
で検索してインデックスファイルを探し、そのURLを書き留めておきます。また、インデックスファイルに書いてあるセグメントファイルのURLも書き留めておきます。
インデックスファイルはこんな長いやつです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #EXTM3U #EXT-X-VERSION:3 #EXT-X-KEY:METHOD=AES-128,URI="https://manifest.example.net/license/v1/aes128/4394098882001",IV=0x2d21 #EXT-X-PLAYLIST-TYPE:VOD #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-TARGETDURATION:10 #EXTINF:10.010, https://video-cdn.example.com/media/v1/hls/v5/segment0.ts #EXTINF:10.010, https://video-cdn.example.com/media/v1/hls/v5/segment1.ts #EXTINF:10.010, https://video-cdn.example.com/media/v1/hls/v5/segment2.ts #EXTINF:10.010, https://video-cdn.example.com/media/v1/hls/v5/segment3.ts #EXTINF:10.010, https://video-cdn.example.com/media/v1/hls/v5/segment4.ts #EXTINF:10.010, https://video-cdn.example.com/media/v1/hls/v5/segment5.ts #EXTINF:10.010,
ここで、セグメントファイルがたくさん書かれていることが分かりますが、セグメントファイルが絶対URLの場合と相対URLの場合があります。絶対URLだと最後にffmpeg
で結合する時にアクセスしてしまうので、相対パスに書き換えておきましょう。
また、EXT-X-KEY:METHOD=AES-128
ではなんちゃって暗号化を指定しています。ここがhttps://
の場合や、この項目がない場合は何もしなくてOKですが、面倒な場合もあるのでその時はHLS
暗号化とかでググったり、がんばったりしてください。
インターネットを切る これからの作業にインターネット接続は不要なので切断します。
キャッシュを抜く https://github.com/shosatojp/ffcache
このプログラムを使ってFirefoxのキャッシュを抜き取ります。
Firefoxのキャッシュフォルダは/home/me/.cache/mozilla/firefox/hogehogehoge.default/cache2
です。
1 2 3 4 export FFCACHE=/home/me/.cache/mozilla/firefox/hogehogehoge.default/cache2 ffcache $FFCACHE https://video-cdn.example.com/media/v1/hls/v5/index.m3u8 index.m3u8 ffcache $FFCACHE https://video-cdn.example.com/media/v1/hls/v5/segment0.ts segment0.ts ...
こんな感じでダウンロードします。手作業では無理ゲーなのでシェルスクリプト書くなり、Python書くなりします。
結合する 最後にffmpeg
で結合します。
1 ffmpeg -allowed_extensions ALL -i index.m3u8 -c copy -o out.mp4
実装例
GithubActionsのArtifactsで配布されているPythonバインディングを利用して実装してみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import timeimport globfrom ffcache import FirefoxCacheimport reimport osimport argparseimport subprocessimport shutilparser = argparse.ArgumentParser() parser.add_argument('prefix' , type=str) parser.add_argument('--out' , '-o' , type=str, default='out.mp4' ) parser.add_argument('--cache' , '-c' , type=str, required=True ) args = parser.parse_args() cache = FirefoxCache(args.cache) outdir = f'cache-{time.time()} ' if not os.path.exists(outdir): os.mkdir(outdir) for e in cache.records: if re.match(args.prefix, e.key): print(e.key) _, file = os.path.split(e.key) e.save(os.path.join(outdir, file), True ) index = glob.glob(os.path.join(outdir, 'index*.m3u8' ))[0 ] subprocess.run(['ffmpeg' , '-allowed_extensions' , 'ALL' , '-i' , index, '-c' , 'copy' , args.out]) shutil.rmtree(outdir)