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

概要

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

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

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

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

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

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

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

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