From 9936526bf799f38abd5b00dac572dd7aa24de198 Mon Sep 17 00:00:00 2001 From: satomichan Date: Sat, 27 Sep 2025 14:57:17 +0900 Subject: [PATCH] =?utf8?q?NHK=E3=81=AEURL=E5=A4=89=E6=9B=B4=E3=81=AB?= =?utf8?q?=E5=AF=BE=E5=BF=9C.=20=E9=8C=B2=E9=9F=B3=E9=83=A8=E3=82=92=20rad?= =?utf8?q?i.sh=20=E3=81=8B=E3=82=89=E6=96=B0=E8=A6=8F=E5=AE=9F=E8=A3=85?= =?utf8?q?=E3=81=AE=20dl-radiru2.pl=20=E3=81=AB=E5=A4=89=E6=9B=B4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- dl-radiru2.pl | 80 ++++++++++++++++++++++++++++++++++++++++++++++ rec-nhk-radio.pl | 82 ++++++++++++++++++++++++++++++------------------ 2 files changed, 132 insertions(+), 30 deletions(-) create mode 100755 dl-radiru2.pl diff --git a/dl-radiru2.pl b/dl-radiru2.pl new file mode 100755 index 0000000..bdf6178 --- /dev/null +++ b/dl-radiru2.pl @@ -0,0 +1,80 @@ +#!/usr/bin/env -S perl -w + +# NHK らじる★らじる を録音するスクリプト. +# (https://satomichan.jp/rec-radiko) +# +# 録音には ffmpeg を利用しています. +# +# 使い方: +# dl-radiru2.pl <放送局ID> <録音長さ 時間:分:秒> <出力先ファイル名> +# +# <放送局ID> := <地域ID>-<放送ID> +# <地域ID> := sapporo | sendai | tokyo | nagoya | osaka +# | hiroshima | matsuyama | fukuoka +# <放送ID> := r1 | r2 | fm (r2のときは地域IDは不要) +# ex) dl-radiru2.pl osaka-fm 0:05:20 radiradi.mp3 +# +# Copyright 2025 FUKUDA Satomi (https://satomichan.jp/) +# +# Licensed under the Apache License, Version 2.0 (the “License”); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an “AS IS” BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# +# See the License for the specific language governing permissions and +# limitations under the License. + +use strict; +use warnings; + +use utf8; +binmode STDIN, ":utf8"; +binmode STDOUT, ":utf8"; +binmode STDERR, ":utf8"; + +use HTTP::Tiny; #sudo apt install libhttp-tiny-perl +use Encode; +use XML::XPath; #sudo apt install libxml-xpath-perl +use XML::XPath::XMLParser; + +my $config_url = 'https://www.nhk.or.jp/radio/config/config_web.xml'; + +my($station, $duration, $output) = @ARGV; + +$station = 'tokyo-r2' if $station eq 'r2'; +my($area, $wave) = $station =~ /^([a-z]+)-([a-z]+)$/ or die "Bad Station Error: $station"; +my $hls_url = get_hls_url($config_url, $area, $wave); + +#録音実行 +exit system('ffmpeg', '-fflags', '+discardcorrupt', #破損したフレームを破棄 + '-loglevel', 'fatal', #ログレベル 致命的なもののみ出力 + '-i', $hls_url, '-t', $duration, $output); + + + + + +#HLS(.m3u8) の URL を取得 +sub get_hls_url { + my($url, $area, $wave) = @_; + my $resp = HTTP::Tiny->new->get($url); + + if ($resp->{success}) { + my $body = Encode::decode('UTF-8', $resp->{content}); + my $xpath = XML::XPath->new($body); + my $nodeset = $xpath->find("/radiru_config/stream_url/data/area[text()='$area']/../${wave}hls/text()"); + foreach my $a_node ($nodeset->get_nodelist) { + #最初の1つだけを返す + return XML::XPath::XMLParser::as_string($a_node); + } + + }else{ + die "Failed GET: $url"; + } + +} + diff --git a/rec-nhk-radio.pl b/rec-nhk-radio.pl index 2dc04de..c08fe69 100755 --- a/rec-nhk-radio.pl +++ b/rec-nhk-radio.pl @@ -3,9 +3,6 @@ # 指定時刻に NHK らじる★らじる を録音するための at job を発行するツール. # (https://satomichan.jp/rec-radiko) # -# 録音には radish (https://github.com/uru2/radish) を利用しています. -# m4a形式で録音後, mp3形式に変換をします. -# # 使い方は引数なしで実行すると表示されます. # # Copyright 2024-2025 FUKUDA Satomi (https://satomichan.jp/) @@ -35,17 +32,19 @@ use Getopt::Long qw(:config posix_default no_ignore_case gnu_compat); use Time::Piece; use Encode; +my $FILE_PERMISSION = 644; + # コマンドのフルパスを取得 my $AT = `which at | tr -d '\n'` or die 'at がインストールされていないか, パスが通っていません.'; -my $RADISH = `which radi.sh | tr -d '\n'` or die 'radi.sh がインストールされていないか, パスが通っていません.'; my $FFMPEG = `which ffmpeg | tr -d '\n'` or die 'ffmpeg がインストールされていないか, パスが通っていません.'; my $BASH = `which bash | tr -d '\n'`; my $ECHO = `which echo | tr -d '\n'`; my $SLEEP = `which sleep | tr -d '\n'`; my $RM = `which rm | tr -d '\n'`; my $CHMOD = `which chmod | tr -d '\n'`; +my $DL = `which dl-radiru2.pl | tr -d '\n'` or die 'dl-radiru2.pl がインストールされていないか, パスが通っていません.'; my $GETTT = `which get-nhk-title.pl | tr -d '\n'`; @@ -58,21 +57,15 @@ die "保存先 $DIR_SAVE_TO に書き込み権限がありません." -# 動作モード +# オプション解析 my $is_check_mode = 0; my $is_verbose_mode = 0; -my $is_conv_to_mp3 = 1; -my $SLEEP_TIME_SEC = 5; -my $FILE_PERMISSION = 644; - - - -# オプション解析 -my %opts; -GetOptions( \%opts, ("check|c", "verbose", "no-conversion-to-mp3|n") ); -$is_check_mode = 1 if $opts{'check'}; -$is_verbose_mode = 1 if $opts{'verbose'}; -$is_conv_to_mp3 = 0 if $opts{'no-conversion-to-mp3'}; +my $offset_time_sec = 35; #開始オフセット(秒) +my $end_margin_sec = 30; #後方余白(秒) +GetOptions('check|c' => \$is_check_mode, + 'verbose' => \$is_verbose_mode, + 'offset-time=i' => \$offset_time_sec, + 'end-margin=i' => \$end_margin_sec ); @@ -80,14 +73,30 @@ $is_conv_to_mp3 = 0 if $opts{'no-conversion-to-mp3'}; unless (@ARGV) { my $script_name = basename($0, ''); my $usage = << " EOM_USAGE"; - USAGE) $script_name [-c|--check] [--verbose] [-n|--no-conversion-to-mp3] - <放送局ID> <録音開始日> <録音開始時刻> [<録音長(分)> <タイトル>] + USAGE) $script_name [ -c | --check | --verbose ] + [--offset-time <開始オフセット(秒)>] + [--end-margin <後方余白(秒)>] + <放送局ID> <録音開始日> <録音開始時刻> [<録音長さ(分)> <タイトル>] ex) $script_name tokyo-fm 2025-05-31 1230 91 scramble_taniyama-hiroko ex) $script_name --check tokyo-fm 2025-05-31 12:30 91 scramble_taniyama-hiroko ex) $script_name sendai-fm 2025-09-01 1230 - <放送局ID>は radi.sh -l で確認できます. ex) tokyo-r1, r2, tokyo-fm + --check または -c を指定すると, job発行はせずにコマンドの確認をする チェックモード になります. + --verbose を指定すると, job発行コマンドと番組表の内容を表示してからjob発行をする, + 冗長表示モード になります. + + <開始オフセット(秒)> には, 録音開始時刻の00秒から実際に録音を開始するまでの秒数を指定します. + デフォルトは $offset_time_sec 秒です. + + <後方余白(秒)> には, 指定の録音長さの後に余分に録音する秒数を指定します. + デフォルトは $end_margin_sec 秒です. + + <放送局ID> : 地域(sapporo, sendai, tokyo, nagoya, osaka, hiroshima, matsuyama, fukuoka) と + 放送(r1, r2, fm) の組み合わせ. ただし r2 には地域なし. + ex) tokyo-r1, matsuyama-fm, r2 + <録音長(分)> <タイトル> を省略したときには, get-nhk-title.pl を使用して取得します. + <録音開始日> を省略したときには, 現在時刻以降の直近の <録音開始時刻> から録音を開始します. EOM_USAGE @@ -120,7 +129,6 @@ my($min, $title) = @ARGV; if (!$min && !$title && $GETTT) { my $minutes_title = `$GETTT --short --escape --shinyabin $station $date $time`; ($min, $title) = split(/ /, $minutes_title); - $min++; } $title = Encode::decode('UTF-8', $title); @@ -128,21 +136,16 @@ $title =~ s/\s+/_/g; die "無効な引数です." unless ($y && $m && $d && $time_h <= 24 && $time_h >= 0 && $time_m <= 59 && $time_m >= 0 && $min > 0 && length $title); -my $file_name_base = sprintf('%04d-%02d-%02d_%02d%02d_nhk-%s_%s_%imin', $y, $m, $d, $time_h, $time_m, $station, $title, $min); -my $m4a = "${file_name_base}.m4a"; +my $file_name_base = sprintf('%04d-%02d-%02d_%02d%02d_nhk-%s_%s_%dmin', $y, $m, $d, $time_h, $time_m, $station, $title, $min); my $mp3 = "${file_name_base}.mp3"; +my $duration = get_duration_string($min, $end_margin_sec); -# コマンド構築 -my $rec_cmd = "cd $ENV{RADIKO_SAVE_TO}; $RADISH -t nhk -s $station -d $min -o $m4a; $CHMOD $FILE_PERMISSION $m4a; "; -if ($is_conv_to_mp3) { - $rec_cmd .= "$FFMPEG -i $m4a $mp3 &> /dev/null; $SLEEP $SLEEP_TIME_SEC; ". - "if [ -s $mp3 ]; then $RM $m4a; $CHMOD $FILE_PERMISSION $mp3; fi"; -} +# コマンド構築 +my $rec_cmd = "cd $ENV{RADIKO_SAVE_TO}; $SLEEP $offset_time_sec; $DL $station $duration $mp3; $CHMOD $FILE_PERMISSION $mp3; "; my $at_time = sprintf('%d:%02d %02d.%02d.%02d', $time_h, $time_m, $d, $m, ($y - int($y/100)*100 ) ); - my $cmd = qq{$ECHO -e '$BASH << EOC\\n$rec_cmd\\nEOC' | $AT "$at_time"}; @@ -168,6 +171,8 @@ if ($is_check_mode) { + + #こんどの (現在時刻以降の直近の) 指定時刻 のときの日付(今日or明日) sub get_date_next_time { my ($hhmm) = @_; @@ -185,3 +190,20 @@ sub get_date_next_time { return ($now + Time::Seconds::ONE_DAY)->ymd; } } + + + +#時間長さ(分,秒) を 「時間:分:秒」形式に整形 +sub get_duration_string { + my($min, $sec) = @_; + + if($sec > 59) { + $min += int($sec / 60); + $sec = $sec % 60; + } + + my $hr = int($min / 60); + $min = $min % 60; + + return sprintf('%d:%02d:%02d', $hr, $min, $sec); +} -- 2.43.0