前回解読したように
JSONデータの処理
JSON
JSONデータは"{ }"
{"main":{"site_id":"0442","program_name":"音の風景","mode":0,"media_type":"radio",
"media_code":"05,06,07","media_name":"NHKラジオ第1、NHKラジオ第2、NHK-FM",
のようなデータの場合、main[]という大きなデータブロック
site_id = "0442"
program_name = "音の風景"
mode = 0
media_type = "radio"
といった変数が存在していることを示します。
JSONデータは
1 #!/usr/bin/python
2
3 import sys,pprint,json,requests
4
5 query = sys.argv[1].split("=")[1] # "p=0442_01_3834250"の"0442_01_3834250"を取り出す
6 q = []
7 q = query.split("_")
8 # print(q)
9
10 base_url = 'https://www.nhk.or.jp/radioondemand/json/'
11 bangumi_id = "{}/bangumi_{}_{}.json".format(q[0], q[0], q[1])
12 url = base_url + bangumi_id
13 # print(url)
14
15 response = requests.get(url)
16 # print(response.text)
17
18 prog_info = json.loads(response.text)
19 pprint.pprint(prog_info)
3行目でインポートしている各モジュールのうち、sys、pprint、jsonはPython本体に付属しているものの、requestsは別途配布されているので、見つからない場合は "pip install requests" する必要があります。
HTTP回りを処理するPython付属のモジュールはurllibですが、requestsモジュールを使う方が簡単に書けます。Plamo Linuxではrequestsはデフォルトでインストールするようにしています。
5行目は引数として指定された"p=0442_
このq[]を使って、前回見た"player_
このスクリプトを"get_
$ python ./get_json.py 'p=0308_01_3837107' {'main': {'cast': None, 'corner_detail': None, 'corner_id': '01', 'corner_name': None, 'detail_list': [{'file_list': [{'aa_contents_id': '[radio]vod;名曲スケッチ「フルート協奏曲\u3000' 'ニ長調\u3000' 'K。314」\u3000' '「協奏交響曲\u3000' 'K。364」;r3,130;2023012670918;2023-01-26T00:50:00+09:00_2023-01-26T01:00:00+09:00', 'aa_measurement_id': 'vod', 'aa_vinfo1': '名曲スケッチ「フルート協奏曲\u3000' 'ニ長調\u3000K。314」\u3000' '「協奏交響曲\u3000K。364」', 'aa_vinfo2': 'r3,130', 'aa_vinfo3': '2023012670918', 'aa_vinfo4': '2023-01-26T00:50:00+09:00_2023-01-26T01:00:00+09:00', 'close_time': '2023-02-02T01:00:00+09:00', 'file_id': '3837551', 'file_name': 'https://nhks-vh.akamaihd.net/i/radioondemand/r/308/s/stream_308_9bac22215bc547c9d67b69c1774452b2.mp4/master.m3u8?set-akamai-hls-revision=5', 'file_title': '名曲スケッチ「フルート協奏曲\u3000' 'ニ長調\u3000K。314」\u3000' '「協奏交響曲\u3000K。364」', ....
「名曲スケッチ」
ざっと見たところ、"0308_
$ cat -n get_json.py .... 21 for i in prog_info['main']['detail_list'] : 22 for j in i['file_list']: 23 if j['file_id'] == q[2] : 24 pprint.pprint(j) $ python ./get_json.py 'p=0308_01_3837107' {'aa_contents_id': '[radio]vod;名曲スケッチ「序曲“1812年”」\u3000' '「イタリア奇想曲」;r3,130;2023012470437;2023-01-24T00:50:00+09:00_2023-01-24T01:00:00+09:00', 'aa_measurement_id': 'vod', 'aa_vinfo1': '名曲スケッチ「序曲“1812年”」\u3000「イタリア奇想曲」', 'aa_vinfo2': 'r3,130', 'aa_vinfo3': '2023012470437', 'aa_vinfo4': '2023-01-24T00:50:00+09:00_2023-01-24T01:00:00+09:00', 'close_time': '2023-01-31T01:00:00+09:00', 'file_id': '3837107', 'file_name': 'https://nhks-vh.akamaihd.net/i/radioondemand/r/308/s/stream_308_3c1e4c834be190baf52dc08cc16d1c5f.mp4/master.m3u8?set-akamai-hls-revision=5', 'file_title': '名曲スケッチ「序曲“1812年”」\u3000「イタリア奇想曲」', 'file_title_sub': '', 'onair_date': '1月24日(火)午前0:50放送', 'open_time': '2023-01-24T01:00:00+09:00', 'seq': 1, 'share_url': 'https://www2.nhk.or.jp/radio/pg/sharer.cgi?p=0308_01_3837107'}
file_
配信データの録音
専用プレイヤーへのリンクから聴きたい番組のURLやタイトルを取り出せるようになったので、次はこの番組を録音してみましょう。このような処理には動画処理用万能ツールffmpegが便利です。
ffmpegには膨大な機能とそれを指定するためのオプションがあるものの、あまり凝ったことをしないのなら、入力元のURLと出力先のファイル名を指定するだけで利用できます。まずはコマンドラインからテストしてみます。
$ ffmpeg -i 'https://nhks-vh.akamaihd.net/i/radioondemand/r/308/s/stream_308_3c1e4c834be190baf52dc 08cc16d1c5f.mp4/master.m3u8?set-akamai-hls-revision=5' output.mp4 ffmpeg version 4.3.3 Copyright (c) 2000-2021 the FFmpeg developers built with gcc 11.1.0 (GCC) ... [hls @ 0x9ab000] Skip ('#EXT-X-VERSION:3') [hls @ 0x9ab000] Skip ('#EXT-X-INDEPENDENT-SEGMENTS') [hls @ 0x9ab000] Opening 'https://vod-stream.nhk.jp/radioondemand/r/308/s/stream_308_3c1e4c834be19 0baf52dc08cc16d1c5f/index_48k.m3u8?aka_me_session_id=AAAAAAAAAAAZxdRjAAAAAMItWy+n4fZKo860UpflRwlVi p+F8LhDEFEYA%2fuAA+86w%2fGi6kXpOAE2PtytMb0AJMzJGTF41dgU&aka_media_format_type=hls' for reading [hls @ 0x9ab000] Skip ('#EXT-X-VERSION:3') [hls @ 0x9ab000] Opening 'https://vod-stream.nhk.jp/radioondemand/r/308/s/stream_308_3c1e4c834be19 0baf52dc08cc16d1c5f/serve.key?aka_me_session_id=AAAAAAAAAAAZxdRjAAAAAMItWy+n4fZKo860UpflRwlVip+F 8LhDEFEYA%2fuAA+86w%2fGi6kXpOAE2PtytMb0AJMzJGTF41dgU' for reading .... [aac @ 0xc10b00] Packet corrupt (stream = 0, dts = NOPTS). [aac @ 0xbefbc0] TYPE_FIL: Input buffer exhausted before END element found Error while decoding stream #0:0: Invalid data found when processing input size= 9242kB time=00:10:00.02 bitrate= 126.2kbits/s speed=40.4x video:0kB audio:9132kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 1.197712% [aac @ 0xc1cb80] Qavg: 731.169 $ file output.mp4 output.mp4: ISO Media, MP4 Base Media v1 [IS0 14496-12:2003]
あれこれエラーメッセージは出ているもののoutput.
さて、それではこの機能をスクリプトに組み込んでみましょう。Pythonにはffmpegを使うためのバインディング
21 for i in prog_info['main']['detail_list'] :
22 for j in i['file_list']:
23 if j['file_id'] == q[2] :
24 # pprint.pprint(j)
25 URL = j['file_name']
26 filename = j['file_title'].replace('\u3000','_') + '.mp4'
27 # print(URL, filename)
28 break
29
30 import subprocess
31 cmd = ['ffmpeg', '-i', URL, filename]
32 # print(cmd)
33 subprocess.run(cmd)
25行目と26行目でJSONデータから配信元のURLと保存先のファイル名を作成し、31行目で先に試したffmpegのコマンドラインを作成、33行目でそれを実行する、という流れです。
26行目の"replace()"はファイル名中の全角空白
さて、それでは実際に録音できるか試してみましょう。
$ python ./json_01.py 'p=0308_01_3837107' ffmpeg version 4.3.3 Copyright (c) 2000-2021 the FFmpeg developers ... Input #0, hls, from 'https://nhks-vh.akamaihd.net/i/radioondemand/r/308/s/stream_308_3c1e4c834b e190baf52dc08cc16d1c5f.mp4/master.m3u8?set-akamai-hls-revision=5': Duration: 00:10:00.06, start: 1.999989, bitrate: 0 kb/s Program 0 Metadata: variant_bitrate : 48625 Stream #0:0: Audio: aac (HE-AAC), 48000 Hz, stereo, fltp, 46 kb/s ... Output #0, mp4, to '名曲スケッチ「序曲“1812年”」_「イタリア奇想曲」.mp4': Metadata: encoder : Lavf58.45.100 Stream #0:0: Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s ... [aac @ 0xe54e40] TYPE_FIL: Input buffer exhausted before END element found Error while decoding stream #0:0: Invalid data found when processing input size= 9242kB time=00:10:00.02 bitrate= 126.2kbits/s speed=32.9x video:0kB audio:9132kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 1.197712% [aac @ 0xe54500] Qavg: 731.169 $ ls *mp4 名曲スケッチ「序曲“1812年”」_「イタリア奇想曲」.mp4
後述するような問題はあるものの、とりあえず今回作った骨組みだけのスクリプトでも聴き逃しサービスの番組をダウンロードできました。
今回作成したスクリプトは実際に動くものの、試してみられた方は気づくように、長い番組を録音しようとしても頭から十数分程度しか録音できず、録音したデータにも音飛びやノイズが目立ちます。
あれこれ試してみた結果、どうやらこれはエラーメッセージ等が示すように、録音に利用しているffmpegが期待するHLSの書式と聴き逃しサービスが提供しているHLSの書式が少し異なっているためで、ffmpegの代わりにgstreamerやvlcを使うと多少は改善するようです。次回はそのあたりの改善方法と番組情報JSONデータをさらに利用する方法を考えてみます。