rtmpdumpでradikoにアクセスする手順
rec_radiko.shをさらに自分好みに修正したい。そのためにはスクリプトの中で何をしているのか?ちゃんと調べておく必要がある。すでに以下のページで詳しい解説が行われているが、それを正確に理解するために自分自身でスクリプトの手順を1つずつ実行して体感してみるのだ。
(1)事前準備
- まずは一連の作業のため~/Downloads/radiko_testフォルダを作って、そこで作業することにした。
$ mkdir ~/Downloads/radiko_test $ cd ~/Downloads/radiko_test
(2)player.swfをダウンロード
- http://radiko.jp/player/swf/player_3.0.0.01.swfにアクセスして、player.swfをダウンロードする。
$ /usr/local/bin/wget -q -O ./player.swf http://radiko.jp/player/swf/player_3.0.0.01.swf $ ls player.swf
(3)authkey.jpgを取り出す
- (2)でダウンロードしたplayer.swfの中から、authkey.jpgを取り出す。
$ /usr/local/bin/swfextract -b 14 player.swf -o authkey.jpg $ ls authkey.jpg player.swf
(4)auth1_fmsをダウンロード
- auth1_fmsをダウンロードする
$ /usr/local/bin/wget -q \ --header="pragma: no-cache" \ --header="X-Radiko-App: pc_1" \ --header="X-Radiko-App-Version: 2.0.1" \ --header="X-Radiko-User: test-stream" \ --header="X-Radiko-Device: pc" \ --post-data='\r\n' \ --no-check-certificate \ --save-headers \ https://radiko.jp/v2/api/auth1_fms $ ls auth1_fms authkey.jpg player.swf
- ダウンロードしたauth1_fmsの内容はこんな感じ。
$ cat auth1_fms HTTP/1.1 200 OK Server: nginx Date: Thu, 24 Jan 2013 01:32:37 GMT Content-Type: text/plain Transfer-Encoding: chunked Connection: keep-alive X-Radiko-AppType: pc X-RADIKO-AUTHTOKEN: 2IjGnHwXqhjyXXM9OJK4Cg X-Radiko-AuthWait: 0 X-Radiko-Delay: 0 X-Radiko-KeyLength: 16 X-Radiko-KeyOffset: 114930 X-Radiko-AppType=pc X-RADIKO-AUTHTOKEN=2IjGnHwXqhjyXXM9OJK4Cg X-Radiko-AuthWait=0 X-Radiko-Delay=0 X-Radiko-KeyLength=16 X-Radiko-KeyOffset=114930
(5)authtoken, offset, lengthを取り出す
- (4)でダウンロードしたauth1_fmsから、authtoken, offset, lengthを取り出す。
$ authtoken=`perl -ne 'print $1 if(/x-radiko-authtoken: ([\w-]+)/i)' auth1_fms` $ offset=`perl -ne 'print $1 if(/x-radiko-keyoffset: (\d+)/i)' auth1_fms` $ length=`perl -ne 'print $1 if(/x-radiko-keylength: (\d+)/i)' auth1_fms` $ echo $authtoken $offset $length 2IjGnHwCqhjyRRM9OJK4Cg 114930 16
(6)base64エンコードしたpartialkeyを取得
$ partialkey=`dd if=authkey.jpg bs=1 skip=${offset} count=${length} 2> /dev/null | base64` $ echo $partialkey gyXn94HLdhxyXe2T1p3lxA==
(7)auth2_fmsをダウンロード
- (5)のauthtoken、(6)のpartialkeyをヘッダーに添付して、auth2_fmsをダウンロードする。
$ /usr/local/bin/wget -q \ --header="pragma: no-cache" \ --header="X-Radiko-App: pc_1" \ --header="X-Radiko-App-Version: 2.0.1" \ --header="X-Radiko-User: test-stream" \ --header="X-Radiko-Device: pc" \ --header="X-Radiko-Authtoken: ${authtoken}" \ --header="X-Radiko-Partialkey: ${partialkey}" \ --post-data='\r\n' \ --no-check-certificate \ https://radiko.jp/v2/api/auth2_fms $ ls auth1_fms auth2_fms authkey.jpg player.swf
- ダウンロードしたauth2_fmsの内容はこんな感じ。
$ cat auth2_fms
JP13,東京都,tokyo Japan
(8)stream_urlを取得
- FMJ = J-WAVEを受信する場合、"http://radiko.jp/v2/station/stream/FMJ.xml"にアクセスして、xmlを取得する。
$ channel_xml=`/usr/local/bin/wget -q "http://radiko.jp/v2/station/stream/FMJ.xml" -O -` $ /usr/local/bin/wget -q "http://radiko.jp/v2/station/stream/FMJ.xml" -O - <?xml version="1.0" encoding="utf8" ?>- rtmpe://w-radiko.smartstream.ne.jp/FMJ/_definst_/simul-stream.stream
- rtmpe://w-radiko.smartstream.ne.jp/FMJ/_definst_/simul-stream.stream
- xmlの中からurlを取得する。
$ stream_url=`echo $channel_xml | sed 's/^\(\)/\1UTF-8\2/' | xpath "//url/item[1]/text()" 2>/dev/null` $ echo $stream_url rtmpe://w-radiko.smartstream.ne.jp/FMJ/_definst_/simul-stream.stream
- urlを3つのパーツに分割する。($stream_url_partsは配列になっている)
$ stream_url_parts=(`echo ${stream_url} | perl -pe 's!^(.*)://(.*?)/(.*)/(.*?)$/!$1://$2 $3 $4!'`) $ echo ${stream_url_parts[@]} rtmpe://w-radiko.smartstream.ne.jp FMJ/_definst_ simul-stream.stream
(9)rtmpdumpでダウンロード
- (5)のauthtoken、(8)のstream_urlを利用して、rtmpdumpでradikoにアクセスすることで、ようやく録音できた!
$ /usr/local/bin/rtmpdump -v \ -r ${stream_url_parts[0]} \ --app ${stream_url_parts[1]} \ --playpath ${stream_url_parts[2]} \ -W http://radiko.jp/player/swf/player_3.0.0.01.swf\ -C S:"" -C S:"" -C S:"" -C S:$authtoken \ --live \ --stop 30 \ --flv "J-WAVE.flv" RTMPDump 2.4 (c) 2010 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL WARNING: No application or playpath in URL! Connecting ... WARNING: Trying different position for server digest! INFO: Connected... Starting Live Stream For duration: 30.000 sec INFO: Metadata: INFO: StreamTitle 182.783 kB / 30.04 sec Download complete $ ls J-WAVE.flv auth1_fms auth2_fms authkey.jpg player.swf
長い道のりであった...。これでやっとJ-WAVE.flvというファイルになって、30秒間の録音ができた!
rec_radiko.shの改良
- 以上、rec_radiko.shを体感してみて、その処理は認証と録音の2つに大別できると感じた。
- そして一度認証されれば認証状態はしばらく継続するようだ。
そこで、rtmpdumpでアクセスしてエラーが発生した時だけ認証する方式に変更してみた。- 週数回の予約録音目的では、ほぼ毎回アクセスエラーが発生して認証が必要になるのでやめた。
- -aオプションを追加して、エリア情報を取得できるようにしてみた。
- -oオプションを拡張して、ディレクトリ名とファイル名を変更する自由度を高めてみた。
- flv → m4a変換(無劣化)も追加
#!/bin/sh # Original1: https://gist.github.com/875864 saiten / rec_radiko.sh # Original2: http://backslash.ddo.jp/wordpress/archives/1020 http://backslash.ddo.jp/tools/rec_radiko2.txt # Install: wget swftools rtmpdump ffmpeg http://d.hatena.ne.jp/zariganitosh/20130120/radiko_recoding_again # 使い方 show_usage() { echo "Usage: $COMMAND [-a] [-o output_path] [-t recording_seconds] station_ID" 1>&2 echo ' -a Output area info(ex. 'JP13,東京都,tokyo Japan'). No recording.' 1>&2 echo ' -o Default output_path = $HOME/Downloads/${station_name}_`date +%Y%m%d-%H%M`.flv' 1>&2 echo ' a/b/c/ = $HOME/Downloads/a/b/c/J-WAVE_20130123-1700.flv' 1>&2 echo ' a/b/c = $HOME/Downloads/a/b/c.flv' 1>&2 echo ' /a/b/c/ = /a/b/c/J-WAVE_20130123-1700.flv' 1>&2 echo ' /a/b/c = /a/b/c.flv' 1>&2 echo ' ./a/b/c/ = ./a/b/c/J-WAVE_20130123-1700.flv' 1>&2 echo ' ./a/b/c = ./a/b/c.flv' 1>&2 echo ' -t Default recording_seconds = 30' 1>&2 echo ' 60 = 1 minute, 3600 = 1 hour, 0 = go on recording until stopped(control-C)' 1>&2 } # 認証 radiko_authorize() { echo "==== authorize ====" # # get player # if [ ! -f $playerfile ]; then echo $playerfile downloading... /usr/local/bin/wget -q -O $playerfile $playerurl if [ $? -ne 0 ]; then echo "failed get player" exit 1 fi fi # # get keydata (need swftool) # if [ ! -f $keyfile ]; then echo $keyfile extracting... # /usr/local/bin/swfextract -b 5 $playerfile -o $keyfile <---radiko仕様変更点 /usr/local/bin/swfextract -b 14 $playerfile -o $keyfile if [ ! -f $keyfile ]; then echo "failed get keydata" exit 1 fi fi # # access auth1_fms # rm -f auth1_fms /usr/local/bin/wget -q \ --header="pragma: no-cache" \ --header="X-Radiko-App: pc_1" \ --header="X-Radiko-App-Version: 2.0.1" \ --header="X-Radiko-User: test-stream" \ --header="X-Radiko-Device: pc" \ --post-data='\r\n' \ --no-check-certificate \ --save-headers \ https://radiko.jp/v2/api/auth1_fms if [ $? -ne 0 ]; then echo "failed auth1 process" exit 1 fi # # get partial key # authtoken=`perl -ne 'print $1 if(/x-radiko-authtoken: ([\w-]+)/i)' auth1_fms` offset=`perl -ne 'print $1 if(/x-radiko-keyoffset: (\d+)/i)' auth1_fms` length=`perl -ne 'print $1 if(/x-radiko-keylength: (\d+)/i)' auth1_fms` partialkey=`dd if=$keyfile bs=1 skip=${offset} count=${length} 2> /dev/null | base64` echo "authtoken: ${authtoken}\n offset: ${offset}\n length: ${length}\n partialkey: $partialkey" # # access auth2_fms # rm -f auth2_fms /usr/local/bin/wget -q \ --header="pragma: no-cache" \ --header="X-Radiko-App: pc_1" \ --header="X-Radiko-App-Version: 2.0.1" \ --header="X-Radiko-User: test-stream" \ --header="X-Radiko-Device: pc" \ --header="X-Radiko-Authtoken: ${authtoken}" \ --header="X-Radiko-Partialkey: ${partialkey}" \ --post-data='\r\n' \ --no-check-certificate \ https://radiko.jp/v2/api/auth2_fms if [ $? -ne 0 -o ! -f auth2_fms ]; then echo "failed auth2 process" exit 1 fi echo "authentication success" areaid=`perl -ne 'print $1 if(/^([^,]+),/i)' auth2_fms` echo "areaid: $areaid" } # 録音 radiko_record() { echo "==== recording ====" # # get stream-url # channel_xml=`/usr/local/bin/wget -q "http://radiko.jp/v2/station/stream/${channel}.xml" -O -` stream_url=`echo $channel_xml | sed 's/^\(<?xml .*\)[Uu][Tt][Ff]8\(.* ?>\)/\1UTF-8\2/' | xpath "//url/item[1]/text()" 2>/dev/null` stream_url_parts=(`echo ${stream_url} | perl -pe 's!^(.*)://(.*?)/(.*)/(.*?)$/!$1://$2 $3 $4!'`) # # get authtoken # authtoken=`perl -ne 'print $1 if(/x-radiko-authtoken: ([\w-]+)/i)' auth1_fms` # # /usr/local/bin/rtmpdump # echo "save as '$output'" /usr/local/bin/rtmpdump -v \ -r ${stream_url_parts[0]} \ --app ${stream_url_parts[1]} \ --playpath ${stream_url_parts[2]} \ -W $playerurl \ -C S:"" -C S:"" -C S:"" -C S:$authtoken \ --live \ --stop "${rectime:=30}" \ --flv "${output}" } # 引数解析 COMMAND=`basename $0` while getopts aho:t: OPTION do case $OPTION in a ) OPTION_a="TRUE" ;; o ) OPTION_o="TRUE" ; VALUE_o="$OPTARG" ;; t ) OPTION_t="TRUE" ; VALUE_t="$OPTARG" ;; * ) show_usage ; exit 1 ;; esac done shift $(($OPTIND - 1)) #残りの非オプションな引数のみが、$@に設定される if [ $# = 0 -a "$OPTION_a" != "TRUE" ]; then show_usage ; exit 1 fi # オプション処理 channel=$1 if [ "$OPTION_o" = "TRUE" ]; then if echo $VALUE_o|grep -q -v -e '^./\|^/'; then mkdir -p $HOME/Downloads ; cd $HOME/Downloads fi fname_ext="${VALUE_o##*/}" fname="${fname_ext%.*}" fext="${fname_ext#$fname}" wdir="${VALUE_o%/*}"; [ "$fname_ext" = "$wdir" ] && wdir="" fi if [ "$OPTION_t" = "TRUE" ]; then rectime=$VALUE_t fi mkdir -p ${wdir:=$HOME/Downloads} ; cd $wdir ; wdir=`pwd` station_name=`curl -s http://radiko.jp/v2/api/program/station/today?station_id=$channel|xpath "//station/name/text()" 2>/dev/null` output="${wdir}/${fname:=${station_name}_`date +%Y%m%d-%H%M`}${fext:=.flv}" # playerurl=http://radiko.jp/player/swf/player_2.0.1.00.swf <---radiko仕様変更点 playerurl=http://radiko.jp/player/swf/player_3.0.0.01.swf playerfile=./player.swf keyfile=./authkey.jpg if [ "$OPTION_a" = "TRUE" ]; then radiko_authorize && cat auth2_fms|grep -e '^\w\+' else radiko_authorize && radiko_record fi ffmpeg -v quiet -y -i "${output}" -acodec copy "${output%.*}.m4a" rm -f "${output}"