Raspberry PiでNHKラジオを自動録音する

これはとっくにここにポストしていた気がするけど、実はまだだったのですね。
ここに来るまでけっこう苦労したので載せておきます。

基本的には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の助言により改良。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です