シェルスクリプトで radiko タイムフリーを保存する

🔗 radikoのタイムフリーを保存する方法

上記の記事を大いに参考にさせていただき、というか重要な部分を丸パクりさせていただき、シェルスクリプトを作った。
 くれぐれも。
 ラジコのタイムフリーを保存することには、なんらかの違法性があるかもしれない。筆者がインターネットをめぐる著作権の法律を読んだ感じでは、公式が配布するコンテンツを保存することは、私的の利用に限れば問題ない、のではないかと思っている。
 しかし専門家にはいわせれば違うのかもしれない。
 確実なのは、ダウンロードしたものをどこかへ公開するのはアウトということだ。

radiko-dl.sh

要 swftools、ffmpeg。

brew install swftools
brew install ffmpeg

2019 年 12 月 25 日深夜 1 時から 3 時、TBS ラジオの「爆笑問題カーボーイ」を保存するとしたら、以下のように書く。

radiko-dl.sh -id TBS -ft 20191225010000 -to 20191225030000 -n 'JUNK 爆笑問題カーボーイ'

radiko-dl.sh -id 放送局ID -ft 開始時間 -to 終了時間
 必要に応じて-n '番組名'をつけると、ファイルに番組名をつける。
 -o file/to/path で保存先を指定できる。チルダをクオートで囲むと展開されないので、-o "$HOME/Downloads" という感じがいいかもしれない。
 デフォルトでは Downloads フォルダ内に radiko フォルダを生成してダウンロード先とする。必要なら一行目の dir のパスを書き直すのがいいと思う。

-a オプションは視聴エリアを表示するオプション。

📝 radiko-dl.sh

#!/bin/zsh

dir=~/Downloads/radiko
area='off'

# オプションを取得
while [ $# -gt 0 ]
do
    case $1 in
        -id) shift; id="$1" ;;
        -ft) shift; ft="$1" ;;
        -to) shift; to="$1" ;;
        -n) shift; program="$1" ;;
        -o) shift; dir="$1" ;;
        -a) area='on' ;;
        *) break ;;
    esac
    shift
done

# ファイル名を決める
if test -z "$program"; then
    filename=`echo "$id"_"$ft"`
else
    ft_unix=`date -j -f "%Y%m%d%H%M%S" "$ft" +%s`
    hiduke=`date -r "$ft_unix" +"%Y-%m-%d-%H%M"`
    filename=`echo ${program}'_'${hiduke}`    
fi

# フォルダへ移動
if test -d "$dir"; then
    cd "$dir"
else
    mkdir "$dir"
    cd "$dir"
fi

# 必要書類をそろえる
if ! test -e 'myplayer-release.swf'; then
    curl -O http://radiko.jp/apps/js/flash/myplayer-release.swf
fi
if ! test -e 'authkey.png'; then
    swfextract myplayer-release.swf -b 12 -o authkey.png
fi

# 承認ひとつめ
auth1=`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 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1 Safari/605.1.15' \
    -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'`

authtoken=`echo "${auth1}" | grep -i 'x-radiko-authtoken' | awk -F'[=]' '{print$2}' | tr -d "\r"`
token=`echo "${auth1}" | grep -i 'x-radiko-authtoken' | awk -F'[=]' '{print$1}' | tr -d "\r"`
length=`echo "${auth1}" | grep -i 'x-radiko-keylength' | awk -F'[=]' '{print$2}' | tr -d "\r"`
offset=`echo "${auth1}" | grep -i 'x-radiko-keyoffset' | awk -F'[=]' '{print$2}' | tr -d "\r"`
echo $authtoken
echo $offset
echo $length

partialkey=`dd if=authkey.png ibs=1 skip=${offset} count=${length} 2> /dev/null | base64`
echo $partialkey

# 承認ふたつめ
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 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1 Safari/605.1.15' \
    -H 'X-Radiko-Device: pc' \
    -H $token': '$authtoken \
    -H 'X-Radiko-App-Version: 4.0.0' \
    -H 'X-Radiko-User: test-stream' \
    -H 'X-Radiko-Partialkey: '$partialkey \
    -H 'X-Radiko-App: pc_ts' > auth2

# エリアを表示して終了
if test "$area" = 'on'; then
    cat auth2 | tr -d '\r' | grep -v ^$ | awk -F'[,]' '{print $1}'
    exit 0
fi

# オプションがそろっているかチェック
if test -z "$id" || test -z "$ft" || test -z "$to"; then
    echo '-id 放送局ID -ft 開始時間(20190430133000) -to 終了時間'
    exit 1
fi

# 放送済みかどうかチェック
to_unix=`date -j -f "%Y%m%d%H%M%S" "$to" +%s`
now=`date +%s`
if test $to_unix -ge $now; then
    echo '放送終了前です'
    exit 0
fi

# ダウンロード
ffmpeg \
    -loglevel fatal \
    -content_type 'application/x-www-form-urlencoded' \
    -headers 'Referer: http://radiko.jp/' \
    -headers 'Pragma: no-cache' \
    -headers 'X-Radiko-AuthToken: '$authtoken \
    -i 'https://radiko.jp/v2/api/ts/playlist.m3u8?station_id='$id'&l=15&ft='$ft'&to='$to \
    -bsf:a aac_adtstoasc -acodec copy "${filename}".m4a

# 通知など出してみる
osascript -e 'display notification "'"$filename"' 完了しました" with title "radiko-dl.sh" sound name "Glass"'

開始時刻、終了時刻、番組名

このスクリプトだと開始時刻、終了時刻とかを調べるのがダルい。
 なので番組表から検索するスクリプトも書いてみた。
 radiko-dl.sh へ渡すオプションを生成することを目的としている。

📝 findradiko.sh

#!/bin/zsh

# 作業場のフォルダへ移動する。radiko-dl.sh の作業場とあわせる
cd ~/Downloads/radiko
quick='off'
day='today'

# オプションの取得
while [ $# -gt 0 ]
do
    case $1 in
        -i) shift; id="$1" ;;
        -k) shift; keyword="$1" ;;
        -d) shift; day="$1" ;;
        -q) quick='on' ;;
        *) break ;;
    esac
    shift
done

# 放送局ID を表示する関数
function showStID() {
    curl -s http://radiko.jp/v2/station/list/"${1}".xml | grep -E '<\/id>|<\/name>' | sed -e 's/ //g' | perl -pe 's/<\/id>\n/ /g' | sed -e 's/<id>//g' | perl -pe 's/<name>//g' | perl -pe 's/<\/name>\n/\n/g'
}

# 視聴エリアの取得
if test -e 'auth2'; then
    area=`cat auth2 | tr -d '\r' | grep -v ^$ | awk -F'[,]' '{print $1}'`
else
    area=`sh radiko-dl.sh -a`
fi

# オプションのチェック
if test -z "$id" || test -z "$keyword"; then
    echo '-i 放送局ID -k キーワード'
    echo 'エリアコード:'$area
    echo '------------------------------'
    showStID $area
    exit 1
fi

# 日付のチェック
if ! test "$day" = 'today'; then;
    if test `echo "${#day}"` -eq 8; then;
        day='date/'"$day"
    else
        echo '日付は8桁で。(2019年10月3日 → 20191003 みたいな)'
        exit 0
    fi
fi

# 番組表の取得
prog=`curl -s http://radiko.jp/v3/program/"${day}"/"${area}".xml | grep -B 1 "${keyword}" | grep -E 'prog id=|master_id=|title'`

# 番組名、開始時刻、終了時刻の取得
title=`echo "${prog}" | grep -o "<title>[^<]*</title>" | sed -e 's/<[^>]*>//g'`
ft=`echo "$prog" | tr '"' '\n' | grep -A 1 'ft=' | grep -vE 'ft=|--'`
to=`echo "$prog" | tr '"' '\n' | grep -A 1 'to=' | grep -vE 'to=|--'`

# 複数の番組が見つかった場合を想定して for ループで表示。
# -q オプションがついている場合は、そのまま保存に入る。
# あんまり連続したら怒られそうなので「sleep 60」で一分間を置く。必要ないかも。
wl=`echo $title | wc | awk '{print $1}'`

for num in `seq $wl`
do           
    namae=`echo $title | sed -n "$num"p`
    hajimari=`echo $ft | sed -n "$num"p`
    owari=`echo $to | sed -n "$num"p`

    if test "$quick" = 'off'; then
        echo  '-n '\'"${namae}"\'' -id '"${id}"' -ft '"${hajimari}"' -to '"${owari}"
    else #下の部分、radiko-dl.sh のパスに書きかえる。
        sh radiko-dl.sh -id "${id}" -ft "${hajimari}" -to "${owari}" -n "${namae}"
        if test $wl -gt $num; then
            sleep 60
        fi
    fi
done

使い方としては、
findradiko.sh -i 放送局ID -k キーワード
 で、きょうの番組表から検索する。
 2019 年 12 月 24 日 に「爆笑問題」というキーワードでこのスクリプトを実行すると、以下のような結果が返ってくる。

$ findradiko.sh -i TBS -k '爆笑問題'
-n 'JUNK 爆笑問題カーボーイ' -id TBS -ft 20191225010000 -to 20191225030000

という感じ。
 この結果をコピペして radiko-dl.sh に渡せばダウンロードがはじまる。

検索範囲はその日の放送に限る。しかし 0 時をすぎた深夜の番組もヒットする。
 日にちを指定する場合は -d オプションを使用する。「西暦月日」を 8 桁の数字にして渡
す。2019 年 12 月 22 日なら、-d 20191222。
 なお、ラジコのタイムフリーで聞ける過去の放送は、一週間前までだ。
 検索の対象となるのは番組名だけだ。パーソナリティーやゲストの名前で検索してもヒットしない。

$ findradiko.sh -i FMGUNMA -k '有吉弘行の' -d 20191222
-n '有吉弘行のSUNDAY NIGHT DREAMER' -id FMGUNMA -ft 20191222200000 -to 20191222205500
-n '有吉弘行のSUNDAY NIGHT DREAMER' -id FMGUNMA -ft 20191222210000 -to 20191222215500

このようにひとつの番組がわかれていて複数見つかったりもする。
 -q オプションをつけると自動で radiko-dl.sh にデータを渡して自動でダウンロードをはじめる。
(下から 6 行目の「sh radiko-dl.sh」を正確なパスに書き換える必要がある。デスクトップに radiko-dl.sh があるとしたら sh ~/Desktop/radiko-dl.sh という感じ)。

複数の番組がある場合、ひとつのダウンロードが終わってから一分待ち、次のダウンロードをはじめる。あんまり連続してダウンロードすると怒られそうなのでそうした。

crontab に登録する

cron はスリープ復帰してくれないが、タイムフリーのダウンロードなら時間はあまり関係ない。いつでも、Mac が起きている時間に cron をセットしておけばいいはず。

3 3 * * wed /パス/findradiko.sh -i TBS -k 'JUNK 爆笑問題カーボーイ' -q

こんな感じで-qオプションをつけておけば、番組終了の 3 分後、つまり水曜の 3 時 3 分に自動で番組が保存されて、いいと思う。

【Mac】定期的にスクリプトを実行する crontab | 林檎コンピュータ

Catalina で crontab を動かす場合は、下記の記事を参考。
macOS 10.15 catalina にしたら crontab が動かない | 林檎コンピュータ

また、上述のブログさまはエリアフリーにログインする方法も記事にしてくれている。

🔗 radikoのエリアフリーを保存する方法

記事のスクリプトを組み入れてログインすれば、全国のラジオをダウンロードできるのではないか、と思う。やってないけど。