From 9aa4e2fb89702780adeabd8416befa1896cce42a Mon Sep 17 00:00:00 2001 From: satomichan Date: Tue, 2 Sep 2025 13:57:32 +0900 Subject: [PATCH] =?utf8?q?NHK=E7=95=AA=E7=B5=84=E8=A1=A8=E5=8F=96=E5=BE=97?= =?utf8?q?=E3=83=84=E3=83=BC=E3=83=AB=20get-nhk-title.pl=20=E6=96=B0?= =?utf8?q?=E8=A6=8F=E5=AE=9F=E8=A3=85,=20get-nhk-radio.pl=20=E3=81=8B?= =?utf8?q?=E3=82=89=E5=88=A9=E7=94=A8=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?utf8?q?=E3=81=86=E3=81=AB.?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- get-nhk-title.pl | 282 +++++++++++++++++++++++++++++++++++++++++++++++ rec-nhk-radio.pl | 13 ++- 2 files changed, 294 insertions(+), 1 deletion(-) create mode 100755 get-nhk-title.pl mode change 100644 => 100755 rec-nhk-radio.pl diff --git a/get-nhk-title.pl b/get-nhk-title.pl new file mode 100755 index 0000000..2f69f1e --- /dev/null +++ b/get-nhk-title.pl @@ -0,0 +1,282 @@ +#!/usr/bin/env -S perl -w + + sub usage {print Encode::decode('UTF-8',<<'_END_OF_USAGE_'); +get-nhk-title.pl ---- NHK番組表 取得ツール + (https://satomichan.jp/rec-radiko) + +NHKオンライン テキスト版 (https://k.nhk.jp/) で提供されている +番組表から 番組情報(放送長さ・タイトル・プレイリスト等の詳細情報) を +取得するツールです. + +使い方: + get-nhk-title.pl [--short | -s] [--escape | -e] + [--li ] [--nominutes | -m] + + + には放送局を指定します. 指定できるのは, + r2 (第2放送), + sapporo-r1, sendai-r1, tokyo-r1, nagoya-r1, osaka-r1, + hiroshima-r1, matsuyama-r1, fukuoka-r1 (ここまで第1放送), + sapporo-fm, sendai-fm, tokyo-fm, nagoya-fm, osaka-fm, + hiroshima-fm, matsuyama-fm, fukuoka-fm (ここまでFM放送) です. + + には番組開始の日付(暦日)を指定します. + 書式は YYYY-MM-DD です. 例) 2025-09-01 (2025年9月1日の場合) + + には番組開始の時刻を指定します. + 書式は [h]h:mm または [h]hmm です (24時間制). + 深夜24時以降を指定する場合は, 日付を翌日にし, + 0:00 以降の時刻を指定します. + コロンはあってもなくても構いません. 時の先頭の 0 は省略可能です. + 指定の日時にちょうど始まる番組の情報が取得されます. + 例) 12:30 (昼の12時半の場合), 905 (午前9時05分の場合), + 1515 (午後3時15分の場合), 0305 (深夜3時05分の場合) + + --short または -s を指定しないと Long出力モードになります. + 放送長さ・番組タイトル・プレイリスト等の詳細情報が複数行にわたって + 出力されます. + + --short または -s を指定すると Short出力モードになります. + 放送長さ・番組タイトルが 1つの半角スペースで区切って出力されます. + + Short出力モード のときに --escape または -e も指定されていると, + 番組タイトルのうち スペース類を _ に, 半角の記号類を全角に変換し, + 両端を ' で挟んで 出力します. + + Short出力モード のときに --li も指定されていると, + 番組タイトルに使われる 番組表内部 HTML の li要素 が + 番目のものに変更されます. + デフォルトでは 1 行目となっています(--li 1 と等価). + には 1 以上の自然数を指定します. カンマ(,)で区切って + 複数指定することも出来ます. 例) --li 3,4 + + Short出力モード のときに --shinyabin を指定すると, + 番組が ラジオ深夜便 だった場合に 番組タイトルを 3,4行目などを組み + 合わせたものを出力します (1行目のみだと特集内容がわからないため). + + + --nominutes または -m を指定されていると, 放送長さ を出力しません. + (Long出力モード, Short出力モード ともに) + +実行例: + get-nhk-title.pl -s -e --shinyabin tokyo-r1 2025-09-08 1230 + +留意事項: + 当然ですが, 現行法規で認められた範囲内かつ NHK が認める範囲内で + ご使用ください. +_END_OF_USAGE_ +} + +# 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 STDOUT, ":utf8"; +binmode STDERR, ":utf8"; + +use HTTP::Tiny; #sudo apt install libhttp-tiny-perl +use Encode; +use HTML::Entities; +use Getopt::Long qw(:config posix_default no_ignore_case gnu_compat); + + + +#オプション解析 +my %option = (li => 1); +GetOptions( \%option, ('short|s', 'li=s', 'nominutes|m', 'escape|e', 'shinyabin') ); + +my ($station, $date, $time) = @ARGV; + +usage(), exit unless ($station && $date && $time); + +my ($day) = $date =~ /^\d{4}-\d{2}-(\d{2})$/ or die "Date Format Error ('$date' must be YYYY-MM-DD)"; +my ($h, $m) = $time =~ /^(\d{1,2}):?(\d{2})$/ or die "Time Format Error ('$time' must be HHMM or HH:MM)"; + +my ($area, $ch) = @{station2area_channel($station)}; + + +#午前0時-4時台のときは前日の番組表を参照する +if ($h < 5) { + use Time::Piece; + my $t = Time::Piece->strptime($date, '%Y-%m-%d'); + $t -= Time::Seconds::ONE_DAY; + $date = $t->ymd; +} + +#番組情報(長さ・タイトル・詳細)出力 +my $program = get_timetable($area, $ch, $date, $day, $h, $m); +if ($program) { + if ($option{short}) { + #Short 表示モード + print "$program->{minutes} " unless $option{nominutes}; + + my $title = ''; + + #ラジオ深夜便対策 + if ($option{shinyabin} && $program->{list}[0] =~ /^ラジオ深夜便/) { + $title = 'ラジオ深夜便'; + $option{li} = '3,4'; + } + + #タイトル取得・エスケープ + $title .= $program->{list}[$_ -1] for split(/,/, $option{li}); + $title = "'". escape($title). "'" if $option{escape}; + + print "$title"; + + }else{ + #Long 表示モード + print $option{nominutes} ? "" : "$program->{minutes}分間\n"; + print "$_\n" for @{$program->{list}}; + } +} + +exit; + + + +#番組表 GET +sub get_timetable { + my ($area, $ch, $tt_ymd, $d, $h, $m) = @_; + # └番組表の年月日, └暦日 + + my $kanji_time = kanji_time($d, $h, $m); + my $url = "https://k.nhk.jp/timetable/read/c.html?a=$area&c=$ch&d=$tt_ymd&f=top"; + + my $resp = HTTP::Tiny->new->get($url); + + if ($resp->{success}) { + my $body = Encode::decode('UTF-8', $resp->{content}); + + my ($minutes, $entry) = + $body =~ m|
${kanji_time}から.+?分(放送時間(\d{1,3})分間)
    (.+?)
| + or die "Cannot get program info starting at the specified date-time. TIME: ${kanji_time} URL: $url"; + + $entry = HTML::Entities::decode_entities($entry); #HTML文字実体参照(&xxxx;) デコード + $entry =~ s/
/\n/g; + + my @list = $entry=~ m|
  • (.+?)
  • |sg; + return {minutes => $minutes, list => \@list}; + + }else{ + die "Not success GET : $url"; + } +} + + + +#「日・時・分」から 「(0日)?(午前|午後)0時00分」文字列を生成 +sub kanji_time { + my ($d, $h, $m) = @_; + + my $day = ''; + my $ampm; + + if ($h < 5) { + $day = sprintf('%i日', $d); + $ampm = '午前'; + + }elsif ($h <= 11) { + $ampm = '午前'; + + }else{ + $ampm = '午後'; + $h -= 12; + } + + return sprintf("%s%s%i時%02i分", $day, $ampm, $h, $m); +} + + + +#エリアコード・チャンネルコード +sub station2area_channel { + my ($station) = @_; + + return ['001','06'] if $station eq 'r2'; + + my ($base, $channel) = $station =~ /^(.\w+)-(r1|fm)$/ or die "'$station' : Unknown station."; + + my %area_code = ( + sapporo => '700', + sendai => '600', + tokyo => '001', + nagoya => '300', + osaka => '200', + hiroshima => '400', + matsuyama => '800', + fukuoka => '501', + ); + + die "'$station' : Unknown area." unless $area_code{$base}; + + + my %channel_code = ( + r1 => '05', + fm => '07', + ); + + die "'$station' : Unknown area." unless $channel_code{$channel}; + + + return [$area_code{$base}, $channel_code{$channel}]; +} + + + +#ファイル名不適記号類を全角文字化 +sub escape { + my($str) = @_; + + #空白 -> _ にする + $str =~ s/\s+/_/g; + $str =~ s/ +/_/g; + + #半角記号類 -> 全角にする + for ($str) { + s/"/”/g; + s/'/’/g; + s/`/`/g; + s/,/,/g; + s/\././g; + s//>/g; + s/\|/|/g; + s/:/:/g; + s/;/;/g; + s/\?/?/g; + s/!/!/g; + s/&/&/g; + s/%/%/g; + s/~/ï¿£/g; + s/\$/$/g; + s/\*/*/g; + s/\\/ï¿¥/g; + s/\////g; + s/\././g; + s/\+/+/g; + s/\(/(/g; + s/\)/)/g; + s/\[/ï¼»/g; + s/]/ï¼½/g; + s/{/{/g; + s/}/}/g; + } + + return $str; +} + diff --git a/rec-nhk-radio.pl b/rec-nhk-radio.pl old mode 100644 new mode 100755 index 59523d7..bb4a845 --- a/rec-nhk-radio.pl +++ b/rec-nhk-radio.pl @@ -44,6 +44,7 @@ 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 $GETTT = `which get-nhk-title.pl | tr -d '\n'`; @@ -75,11 +76,14 @@ $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] [-n|--no-conversion-to-mp3] <放送局ID> <録音開始日> <録音開始時刻> <録音長(分)> <タイトル> + USAGE) $script_name [-c|--check] [-n|--no-conversion-to-mp3] <放送局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 + <録音長(分)> <タイトル> を省略したときには, get-nhk-title.pl を使用して取得します. EOM_USAGE @@ -92,6 +96,13 @@ unless (@ARGV) { # 引数 解析 my($station, $date, $time, $min, $title) = @ARGV; +#番組長さ・タイトル取得 +if (!$min && !$title && $GETTT) { + my $minutes_title = `$GETTT --short --escape --shinyabin $station $date $time`; + ($min, $title) = split(/ /, $minutes_title); + $min++; +} + my($y, $m, $d) = split(/-/, $date); my($time_h, $time_m) = ($1, $2) if $time =~ /(\d{1,2}):?(\d{2})/; $title =~ s/\s+/_/g; -- 2.43.0