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;
}
+
+
+#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;
+}