リトライ機能を追加. fm しか録音できないようになっていたのを修正.
authorsatomichan <git-kawasemi.20250926@...>
Sun, 28 Sep 2025 08:44:19 +0000 (17:44 +0900)
committersatomichan <git-kawasemi.20250926@...>
Sun, 28 Sep 2025 16:18:05 +0000 (01:18 +0900)
radiru2-dl.pl
rec-nhk-radio.pl

index e44508449c3db0fab710627458d246ab34158144..ed5480de989e7a01d87a6999249de1a38764b05b 100755 (executable)
@@ -40,19 +40,66 @@ use HTTP::Tiny;  #sudo apt install libhttp-tiny-perl
 use Encode;
 use XML::XPath;  #sudo apt install libxml-xpath-perl
 use XML::XPath::XMLParser;
+use Getopt::Long qw(:config posix_default no_ignore_case gnu_compat);
+#use Time::Piece;
+use IPC::Open3 qw(open3);
+use Symbol qw(gensym);
+sub dp;
 
 my $config_url = 'https://www.nhk.or.jp/radio/config/config_web.xml';
 
-my($station, $duration, $output) = @ARGV;
 
+
+# オプション解析
+my $is_check_mode   = 0;
+my $is_verbose_mode = 0;
+my $retry           = 5;
+GetOptions('check|c'   => \$is_check_mode,
+           'verbose|v' => \$is_verbose_mode,
+           'retry|r=i' => \$retry,
+           );
+my($station, $req_duration, $output) = @ARGV;
+
+#HLS URL 取得
 $station = 'tokyo-r2' if $station eq 'r2';
-my($area, $wave) = $station =~ /^([a-z]+)-([a-z]+)$/ or die "Bad Station Error: $station";
+my($area, $wave) = $station =~ /^([a-z]+)-([a-z0-9]+)$/ or die "Bad Station Error: $station";
+dp "area: $area,  wave: $wave";
 my $hls_url      = get_hls_url($config_url, $area, $wave);
+dp "hls_url: $hls_url";
+
+#録音が求められている秒数
+my $req_duration_sec = duration2sec($req_duration);
+
+
 
 #録音実行
-exit system('ffmpeg', '-fflags',   '+discardcorrupt',  #破損したフレームを破棄
-                      '-loglevel', 'fatal',            #ログレベル 致命的なもののみ出力
-                      '-i', $hls_url, '-t', $duration, $output);
+exit if $is_check_mode;
+my $total_recorded_sec = 0; #録音済みの秒数
+my $ret;
+for(my $try = 0; $try <= $retry; $try++){
+    #出力ファイル名
+    my $numbered_filename = gen_filename($output, $try);
+
+    #今回録音時間
+    my $this_duration = sec2duration($req_duration_sec - $total_recorded_sec);
+
+    dp "try: $try    numbered_filename: $numbered_filename    this_duration: $this_duration";
+
+    $ret = system('ffmpeg', '-fflags',   '+discardcorrupt',  #破損したフレームを破棄
+                            '-loglevel', 'fatal',            #ログレベル 致命的なもののみ出力
+                            '-y',                            #Overwrite -> Yes
+                            '-i', $hls_url, '-t', $this_duration, $numbered_filename);
+
+    #出力ファイルが存在する?
+    next unless -f $numbered_filename;
+
+    #時間どおりに録音されている?
+    my $this_time_rec_sec  = get_rec_duration_by_file($numbered_filename); dp "this_time_rec_sec : $this_time_rec_sec";
+    $total_recorded_sec   += $this_time_rec_sec;                           dp "total_recorded_sec: $total_recorded_sec";
+    last if ($req_duration_sec <= $total_recorded_sec);
+}
+
+exit $ret;
 
 
 
@@ -78,3 +125,101 @@ sub get_hls_url {
 
 }
 
+
+
+#ffmpeg で録音時間を取得 (秒数で返す)
+sub get_rec_duration_by_file {
+    my($file) = @_;
+    my $err = gensym;
+    my $pid = open3(undef, undef, $err, "ffmpeg -i $file");
+    my $info = do { local $/; <$err> };
+    waitpid($pid, 0);
+
+    if ($info =~ /Duration: (\d+:\d{2}:\d{2})\./) {
+        return duration2sec($1);
+    }
+
+    return;
+}
+
+
+
+sub duration2sec {
+    my($dur_str) = @_;
+
+    my $total_sec = 0;
+    my $base      = 1;
+
+    my @units = split(/:/, $dur_str);
+    foreach my $an_unit (reverse @units) {
+        die "Bad Duration Format Error: $dur_str" unless $an_unit =~ /^\d+$/;
+        $total_sec += $base * $an_unit;
+        $base *= 60;
+    }
+
+    return $total_sec;
+}
+
+
+
+sub sec2duration {
+    my($sec) = @_;
+
+    my $min += int($sec / 60);
+    $sec     = $sec % 60;
+
+    my $hr   = int($min / 60);
+    $min     = $min % 60;
+
+    return sprintf('%d:%02d:%02d', $hr, $min, $sec);
+}
+
+
+
+sub gen_filename {
+    my($orig_name, $num) = @_;
+    
+    return $orig_name if $num == 0;
+
+    my $base = $orig_name;
+    my $ext  = '';
+    if(  $orig_name =~ /^(.+?)(\.[a-z0-9]+)$/  ){
+        $base = $1;
+        $ext  = $2;
+    }
+
+    return "${base}_${num}${ext}";
+}
+
+
+
+#DEBUG PRINT
+sub dp {
+    my $msg = join('', @_);
+    print STDERR "$msg\n" if $is_verbose_mode;
+}
+
+
+__END__
+dp gen_filename("aaaaa.mp3.mp4", 1);
+dp gen_filename("aaaaa.mp3.mp4", 2);
+dp gen_filename("dddd/ccccc.eee/aaaaa.mp3", 1);
+dp gen_filename("dddd/ccccc.eee/aaaaa.mp3", 2);
+dp gen_filename("aa/aaa", 1);
+dp gen_filename("aa/aaa", 2);
+
+
+sub get_end_time {
+    my($dur) = @_;
+
+    my $now = localtime;
+    dp "now: ". $now->datetime;
+
+    my $dur_sec = duration2sec($dur);
+    dp "dur_sec: $dur_sec";
+
+    my $end_time = $now + $dur_sec;
+    dp "end_time: ". $end_time->datetime;
+
+    return $end_time;
+}
index 57ae68a690e8f705f31432a4c99ff246dfe1c779..84190f86228e00a2772bd57c3f9e6e3a8277ea0e 100755 (executable)
@@ -144,7 +144,7 @@ my $duration = get_duration_string($min, $end_margin_sec);
 
 
 # コマンド構築
-my $rec_cmd = "cd $ENV{RADIKO_SAVE_TO}; $SLEEP $offset_time_sec; $DL $station $duration $mp3; $CHMOD $FILE_PERMISSION $mp3; ";
+my $rec_cmd = "cd $ENV{RADIKO_SAVE_TO}; $SLEEP $offset_time_sec; $DL $station $duration $mp3; $CHMOD $FILE_PERMISSION $file_name_base*.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"};