ホーム > サーバ > とあるサイトの録音手段 (シェルスクリプト)

とあるサイトの録音手段 (シェルスクリプト)

2011 年 3 月 21 日 コメントをどうぞ コメント

13日にradikoが全国で聴取できるようになった事は記憶に新しいですが、
その仕様変更の際にRTMPDumpによるストリーミングの保存が不可能となってしまいました。

しかしながら、ネットのチカラは恐ろしいですね。
仕様変更に対応したシェルスクリプトが、saiten様により19日に公開されました。

簡易radiko録音ツール。要swftools

私もこちらのページを参考にSWFTOOLSを導入してみた所、
見事にストリーミングの保存が可能となりました。

ですが、上記のスクリプトだとリトライ機能が無いため
予約録音に失敗してしまう場合があります。

そこで、上記のスクリプトをベースに、いくつかの機能を追加してみました。

[2012/05/25] MP3形式での出力について追記
[2012/10/24] radikoの仕様変更に対応
[2016/04/08] radikoの仕様変更に対応 + α
[2017/01/15] radikoの仕様変更に対応


—–

SWFTOOLS・RTMPDump・FFmpegがインストールされていることを前提とします。
それぞれのインストール場所にあわせて、パスなどを書き換えてください。
(筆者は CentOS 6.8 および Raspbian Jessie 上での動作を確認しております。)

#!/bin/bash

if [ $# -eq 3 ]; then
  OUTFILEPREFIX=$1
  RECTIMEMIN=$2
  CHANNEL=$3
else
  echo "usage : $0 OUTFILEPREFIX RECTIMEMIN CHANNEL"
  exit 1
fi

OUTFILEPREFIX=$1
RECTIMEMIN=$2
CHANNEL=$3

RTMPDUMP=/path/to/rtmpdump
FFMPEG=/path/to/ffmpeg

OUTFILEBASEPATH=/path/to/radiko
OUTFILENAME=${OUTFILEBASEPATH}/${OUTFILEPREFIX}/${OUTFILEPREFIX}_`date +%Y-%m-%d`
FLVFILEEXT=".flv"
AACFILEEXT=".m4a"
MP3FILEEXT=".mp3"

# 1->AAC, 2->MP3
OUTFILETYPE=1

MARGINTIMEMIN=1
RECTIME=`expr ${RECTIMEMIN} \* 60 + ${MARGINTIMEMIN} \* 2 \* 60`

# Radiko Premium
#mail=$3
#pass=$4

cd ${OUTFILEBASEPATH}

if [ ! -d ${OUTFILEBASEPATH}/${OUTFILEPREFIX} ]; then
  mkdir ${OUTFILEBASEPATH}/${OUTFILEPREFIX}
fi

playerurl=http://radiko.jp/apps/js/flash/myplayer-release.swf
cookiefile=./cookie.txt
playerfile=./player.swf
keyfile=./authkey.png

###
# radiko premium
###
if [ $mail ]; then
  wget -q --save-cookie=$cookiefile \
       --keep-session-cookies \
       --post-data="mail=$mail&pass=$pass" \
       https://radiko.jp/ap/member/login/login

  if [ ! -f $cookiefile ]; then
    echo "failed login"
    exit 1
  fi
fi

#
# get player
#
if [ ! -f $playerfile ]; then
  wget -q -O $playerfile $playerurl

  if [ $? -ne 0 ]; then
    echo "failed get player"
    exit 1
  fi
fi

#
# get keydata (need swftools)
#
if [ ! -f $keyfile ]; then
  swfextract -b 12 $playerfile -o $keyfile

  if [ ! -f $keyfile ]; then
    echo "failed get keydata"
    exit 1
  fi
fi

if [ -f auth1_fms_${OUTFILEPREFIX}_${CHANNEL} ]; then
  rm -f auth1_fms_${OUTFILEPREFIX}_${CHANNEL}
fi

#
# access auth1_fms
#
wget -q \
     --header="pragma: no-cache" \
     --header="X-Radiko-App: pc_ts" \
     --header="X-Radiko-App-Version: 4.0.0" \
     --header="X-Radiko-User: test-stream" \
     --header="X-Radiko-Device: pc" \
     --post-data='\r\n' \
     --no-check-certificate \
     --load-cookies $cookiefile \
     --save-headers \
     --tries=5 \
     --timeout=5 \
     -O auth1_fms_${OUTFILEPREFIX}_${CHANNEL} \
     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_${OUTFILEPREFIX}_${CHANNEL}`
offset=`perl -ne 'print $1 if(/x-radiko-keyoffset: (\d+)/i)' auth1_fms_${OUTFILEPREFIX}_${CHANNEL}`
length=`perl -ne 'print $1 if(/x-radiko-keylength: (\d+)/i)' auth1_fms_${OUTFILEPREFIX}_${CHANNEL}`

partialkey=`dd if=$keyfile bs=1 skip=${offset} count=${length} 2> /dev/null | base64`

echo "authtoken: ${authtoken}"
echo "offset: ${offset}"
echo "length: ${length}"
echo "partialkey: $partialkey"

rm -f auth1_fms_${OUTFILEPREFIX}_${CHANNEL}

if [ -f auth2_fms_${OUTFILEPREFIX}_${CHANNEL} ]; then
  rm -f auth2_fms_${OUTFILEPREFIX}_${CHANNEL}
fi

#
# access auth2_fms
#
wget -q \
     --header="pragma: no-cache" \
     --header="X-Radiko-App: pc_ts" \
     --header="X-Radiko-App-Version: 4.0.0" \
     --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' \
     --load-cookies $cookiefile \
     --no-check-certificate \
     --tries=5 \
     --timeout=5 \
     -O auth2_fms_${OUTFILEPREFIX}_${CHANNEL} \
     https://radiko.jp/v2/api/auth2_fms

if [ $? -ne 0 -o ! -f auth2_fms_${OUTFILEPREFIX}_${CHANNEL} ]; then
  echo "failed auth2 process"
  exit 1
fi

echo "authentication success"

areaid=`perl -ne 'print $1 if(/^([^,]+),/i)' auth2_fms_${OUTFILEPREFIX}_${CHANNEL}`
echo "areaid: $areaid"

rm -f auth2_fms_${OUTFILEPREFIX}_${CHANNEL}

#
# get stream-url
#

if [ -f ${CHANNEL}.xml ]; then
  rm -f ${CHANNEL}.xml
fi

wget -q "http://radiko.jp/v2/station/stream/${CHANNEL}.xml"

stream_url=`echo "cat /url/item[1]/text()" | xmllint --shell ${CHANNEL}.xml | tail -2 | head -1`
url_parts=(`echo ${stream_url} | perl -pe 's!^(.*)://(.*?)/(.*)/(.*?)$/!$1://$2 $3 $4!'`)

rm -f ${CHANNEL}.xml

#
# rtmpdump
#
RETRYCOUNT=0
while :
do
  ${RTMPDUMP} -v \
              -r ${url_parts[0]} \
              --app ${url_parts[1]} \
              --playpath ${url_parts[2]} \
              -W $playerurl \
              -C S:"" -C S:"" -C S:"" -C S:$authtoken \
              --live \
              --flv ${OUTFILENAME}${FLVFILEEXT} \
              --stop ${RECTIME}
  if [ $? -ne 1 -o `wc -c ${OUTFILENAME}${FLVFILEEXT} | awk '{print $1}'` -ge 10240 ]; then
    break
  elif [ ${RETRYCOUNT} -ge 5 ]; then
    echo "failed rtmpdump"
    exit 1
  else
    RETRYCOUNT=`expr ${RETRYCOUNT} + 1`
  fi
done

if [ ${OUTFILETYPE} -ne 2 ]; then
  #AAC
  ${FFMPEG} -y -i "${OUTFILENAME}${FLVFILEEXT}" -vn -acodec copy "${OUTFILENAME}${AACFILEEXT}"
else
  #MP3
  ${FFMPEG} -y -i "${OUTFILENAME}${FLVFILEEXT}" -vn -ab 96k "${OUTFILENAME}${MP3FILEEXT}"
fi

rm -f ${OUTFILENAME}${FLVFILEEXT}

使い方は先日掲載したスクリプトと同様に

title 60 TBS

という引数を与えると、TBSラジオを62分間録音し、”title_YYYY-mm-dd.m4a”として保存します。
(“YYYY-mm-dd”は実行した日付に置換されます。 例: 2011-03-21)
(要するに、タイムラグをカバーするために前後に1分ずつ余裕を持たせます。
cronで予約する場合は開始1分前から実行されるように設定してください。)

なお、リトライ機能が付いているため、録音開始が最大で
auth1_fms + auth2_fms + rtmpdump =
5[秒] × 5[回] + 5[秒] × 5[回] + 30[秒] × 5[回] = 200[秒]
程度遅延する可能性があります。

しかし、試しに15番組ほどcronで予約録音を行ってみましたが、
成功確率は100%で、最大開始遅延時間は約35秒でした。
(リトライ機能が無いスクリプトだと、成功確率はかなり落ちます。)
これなら1分の余裕を持たせれば、特に問題は無いように思われます。
(気になる方は余白を多めに取ることをオススメします。)

被災地の方から録音を依頼されている場合などで、
どうしても保存しておきたい番組がある方はお試しください。

[2012/05/25追記]
MP3形式での出力に対応してみました。
スクリプト内の”OUTFILETYPE”を”2″へ変更すると、MP3ファイルとして出力されます。
ビットレートはとりあえず96kbpsとしましたが、必要に応じて変更してください。
ただし、筆者としては音質が劣化しないAAC形式での出力をオススメします。

[2012/10/24追記]
radikoの仕様変更に対応しました。
参考: RTMPDump 2 (>>237)
スクリプトの実行に問題がある場合は、古い”player.swf”や”authkey.png”を削除してください。

[2016/04/08追記]
radikoの仕様変更に対応しました。
サブディレクトリ作成処理の追加や、AAC形式の拡張子の変更なども行っています。

[2017/01/15追記]
radikoの仕様変更に対応しました。

カテゴリー: サーバ タグ: , ,
  1. qnv
    2011 年 10 月 8 日 21:29 | #1

    こんにちは、

    新しく始まったラジオ番組の録音方法を模索していたところ、
    ここに行き着きました。

    Vine linux 5.2 でためしてみたところ、
    公開いただいているスクリプト (+ パスの修正3行)
    でうまくいきました。

    お礼がてら、動作報告です。

  2. タツヤ
    2011 年 10 月 17 日 01:51 | #2

    こんにちは。
    遅くなりましたが、動作報告ありがとうございました。
    お役に立てましたら幸いです。

  3. Anonymous
    2012 年 2 月 23 日 10:24 | #3

    素晴らしい!
    スクリプトの勉強にもなります。
    ありがとうございます!

  4. toyo36jeff
    2012 年 5 月 4 日 17:11 | #4

    大変参考になりました。
    ありがとうございました。

  5. 匿名
    2012 年 10 月 24 日 18:15 | #5

    ERROR: RTMP_Connect0, failed to connect socket. 111 (Connection refused)
    failed rtmpdump

    と出て失敗するのですが・・・
    プレイヤーのバージョンが変わったんでしょうか?

  6. タツヤ
    2012 年 10 月 24 日 22:36 | #6

    スクリプトの更新を行いました。詳細は本文をご覧ください。

  7. 匿名
    2012 年 10 月 25 日 13:54 | #7

    以前使っていたスクリプトが仕様変更により動作しなくなってしまったので検索してここにたどり着きました。
    実行してみると
    >: not foundnew.sh: 2: rec_radiko_new.sh:
    >rec_radiko_new.sh: 151: rec_radiko_new.sh: Syntax error: “elif” unexpected (expecting “then”)
    と出てしまいます。
    スクリプトに関しての知識が皆無のためエラー原因が分かりません。どこがおかしいのでしょうか

  8. i-love-radio
    2012 年 10 月 26 日 15:53 | #8

    仕様変更への対応、お疲れ様でした。
    ありがたく使わせて頂きます!

  9. 匿名
    2012 年 10 月 27 日 11:11 | #9

    #5です
    すばやい更新ありがとうございました!

  10. is
    2012 年 10 月 28 日 13:33 | #10

    古いPowerPC G3のMacに、debian6.0.6をインストールした環境で、
    シェルスクリプト、ありがたく使わせていただいています。

    自分のスキルが低くて、書かれたものを実行することしかできませんが、
    一応動作報告させていただきます。感謝!

  11. foo
    2012 年 10 月 29 日 09:58 | #11

    更新ありがとうございます!
    動作しましたー
    Ubuntu 10.04.4 LTS on Hyper-V Server 2008 R2 @ 兵庫