Skip to main content

radikoでラジオ番組をタイムフリー録音してGoogleドライブにアップロードする

概要

radikoの指定した番組の放送終了後にVPS上でタイムフリー録音をダウンロードし、Google Driveにアップロードするようにしました。(自分のみの私的利用を目的としています)

わたしはラジオが好きでいつも作業したりコードを書いたりしながら聞いています。radikoのタイムフリー録音を使うと、1週間以内であれば放送終了後の番組を聞くことができるのですが、1週間以上経っても後から聞き返したくなることがあります。

番組をダウンロードするフリーソフトとしては、らじれこという優れたものがあります。これを活用して、1週間に1回、その週の番組をまとめてダウンロードしていました。しかし、聞く番組が増えてくると手でダウンロードボタンを押すのが面倒になってきますし、ダウンロードをし忘れることもあります。

技術屋としては技術で解決したいところです。色々調べてみると、タイムフリー録音するスクリプトを先人が作ってくれていましたので、それを活用して自動で録音する仕組みを作りました。

仕組みとしては以下のようになっています。

1個目と2個目については、エラーが発生した場合はtry~exceptでつかまえてSlackにwebhookで通知しています。

技術選定の理由はこんな感じです。

  • VPS
    • radikoへアクセスするアウトバウンドの通信がそれなりにあるので、通信量に課金がされるGCPなどのクラウドではなく、元々借りていた通信量に課金がされないVPSを利用。
      • とりあえずcronで実行できるので楽ですね。cronが取っ散らかっていく問題はありますが…
      • VPSへのコードデプロイはGitHubのmainブランチにpushしたらVPS上でコードをpullするGitHub Actionsで行っています。
      • ちなみにConoHa(東京リージョン)ではradikoのフリープランでも東京の番組が聞けます。XServer VPSではプレミアムプランでなければ聞くことができませんでした。(サーバがあるリージョンの問題っぽい)
  • Googleドライブ
    • Googleドライブからローカルへのダウンロードに通信量がかからず、選択した容量で月額決まった料金となる上に、先述の通り、GoogleドライブはWindowsからネットワークドライブのように扱えて便利なため。スマートフォンからもアプリでアクセス可能。
      • 最初は何も考えずGCPのCloud Storageを使おうとしたのですが、このメリットを思い出して変更しました。技術選定大事。
      • 保存容量が15GBまでなら無料、200GBでも月380円で済みます。ちなみに1時間番組1本で20MB程度です。

Tips

以下、実装の過程で出会った技術的なTipsを書き留めます。

Pythonでのsubprocess.run()のエラーハンドリング

Pythonからコマンドを実行するときに使うsubprocess.run()ですが、正常に実行されたときとエラーが起きた時で処理を分けて、エラーの場合はエラーメッセージを取得したいというケースがあります。

解決策としては、引数にcapture_output=Truetext=Trueを指定します。前者により出力を受け取り、後者により出力をbyte型ではなく文字列で受け取ります。 リターンコード、標準出力、標準エラー出力はreturncode, stdout, stderrで受け取ることができます。

# 任意のコマンド
cmd = "bash hoge.sh"
res = subprocess.run(cmd, shell=True, text=True, capture_output=True)
if res.returncode == 0:
    logger.info(f"success | {res.stdout}")
else:
    logger.error(f"error | {res.stderr}")

引数にcheck=Trueを指定すると、returncodeが0ではないときにsubprocess.CalledProcessErrorの例外を起こすことができます。

# 上の例と同じことができる
cmd = "bash hoge.sh"
try:
    res = subprocess.run(cmd, shell=True, text=True, capture_output=True, check=True)
    logger.info(res.stdout)
except subprocess.CalledProcessError as e:
    logger.error(e)
    logger.error(res.stderr)

Pythonスクリプト中での相対パスを固定する

このようなディレクトリ構造において、以下のスクリプトをmain.pyで保存します。

hoge
|-- fuga
    |-- main.py
import os

print(os.getcwd())

このスクリプトは、カレントディレクトリがhogeかfugaかで返ってくる値が異なります。

hoge $ python ./fuga/main.py
hoge

hoge/fuga $ python main.py
hoge/fuga

これでは、コード中で相対パスでファイルを読み込んでいるとき(プロジェクトディレクトリであるfugaを起点にするようなパターン)、cronなどでシェルから実行する場合、カレントディレクトリによって挙動が変わり不便です。

Python>=3.9では、以下のようにos.chdir(os.path.dirname(__file__))を足してあげることで、コードが存在するディレクトリを起点にそれより下のコードが実行されて便利です。

import os

os.chdir(os.path.dirname(__file__))
print(os.getcwd())

参考:Pythonで実行中のファイルの場所(パス)を取得する__file__ | note.nkmk.me