続・玩式草子 ―戯れせんとや生まれけん―

第64回最近の『らじる★らじる』(その2)

インターネット同時配信を調べた前回に引き続き、今回は「らじる★らじる」のもう1つの魅力である聴き逃し配信を調べてみましょう。⁠聴き逃し配信」はNHKラジオで放送(配信)した番組を、一週間、何度でも聴くことができるサービスです。

以前は、時間調整用の埋め草的な番組(⁠⁠名曲ヒットパレード」「ジャズSPアワー」等)や定型的なニュース(⁠⁠株式市況」「気象通報」等)など「聴き逃し配信」には登録されなかった番組もありました。しかしながら、来年度に予定されている放送波の一波削減に伴い、⁠らじる★らじる」も再放送の機会として積極的に使うことになったようで、最近では5分程度の番組(⁠⁠エターナルナンバーズ」「みんなのうた」等)「聴き逃し配信」で聴けるようになっています。

また、従来は別ページで管理されていた語学講座や高校講座も同じページから利用できるようになり、一覧ページはずいぶん賑やかです。

「らじる★らじる」の聴き逃し配信のページ
「らじる★らじる」の聴き逃し配信のページ

「インスペクター」による調査

まずは前回同様、Firefoxの「ウェブ開発ツール⁠⁠-インスペクターを使って聴き逃しサービスのページを調べてみます。今回対象にしたのは、⁠名曲スケッチ」という10分間の番組です。

インスペクターを用いた配信ページの調査
インスペクターを用いた配信ページの調査

表示画面の色を手掛かりに調べていくと、⁠nol_audio_player_base⁠というclass名を持つ<div>タグ部で配信元のURLが見つかりました。以前は⁠nhk.or.jp⁠ドメインから配信していたように記憶していますが、最近では⁠vod-stream.nhk.jp⁠という配信専用ドメインが用意されたようです。

<div class="nol_audio_player_base" data-hlsurl="https://vod-stream.nhk.jp/radioondemand/r/K7NR257MJ5/s/stream_K7NR257MJ5_dd92abfe668c31bb2f75f56b92ad6ec8/index.m3u8" data-aa="[radio]vod;名曲スケッチ 「波を越えて」「ドナウ川のさざ波」;r2,130;2025121666476;2025-12-16T09:45:00+09:00_2025-12-16T09:55:00+09:00"><a href="javascript:slidedownPlayer(0);">このページで再生</a></div>

“data-hlsurl⁠として参照されているURL(https://vod-stream.nhk.jp/....)はAPI経由でサーバーが返す一時的なURL(署名付きURL)で、どの程度持続するかはよくわからないものの、コピペしてvlc等のメディアプレイヤーからも再生できたので、少なくとも配信期間中は同じURLで利用できそうです。

配信元がわかったので、vlcを使って直接ダウンロードしてみたところ、最初のハンドシェイク時に多少のエラーは出るものの、特に問題なく配信データをダウンロードできました。下記の実行例では、--sout=file/ts:の指定で、ダウンロードしたデータをフォーマット変換等は行わずにMPEG-TS形式で⁠test_output.ts⁠に保存しています。

$ vlc --play-and-exit --intf dummy "https://vod-stream.nhk.jp/radioondemand/r/K7NR257MJ5/s/stream_K7NR257MJ5_dd92abfe668c31bb2f75f56b92ad6ec8/index.m3u8" --sout=file/ts:test_output.ts
VLC media player 3.0.22-rc2 Vetinari (revision 3.0.22-rc2-3-g02102a8890)
[0000000010d7e5a0] dummy interface: using the dummy interface module...
[00007fcbf4007480] gnutls tls client error: TLS handshake error: Error in the push function.
[00007fcbf4007480] main tls client error: TLS session handshake error
...
[00007fcbf4232420] adaptive demux: Changing stream format Unknown -> Packed AAC
[00007fcbec004cb0] mpeg4audio demux packetizer: AAC channels: 2 samplerate: 24000
[0000000010cf7e50] main playlist: end of playlist, exiting
$ ls -lh test_output.ts
-rw-r--r-- 1 kojima users 6.6M 12月 15日  19:47 test_output.ts
$ file test_output.ts
test_output.ts: MPEG transport stream data

前回紹介したラジオとの同時配信の場合、配信データはリアルタイムで流れてくるため、10分の番組を録音するには10分かかります。一方、聴き逃し配信ではあらかじめ用意されているデータを受け取るだけなので、ずっと短い時間で録音(ダウンロード)できます。今回の例では、timeコマンド経由で試したところ10分の番組が15秒ほどでダウンロードできました。もちろん、ダウンロードの指定(⁠⁠--sout=file/ts:..⁠⁠)をしなければ、vlcは時間の同期を取りながら10分間の番組として再生します。

「デバッガー」による聴き逃し配信の解析

とりあえず聴き逃し配信をダウンロードすることは可能になったものの、配信元URLを調べるために毎回インスペクターを立ち上げるのは面倒です。また、保存した録音データには番組情報等を記録しておきたいのがエアチェック・マニアのさがでしょう(苦笑⁠⁠。そこで、デバッガーを用いて、聴き逃し配信の仕組みを調べてみることにしました。

デバッガーによるJavaScriptの調査
デバッガーによるJavaScriptの調査

デバッガーで調べたところ、聴き逃し配信の各ページは⁠ondemand/detail.html⁠?p=K7NR257MJ5_01というクエリで番組IDを与え、このページで実行されるondemand_detail.jsというJavaScriptがデータベースから番組情報を取り出すようです。⁠ondemand_detail.js⁠を眺めたところ、先頭部分に番組情報データベースへのAPIらしきURI(rapiuri)が定義されていました。

        /////////らじる★らじる 聴き逃し(詳細)/////////
        var pagedomain = document.domain,
            apipath = pagedomain.indexOf("stg-") > -1 || pagedomain.indexOf("pre-") > -1 || pagedomain.indexOf("dev-") > -1 || pagedomain.indexOf("ter-") > -1 ? "stg-" : "",
            rapiuri = "https://www.nhk.or.jp/" + apipath + "radio-api/app/v1/web/ondemand/series?site_id={siteid}&corner_site_id={cornerid}",
            itemperpage = 10,

途中の⁠apipath⁠は3行目の処理を見る限りテスト環境用の設定で、本番環境では⁠https://www.nhk.or.jp/radio-api/app/v1/web/ondemand/series?site_id={siteid}&corner_site_id={cornerid}⁠がデータベース用のAPIでしょう。

さて、ここで指定するsiteidcorneridは何かな、と、スクリプトを調べていくと、jQueryのドキュメント・レディ部にそれらしい処理がありました。

        $(function() {
            if (getQuery("p")) {
                var a = getQuery("p");
                if (a.indexOf("_") > -1 && a.length < 14) {
                    var e = a.split("_");
                    $.getJSON(rapiuri.replace("{siteid}", e[0]).replace("{cornerid}", e[1]), function(s) {

ざっと見、クエリとして与えた⁠p=K7NR257MJ5_01⁠からK7NR257MJ5_01⁠a⁠に代入し、簡単なチェックをしてから⁠_⁠(アンダースコア)で分割、前半のK7NR257MJ5⁠rapiuri⁠siteid⁠、後半の01corneridに置換して、完成したURIからJSONデータを取り出す($.getJSON()⁠⁠、という処理のようです。

ということで、さっそくこのURIを叩いてみたところ、無事JSON形式で番組情報が返ってきました。

$ curl 'https://www.nhk.or.jp/radio-api/app/v1/web/ondemand/series?site_id=K7NR257MJ5&corner_site_id=01' | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  5301 100  5301   0     0 43230     0  --:--:-- --:--:-- --:--:-- 43450
{
  "id": 174,
  "title": "名曲スケッチ",
  "radio_broadcast": "R2,FM",
  "schedule": "【ラジオ第2】月・火・木・金 午前9時45分、午後10時00分 /土曜 午前8時30分、午前11時45分【NHK-FM】火曜~金曜 午前1時50分/土曜 午後0時15分、午後9時50分/日曜 午前5時40分",
  "corner_name": "",
  "thumbnail_url": "https://img.nhk.jp/static/assets/images/radioseries/rs/K7NR257MJ5/K7NR257MJ5-eyecatch_56ba75a4f8ae94f3bf0be92216135097.png",
  "series_description": "「名曲アルバム」から生まれたラジオ番組。世界の名曲を2曲お届けします。",
  "series_url": "https://www.nhk.jp/p/sketch/rs/K7NR257MJ5/",
  "share_text_title": "名曲スケッチ",
  "share_text_url": "https://www.nhk.or.jp/radioondemand/share/174_1389.html",
  "share_text_description": "#radiru",
  "episodes": [
    {
      "id": 4289434,
      "program_title": "名曲スケッチ 「波を越えて」「ドナウ川のさざ波」",
      "onair_date": "12月16日(火)午前9:45放送",
      "closed_at": "2025年12月23日(火)午前9:55配信終了",
      "stream_url": "https://vod-stream.nhk.jp/radioondemand/r/K7NR257MJ5/s/stream_K7NR257MJ5_dd92abfe668c31bb2f75f56b92ad6ec8/index.m3u8",
      "aa_contents_id": "[radio]vod;名曲スケッチ 「波を越えて」「ドナウ川のさざ波」;r2,130;2025121666476;2025-12-16T09:45:00+09:00_2025-12-16T09:55:00+09:00",
      "annotation_title": "",
      "annotation_url": "",
      "program_sub_title": " イヴァノヴィチ作曲、岩河三郎・編曲 (管弦楽)東京フィルハーモニー交響楽団 (指揮)渡邉暁雄"
    },
    {
      "id": 4289203,
      "program_title": "名曲スケッチ 「歌劇“椿姫”前奏曲」「歌劇“ノートルダム”間奏曲」",
      "onair_date": "12月15日(月)午前9:45放送",
      "closed_at": "2025年12月22日(月)午前9:55配信終了",
      ...

このように、番組IDを指定して番組情報DBのAPIを叩くと、⁠title⁠⁠schedule⁠⁠、⁠series_descripition⁠といった番組の概要と共に、聴き逃し配信で聴くことのできる各回の情報をepisodesというリストに収めたJSONデータが得られます。先に見た配信プレイヤーはこれらのデータを使って表示画面を作っているのでしょう。ざっと見、そう難しい処理でも無さそうなのでPythonでトレースしてみました。

Pythonを使った聴き逃し配信解析ツール

Pythonでは、HTTP回りの処理をするrequests、JSONデータを扱うjson等、豊富なモジュールが揃っています。とりあえず今回は「指定した引数を元に番組情報データベースを叩いて、その結果を整形・表示する」程度の処理にしてみました。

#!/usr/bin/python
#-*- coding: utf-8 -*-

import sys, requests, json

bangumi_id = sys.argv[1]

'''
p=K7NR257MJ5_01 みたいな指定の場合、'p='の部分をカットする
'''
if 'p=' in bangumi_id:
    bangumi_id = bangumi_id.replace('p=','')

(pr, ser) = bangumi_id.split('_')
api_url = f"https://www.nhk.or.jp/radio-api/app/v1/web/ondemand/series?site_id={pr}&corner_site_id={ser}"
res = requests.get(api_url)

'''
週一の番組では'episodes'は一つだが、帯番組だと複数の番組情報が返るので、
とりあえず一覧しておく
'''
bangumi_info = res.json()['episodes']
print(f"bangumi count:{len(bangumi_info)}")
for i in range(len(bangumi_info)) :
    print(f"title:{bangumi_info[i]['program_title']}")
    print(f"date:{bangumi_info[i]['onair_date']}")
    print(f"url:{bangumi_info[i]['stream_url']}")
    print(f"subtitle:{bangumi_info[i]['program_sub_title']}")
    print("")

このスクリプトを⁠dl_test.py⁠という名前で保存し、引数として「聴き逃し配信」「クラシックの庭」ページから得られる⁠p=LG96ZW5KZ4_01⁠を与えて実行してみると、以下のような結果になりました。

$ python ./dl_test.py  p=LG96ZW5KZ4_01
bangumi count:4
title:クラシックの庭 ヨハン・シュトラウスのワルツ「美しく青きドナウ」
date:12月17日(水)午後2:00放送
url:https://vod-stream.nhk.jp/radioondemand/r/LG96ZW5KZ4/s/stream_LG96ZW5KZ4_66adc770838490d5c05069b2aecd9116/index.m3u8
subtitle:登レイナ

title:クラシックの庭 チャイコフスキーのピアノ協奏曲第2番
date:12月16日(火)午後2:00放送
url:https://vod-stream.nhk.jp/radioondemand/r/LG96ZW5KZ4/s/stream_LG96ZW5KZ4_6e810514ce557f3b1a990b223a5b1a6f/index.m3u8
subtitle:登レイナ

title:クラシックの庭 ショスタコーヴィチのチェロ協奏曲第1番
date:12月15日(月)午後2:00放送
url:https://vod-stream.nhk.jp/radioondemand/r/LG96ZW5KZ4/s/stream_LG96ZW5KZ4_97908a3c6c1be032ed43c89a05bcd079/index.m3u8
subtitle:登レイナ

title:クラシックの庭 ベートーベンの交響曲第6番「田園」
date:12月11日(木)午後2:00放送
url:https://vod-stream.nhk.jp/radioondemand/r/LG96ZW5KZ4/s/stream_LG96ZW5KZ4_8c033fbdb28646542514cf937897eaa2/index.m3u8
subtitle:田添菜穂子

得られたURLを元に、⁠subprocess⁠モジュール経由で⁠vlc⁠⁠ffmpeg⁠を外部コマンドとして実行し、配信データをダウンロード、結果を適切な形式(AAC等)に変換してmp4コンテナに格納、その際、⁠mutagen⁠モジュールを使ってtitleやdate、subtitleといったデータをタグ情報に書き込む、といった一連の処理を追加していけば「聴き逃し配信ダウンローダー」になります。しかしながら、このデータだけでは寂しすぎると感じるのは筆者だけではないでしょう。そのあたりを次回の宿題としておきます。


今回は聴き逃し配信のページから番組IDを調べ、そのIDを元に番組情報データベースから配信元URLや番組タイトル等を得るあたりを調べてみました。とりあえずこれだけでもダウンローダーは書けるものの、しばらくイジっていると、楽曲情報や出演者等、より詳しい情報も拾ってみたくなります。次回はそのあたりの機能拡張について考えてみる予定です。

おすすめ記事

記事・ニュース一覧