NHKラジオ放送の録音

以前NHKラジオの語学番組をスマホのアプリで録音していたが、そのアプリではできなくなっていたという話が家族からあった。調べてみるとffmpeg を利用して録音できるようであった。

録音時に録音のためのアプリケーションを起動させる必要があるが、実質的に常時OSレベルでは起動しておくことになる。手元のパソコンで常時起動させているものがないが、NASが常時起動しており、そのなかでDockerが使えるのでそれを利用して試してみることにした。

mp3ファイルに録音し、番組の基礎的な情報をmp3ファイルのメタデータに含めるようにする。

NHKラジオ番組

インターネットストリーミング

NHKネットラジオ らじる★らじるで番組がインターネットストリーミングされている。

特定の番組を正確に切り出してその番組だけを録音するのではなく、ストリーミングされているデータを録音するだけであれば、ffmpegで録音できるということを以下などから知った。

らじるらじる m3u8 を ffmpeg で録音する(8放送局)2017/9 以降対応

番組表

また、番組の情報が下記のNHKのサイトからダウンロードできることがわかった。

番組表API

番組表のデータから番組名などを取得することが可能である。番組データが取得できれば、mp3ファイルのメタデータに設定ができる。

番組表APIの扱いなどについては次のWebなどが参考になる。

NHK番組表APIからJSONデータを取得してjqで編集する(データ取得編

録音環境

OS

先にも触れたとおり、Docker上で実行しているが、OSは Ubuntu 16.04.4 LTS である。

コマンド

あらかじめインストールされているコマンドとしてbash, sed を利用している。

インストールしたものとしては以下がある。

ディレクトリ構造

Debian内で利用しているディレクトリは以下の通り。

コマンドのパスは適切に指定してあって実行できようになっているものとする。

スクリプト

/shared/scriptにあるとして記述している。変更する場合にはnhkr1-2.sh などのcronから起動されるスクリプトを修正する必要がある。
実行用スクリプトなのでファイルのパーミッションが実行可能になっていないとcronから起動できない。

  • env.sh
    • 環境の設定用ファイル
  • record.sh
    • 録音するためのShellスクリプト
  • curlnhk.sh
    • 番組表を取得するShellスクリプト
  • select.sh
    • 番組表から当該時刻に放送されている番組情報を抽出するためのShellスクリプト
  • select-??.jq
    • 番組表から当該時刻に放送されている番組情報を抽出するためのjqスクリプト
  • genmeta.sed
    • 抽出された番組のJSONからffmpegの引数形式に変換するsedスクリプト
  • nhkr1-2.sh
    • NHKラジオ第1放送を2分間録音するスクリプト例
  • nhkfm-5.sh
    • NHKラジオFMを5分間録音するスクリプト例

データ

DockerのDebianからアクセスできるNASの領域を/sharedとしたので、以下ではそのようにする。他のディレクトリに変更しても問題がないはず。

  • /shared
  • /shared/NHK
    • 録音データ等はこれ以下に保存される
  • /shared/NHK/nhk.log
    • ログデータのファイル
  • /shared/NHK/HHMM
    • HHは時間、MMは分を表す。
  • /shared/NHK/json
    • NHK番組表のディレクトリ
  • /shared/NHK/json/YYYY-mm-DD.json
    • YYYYは西暦年、mmは月、DDは日を表す。当該日の番組表のJSONファイル

環境変数

環境の設定を env.sh でしている。

録音パラメータのCHANNELとMINUTEの設定は、record.shで行うべきであるが他のスクリプトを動作させるデバッグ用に仮の値を設定している。

番組表の日は0時を過ぎても放送終了までは前日に含まれるためJSONDATEがDATEとは別にある。翌日5時までは前日に含まれるようにとりあえずしているが、違うかも。

env.sh

## 録音パラメータ(番組の地域、チャネルと長さの設定が必要)
export AREA=130		# 東京
export CHANNEL=r2	# ラジオ第2
#export MINUTE=16	# 15+1 (15分番組の場合には若干の伝送のずれを考慮して長めに)
export MINUTE=1	# 1分(テスト用)

## 環境の設定(実行環境に応じて設定が必要)
export TZ=JST-9		# 手元の環境では設定されていないため
export PATH=/usr/local/bin:${PATH}

export TD="+09:00"				# 時差
export DATE=$(date "+%Y-%m-%d")	# 日付
export JSONDATE=$(date --date '5 hours ago' "+%Y-%m-%d")	# 番組表用日付
export TIME=$(date "+%H%M")	# 時刻(ファイル名用)
export TIMESS=$(date "+%H:%M:00")	# 時刻(秒まで)
export SELECTDATE="${DATE}T${TIMESS}${TD}"	# 抽出する日付時刻
export PREFIXDIR="/shared/NHK"	# データディレクトリ
export OUTDIR="${PREFIXDIR}/${TIME}"	# 開始時刻毎に別のディレクトリに
export LOGFILE=${PREFIXDIR}/nhk.log	# ログファイル
export JSONDIR="${PREFIXDIR}/json/" 	# 番組表ディレクトリ 
export JSONFILE="${JSONDIR}${DATE}-${CHANNEL}.json"	# 出力ファイル名

メタデータ付け

録音しmp3ファイルにメタデータを付けるためには、番組表データ取得し、そこから録音している番組のデータを抽出する必要がある。

番組表データ取得

curl を利用して番組データを取得する。番組データは JSON形式で得られる。

${JSONDIR}${DATE}-${CHANNEL}.json に番組表を取得する。

APIでの1日あたりの番組データダウンロード利用回数に制限があるため、ファイルに保存してそれを利用するようにしている。

curlnhk.sh

#!/bin/bash
# source env.sh	# 環境設定(単独で動作確認するためのデバッグ用)

# API アクセスキーの設定
KEY="????????????????????????" # 取得したキーを設定

# JSONDIRディレクトリがなければ作成する
if [ ! -d ${JSONDIR} ]
then
	mkdir ${JSONDIR}
fi

# 既に番組表データがあれば再取得しない
if [ -e ${JSONFILE} ]
then
	exit
fi

# 番組表データを取得する
curl http://api.nhk.or.jp/v2/pg/list/${AREA}/${CHANNEL}/${DATE}.json?key=${KEY} > ${JSONFILE}

番組データの抽出

番組表のjsonデータから当該時刻に放送されている番組を抽出する。

当該時刻が番組の開始時刻以降、終了時刻よりも前のものを jq を利用して抽出する。当該時刻は jq のスクリプトでは selectdate であらわす。jq の起動時に引数で値を設定している(select.sh 参照)。

番組を抽出して、番組データの .title, .subtitle, .act, .service.name, .start_time をそれぞれ、title, description, artist, genre, date の値として出力する。jq と 後の sed のスクリプトを変更すれば録音ファイルに付与するメタデータを変更することができる。

CHANNELによって取り出す要素が異なっているので、別スクリプトに分けている。違いは .list.の後のr1からr3の部分のみである。jq の使い方をよく分かってないのでCHANNELごとに分けているが、1つのファイルまとめることができそうな気がする。

select-r1.jq (ラジオ第1用)

.list.r1[] 
| select (.start_time <= $selectdate ) | select(.end_time > $selectdate ) 
| { title: .title , description: .subtitle , artist: .act, genre: .service.name , date: .start_time }

select-r2.jq (ラジオ第2用)

.list.r2[] 
| select (.start_time <= $selectdate ) | select(.end_time > $selectdate ) 
| { title: .title , description: .subtitle , artist: .act, genre: .service.name , date: .start_time }

select-r3.jq (ラジオFM用)

.list.r3[] 
| select (.start_time <= $selectdate ) | select(.end_time > $selectdate ) 
| { title: .title , description: .subtitle , artist: .act, genre: .service.name , date: .start_time }

select.sh

select-??.jq を利用するシェルスクリプト

#!/bin/bash
# source env.sh	# 環境設定(単独で動作確認するためのデバッグ用)

# 必要な番組情報JSONファイルがなければ取得する
if [ ! -e ${JSONFILE} ]
then
    bash ./curlnhk.sh \
		>> ${LOGFILE} 2>&1
fi

jq --arg selectdate "${SELECTDATE}" -f select-${CHANNEL}.jq  ${JSONFILE} 

たとえば、jqによって出力されるラジオ第2の2018年5月27日23時20分頃のデータは以下の通りであった。

{
  "title": "私の日本語辞典「日本語の数詞をさかのぼる」(4)",
  "description": "青山学院大学名誉教授…安田尚道,【アナウンサー】秋山和平",
  "artist": "青山学院大学名誉教授…安田尚道,【アナウンサー】秋山和平",
  "genre": "NHKラジオ第2",
  "date": "2018-05-27T23:05:00+09:00"
}

メタデータのフォーマット変換

jq で抽出した番組データを入力として、sedで ffmpegの引数の形式に変換する。

genmeta.sed

s/[ \t]*"title":[ \t]*"(.*)",[ \t]*/-metadata title=\1/p
s/[ \t]*"title":[ \t]*"(.*)「(.*)」[ \t]*",[ \t]*/-metadata title=\1「\2」\n-metadata album=\1/p
s/[ \t]*"description":[ \t]*"(.*)",[ \t]*/-metadata album_artist=\1/p
s/[ \t]*"artist":[ \t]*"(.*)",[ \t]*/-metadata artist=\1/p
s/[ \t]*"genre":[ \t]*"(.*)",[ \t]*/-metadata genre=\1/p
s/[ \t]*"date":[ \t]*"(....-..-..)T..:..:..\+..:..".*/-metadata date=\1/p
/^\{/d
d

上記のjq の出力を sed -r -f genmeta.sed で処理した結果の出力は以下のようになる。

-metadata title=私の日本語辞典「日本語の数詞をさかのぼる」(4)
-metadata album_artist=青山学院大学名誉教授…安田尚道,【アナウンサー】秋山和平
-metadata artist=青山学院大学名誉教授…安田尚道,【アナウンサー】秋山和平
-metadata genre=NHKラジオ第2
-metadata date=2018-05-27

録音

ffmpegを利用して録音を行う。

録音の流れ

record.shでは、CHANNELで指定されたストリームからこのコマンドが起動された時点から DURATIONで指定された秒数録音を行う。record.shではストリームや秒数を設定していないので、環境変数にセットしておいてから呼び出す。後の、nhkr1-2.sh のようにする。

ストリーム配信の遅延などがあるために、番組の最初からではなく、少し前から録音されるはず。また、最後が切れてしまわないように、少し長めに録音をする必要がある。

本来は番組情報などを基に、番組部分のみを録音するようにすべきであろう。

record.sh

#!/bin/bash
export JSONFILE=&quot;${JSONDIR}${JSONDATE}-${CHANNEL}.json&quot;	# 出力ファイル名

# logへの時刻の記録
date &gt;&gt; ${LOGFILE} 2&gt;&amp;1

# 番組の時間の秒集の計算
DURATION=<code>expr ${MINUTE} \* 60</code>

# チャネルによってURLを得る
case $CHANNEL in
    r1) PLAYPATH='https://nhkradioakr1-i.akamaihd.net/hls/live/511633/1-r1/1-r1-01.m3u8' 	;;
    r2) PLAYPATH='https://nhkradioakr2-i.akamaihd.net/hls/live/511929/1-r2/1-r2-01.m3u8'	;;
    r3) PLAYPATH='https://nhkradioakfm-i.akamaihd.net/hls/live/512290/1-fm/1-fm-01.m3u8'	;;
    *) exit 1 ;;
esac

# 出力ディレクトリがなければ作成
if [ ! -d &quot;${OUTDIR}&quot; ]
then
    mkdir &quot;${OUTDIR}&quot;
fi

# 録音
## <code>bash ./select.sh | sed -r -f genmeta.sed</code> はメタデータの設定のためなので不要なら削除して良い
ffmpeg \
		-i &quot;${PLAYPATH}&quot; \
        -t &quot;${DURATION}&quot; \
		<code>bash ./select.sh | sed -r -f genmeta.sed</code>  \
        -y   &quot;${OUTDIR}/${HEAD}${CHANNEL}_${DATE}_${TIME}.mp3&quot; \
		&gt;&gt; ${LOGFILE} 2&gt;&amp;1

# logへの時刻の記録		
date &gt;&gt; ${LOGFILE} 2&gt;&amp;1

使用法

番組のチャンネルと時間の長さに応じて実行用スクリプトを作成する。そのスクリプトをcronによって起動するよう設定する。

ここで示したスクリプトでは番組ではなく、開始時刻でファイルができるディレクトリを区別している。家族が同じ時間帯に録音したい別の番組がないとのことであったので、開始時刻によるディレクトリ分けを採用した。

メタ情報から番組情報を取り出してその単位のディレクトリにした方が良いであろう。

実行用シェルスクリプトファイル例

たとえば、ラジオ第1の2分番組なら、nhkr1-2.shのようなファイルを作成する。録音パラメータを環境変数に設定した後にrecord.shを呼び出す。

スクリプトのフルパスを指定していないので、スクリプトが正しく呼び出されるようにカレントディレクトリをスクリプトディレクトリにしておく。

また、そのシェルスクリプトファイルを実行可能にしておく。

nhkr1-2.sh

#!/bin/bash
cd /shared/script	# スクリプトディレクトリ
source env.sh		# 環境設定
## 録音パラメータ(番組のチャネルと長さの設定が必要)
export CHANNEL=r1	# ラジオ第1
export MINUTE=2		# 2分

# 録音の実行
./record.sh

nhkfm-5.sh

ラジオFMの5分番組なら、nhkfm-5.shのようにする。この際にFMのCHANNELはr3である。CHANNELは、NHKの番組表APIのサイトでサービスIDを調べることでわかる。

#!/bin/bash
cd /shared/script	# スクリプトディレクトリ
source env.sh		# 環境設定
## 録音パラメータ(番組のチャネルと長さの設定が必要)
export CHANNEL=r3	# FM
export MINUTE=5		# 5分

# 録音の実行
./record.sh

cronの設定

試したDocker内のUbuntu環境では /etc/cron.d に直接ファイルを作成する必要があった。crontabが使えるなら使った方が良い。

説明の都合上 /shared/script というディレクトリ下にスクリプトすべてがあるとしている。

たとえば、次の cron.conf のファイルを作成し、crontab cron.conf を実行すれば、日、月、火、水の17時45分から2分間ラジオ第1が録音がされる。
crontab cron.conf の実行によって/var/spool/cron/crontabsの配下にユーザ名のファイルが作成される。rootで実行した場合には、/var/spool/cron/crontabs/root が作成され、その内容は cron.conf で指定したものになっているはず。

cron.conf

45 17 * * 0-3 /shared/script/nhkr1-2.sh  

今後

とりあえず動作するようにしただけなので、改善した方が良い点は多い。もっとも、とりあえず録音できて、家族からは特に不満がないようなので必要が出てくるまではこのままで良いことにしておく。