T2-Lab

RaRaPla ― ダークで軽快なラジオプレイヤーをつくってみた

Date :
Categories : Tech

Windows でラジオを聴くのに、余計な重量物はいりません。Qt と FFmpeg、ちょっと賢い補助を添えて、radikoRadio Browserを同じ画面にまとめました。名前は RaRaPla。見た目は黒くて静か、触るとキビキビ。スクロールはヌルッと、再生はカチッと。派手さより体感速度に振っています。

この記事では、リポジトリの見どころと設計の癖、そして動かし方をまとめます。

何ができるのか

radiko の現在地エリアで聴ける局を自動一覧化して、ワンクリックで再生。ついでにRadio Browser のコミュニティ局も検索・再生できます。UI は PySide6(Qt)、テーマは qdarkstyle ベースに追加スタイル。右にチャンネル、左に詳細とプレイヤー。出力デバイスの切替と音量は素直にコンボボックスとスライダーで。

radiko の地域制限は当然そのままです。無茶はしません。その代わり、ローカルで動く軽量プロキシが“再生しやすい形に整えてから”プレイヤーへ渡す 仕立てになっています。プレイリストを読み解いて、URL を手元向けに組み替える通訳係だと思ってください。再生機能とネット側の事情の間で、気まずい沈黙を埋める役です。

仕組みの中心

src/rarapla/proxy/radiko_proxy.py が核。aiohttp で /live/{station}.m3u8 を受けたら、内部で Streamlink を使って master m3u8 を取得し、相対も絶対も含めてセグメントやサブプレイリストの URL をローカル宛てに書き換えます。Qt 側から見えるのは常にローカルの m3u8。実体のメディアは /seg がいい感じに橋渡しします。

403 が出たら解決キャッシュを捨てて再解決。回線が気まぐれでも、数回は落ち着いてやり直す仕込みです。既定ポートは 3032、埋まっていたら空いてるポートにスライド。

UI の小技

ui/widgets/SmoothScrollMixin が QScrollArea と QListWidget にミックスインされ、ピクセル単位スクロール+減速アニメーションを実現。標準のガタつきは記憶の彼方へ。 チャンネルはカード表示で、英数字だらけの長い名称にはゼロ幅スペース挿入+エリプシスで折り合いを付けます。画像は QNetworkAccessManager で読み込み、UA も指定。 詳細パネルは画像を幅 340px に整えて、テキストは HTML(リンク可)。

コントロールとワーカー

controllers/now_refresher.py が定期で radiko の“いま放送中”を取り直し、差分だけ UI を更新。controllers/playback_controller.py は再生の司令塔で、radiko 再生中は数分おきに解決をリフレッシュして安定を担保。Radio Browser の直 URL 再生では services/icy_watcher.pyICY メタデータを別スレッドで監視し、曲名などを面倒見よく面板へ反映します。 ネットワークの重い処理は ui/workers/* の QThread 経由。UI を固めるのは悪、という価値観で設計しています。

使い方

Windows 11 での動作を主に想定しています(ほかの OS でも PySide6/QtMultimedia が動くなら挑戦可)。

まず環境作成:

Terminal window
setup_env.bat

中で venv 作成 → 有効化 → pip install -e . までやります。ログは色つき、進捗表示は正義。

起動はこれだけ:

Terminal window
rarapla
:: あるいは
python -m rarapla

初回は rb_presets.json が生成されます。J-POP や jazz、vocaloid といったタグプリセットはここで増減・編集できます。

Windows 配布(Nuitka)

配布用のスタンドアロンは build.bat。Qt の multimedia / platforms プラグインを明示同梱、--include-package=streamlink.plugins なども指定済み。

Terminal window
build.bat

音が出ない系のトラブルは、だいたい Qt Multimedia プラグインの同梱漏れbuild.bat を眺めてから嘆けば、嘆く量が減ります。

ディレクトリの見どころ(抜粋)

data/ は radiko と Radio Browser の薄いクライアント。radiko の番組詳細は Date API → Weekly API の順で取りに行く守備的実装。 services/ は QMediaPlayer を包む PlayerService と、ブロック長を解釈して読む IcyWatcherui/ は MainWindow と各ウィジェット/コントローラ。カード選択 → 別スレッドで番組詳細取得 → プレイヤー準備、の流れが素直に追えます。 proxy/ は再掲の通訳担当。エラー時の再解決やポート回避はここ。 定数は config.py に集約。ウィンドウ高さ、音量レンジ、各タイムアウト、ポーリング間隔までここで統一。

テストと CI

tests/ には、

  • radiko クライアントの時刻固定テスト、Now/Date/Weekly の分岐
  • m3u8 書き換えが 相対・絶対 URL ともに /seg?... へ変換されること
  • 既定ポートが埋まっているときの自動スライド
  • Radio Browser の JSON パースと Channel
  • ロギングの抑制設定

が入っています。GitHub Actions の CI は Windows / Ubuntu で black / flake8 / mypy(strict) / pytest を回します。壊したらバレるやつ。

ハマりどころと回避策

音が出ない → Qt のマルチメディアプラグインを確認。 radiko が 403/404 → プロキシが自動で再解決するので、数秒待ってから再生を入れ直すのが現実的。 番組画像や説明が出ない → そもそも提供されていないケースがあります。

ライセンスと感謝

MIT ライセンスです。radiko のデータ、Radio Browser コミュニティ、Qt / PySide6 とその周辺に感謝。地域や利用条件は各自の居場所のルールに従ってください。ここはインターネット、でも現実の上に建っています。