radiko.jp のタイムフリーの録音(保存、ダウンロード)ができないか調べてみました。
環境
- macOS Sierra バージョン10.12.4(16E195)
- swfextract - part of swftools 0.9.2
- ffmpeg version 3.3
Homebrew をインストールして、swftools と ffmpeg をインストールしておきました。
$ brew install swftools
$ brew install ffmpeg
radiko.jp の通信の内容
最初に radiko.jp の通信の内容を調べてみました。
大きな流れは認証してからタイムフリー聴取をするようです。
1. 認証
認証の流れは次の通りのようです。
- swf ファイルをダウンロードする
- ダウンロードした swf ファイルの中から画像ファイルを抽出する
- 認証 1(auth1_fms)にアクセスして、
X-Radiko-Authtoken{1}
,X-Radiko-KeyLength{2}
,X-Radiko-KeyOffset{3}
を取得する X-Radiko-KeyLength{2}
,X-Radiko-KeyOffset{3}
をもとに画像ファイルからX-Radiko-Partialkey{4}
を抽出する- 認証 2(auth2_fms)に
X-Radiko-Authtoken{1}
,X-Radiko-Partialkey{4}
を送信する
1.1. swf ファイルのダウンロード
curl 'http://radiko.jp/apps/js/flash/myplayer-release.swf' \
-XGET \
-H 'Referer: http://radiko.jp/' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30'
1.2. 認証 1
curl 'https://radiko.jp/v2/api/auth1_fms' \
-XPOST \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Referer: http://radiko.jp/' \
-H 'Pragma: no-cache' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30' \
-H 'X-Radiko-Device: pc' \
-H 'X-Radiko-App-Version: 4.0.0' \
-H 'X-Radiko-User: test-stream' \
-H 'X-Radiko-App: pc_ts' \
--data $'\r\n'
1.3. 認証 2
curl 'https://radiko.jp/v2/api/auth2_fms' \
-XPOST \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Referer: http://radiko.jp/' \
-H 'Pragma: no-cache' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30' \
-H 'X-Radiko-Device: pc' \
-H 'X-Radiko-Authtoken: mNzMUHWKtgzPYNPa0HiYyg' \
-H 'X-Radiko-App-Version: 4.0.0' \
-H 'X-Radiko-User: test-stream' \
-H 'X-Radiko-Partialkey: /wDrfge1MkkLHBUZ25GeAA==' \
-H 'X-Radiko-App: pc_ts' \
--data $'\r\n'
2. タイムフリー聴取
2.1. プレイリスト 1
curl 'https://radiko.jp/v2/api/ts/playlist.m3u8?station_id=LFR&l=15&ft=20170423010000&to=20170423030000' \
-XPOST \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Referer: http://radiko.jp/' \
-H 'Pragma: no-cache' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30' \
-H 'X-Radiko-Authtoken: mNzMUHWKtgzPYNPa0HiYyg' \
--data 'flash=1'
2.2. プレイリスト 2
curl 'https://radiko.jp/v2/api/ts/chunklist/TcAwn1TY.m3u8' \
-XGET \
-H 'Referer: http://radiko.jp/' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30'
実行
1. 認証
1.1. swf ファイルのダウンロード
-O
のオプションをつけて myplayer-release.swf
ファイルを保存しました。
$ curl 'http://radiko.jp/apps/js/flash/myplayer-release.swf' \
> -XGET \
> -H 'Referer: http://radiko.jp/' \
> -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30' \
> -O
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 669k 100 669k 0 0 243k 0 0:00:02 0:00:02 --:--:-- 243k
1.1.1. 画像ファイルを抽出する
swf ファイルの内容を確認します。
$ swfextract myplayer-release.swf
Objects in file myplayer-release.swf:
[-i] 1 Shape: ID(s) 1
[-i] 1 MovieClip: ID(s) 2
[-b] 10 Binarys: ID(s) 3-12
[-f] 1 Frame: ID(s) 0
Binarys
の最後の 12
の番号がついたファイルが “それ” らしいので、オプションに -b 12
をつけて実行してみます。
$ swfextract myplayer-release.swf -b 12 -o authkey.png
authkey.png
の名前で画像ファイルが抽出できたようです。
$ ls authkey.png
authkey.png
1.2. 認証 1
認証 1(auth1_fms)にアクセスして、X-Radiko-AuthToken{1}
, X-Radiko-KeyLength{2}
, X-Radiko-KeyOffset{3}
を取得します。
$ curl 'https://radiko.jp/v2/api/auth1_fms' \
> -XPOST \
> -H 'Content-Type: application/x-www-form-urlencoded' \
> -H 'Referer: http://radiko.jp/' \
> -H 'Pragma: no-cache' \
> -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30' \
> -H 'X-Radiko-Device: pc' \
> -H 'X-Radiko-App-Version: 4.0.0' \
> -H 'X-Radiko-User: test-stream' \
> -H 'X-Radiko-App: pc_ts' \
> --data $'\r\n'
X-Radiko-AppType=pc
X-Radiko-AppType2=pc
X-RADIKO-AUTHTOKEN=wpU-C0IvwTPf_magS8T7gA
X-Radiko-AuthWait=0
X-Radiko-Delay=15
X-Radiko-KeyLength=16
X-Radiko-KeyOffset=180274
please send a part of key
please send a part of key
と表示されていますので、先に抽出した画像ファイル(画像ファイルがキーとなっているらしい)から X-Radiko-KeyOffset{3}
の次の 1 バイトを開始位置として、X-Radiko-KeyLength{2}
の長さの分だけを抽出して、送信するように、とのことのようです。
1.2.1. Partialkey を抽出する
X-Radiko-KeyLength{2}
, X-Radiko-KeyOffset{3}
をもとに画像ファイルから X-Radiko-Partialkey{4}
を抽出します。
オプションは次の通りです。
- if: 先に抽出した画像ファイル
- ibs: 一度に指定したバイトのブロックを読み出す
- skip: 認証 1 で取得した
X-Radiko-KeyOffset{3}
の値 - count: 認証 1 で取得した
X-Radiko-KeyLength{2}
の値
dd
コマンドで抽出できるのはバイナリーなので、base64
コマンドにパイプで渡して文字列にしました。
$ dd if=authkey.png ibs=1 skip=180274 count=16 | base64
16+0 records in
0+1 records out
16 bytes transferred in 0.000333 secs (48038 bytes/sec)
LfX/AD/OhWw+4qRkjqOp7w==
最後の行の LfX/AD/OhWw+4qRkjqOp7w==
が X-Radiko-Partialkey{4}
です。
1.3. 認証 2
認証 2(auth2_fms)に X-Radiko-Authtoken{1}
, X-Radiko-Partialkey{4}
を送信します。
送信すると言っても POST のデータ部分は改行だけのようで、次のヘッダーに指定するようです。
- X-Radiko-Authtoken:
X-Radiko-Authtoken{1}
- X-Radiko-Partialkey:
X-Radiko-Partialkey{4}
$ curl 'https://radiko.jp/v2/api/auth2_fms' \
> -XPOST \
> -H 'Content-Type: application/x-www-form-urlencoded' \
> -H 'Referer: http://radiko.jp/' \
> -H 'Pragma: no-cache' \
> -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30' \
> -H 'X-Radiko-Device: pc' \
> -H 'X-Radiko-Authtoken: wpU-C0IvwTPf_magS8T7gA' \
> -H 'X-Radiko-App-Version: 4.0.0' \
> -H 'X-Radiko-User: test-stream' \
> -H 'X-Radiko-Partialkey: LfX/AD/OhWw+4qRkjqOp7w==' \
> -H 'X-Radiko-App: pc_ts' \
> --data $'\r\n'
JP13,東京都,tokyo Japan
認証に成功すると、地域の情報が表示されるようです。
認証した時の X-Radiko-Authtoken{1}
がタイムフリー聴取する時に必要になるようです。
2. タイムフリー聴取
2.1. プレイリスト 1
ヘッダーに X-Radiko-Authtoken{1}
を指定します。
POST しているデータは flash=1
となっているようです。
クエリー文字列は次の通りです。
- station_id: 放送局 (http://radiko.jp/v2/station/list/JP13.xml) 地域ごとに異なる
- l: 15(謎)
- ft: 開始日時
- to: 終了日時
$ curl 'https://radiko.jp/v2/api/ts/playlist.m3u8?station_id=LFR&l=15&ft=20170423223000&to=20170423230000' \
> -XPOST \
> -H 'Content-Type: application/x-www-form-urlencoded' \
> -H 'Referer: http://radiko.jp/' \
> -H 'Pragma: no-cache' \
> -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30' \
> -H 'X-Radiko-Authtoken: wpU-C0IvwTPf_magS8T7gA' \
> --data 'flash=1'
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=52973,CODECS="mp4a.40.5"
https://radiko.jp/v2/api/ts/chunklist/JZTdYq-2.m3u8
プレイリスト 1 にアクセスしてみると、さらにプレイリストの URL が記述されているようでした。
2.2. プレイリスト 2
プレイリスト 2 にアクセスしてみると、aac ファイルの URL が記述されているようでした。
プレイリスト 2 の URL に chunklist
とあるように、5 秒ごとのファイルに分割されたものの集合になっているようです。
$ curl 'https://radiko.jp/v2/api/ts/chunklist/JZTdYq-2.m3u8' \
> -XGET \
> -H 'Referer: http://radiko.jp/' \
> -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30'
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:NO
#EXT-X-TARGETDURATION:5
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-PROGRAM-DATE-TIME:2017-04-23T22:30:00+09:00
#EXTINF:5,
http://media.radiko.jp/sound/b/LFR/20170423/20170423_223000_AxevQ.aac
#EXT-X-PROGRAM-DATE-TIME:2017-04-23T22:30:05+09:00
#EXTINF:5,
http://media.radiko.jp/sound/b/LFR/20170423/20170423_223005_Lggb3.aac
# ・・・略・・・
#EXT-X-PROGRAM-DATE-TIME:2017-04-23T22:59:50+09:00
#EXTINF:5,
http://media.radiko.jp/sound/b/LFR/20170423/20170423_225950_mcvZi.aac
#EXT-X-PROGRAM-DATE-TIME:2017-04-23T22:59:55+09:00
#EXTINF:5,
http://media.radiko.jp/sound/b/LFR/20170423/20170423_225955_Rt10X.aac
#EXT-X-ENDLIST
2.3. 録音(保存、ダウンロード)
ffmpeg
コマンドを使うと、プレイリスト 1 を入力に指定することで、録音(保存、ダウンロード)ができるようです。
ヘッダーに X-Radiko-AuthToken{1}
をつける必要があるようです。
$ ffmpeg \
> -content_type 'application/x-www-form-urlencoded' \
> -headers 'Referer: http://radiko.jp/' \
> -headers 'Pragma: no-cache' \
> -headers 'X-Radiko-AuthToken: wpU-C0IvwTPf_magS8T7gA' \
> -user_agent 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30' \
> -method 'POST' \
> -i 'https://radiko.jp/v2/api/ts/playlist.m3u8?station_id=LFR&l=15&ft=20170423223000&to=20170423230000' \
> -c copy \
> out.aac
ffmpeg version 3.3 Copyright (c) 2000-2017 the FFmpeg developers
built with Apple LLVM version 8.1.0 (clang-802.0.41)
configuration: --prefix=/usr/local/Cellar/ffmpeg/3.3 --enable-shared --enable-pthreads --enable-gpl --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-libmp3lame --enable-libx264 --enable-libxvid --enable-opencl --disable-lzma --enable-vda
libavutil 55. 58.100 / 55. 58.100
libavcodec 57. 89.100 / 57. 89.100
libavformat 57. 71.100 / 57. 71.100
libavdevice 57. 6.100 / 57. 6.100
libavfilter 6. 82.100 / 6. 82.100
libavresample 3. 5. 0 / 3. 5. 0
libswscale 4. 6.100 / 4. 6.100
libswresample 2. 7.100 / 2. 7.100
libpostproc 54. 5.100 / 54. 5.100
[https @ 0x7fa3f4504080] No trailing CRLF found in HTTP header.
Input #0, hls,applehttp, from 'https://radiko.jp/v2/api/ts/playlist.m3u8?station_id=LFR&l=15&ft=20170423223000&to=20170423230000':
Duration: 00:30:00.00, start: 23567.910400, bitrate: N/A
Program 0
Metadata:
variant_bitrate : 52973
Stream #0:0: Audio: aac (HE-AAC), 48000 Hz, stereo, fltp, 46 kb/s
Metadata:
variant_bitrate : 52973
Output #0, adts, to 'out.aac':
Metadata:
encoder : Lavf57.71.100
Stream #0:0: Audio: aac (HE-AAC), 48000 Hz, stereo, fltp, 46 kb/s
Metadata:
variant_bitrate : 52973
Stream mapping:
Stream #0:0 -> #0:0 (copy)
Press [q] to stop, [?] for help
size= 10546kB time=00:29:59.86 bitrate= 48.0kbits/s speed= 17x
video:0kB audio:10546kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.000000%
ヘッダーの最後の改行が見つからない警告が出まして、
[https @ 0x7fa3f4504080] No trailing CRLF found in HTTP header.
#3268 (CRLF problem with custom headers when playing hls streams on windows) – FFmpeg と同じことかと思いまして、明示的に改行をつけてみたのですが、エラーになってしまうので放置しました。
それから、flash=1
のデータを送信したかったのですが、post_data
の使い方がわからなくて、Error setting option post_data to value flash=1.
のエラーになってしまうので、送信しないことにしました。
FFmpeg のドキュメントにコマンドのサンプルがなかったので、わかりませんでした。
3.11 http
post_data
Set custom HTTP post data.
それでも録音(保存、ダウンロード)したファイルには影響はなく、大丈夫だと思うのですけど。
終わり
認証を済ませておけば ffmpeg
コマンド 1 つで録音(保存、ダウンロード)できるようでした。
ただ、ある程度時間が経過すると認証の期限的なものが切れるようですので、改めて認証しなければいけなそうでした。