NHKのURL変更に対応. 録音部を radi.sh から新規実装の dl-radiru2.pl に変更.
authorsatomichan <git-kawasemi.20250926@...>
Sat, 27 Sep 2025 05:57:17 +0000 (14:57 +0900)
committersatomichan <git-kawasemi.20250926@...>
Sat, 27 Sep 2025 07:53:27 +0000 (16:53 +0900)
dl-radiru2.pl [new file with mode: 0755]
rec-nhk-radio.pl

diff --git a/dl-radiru2.pl b/dl-radiru2.pl
new file mode 100755 (executable)
index 0000000..bdf6178
--- /dev/null
@@ -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";
+    }
+
+}
+
index 2dc04dec05610f2bb3bdb67325fbdb10d47e8064..c08fe6932a9c4463e6654e35be43b313819b7bf3 100755 (executable)
@@ -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);
+}