これはとっくにここにポストしていた気がするけど、実はまだだったのですね。
ここに来るまでけっこう苦労したので載せておきます。
基本的にはriocamposさんのこのページを参考にしました。違いは http://www.nhk.or.jp/radio/config/config_web.xml を毎回参照して、akamaihd.net のURLが変更されていても対応することです。
ではRubyスクリプト。読めばわかるので説明は略。
#!/usr/bin/env ruby
# Usage: record_nhk.rb [options] area station duration outdir
require 'optparse'
require "rexml/document"
require "open-uri"
URL = "http://www.nhk.or.jp/radio/config/config_web.xml"
# MYAREA is one of the followings
# sapporo
# sendai
# tokyo
# nagoya
# osaka
# hiroshima
# matsuyama
# fukuoka
params = ARGV.getopts(nil, 'title:', 'artist:', 'album:')
myarea = ARGV[0]
station = ARGV[1]
duration = ARGV[2]
outdir = ARGV[3]
STDERR.puts "1: fetching config..."
begin
xml = URI.open(URL, open_timeout: 30, read_timeout: 30).read
rescue OpenURI::HTTPError, SocketError, Errno::ECONNREFUSED, Errno::ETIMEDOUT => e
STDERR.puts "Failed to fetch NHK config: #{e.message}"
exit 1
end
STDERR.puts "2: got xml, parsing..."
doc = REXML::Document.new(xml)
radiru_config = nil
doc.each do |e|
if e.kind_of?(REXML::Element) and e.name == "radiru_config"
radiru_config = e
break
end
end
stream_url= radiru_config.elements['stream_url']
area = nil
stream_url.each do |data|
if data.kind_of?(REXML::Element)
if data.elements['area'].text == myarea
area = data
break
end
end
end
case station
when 'r1'
surl = area.elements['r1hls'].text.strip
when 'r2'
surl = area.elements['r2hls'].text.strip
when 'fm'
surl = area.elements['fmhls'].text.strip
else
surl = area.elements['fmhls'].text.strip
end
STDERR.puts "3: surl=#{surl}"
now = Time.now.to_s.sub(/(\d\d\d\d-\d\d-\d\d)\s+(\d\d:\d\d):\d\d.*$/){ $1 + '_' + $2.sub(/:/, '-') }
meta = ""
if params['title']
meta += " -metadata title=\"" + params['title'] + "\""
end
if params['artist']
meta += " -metadata artist=\"" + params['artist'] + "\""
end
if params['album']
meta += " -metadata album=\"" + params['album'] + "\""
end
def hms_to_seconds(hms)
h, m, s = hms.split(':').map(&:to_i)
h * 3600 + m * 60 + s
end
timeout_sec = hms_to_seconds(duration) + 60
ffmpeg_cmd = sprintf(
"timeout %d ffmpeg -y -rw_timeout 30000000 -nostdin -t %s -i %s %s -write_xing 0 %s/%s.mp3",
timeout_sec, duration, surl, meta, outdir, now
)
STDERR.puts "4: cmd=#{ffmpeg_cmd}"
lockfile = "/tmp/record_nhk_#{station}_#{myarea}.lock"
File.open(lockfile, File::CREAT, 0644) do |f|
unless f.flock(File::LOCK_EX | File::LOCK_NB)
STDERR.puts "Already running, exiting."
exit 1
end
system(ffmpeg_cmd)
STDERR.puts "5: done"
end
使いかたはcronでこんな感じです。趣味がわかってしまう…
sleep 30は遅延を考慮してですが、もっと多いほうがいいかも。
05 16 * * 0 sleep 30;/home/pi/NHK/record_nhk.rb –artist “DJ日本史” –album “トーク” tokyo r1 00:50:00 /disk/Music/Radio/NHK-R1/DJ
30 19 * * 1-5 sleep 30;/home/pi/NHK/record_nhk.rb –artist “ベスト・オブ・クラシック” –album “クラシック” tokyo fm 01:40:00 /disk/Music/Radio/NHK-FM/Classic
15 21 * * 1-5 sleep 30;/home/pi/NHK/record_nhk.rb –artist “青春アドベンチャー” –album “ラジオドラマ” tokyo fm 00:15:00 /disk/Music/Radio/NHK-FM/Adventure
00 22 * * 6 sleep 30;/home/pi/NHK/record_nhk.rb –artist “FMシアター” –album “ラジオドラマ” tokyo fm 01:00:00 /disk/Music/Radio/NHK-FM/Theatre
05 10 * * 6 sleep 30;/home/pi/NHK/record_nhk.rb –artist “真打ち競演” –album “落語” tokyo r1 00:50:00 /disk/Music/Radio/NHK-R1/Sin-uti
20 19 * * 0 sleep 30;/home/pi/NHK/record_nhk.rb –artist “新日曜名作座” –album “ラジオドラマ” tokyo r1 00:30:00 /disk/Music/Radio/NHK-R1/Meisaku
30 10 * * 1-3 sleep 30;/home/pi/NHK/record_nhk.rb –artist “遠山顕の英会話楽習” –album “英語” tokyo r2 00:15:00 /disk/Music/Radio/NHK-R2/Rakushuu
2026-07-17 追記: いろいろ不具合があったのでClaudeの助言により改良。
