Last active
January 8, 2020 08:57
-
-
Save panam510/cceb12880bacc979358a1d28ce10a6a0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/perl | |
| # ニコニコチャンネル RSS抽出スクリプト | |
| use 5.14.2; | |
| use warnings; | |
| use utf8; | |
| use Time::Piece; | |
| use Time::Seconds; | |
| use XML::FeedPP; | |
| use Getopt::Std; | |
| $Getopt::Std::STANDARD_HELP_VERSION = 1; | |
| our $VERSION = 20200108; | |
| binmode STDOUT, ":utf8"; | |
| our $tzoffset = 32400; #ニコニコ動画側のタイムゾーンオフセット。JST+0900=32400秒決め打ち | |
| # 引数の処理 | |
| our($opt_c, $opt_s, $opt_w); | |
| getopts("c:s:w"); | |
| my $count = $opt_c // 1; #件数の指定がなければ1件とする | |
| my $search_name = $opt_s; #タイトルで絞る場合の検索文字列 | |
| my $is_within_week = $opt_w; #タイムシフト可能期間のものを抽出する指定 | |
| my $feedurl = shift; | |
| if ( !(defined($feedurl)) ){ | |
| #URLの指定がない | |
| die "feedurl is required.\nUsage: $0 [-c count] [-s string] feedurl\n"; | |
| }elsif ( ($count !~ /^[-]?[0-9]+$/) ) { | |
| #件数指定が整数でない | |
| die "count is not number.\nUsage: $0 [-c count] [-s string] feedurl\n"; | |
| } | |
| #フィード読み込み | |
| my $feed = XML::FeedPP->new( $feedurl ); | |
| (my $mode = $feed->link) =~ s/.*\///; #フィード内にあるURLの末端("video"か"live")を読み、動画か生放送かを判定 | |
| my $i = 0; | |
| if ($mode eq "live"){ #末端"live"なら生放送用モード | |
| my %pairs; #キー:時刻、値:放送URLのハッシュ | |
| my $now_epoch = time; | |
| if ($count > 0){ | |
| #countが正数のモード。終了済みの放送を、放送終了時刻が近い順に出力。 | |
| foreach my $item ($feed->get_item) { | |
| #通常のRSSで使うpubDateでなく、nicoch:end_time=放送終了時刻を使って判断する | |
| #RFC822形式の日時をパース | |
| my $end_time = Time::Piece->strptime($item->get('nicoch:end_time'),'%a, %d %b %Y %H:%M:%S %z'); | |
| #比較・ソート用にエポック値としておく | |
| my $end_epoch = $end_time->epoch; | |
| #まだ終わっていない放送は弾き、終了済みの放送のみを処理する | |
| if ( $end_epoch < $now_epoch ) { | |
| #名前での指定がない場合は無条件でハッシュへ、名前指定があれば当てはまる場合だけハッシュへ入れる | |
| if( !(defined($search_name)) || index($item->title,$search_name) != -1 ){ | |
| #-wオプションが未指定の場合は無条件でハッシュへセット | |
| if(!$is_within_week){ | |
| #放送済みの場合、放送終了時刻をキーにする | |
| $pairs{$end_epoch} = $item->link; | |
| }else{ | |
| #-wオプションが指定された場合の処理 | |
| #タイムシフト可能期間(終了日から7日後の23:59:59。タイムゾーンはJST+0900決め打ち)を算出、エポック秒に変換 | |
| my $ts_limit_epoch = Time::Piece->strptime(($end_time + $tzoffset + ONE_WEEK)->ymd.'T23:59:59+0900',"%FT%T%z")->epoch; | |
| #タイムシフト可能期間内の放送だけハッシュへセット | |
| if( $ts_limit_epoch >= $now_epoch ){ | |
| $pairs{$end_epoch} = $item->link; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| #URL出力 | |
| foreach my $epoch (sort {$b <=> $a}(keys %pairs)){ #放送終了時刻の降順でソートしつつ処理 | |
| my $movieurl = $pairs{$epoch}; | |
| #終了済みの放送URLを出力 | |
| say $movieurl; | |
| if (++$i >= $count){ #引数で指定された件数を超えたら終了 | |
| last; | |
| } | |
| } | |
| }elsif($count < 0){ | |
| #countが負数のモード。未来日付の放送を、放送開始時刻が近い順に出力。 | |
| $count = abs($count); | |
| foreach my $item ($feed->get_item) { | |
| #nicoch:start_timeを使って判断する | |
| my $start_epoch = Time::Piece->strptime($item->get('nicoch:start_time'),'%a, %d %b %Y %H:%M:%S %z')->epoch; | |
| #開始済みの放送は弾き、未開始の放送のみを処理する | |
| if ( $start_epoch > $now_epoch ) { | |
| #名前での指定がない場合は無条件でハッシュへ、名前指定があれば当てはまる場合だけハッシュへ入れる | |
| if( !(defined($search_name)) || index($item->title,$search_name) != -1 ){ | |
| #未放送の場合、放送開始時刻をキーにする | |
| $pairs{$start_epoch} = $item->link; | |
| } | |
| } | |
| } | |
| foreach my $epoch (sort {$a <=> $b}(keys %pairs)){ #放送開始時刻の昇順でソートしつつ処理 | |
| my $movieurl = $pairs{$epoch}; | |
| #未開始の放送URLを出力 | |
| say $movieurl; | |
| if (++$i >= $count){ #引数で指定された件数を超えたら終了 | |
| last; | |
| } | |
| } | |
| }else{ | |
| #count=0のモード。放送中のものを放送開始時刻順に出力。 | |
| foreach my $item ($feed->get_item) { | |
| #nicoch:start_timeを使って判断する | |
| my $start_epoch = Time::Piece->strptime($item->get('nicoch:start_time'),'%a, %d %b %Y %H:%M:%S %z')->epoch; | |
| my $end_epoch = Time::Piece->strptime($item->get('nicoch:end_time'),'%a, %d %b %Y %H:%M:%S %z')->epoch; | |
| if ( ($start_epoch <= $now_epoch) && ($end_epoch >= $now_epoch) ) { | |
| #放送中のみを処理する | |
| if( !(defined($search_name)) || index($item->title,$search_name) != -1 ){ | |
| $pairs{$start_epoch} = $item->link; | |
| } | |
| } | |
| } | |
| foreach my $epoch (sort {$a <=> $b}(keys %pairs)){ #放送開始時刻の昇順でソートしつつ処理 | |
| my $movieurl = $pairs{$epoch}; | |
| #放送中のURLを出力 | |
| #「同一チャンネルで複数番組を同時放送」の可能性があるが、未検証。とりあえず全件出るはず | |
| say $movieurl; | |
| } | |
| } | |
| }elsif($mode eq "video"){ #末端"video"なら動画用モード | |
| if ($count < 0){ | |
| #動画のときは負のcountを認めない(未来日付の動画はないため) | |
| die "For video feedurl, count can't be negative.\n" | |
| } | |
| #FeedPPの標準メソッドでソートを行う(対象はpubDate) | |
| $feed->normalize; | |
| foreach my $item ($feed->get_item){ | |
| #動画URLを新しいものから出力 | |
| if( !(defined($search_name)) || index($item->title,$search_name) != -1){ | |
| say $item->link; | |
| if (++$i >= $count){ #引数で指定された件数を超えたら終了 | |
| last; | |
| } | |
| } | |
| } | |
| }else{ | |
| #生放送/動画じゃないっぽいRSSを読んだら終了 | |
| die "feedurl: unknown format (Neither http://ch.nicovideo.jp/chXXX/video nor http://ch.nicovideo.jp/chXXX/live)." | |
| } | |
| sub HELP_MESSAGE { | |
| say "Usage: $0 [-c count] [-s string] [-w] feedurl"; | |
| say ''; | |
| say 'feedurlの例は以下の通り'; | |
| say ' http://ch.nicovideo.jp/chXXX/video?rss=2.0'; | |
| say ' http://ch.nicovideo.jp/chXXX/live?rss=2.0'; | |
| say ''; | |
| say 'Option:'; | |
| say ' -c count 出力するURLの件数を整数で指定、省略した場合は1。'; | |
| say ' 指定したURLが動画の場合と生放送の場合で挙動が異なる。'; | |
| say ' 動画 :正の整数のみ許容、動画のURLを新しいものから出力。'; | |
| say ' 生放送:正の整数を渡した場合、終了した放送のURLを、終了時刻が新しいものから出力。'; | |
| say ' 負の整数を渡した場合、未放送のURLを、開始時刻が近いものから出力。'; | |
| say ' 0を渡した場合、放送中のURLを出力。'; | |
| say ' -s string 放送のタイトルで絞り込み、ヒットしたURLのみ出力。'; | |
| say ' -w 生放送のタイムシフト視聴期間内(放送終了から7日後の23時59分59秒まで)の放送URLのみ出力する。'; | |
| say ' 生放送、かつcountが正の整数の場合のみ有効。'; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment