diff options
Diffstat (limited to 'lib/WWW/StrawViewer')
-rw-r--r-- | lib/WWW/StrawViewer/Activities.pm | 93 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/Authentication.pm | 216 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/Channels.pm | 200 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/CommentThreads.pm | 98 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/GetCaption.pm | 280 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/GuideCategories.pm | 85 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/Itags.pm | 319 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/ParseJSON.pm | 76 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/ParseXML.pm | 311 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/PlaylistItems.pm | 146 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/Playlists.pm | 124 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/RegularExpressions.pm | 89 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/Search.pm | 175 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/Subscriptions.pm | 272 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/Utils.pm | 801 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/VideoCategories.pm | 97 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/Videos.pm | 230 |
17 files changed, 0 insertions, 3612 deletions
diff --git a/lib/WWW/StrawViewer/Activities.pm b/lib/WWW/StrawViewer/Activities.pm deleted file mode 100644 index 39a6a3d..0000000 --- a/lib/WWW/StrawViewer/Activities.pm +++ /dev/null @@ -1,93 +0,0 @@ -package WWW::StrawViewer::Activities; - -use utf8; -use 5.014; -use warnings; - -=head1 NAME - -WWW::StrawViewer::Activities - list of channel activity events that match the request criteria. - -=head1 SYNOPSIS - - use WWW::StrawViewer; - my $obj = WWW::StrawViewer->new(%opts); - my $activities = $obj->activities($channel_id); - -=head1 SUBROUTINES/METHODS - -=cut - -sub _make_activities_url { - my ($self, %opts) = @_; - $self->_make_feed_url('activities', part => 'snippet,contentDetails', %opts); -} - -=head2 activities($channel_id) - -Get activities for channel ID. - -=cut - -sub activities { - my ($self, $channel_id) = @_; - - if ($channel_id eq 'mine') { - return $self->my_activities; - } - - if ($channel_id !~ /^UC/) { - $channel_id = $self->channel_id_from_username($channel_id) // $channel_id; - } - - $self->_get_results($self->_make_activities_url(channelId => $channel_id)); -} - -=head2 activities_from_username($username) - -Get activities for username. - -=cut - -sub activities_from_username { - my ($self, $username) = @_; - return $self->activities($username); -} - -=head2 my_activities() - -Get authenticated user's activities. - -=cut - -sub my_activities { - my ($self) = @_; - $self->get_access_token() // return; - $self->_get_results($self->_make_activities_url(mine => 'true')); -} - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::Activities - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2013-2015 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::Activities diff --git a/lib/WWW/StrawViewer/Authentication.pm b/lib/WWW/StrawViewer/Authentication.pm deleted file mode 100644 index 1fa2368..0000000 --- a/lib/WWW/StrawViewer/Authentication.pm +++ /dev/null @@ -1,216 +0,0 @@ -package WWW::StrawViewer::Authentication; - -use utf8; -use 5.014; -use warnings; - -=head1 NAME - -WWW::StrawViewer::Authentication - OAuth login support. - -=head1 SYNOPSIS - - use WWW::StrawViewer; - my $hash_ref = WWW::StrawViewer->oauth_login($code); - -=head1 SUBROUTINES/METHODS - -=cut - -sub _get_token_oauth_url { - my ($self) = @_; - return $self->get_oauth_url() . 'token'; -} - -=head2 oauth_refresh_token() - -Refresh the access_token using the refresh_token. Returns a HASH ref with the `access_token` or undef. - -=cut - -sub oauth_refresh_token { - my ($self) = @_; - - my $json_data = $self->lwp_post( - $self->_get_token_oauth_url(), - [Content => $self->get_www_content_type, - client_id => $self->get_client_id() // return, - client_secret => $self->get_client_secret() // return, - refresh_token => $self->get_refresh_token() // return, - grant_type => 'refresh_token', - ] - ); - - return $self->parse_json_string($json_data); -} - -=head2 get_accounts_oauth_url() - -Creates an OAuth URL with the 'code' response type. (Google's authorization server) - -=cut - -sub get_accounts_oauth_url { - my ($self) = @_; - - my $url = $self->_append_url_args( - ($self->get_oauth_url() . 'auth'), - response_type => 'code', - client_id => $self->get_client_id() // return, - redirect_uri => $self->get_redirect_uri() // return, - scope => 'https://www.googleapis.com/auth/youtube.force-ssl', - access_type => 'offline', - ); - return $url; -} - -=head2 oauth_login($code) - -Returns a HASH ref with the access_token, refresh_token and some other info. - -The $code can be obtained by going to the URL returned by the C<get_accounts_oauth_url()> method. - -=cut - -sub oauth_login { - my ($self, $code) = @_; - - length($code) < 20 and return; - - my $json_data = $self->lwp_post( - $self->_get_token_oauth_url(), - [Content => $self->get_www_content_type, - client_id => $self->get_client_id() // return, - client_secret => $self->get_client_secret() // return, - redirect_uri => $self->get_redirect_uri() // return, - grant_type => 'authorization_code', - code => $code, - ] - ); - - return $self->parse_json_string($json_data); -} - -sub __AUTH_EOL__() { "\0\0\0" } - -=head2 load_authentication_tokens() - -Will try to load the access and refresh tokens from I<authentication_file>. - -=cut - -sub load_authentication_tokens { - my ($self) = @_; - - if (defined $self->get_access_token and defined $self->get_refresh_token) { - return 1; - } - - my $file = $self->get_authentication_file() // return; - my $key = $self->get_key() // return; - - if (-f $file) { - local $/ = __AUTH_EOL__; - open my $fh, '<:raw', $file or return; - - my @tokens; - foreach my $i (0 .. 1) { - chomp(my $token = <$fh>); - $token =~ /\S/ || last; - push @tokens, $self->decode_token($token); - } - - $self->set_access_token($tokens[0]) // return; - $self->set_refresh_token($tokens[1]) // return; - - close $fh; - return 1; - } - - return; -} - -=head2 encode_token($token) - -Encode the token with the I<key> and return it. - -=cut - -sub encode_token { - my ($self, $token) = @_; - - if (defined(my $key = $self->get_key)) { - require MIME::Base64; - return MIME::Base64::encode_base64($token ^ substr($key, -length($token))); - } - - return; -} - -=head2 decode_token($token) - -Decode the token with the I<key> and return it. - -=cut - -sub decode_token { - my ($self, $token) = @_; - - if (defined(my $key = $self->get_key)) { - require MIME::Base64; - my $bin = MIME::Base64::decode_base64($token); - return $bin ^ substr($key, -length($bin)); - } - - return; -} - -=head2 save_authentication_tokens() - -Encode and save the access and refresh into the I<authentication_file>. - -=cut - -sub save_authentication_tokens { - my ($self) = @_; - - my $file = $self->get_authentication_file() // return; - my $access_token = $self->get_access_token() // return; - my $refresh_token = $self->get_refresh_token() // return; - - if (open my $fh, '>:raw', $file) { - foreach my $token ($access_token, $refresh_token) { - print {$fh} $self->encode_token($token) . __AUTH_EOL__; - } - close $fh; - return 1; - } - - return; -} - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::Authentication - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2013-2015 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::Authentication diff --git a/lib/WWW/StrawViewer/Channels.pm b/lib/WWW/StrawViewer/Channels.pm deleted file mode 100644 index b2c2c99..0000000 --- a/lib/WWW/StrawViewer/Channels.pm +++ /dev/null @@ -1,200 +0,0 @@ -package WWW::StrawViewer::Channels; - -use utf8; -use 5.014; -use warnings; - -=head1 NAME - -WWW::StrawViewer::Channels - Channels interface. - -=head1 SYNOPSIS - - use WWW::StrawViewer; - my $obj = WWW::StrawViewer->new(%opts); - my $videos = $obj->channels_from_categoryID($category_id); - -=head1 SUBROUTINES/METHODS - -=cut - -sub _make_channels_url { - my ($self, %opts) = @_; - return $self->_make_feed_url('channels', %opts); -} - -sub videos_from_channel_id { - my ($self, $channel_id) = @_; - return $self->_get_results($self->_make_feed_url("channels/$channel_id/videos")); -} - -sub videos_from_username { - my ($self, $channel_id) = @_; - return $self->_get_results($self->_make_feed_url("channels/$channel_id/videos")); -} - -=head2 channels_from_categoryID($category_id) - -Return the YouTube channels associated with the specified category. - -=head2 channels_info($channel_id) - -Return information for the comma-separated list of the YouTube channel ID(s). - -=head1 Channel details - -For all functions, C<$channels->{results}{items}> contains: - -=cut - -{ - no strict 'refs'; - - foreach my $method ( - { - key => 'categoryId', - name => 'channels_from_guide_category', - }, - { - key => 'id', - name => 'channels_info', - }, - { - key => 'forUsername', - name => 'channels_from_username', - }, - ) { - *{__PACKAGE__ . '::' . $method->{name}} = sub { - my ($self, $channel_id) = @_; - return $self->_get_results($self->_make_channels_url($method->{key} => $channel_id)); - }; - } - - foreach my $part (qw(id contentDetails statistics topicDetails)) { - *{__PACKAGE__ . '::' . 'channels_' . $part} = sub { - my ($self, $id) = @_; - return $self->_get_results($self->_make_channels_url(id => $id, part => $part)); - }; - } -} - -=head2 my_channel() - -Returns info about the channel of the current authenticated user. - -=cut - -sub my_channel { - my ($self) = @_; - $self->get_access_token() // return; - return $self->_get_results($self->_make_channels_url(part => 'snippet', mine => 'true')); -} - -=head2 my_channel_id() - -Returns the channel ID of the current authenticated user. - -=cut - -sub my_channel_id { - my ($self) = @_; - - state $cache = {}; - - if (exists $cache->{id}) { - return $cache->{id}; - } - - $cache->{id} = undef; - my $channel = $self->my_channel() // return; - $cache->{id} = $channel->{results}{items}[0]{id} // return; -} - -=head2 channels_my_subscribers() - -Retrieve a list of channels that subscribed to the authenticated user's channel. - -=cut - -sub channels_my_subscribers { - my ($self) = @_; - $self->get_access_token() // return; - return $self->_get_results($self->_make_channels_url(mySubscribers => 'true')); -} - -=head2 channel_id_from_username($username) - -Return the channel ID for an username. - -=cut - -sub channel_id_from_username { - my ($self, $username) = @_; - - state $username_lookup = {}; - - if (exists $username_lookup->{$username}) { - return $username_lookup->{$username}; - } - - $username_lookup->{$username} = undef; - my $channel = $self->channels_from_username($username) // return; - $username_lookup->{$username} = $channel->{results}{items}[0]{id} // return; -} - -=head2 channel_title_from_id($channel_id) - -Return the channel title for a given channel ID. - -=cut - -sub channel_title_from_id { - my ($self, $channel_id) = @_; - - if ($channel_id eq 'mine') { - $channel_id = $self->my_channel_id(); - } - - my $info = $self->channels_info($channel_id // return) // return; - - ( ref($info) eq 'HASH' - and ref($info->{results}) eq 'HASH' - and ref($info->{results}{items}) eq 'ARRAY' - and ref($info->{results}{items}[0]) eq 'HASH') - ? $info->{results}{items}[0]{snippet}{title} - : (); -} - -=head2 channels_contentDetails($channelID) - -=head2 channels_statistics($channelID); - -=head2 channels_topicDetails($channelID) - -=cut - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::Channels - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2013-2015 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::Channels diff --git a/lib/WWW/StrawViewer/CommentThreads.pm b/lib/WWW/StrawViewer/CommentThreads.pm deleted file mode 100644 index 1eba143..0000000 --- a/lib/WWW/StrawViewer/CommentThreads.pm +++ /dev/null @@ -1,98 +0,0 @@ -package WWW::StrawViewer::CommentThreads; - -use utf8; -use 5.014; -use warnings; - -=head1 NAME - -WWW::StrawViewer::CommentThreads - Retrieve comments threads. - -=head1 SYNOPSIS - - use WWW::StrawViewer; - my $obj = WWW::StrawViewer->new(%opts); - my $videos = $obj->comments_from_video_id($video_id); - -=head1 SUBROUTINES/METHODS - -=cut - -sub _make_commentThreads_url { - my ($self, %opts) = @_; - return - $self->_make_feed_url( - 'commentThreads', - pageToken => $self->page_token, - %opts - ); -} - -=head2 comments_from_videoID($videoID) - -Retrieve comments from a video ID. - -=cut - -sub comments_from_video_id { - my ($self, $video_id) = @_; - $self->_get_results( - $self->_make_feed_url("comments/$video_id", - sort_by => $self->get_comments_order, - ), - ); -} - -=head2 comment_to_video_id($comment, $videoID) - -Send a comment to a video ID. - -=cut - -sub comment_to_video_id { - my ($self, $comment, $video_id) = @_; - - my $url = $self->_simple_feeds_url('commentThreads', part => 'snippet'); - - my $hash = { - "snippet" => { - - "topLevelComment" => { - "snippet" => { - "textOriginal" => $comment, - } - }, - "videoId" => $video_id, - - #"channelId" => $channel_id, - }, - }; - - $self->post_as_json($url, $hash); -} - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::CommentThreads - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2015-2016 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::CommentThreads diff --git a/lib/WWW/StrawViewer/GetCaption.pm b/lib/WWW/StrawViewer/GetCaption.pm deleted file mode 100644 index 81af4e2..0000000 --- a/lib/WWW/StrawViewer/GetCaption.pm +++ /dev/null @@ -1,280 +0,0 @@ -package WWW::StrawViewer::GetCaption; - -use utf8; -use 5.014; -use warnings; - -=head1 NAME - -WWW::StrawViewer::GetCaption - Save the YouTube closed captions as .srt files for a videoID. - -=head1 SYNOPSIS - - use WWW::StrawViewer::GetCaption; - - my $yv_cap = WWW::StrawViewer::GetCaption->new(%opts); - - print $yv_cap->get_caption($videoID); - -=head1 SUBROUTINES/METHODS - -=head2 new(%opts) - -Options: - -=over 4 - -=item captions => [] - -The captions data. - -=item captions_dir => "." - -Where to save the closed captions. - -=item languages => [qw(en es ro jp)] - -Preferred languages. First found is saved and returned. - -=back - -=cut - -sub new { - my ($class, %opts) = @_; - - my $self = bless {}, $class; - $self->{captions_dir} = undef; - $self->{captions} = []; - $self->{auto_captions} = 0; - $self->{languages} = [qw(en es)]; - - foreach my $key (keys %{$self}) { - $self->{$key} = delete $opts{$key} - if exists $opts{$key}; - } - - foreach my $invalid_key (keys %opts) { - warn "Invalid key: '${invalid_key}'"; - } - - return $self; -} - -=head2 find_caption_data() - -Find a caption data, based on the preferred languages. - -=cut - -sub find_caption_data { - my ($self) = @_; - - my @found; - foreach my $caption (@{$self->{captions}}) { - if (defined $caption->{languageCode}) { - foreach my $i (0 .. $#{$self->{languages}}) { - my $lang = $self->{languages}[$i]; - if ($caption->{languageCode} =~ /^\Q$lang\E(?:\z|[_-])/i) { - - # Automatic Speech Recognition - my $auto = defined($caption->{kind}) && lc($caption->{kind}) eq 'asr'; - - # Check against auto-generated captions - if ($auto and not $self->{auto_captions}) { - next; - } - - # Fuzzy match or auto-generated caption - if (lc($caption->{languageCode}) ne lc($lang) or $auto) { - $found[$i + (($auto ? 2 : 1) * scalar(@{$self->{languages}}))] = $caption; - } - - # Perfect match - else { - $i == 0 and return $caption; - $found[$i] = $caption; - } - } - } - } - } - - foreach my $caption (@found) { - return $caption if defined($caption); - } - - return; -} - -=head2 sec2time(@seconds) - -Convert a list of seconds to .srt times. - -=cut - -sub sec2time { - my $self = shift; - - my @out; - foreach my $sec (map { sprintf '%.3f', $_ } @_) { - push @out, - sprintf('%02d:%02d:%02d,%03d', ($sec / 3600 % 24, $sec / 60 % 60, $sec % 60, substr($sec, index($sec, '.') + 1))); - } - - return @out; -} - -=head2 xml2srt($xml_string) - -Convert the XML data to SubRip format. - -=cut - -sub xml2srt { - my ($self, $xml) = @_; - - require WWW::StrawViewer::ParseXML; - my $hash = eval { WWW::StrawViewer::ParseXML::xml2hash($xml) } // return; - - my $sections; - if ( exists $hash->{transcript} - and ref($hash->{transcript}) eq 'ARRAY' - and ref($hash->{transcript}[0]) eq 'HASH' - and exists $hash->{transcript}[0]{text}) { - $sections = $hash->{transcript}[0]{text}; - } - else { - return; - } - - require HTML::Entities; - - my @text; - foreach my $i (0 .. $#{$sections}) { - my $line = $sections->[$i]; - - if (not defined($line->{'-dur'})) { - if (exists $sections->[$i + 1]) { - $line->{'-dur'} = $sections->[$i + 1]{'-start'} - $line->{'-start'}; - } - else { - $line->{'-dur'} = 10; - } - } - - my $start = $line->{'-start'}; - my $end = $start + $line->{'-dur'}; - - push @text, - join("\n", - $i + 1, - join(' --> ', $self->sec2time($start, $end)), - HTML::Entities::decode_entities($line->{'#text'} // '')); - } - - return join("\n\n", @text); -} - -=head2 get_xml_data($caption_data) - -Get the XML content for a given caption data. - -=cut - -sub get_xml_data { - my ($self, $url) = @_; - - state $lwp = do { - - require LWP::UserAgent; - - my $agent = LWP::UserAgent->new( - timeout => 30, - env_proxy => 1, - agent => - 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36', - ); - - require LWP::ConnCache; - state $cache = LWP::ConnCache->new; - $cache->total_capacity(undef); # no limit - - state $accepted_encodings = do { - require HTTP::Message; - HTTP::Message::decodable(); - }; - - $agent->ssl_opts(Timeout => 30); - $agent->default_header('Accept-Encoding' => $accepted_encodings); - $agent->conn_cache($cache); - - $agent; - }; - - my $req = $lwp->get($url); - - if ($req->is_success) { - return $req->decoded_content; - } - - return; -} - -=head2 save_caption($video_ID) - -Save the caption in a .srt file and return its file path. - -=cut - -sub save_caption { - my ($self, $video_id) = @_; - - # Find one of the preferred languages - my $info = $self->find_caption_data() // return; - - require File::Spec; - my $filename = "${video_id}_$info->{languageCode}.srt"; - my $srt_file = File::Spec->catfile($self->{captions_dir} // File::Spec->tmpdir, $filename); - - # Return the srt file if it already exists - return $srt_file if (-e $srt_file); - - # Get XML data, then transform it to SubRip data - my $xml = $self->get_xml_data($info->{baseUrl} // return) // return; - my $srt = $self->xml2srt($xml) // return; - - # Write the SubRib data to the $srt_file - open(my $fh, '>:utf8', $srt_file) or return; - print {$fh} $srt, "\n"; - close $fh; - - # Return the .srt file path - return $srt_file; -} - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::GetCaption - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2012-2015 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::GetCaption diff --git a/lib/WWW/StrawViewer/GuideCategories.pm b/lib/WWW/StrawViewer/GuideCategories.pm deleted file mode 100644 index 1f164b1..0000000 --- a/lib/WWW/StrawViewer/GuideCategories.pm +++ /dev/null @@ -1,85 +0,0 @@ -package WWW::StrawViewer::GuideCategories; - -use utf8; -use 5.014; -use warnings; - -=head1 NAME - -WWW::StrawViewer::GuideCategories - Categories interface. - -=head1 SYNOPSIS - - use WWW::StrawViewer; - my $obj = WWW::StrawViewer->new(%opts); - my $videos = $obj->youtube_categories('US'); - -=head1 SUBROUTINES/METHODS - -=cut - -sub _make_guideCategories_url { - my ($self, %opts) = @_; - - if (not exists $opts{id}) { - $opts{regionCode} //= $self->get_regionCode; - } - - $self->_make_feed_url('guideCategories', hl => $self->get_hl, %opts); -} - -=head2 guide_categories(;$region_id) - -Return guide categories for a specific region ID. - -=head2 guide_categories_info($category_id) - -Return info for a list of comma-separated category IDs. - -=cut - -{ - no strict 'refs'; - - foreach my $method ( - { - key => 'id', - name => 'guide_categories_info', - }, - { - key => 'regionCode', - name => 'guide_categories', - }, - ) { - *{__PACKAGE__ . '::' . $method->{name}} = sub { - my ($self, $id) = @_; - return $self->_get_results($self->_make_guideCategories_url($method->{key} => $id // return)); - }; - } -} - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::GuideCategories - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2013-2015 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::GuideCategories diff --git a/lib/WWW/StrawViewer/Itags.pm b/lib/WWW/StrawViewer/Itags.pm deleted file mode 100644 index 95ed483..0000000 --- a/lib/WWW/StrawViewer/Itags.pm +++ /dev/null @@ -1,319 +0,0 @@ -package WWW::StrawViewer::Itags; - -use utf8; -use 5.014; -use warnings; - -=head1 NAME - -WWW::StrawViewer::Itags - Get the YouTube itags. - -=head1 SYNOPSIS - - use WWW::StrawViewer::Itags; - - my $yv_itags = WWW::StrawViewer::Itags->new(); - - my $itags = $yv_itags->get_itags(); - my $res = $yv_itags->get_resolutions(); - -=head1 SUBROUTINES/METHODS - -=head2 new() - -Return the blessed object. - -=cut - -sub new { - my ($class) = @_; - bless {}, $class; -} - -=head2 get_itags() - -Get a HASH ref with the YouTube itags. {resolution => [itags]}. - -Reference: http://en.wikipedia.org/wiki/YouTube#Quality_and_formats - -=cut - -sub get_itags { -#<<< - scalar { - 'best' => [ - 38, # mp4 (3072p) (v-a) - [138, # mp4 (2160p-4320p) (v) - 266, # mp4 (2160p-2304p) (v) - ], - ], - - '2160' => [ - [ - 315, # webm HFR (v) - 272, # webm (v) - 313, # webm (v) - 401, # av1 (v) - ], - ], - - '1440' => [ - [ - 308, # webm HFR (v) - 271, # webm (v) - 264, # mp4 (v) - 400, # av1 (v) - ], - ], - - '1080' => [ - [303, # webm HFR (v) - 299, # mp4 HFR (v) - ], - 46, # webm (v-a) - 37, # mp4 (v-a) - [248, # webm (v) - 137, # mp4 (v) - 399, # av1 (v) - ], - 301, # mp4 (live) (v-a) - 96, # ts (live) (v-a) - ], - - '720' => [ - [302, # webm HFR (v) - 298, # mp4 HFR (v) - ], - 45, # webm (v-a) - 22, # mp4 (v-a) - [247, # webm (v) - 136, # mp4 (v) - 398, # av1 (v) - ], - 300, # mp4 (live) (v-a) - 120, # flv (live) (v-a) - 95, # ts (live) (v-a) - ], - - '480' => [ - 44, # webm (v-a) - 35, # flv (v-a) - [244, # webm (v) - 135, # mp4 (v) - 397, # av1 (v) - ], - 94, # mp4 (live) (v-a) - ], - - '360' => [ - 43, # webm (v-a) - 34, # flv (v-a) - [243, # webm (v) - 134, # mp4 (v) - 396, # av1 (v) - ], - 93, # mp4 (live) (v-a) - 18, # mp4 (v-a) - ], - - '240' => [ - 6, # flv (270p) (v-a) - 5, # flv (v-a) - 36, # 3gp (v-a) - 13, # 3gp (v-a) - [242, # webm (v) - 133, # mp4 (v) - 395, # av1 (v) - ], - 92, # mp4 (live) (v-a) - 132, # ts (live) (v-a) - ], - - '144' => [ - 17, # 3gp (v-a) - [278, # webm (v) - 160, # mp4 (v) - 394, # av1 (v) - ], - 91, # mp4 (live) (v-a) - 151, # ts (live) (v-a) - ], - - 'audio' => [172, # webm (192 kbps) - 251, # webm opus (128-160 kbps) - 171, # webm vorbis (92-128 kbps) - 140, # mp4a (128 kbps) - 141, # mp4a (256 kbps) - 250, # webm opus (64 kbps) - 249, # webm opus (48 kbps) - 139, # mp4a (48 kbps) - ], - }; -#>>> -} - -=head2 get_resolutions() - -Get an ARRAY ref with the supported resolutions ordered from highest to lowest. - -=cut - -sub get_resolutions { - my ($self) = @_; - - state $itags = $self->get_itags(); - return [ - grep { exists $itags->{$_} } - qw( - best - 2160 - 1440 - 1080 - 720 - 480 - 360 - 240 - 144 - audio - ) - ]; -} - -sub _find_streaming_url { - my ($self, %args) = @_; - - my $stream = $args{stream} // return; - my $resolution = $args{resolution} // return; - - foreach my $itag (@{$args{itags}->{$resolution}}) { - - if (ref($itag) eq 'ARRAY') { - - $args{dash} || next; - - foreach my $i (@{$itag}) { - - next if not exists $stream->{$i}; - - my $video_info = $stream->{$i}; - my $audio_info = $self->_find_streaming_url(%args, resolution => 'audio', dash => 0); - - if (defined $audio_info) { - $video_info->{__AUDIO__} = $audio_info; - return $video_info; - } - } - - next; - } - - if (exists $stream->{$itag}) { - if ($resolution eq 'audio' and not $args{dash_mp4_audio}) { - if ($itag == 140 or $itag == 141 or $itag == 139) { - next; # skip mp4 audio URLs - } - } - - my $entry = $stream->{$itag}; - - # Ignore segmented DASH URLs (they load pretty slow in mpv) - if (not $args{dash_segmented}) { - next if ($entry->{url} =~ m{^https://manifest\.googlevideo\.com/api/manifest/dash/}); - } - - return $entry; - } - } - - return; -} - -=head2 find_streaming_url(%options) - -Return the streaming URL which corresponds with the specified resolution. - - ( - urls => \@streaming_urls, - resolution => 'resolution_name', # from $obj->get_resolutions(), - dash => 1/0, # include or exclude DASH itags - dash_mp4_audio => 1/0, # include or exclude DASH videos with MP4 audio - dash_segmented => 1/0, # include or exclude segmented DASH videos - ) - -=cut - -sub find_streaming_url { - my ($self, %args) = @_; - - my $urls_array = $args{urls}; - my $resolution = $args{resolution}; - - state $itags = $self->get_itags(); - - if (defined($resolution) and $resolution =~ /^([0-9]+)/) { - $resolution = $1; - } - - my %stream; - foreach my $info_ref (@{$urls_array}) { - if (exists $info_ref->{itag} and exists $info_ref->{url}) { - $stream{$info_ref->{itag}} = $info_ref; - } - } - - $args{stream} = \%stream; - $args{itags} = $itags; - $args{resolution} = $resolution; - - my ($streaming, $found_resolution); - - # Try to find the wanted resolution - if (defined($resolution) and exists $itags->{$resolution}) { - $streaming = $self->_find_streaming_url(%args); - $found_resolution = $resolution; - } - - # Otherwise, find the best resolution available - if (not defined $streaming) { - - state $resolutions = $self->get_resolutions(); - - foreach my $res (@{$resolutions}) { - - $streaming = $self->_find_streaming_url(%args, resolution => $res); - - if (defined($streaming)) { - $found_resolution = $res; - last; - } - } - } - - wantarray ? ($streaming, $found_resolution) : $streaming; -} - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::Itags - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2012-2015 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::Itags diff --git a/lib/WWW/StrawViewer/ParseJSON.pm b/lib/WWW/StrawViewer/ParseJSON.pm deleted file mode 100644 index e2e7d65..0000000 --- a/lib/WWW/StrawViewer/ParseJSON.pm +++ /dev/null @@ -1,76 +0,0 @@ -package WWW::StrawViewer::ParseJSON; - -use utf8; -use 5.014; -use warnings; - -=head1 NAME - -WWW::StrawViewer::ParseJSON - Parse JSON content. - -=head1 SYNOPSIS - - use WWW::StrawViewer::ParseJSON; - my $obj = WWW::StrawViewer::ParseJSON->new(%opts); - -=head1 SUBROUTINES/METHODS - -=cut - -=head2 parse_json_string($json_string) - -Parse a JSON string and return a HASH ref. - -=cut - -sub parse_json_string { - my ($self, $json) = @_; - - if (not defined($json) or $json eq '') { - return {}; - } - - require JSON; - my $hash = eval { JSON::decode_json($json) }; - return $@ ? do { warn "[JSON]: $@\n"; {} } : $hash; -} - -=head2 make_json_string($ref) - -Create a JSON string from a HASH or ARRAY ref. - -=cut - -sub make_json_string { - my ($self, $ref) = @_; - - require JSON; - my $str = eval { JSON::encode_json($ref) }; - return $@ ? do { warn "[JSON]: $@\n"; '' } : $str; -} - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::ParseJSON - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2013-2015 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::ParseJSON diff --git a/lib/WWW/StrawViewer/ParseXML.pm b/lib/WWW/StrawViewer/ParseXML.pm deleted file mode 100644 index b6039b1..0000000 --- a/lib/WWW/StrawViewer/ParseXML.pm +++ /dev/null @@ -1,311 +0,0 @@ -package WWW::StrawViewer::ParseXML; - -use utf8; -use 5.014; -use warnings; - -=encoding utf8 - -=head1 NAME - -WWW::StrawViewer::ParseXML - Convert XML to a HASH ref structure. - -=head1 SYNOPSIS - -Parse XML content and return an HASH ref structure. - -Usage: - - use WWW::StrawViewer::ParseXML; - my $hash_ref = WWW::StrawViewer::ParseXML::xml2hash($xml_string); - -=head1 SUBROUTINES/METHODS - -=head2 xml2hash($xml_string) - -Parse XML and return an HASH ref. - -=cut - -sub xml2hash { - my $xml = shift() // return; - - $xml = "$xml"; # copy the string - - my $xml_ref = {}; - - my %args = ( - attr => '-', - text => '#text', - empty => q{}, - @_ - ); - - my %ctags; - my $ref = $xml_ref; - - state $inv_chars = q{!"#$@%&'()*+,/;\\<=>?\]\[^`{|}~}; - state $valid_tag = qr{[^\-.\s0-9$inv_chars][^$inv_chars\s]*}; - - { - if ( - $xml =~ m{\G< \s* - ($valid_tag) \s* - ((?>$valid_tag\s*=\s*(?>".*?"|'.*?')|\s+)+)? \s* - (/)?\s*> \s* - }gcsxo - ) { - - my ($tag, $attrs, $closed) = ($1, $2, $3); - - if (defined $attrs) { - push @{$ctags{$tag}}, $ref; - - $ref = - ref $ref eq 'HASH' - ? ref $ref->{$tag} - ? $ref->{$tag} - : ( - defined $ref->{$tag} - ? ($ref->{$tag} = [$ref->{$tag}]) - : ($ref->{$tag} //= []) - ) - : ref $ref eq 'ARRAY' ? ref $ref->[-1]{$tag} - ? $ref->[-1]{$tag} - : ( - defined $ref->[-1]{$tag} - ? ($ref->[-1]{$tag} = [$ref->[-1]{$tag}]) - : ($ref->[-1]{$tag} //= []) - ) - : []; - - ++$#{$ref} if ref $ref eq 'ARRAY'; - - while ( - $attrs =~ m{\G - ($valid_tag) \s*=\s* - (?> - "(.*?)" - | - '(.*?)' - ) \s* - }gsxo - ) { - my ($key, $value) = ($1, $+); - $key = join(q{}, $args{attr}, $key); - if (ref $ref eq 'ARRAY') { - $ref->[-1]{$key} = _decode_entities($value); - } - elsif (ref $ref eq 'HASH') { - $ref->{$key} = $value; - } - } - - if (defined $closed) { - $ref = pop @{$ctags{$tag}}; - } - - if ($xml =~ m{\G<\s*/\s*\Q$tag\E\s*>\s*}gc) { - $ref = pop @{$ctags{$tag}}; - } - elsif ($xml =~ m{\G([^<]+)(?=<)}gsc) { - if (ref $ref eq 'ARRAY') { - $ref->[-1]{$args{text}} .= _decode_entities($1); - $ref = pop @{$ctags{$tag}}; - } - elsif (ref $ref eq 'HASH') { - $ref->{$args{text}} .= $1; - $ref = pop @{$ctags{$tag}}; - } - } - } - elsif (defined $closed) { - if (ref $ref eq 'ARRAY') { - if (exists $ref->[-1]{$tag}) { - if (ref $ref->[-1]{$tag} ne 'ARRAY') { - $ref->[-1]{$tag} = [$ref->[-1]{$tag}]; - } - push @{$ref->[-1]{$tag}}, $args{empty}; - } - else { - $ref->[-1]{$tag} = $args{empty}; - } - } - } - else { - if ($xml =~ /\G(?=<(?!!))/) { - push @{$ctags{$tag}}, $ref; - - $ref = - ref $ref eq 'HASH' - ? ref $ref->{$tag} - ? $ref->{$tag} - : ( - defined $ref->{$tag} - ? ($ref->{$tag} = [$ref->{$tag}]) - : ($ref->{$tag} //= []) - ) - : ref $ref eq 'ARRAY' ? ref $ref->[-1]{$tag} - ? $ref->[-1]{$tag} - : ( - defined $ref->[-1]{$tag} - ? ($ref->[-1]{$tag} = [$ref->[-1]{$tag}]) - : ($ref->[-1]{$tag} //= []) - ) - : []; - - ++$#{$ref} if ref $ref eq 'ARRAY'; - redo; - } - elsif ($xml =~ /\G<!\[CDATA\[(.*?)\]\]>\s*/gcs or $xml =~ /\G([^<]+)(?=<)/gsc) { - my ($text) = $1; - - if ($xml =~ m{\G<\s*/\s*\Q$tag\E\s*>\s*}gc) { - if (ref $ref eq 'ARRAY') { - if (exists $ref->[-1]{$tag}) { - if (ref $ref->[-1]{$tag} ne 'ARRAY') { - $ref->[-1]{$tag} = [$ref->[-1]{$tag}]; - } - push @{$ref->[-1]{$tag}}, $text; - } - else { - $ref->[-1]{$tag} .= _decode_entities($text); - } - } - elsif (ref $ref eq 'HASH') { - $ref->{$tag} .= $text; - } - } - else { - push @{$ctags{$tag}}, $ref; - - $ref = - ref $ref eq 'HASH' - ? ref $ref->{$tag} - ? $ref->{$tag} - : ( - defined $ref->{$tag} - ? ($ref->{$tag} = [$ref->{$tag}]) - : ($ref->{$tag} //= []) - ) - : ref $ref eq 'ARRAY' ? ref $ref->[-1]{$tag} - ? $ref->[-1]{$tag} - : ( - defined $ref->[-1]{$tag} - ? ($ref->[-1]{$tag} = [$ref->[-1]{$tag}]) - : ($ref->[-1]{$tag} //= []) - ) - : []; - - ++$#{$ref} if ref $ref eq 'ARRAY'; - - if (ref $ref eq 'ARRAY') { - if (exists $ref->[-1]{$tag}) { - if (ref $ref->[-1]{$tag} ne 'ARRAY') { - $ref->[-1] = [$ref->[-1]{$tag}]; - } - push @{$ref->[-1]}, {$args{text} => $text}; - } - else { - $ref->[-1]{$args{text}} .= $text; - } - } - elsif (ref $ref eq 'HASH') { - $ref->{$tag} .= $text; - } - } - } - } - - if ($xml =~ m{\G<\s*/\s*\Q$tag\E\s*>\s*}gc) { - ## tag closed - ok - } - - redo; - } - elsif ($xml =~ m{\G<\s*/\s*($valid_tag)\s*>\s*}gco) { - if (exists $ctags{$1} and @{$ctags{$1}}) { - $ref = pop @{$ctags{$1}}; - } - redo; - } - elsif ($xml =~ /\G<!\[CDATA\[(.*?)\]\]>\s*/gcs or $xml =~ m{\G([^<]+)(?=<)}gsc) { - if (ref $ref eq 'ARRAY') { - $ref->[-1]{$args{text}} .= $1; - } - elsif (ref $ref eq 'HASH') { - $ref->{$args{text}} .= $1; - } - redo; - } - elsif ($xml =~ /\G<\?/gc) { - $xml =~ /\G.*?\?>\s*/gcs or die "Invalid XML!"; - redo; - } - elsif ($xml =~ /\G<!--/gc) { - $xml =~ /\G.*?-->\s*/gcs or die "Comment not closed!"; - redo; - } - elsif ($xml =~ /\G<!DOCTYPE\s+/gc) { - $xml =~ /\G(?>$valid_tag|\s+|".*?"|'.*?')*\[.*?\]>\s*/sgco - or $xml =~ /\G.*?>\s*/sgc - or die "DOCTYPE not closed!"; - redo; - } - elsif ($xml =~ /\G\z/gc) { - ## ok - } - elsif ($xml =~ /\G\s+/gc) { - redo; - } - else { - die "Syntax error near: --> ", [split(/\n/, substr($xml, pos(), 2**6))]->[0], " <--\n"; - } - } - - return $xml_ref; -} - -{ - my %entities = ( - 'amp' => '&', - 'quot' => '"', - 'apos' => "'", - 'gt' => '>', - 'lt' => '<', - ); - - state $ent_re = do { - local $" = '|'; - qr/&(@{[keys %entities]});/; - }; - - sub _decode_entities { - $_[0] =~ s/$ent_re/$entities{$1}/gor; - } -} - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::ParseXML - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2012-2015 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::ParseXML diff --git a/lib/WWW/StrawViewer/PlaylistItems.pm b/lib/WWW/StrawViewer/PlaylistItems.pm deleted file mode 100644 index ab41a0d..0000000 --- a/lib/WWW/StrawViewer/PlaylistItems.pm +++ /dev/null @@ -1,146 +0,0 @@ -package WWW::StrawViewer::PlaylistItems; - -use utf8; -use 5.014; -use warnings; - -=head1 NAME - -WWW::StrawViewer::PlaylistItems - Manage playlist entries. - -=head1 SYNOPSIS - - use WWW::StrawViewer; - my $obj = WWW::StrawViewer->new(%opts); - my $videos = $obj->videos_from_playlistID($playlist_id); - -=head1 SUBROUTINES/METHODS - -=cut - -sub _make_playlistItems_url { - my ($self, %opts) = @_; - return - $self->_make_feed_url( - 'playlistItems', - pageToken => $self->page_token, - %opts - ); -} - -=head2 add_video_to_playlist($playlistID, $videoID; $position=1) - -Add a video to given playlist ID, at position 1 (by default) - -=cut - -sub add_video_to_playlist { - my ($self, $playlist_id, $video_id, $position) = @_; - - $self->get_access_token() // return; - - $playlist_id // return; - $video_id // return; - $position //= 0; - - my $hash = { - "snippet" => { - "playlistId" => $playlist_id, - "resourceId" => { - "videoId" => $video_id, - "kind" => "youtube#video" - }, - "position" => $position, - } - }; - - my $url = $self->_make_playlistItems_url(pageToken => undef); - $self->post_as_json($url, $hash); -} - -=head2 favorite_video($videoID) - -Favorite a video. Returns true on success. - -=cut - -sub favorite_video { - my ($self, $video_id) = @_; - $video_id // return; - $self->get_access_token() // return; - my $playlist_id = $self->get_playlist_id('favorites', mine => 'true') // return; - $self->add_video_to_playlist($playlist_id, $video_id); -} - -=head2 videos_from_playlist_id($playlist_id) - -Get videos from a specific playlistID. - -=cut - -sub videos_from_playlist_id { - my ($self, $id) = @_; - $self->_get_results($self->_make_feed_url("playlists/$id")); -} - -=head2 favorites($channel_id) - -=head2 uploads($channel_id) - -=head2 likes($channel_id) - -Get the favorites, uploads and likes for a given channel ID. - -=cut - -=head2 favorites_from_username($username) - -=head2 uploads_from_username($username) - -=head2 likes_from_username($username) - -Get the favorites, uploads and likes for a given YouTube username. - -=cut - -{ - no strict 'refs'; - foreach my $name (qw(favorites uploads likes)) { - - *{__PACKAGE__ . '::' . $name . '_from_username'} = sub { - my ($self, $username) = @_; - $self->videos_from_username($username); - }; - - *{__PACKAGE__ . '::' . $name} = sub { - my ($self, $channel_id) = @_; - $self->videos_from_channel_id($channel_id); - }; - } -} - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::PlaylistItems - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2013-2015 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::PlaylistItems diff --git a/lib/WWW/StrawViewer/Playlists.pm b/lib/WWW/StrawViewer/Playlists.pm deleted file mode 100644 index 5d4e07d..0000000 --- a/lib/WWW/StrawViewer/Playlists.pm +++ /dev/null @@ -1,124 +0,0 @@ -package WWW::StrawViewer::Playlists; - -use utf8; -use 5.014; -use warnings; - -=head1 NAME - -WWW::StrawViewer::Playlists - Straw playlists handle. - -=head1 SYNOPSIS - - use WWW::StrawViewer; - my $obj = WWW::StrawViewer->new(%opts); - my $info = $obj->playlist_from_id($playlist_id); - -=head1 SUBROUTINES/METHODS - -=cut - -sub _make_playlists_url { - my ($self, %opts) = @_; - - if (not exists $opts{'part'}) { - $opts{'part'} = 'snippet,contentDetails'; - } - - $self->_make_feed_url( - 'playlists', - pageToken => $self->page_token, - %opts, - ); -} - -sub get_playlist_id { - my ($self, $playlist_name, %fields) = @_; - - my $url = $self->_simple_feeds_url('channels', qw(part contentDetails), %fields); - my $res = $self->_get_results($url); - - ref($res->{results}{items}) eq 'ARRAY' || return; - @{$res->{results}{items}} || return; - - return $res->{results}{items}[0]{contentDetails}{relatedPlaylists}{$playlist_name}; -} - -=head2 playlist_from_id($playlist_id) - -Return info for one or more playlists. -PlaylistIDs can be separated by commas. - -=cut - -sub playlist_from_id { - my ($self, $id, $part) = @_; - $self->_get_results($self->_make_playlists_url(id => $id, part => ($part // 'snippet'))); -} - -=head2 playlists($channel_id) - -Get and return playlists from a channel ID. - -=cut - -sub playlists { - my ($self, $channel_id) = @_; - $self->_get_results( - $self->_make_playlists_url( - ($channel_id and $channel_id ne 'mine') - ? (channelId => $channel_id) - : do { $self->get_access_token() // return; (mine => 'true') } - ) - ); -} - -=head2 playlists_from_username($username) - -Get and return the playlists created for a given username. - -=cut - -sub playlists_from_username { - my ($self, $username) = @_; - my $channel_id = $self->channel_id_from_username($username) // $username; - $self->playlists($channel_id); -} - -=head2 my_playlists() - -Get and return your playlists. - -=cut - -sub my_playlists { - my ($self) = @_; - $self->get_access_token() // return; - $self->_get_results($self->_make_playlists_url(mine => 'true')); -} - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::Playlists - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2013-2015 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::Playlists diff --git a/lib/WWW/StrawViewer/RegularExpressions.pm b/lib/WWW/StrawViewer/RegularExpressions.pm deleted file mode 100644 index edf9dd5..0000000 --- a/lib/WWW/StrawViewer/RegularExpressions.pm +++ /dev/null @@ -1,89 +0,0 @@ -package WWW::StrawViewer::RegularExpressions; - -use utf8; -use 5.014; -use warnings; - -require Exporter; -our @ISA = qw(Exporter); - -=head1 NAME - -WWW::StrawViewer::RegularExpressions - Various utils. - -=head1 SYNOPSIS - - use WWW::StrawViewer::RegularExpressions; - use WWW::StrawViewer::RegularExpressions ($get_video_id_re); - -=cut - -my $opt_begin_chars = q{:;=}; # stdin option valid begin chars - -# Options -our $range_num_re = qr{^([0-9]{1,2}+)(?>-|\.\.)([0-9]{1,2}+)?\z}; -our $digit_or_equal_re = qr/(?(?=[1-9])|=)/; -our $non_digit_or_opt_re = qr{^(?!$range_num_re)(?>[0-9]{1,2}[^0-9]|[0-9]{3}|[^0-9$opt_begin_chars])}; - -# Generic name -my $generic_name_re = qr/[a-zA-Z0-9_.\-]{11,34}/; -our $valid_channel_id_re = qr{^(?:.*/channel/)?(?<channel_id>(?:\w+(?:[-.]++\w++)*|$generic_name_re))(?:/.*)?\z}; - -our $get_channel_videos_id_re = qr{^.*/channel/(?<channel_id>(?:\w+(?:[-.]++\w++)*|$generic_name_re))}; -our $get_channel_playlists_id_re = qr{$get_channel_videos_id_re/playlists}; - -our $get_username_videos_re = qr{^.*/user/(?<username>[-.\w]+)}; -our $get_username_playlists_re = qr{$get_username_videos_re/playlists}; - -# Video ID -my $video_id_re = qr/[0-9A-Za-z_\-]{11}/; -our $valid_video_id_re = qr{^$video_id_re\z}; -our $get_video_id_re = qr{(?:%3F|\b)(?>v|embed|youtu[.]be)(?>[=/]|%3D)(?<video_id>$video_id_re)}; - -# Playlist ID -our $valid_playlist_id_re = qr{^$generic_name_re\z}; -our $get_playlist_id_re = qr{(?:(?:(?>playlist\?list|view_play_list\?p|list)=)|\w#p/c/)(?<playlist_id>$generic_name_re)\b}; - -our $valid_opt_re = qr{^[$opt_begin_chars]([A-Za-z]++(?:-[A-Za-z]++)?(?>${digit_or_equal_re}.*)?)$}; - -our @EXPORT = qw( - $range_num_re - $digit_or_equal_re - $non_digit_or_opt_re - $valid_channel_id_re - $valid_video_id_re - $get_video_id_re - $valid_playlist_id_re - $get_playlist_id_re - $valid_opt_re - $get_channel_videos_id_re - $get_channel_playlists_id_re - $get_username_videos_re - $get_username_playlists_re - ); - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::RegularExpressions - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2012-2013 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::RegularExpressions diff --git a/lib/WWW/StrawViewer/Search.pm b/lib/WWW/StrawViewer/Search.pm deleted file mode 100644 index 67b8982..0000000 --- a/lib/WWW/StrawViewer/Search.pm +++ /dev/null @@ -1,175 +0,0 @@ -package WWW::StrawViewer::Search; - -use utf8; -use 5.014; -use warnings; - -=head1 NAME - -WWW::StrawViewer::Search - Search functions for Straw API v3 - -=head1 SYNOPSIS - - use WWW::StrawViewer; - my $obj = WWW::StrawViewer->new(%opts); - $obj->search_videos(@keywords); - -=head1 SUBROUTINES/METHODS - -=cut - -sub _make_search_url { - my ($self, %opts) = @_; - - return $self->_make_feed_url( - 'search', - - topicId => $self->get_topicId, - regionCode => $self->get_regionCode, - - maxResults => $self->get_maxResults, - order => $self->get_order, - publishedAfter => $self->get_publishedAfter, - publishedBefore => $self->get_publishedBefore, - regionCode => $self->get_regionCode, - relevanceLanguage => $self->get_relevanceLanguage, - safeSearch => $self->get_safeSearch, - channelId => $self->get_channelId, - channelType => $self->get_channelType, - pageToken => $self->page_token, - - ( - $opts{type} eq 'video' - ? ( - videoCaption => $self->get_videoCaption, - videoCategoryId => $self->get_videoCategoryId, - videoDefinition => $self->get_videoDefinition, - videoDimension => $self->get_videoDimension, - videoDuration => $self->get_videoDuration, - videoEmbeddable => $self->get_videoEmbeddable, - videoLicense => $self->get_videoLicense, - videoSyndicated => $self->get_videoSyndicated, - videoType => $self->get_videoType, - eventType => $self->get_eventType, - ) - : () - ), - - %opts, - ); - -} - -=head2 search_for($types,$keywords;\%args) - -Search for a list of types (comma-separated). - -=cut - -sub search_for { - my ($self, $type, $keywords, $args) = @_; - - $keywords //= []; - if (ref $keywords ne 'ARRAY') { - $keywords = [split ' ', $keywords]; - } - - my $url = $self->_make_search_url( - type => $type, - q => $self->escape_string(join(' ', @{$keywords})), - (ref $args eq 'HASH' ? %{$args} : (part => 'snippet')), - ); - - return $self->_get_results($url); -} - -{ - no strict 'refs'; - - foreach my $pair ( - { - name => 'videos', - type => 'video', - }, - { - name => 'playlists', - type => 'playlist', - }, - { - name => 'channels', - type => 'channel', - }, - { - name => 'all', - type => 'video,channel,playlist', - } - ) { - *{__PACKAGE__ . '::' . "search_$pair->{name}"} = sub { - my $self = shift; - $self->search_for($pair->{type}, @_); - }; - } -} - -=head2 search_videos($keywords;\%args) - -Search and return the found video results. - -=cut - -=head2 search_playlists($keywords;\%args) - -Search and return the found playlists. - -=cut - -=head2 search_channels($keywords;\%args) - -Search and return the found channels. - -=cut - -=head2 search_all($keywords;\%args) - -Search and return the results. - -=cut - -=head2 related_to_videoID($id) - -Retrieves a list of videos that are related to the video -that the parameter value identifies. The parameter value must -be set to a YouTube video ID. - -=cut - -sub related_to_videoID { - my ($self, $id) = @_; - return $self->search_for('video', [], {relatedToVideoId => $id}); -} - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::Search - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2013-2015 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::Search diff --git a/lib/WWW/StrawViewer/Subscriptions.pm b/lib/WWW/StrawViewer/Subscriptions.pm deleted file mode 100644 index 063a875..0000000 --- a/lib/WWW/StrawViewer/Subscriptions.pm +++ /dev/null @@ -1,272 +0,0 @@ -package WWW::StrawViewer::Subscriptions; - -use utf8; -use 5.014; -use warnings; - -=head1 NAME - -WWW::StrawViewer::Subscriptions - Subscriptions handler. - -=head1 SYNOPSIS - - use WWW::StrawViewer; - my $obj = WWW::StrawViewer->new(%opts); - my $videos = $obj->subscriptions_from_channelID($channel_id); - -=head1 SUBROUTINES/METHODS - -=cut - -sub _make_subscriptions_url { - my ($self, %opts) = @_; - return $self->_make_feed_url('subscriptions', %opts); -} - -=head2 subscribe_channel($channel_id) - -Subscribe to an YouTube channel. - -=cut - -sub subscribe_channel { - my ($self, $channel_id) = @_; - - my $resource = { - snippet => { - resourceId => { - kind => 'youtube#channel', - channelId => $channel_id, - } - } - }; - - my $url = $self->_simple_feeds_url('subscriptions', part => 'snippet'); - return $self->post_as_json($url, $resource); -} - -=head2 subscribe_channel_from_username($username) - -Subscribe to an YouTube channel via username. - -=cut - -sub subscribe_channel_from_username { - my ($self, $username) = @_; - $self->subscribe_channel($self->channel_id_from_username($username) // $username); -} - -=head2 subscriptions(;$channel_id) - -Retrieve the subscriptions for a channel ID or for the authenticated user. - -=cut - -sub subscriptions { - my ($self, $channel_id) = @_; - $self->_get_results( - $self->_make_subscriptions_url( - order => $self->get_subscriptions_order, - part => 'snippet', - ( - ($channel_id and $channel_id ne 'mine') - ? (channelId => $channel_id) - : do { $self->get_access_token() // return; (mine => 'true') } - ), - ) - ); -} - -=head2 subscriptions_from_username($username) - -Retrieve subscriptions for a given YouTube username. - -=cut - -sub subscriptions_from_username { - my ($self, $username) = @_; - $self->subscriptions($self->channel_id_from_username($username) // $username); -} - -=head2 subscription_videos(;$channel_id) - -Retrieve the video subscriptions for a channel ID or for the current authenticated user. - -=cut - -sub subscription_videos { - my ($self, $channel_id, $order) = @_; - - my $max_results = $self->get_maxResults(); - - my @subscription_items; - my $next_page_token; - - while (1) { - - my $url = $self->_make_subscriptions_url( - order => $self->get_subscriptions_order, - maxResults => 50, - part => 'snippet,contentDetails', - ($channel_id and $channel_id ne 'mine') - ? (channelId => $channel_id) - : do { $self->get_access_token() // return; (mine => 'true') }, - defined($next_page_token) ? (pageToken => $next_page_token) : (), - ); - - my $subscriptions = $self->_get_results($url)->{results}; - - if ( ref($subscriptions) eq 'HASH' - and ref($subscriptions->{items}) eq 'ARRAY') { - push @subscription_items, @{$subscriptions->{items}}; - } - - $next_page_token = $subscriptions->{nextPageToken} || last; - } - - my (undef, undef, undef, $mday, $mon, $year) = localtime; - - $mon += 1; - $year += 1900; - - my @videos; - foreach my $channel (@subscription_items) { - - my $new_items = $channel->{contentDetails}{newItemCount}; - - # Ignore channels with zero new items - $new_items > 0 || next; - - # Set the number of results - $self->set_maxResults(1); # don't load more than 1 video from each channel - # maybe, this value should be configurable (?) - - my $uploads = $self->uploads($channel->{snippet}{resourceId}{channelId}); - - (ref($uploads) eq 'HASH' and ref($uploads->{results}) eq 'HASH' and ref($uploads->{results}{items}) eq 'ARRAY') - || return; - - my $items = $uploads->{results}{items}; - - # Get and store the video uploads from each channel - foreach my $item (@$items) { - my $publishedAt = $item->{snippet}{publishedAt}; - my ($p_year, $p_mon, $p_mday) = $publishedAt =~ /^(\d{4})-(\d{2})-(\d{2})/; - - my $year_diff = $year - $p_year; - my $mon_diff = $mon - $p_mon; - my $mday_diff = $mday - $p_mday; - - my $days_diff = $year_diff * 365.2422 + $mon_diff * 30.436875 + $mday_diff; - - # Ignore old entries - if ($days_diff > 3) { - next; - } - - push @videos, $item; - } - - # Stop when the limit is reached - last if (@videos >= $max_results); - } - - # When there are no new videos, load one from each channel - if ($#videos == -1) { - foreach my $channel (@subscription_items) { - $self->set_maxResults(1); - push @videos, @{$self->uploads($channel->{snippet}{resourceId}{channelId})->{results}{items}}; - last if (@videos >= $max_results); - } - } - - $self->set_maxResults($max_results); - - state $parse_time_re = qr/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/; - - @videos = - sort { - my ($y1, $M1, $d1, $h1, $m1, $s1) = $a->{snippet}{publishedAt} =~ $parse_time_re; - my ($y2, $M2, $d2, $h2, $m2, $s2) = $b->{snippet}{publishedAt} =~ $parse_time_re; - - ($y2 <=> $y1) || ($M2 <=> $M1) || ($d2 <=> $d1) || ($h2 <=> $h1) || ($m2 <=> $m1) || ($s2 <=> $s1) - } @videos; - - return {results => {pageInfo => {totalResults => $#videos + 1}, items => \@videos}}; -} - -=head2 subscription_videos_from_username($username) - -Retrieve the video subscriptions for a username. - -=cut - -sub subscription_videos_from_username { - my ($self, $username) = @_; - $self->subscription_videos($self->channel_id_from_username($username) // $username); -} - -=head2 subscriptions_from_channelID(%args) - -Get subscriptions for the specified channel ID. - -=head2 subscriptions_info($subscriptionID, %args) - -Get details for the comma-separated subscriptionID(s). - -=head3 HASH '%args' supports the following pairs: - - %args = ( - part => {contentDetails,id,snippet}, - forChannelId => $channelID, - maxResults => [0-50], - order => {alphabetical, relevance, unread}, - pageToken => {$nextPageToken, $prevPageToken}, - ); - -=cut - -{ - no strict 'refs'; - foreach my $method ( - { - key => 'id', - name => 'subscriptions_info', - }, - { - key => 'channelId', - name => 'subscriptions_from_channel_id', - } - ) { - *{__PACKAGE__ . '::' . $method->{name}} = sub { - my ($self, $id, %args) = @_; - return $self->_get_results($self->_make_subscriptions_url($method->{key} => $id, %args)); - }; - } -} - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::Subscriptions - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2013-2015 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::Subscriptions diff --git a/lib/WWW/StrawViewer/Utils.pm b/lib/WWW/StrawViewer/Utils.pm deleted file mode 100644 index 153aab9..0000000 --- a/lib/WWW/StrawViewer/Utils.pm +++ /dev/null @@ -1,801 +0,0 @@ -package WWW::StrawViewer::Utils; - -use utf8; -use 5.014; -use warnings; - -=head1 NAME - -WWW::StrawViewer::Utils - Various utils. - -=head1 SYNOPSIS - - use WWW::StrawViewer::Utils; - - my $yv_utils = WWW::StrawViewer::Utils->new(%opts); - - print $yv_utils->format_time(3600); - -=head1 SUBROUTINES/METHODS - -=head2 new(%opts) - -Options: - -=over 4 - -=item thousand_separator => "" - -Character used as thousand separator. - -=item months => [] - -Month names for I<format_date()> - -=item youtube_url_format => "" - -A youtube URL format for sprintf(format, videoID). - -=back - -=cut - -sub new { - my ($class, %opts) = @_; - - my $self = bless { - thousand_separator => q{,}, - youtube_url_format => 'https://www.youtube.com/watch?v=%s', - }, $class; - - $self->{months} = [ - qw( - Jan Feb Mar - Apr May Jun - Jul Aug Sep - Oct Nov Dec - ) - ]; - - foreach my $key (keys %{$self}) { - $self->{$key} = delete $opts{$key} - if exists $opts{$key}; - } - - foreach my $invalid_key (keys %opts) { - warn "Invalid key: '${invalid_key}'"; - } - - return $self; -} - -=head2 extension($type) - -Returns the extension format from a given type. - -From a string like 'video/webm;+codecs="vp9"', it returns 'webm'. - -=cut - -sub extension { - my ($self, $type) = @_; - $type =~ /\bflv\b/i ? q{flv} - : $type =~ /\bwebm\b/i ? q{webm} - : $type =~ /\b3gpp?\b/i ? q{3gp} - : $type =~ m{^video/(\w+)} ? $1 - : $type =~ m{^audio/(\w+)} ? $1 - : q{mp4}; -} - -=head2 format_time($sec) - -Returns time from seconds. - -=cut - -sub format_time { - my ($self, $sec) = @_; - $sec >= 3600 - ? join q{:}, map { sprintf '%02d', $_ } $sec / 3600 % 24, $sec / 60 % 60, $sec % 60 - : join q{:}, map { sprintf '%02d', $_ } $sec / 60 % 60, $sec % 60; -} - -=head2 format_duration($duration) - -Return seconds from duration (PT1H20M10S). - -=cut - -# PT5M3S -> 05:03 -# PT1H20M10S -> 01:20:10 -# PT16S -> 00:16 - -sub format_duration { - my ($self, $duration) = @_; - - $duration // return 0; - my ($hour, $min, $sec) = (0, 0, 0); - - $hour = $1 if ($duration =~ /(\d+)H/); - $min = $1 if ($duration =~ /(\d+)M/); - $sec = $1 if ($duration =~ /(\d+)S/); - - $hour * 60 * 60 + $min * 60 + $sec; -} - -=head2 format_date($date) - -Return string "04 May 2010" from "2010-05-04T00:25:55.000Z" - -=cut - -sub format_date { - my ($self, $date) = @_; - - # 2010-05-04T00:25:55.000Z - # to: 04 May 2010 - - $date =~ s{^ - (?<year>\d{4}) - - - (?<month>\d{2}) - - - (?<day>\d{2}) - .* - } - {$+{day} $self->{months}[$+{month} - 1] $+{year}}x; - - return $date; -} - -=head2 date_to_age($date) - -Return the (approximated) age for a given date of the form "2010-05-04T00:25:55.000Z". - -=cut - -sub date_to_age { - my ($self, $date) = @_; - - $date =~ m{^ - (?<year>\d{4}) - - - (?<month>\d{2}) - - - (?<day>\d{2}) - [a-zA-Z] - (?<hour>\d{2}) - : - (?<min>\d{2}) - : - (?<sec>\d{2}) - }x || return undef; - - my ($sec, $min, $hour, $day, $month, $year) = gmtime(time); - - $year += 1900; - $month += 1; - - my $lambda = sub { - - if ($year == $+{year}) { - if ($month == $+{month}) { - if ($day == $+{day}) { - if ($hour == $+{hour}) { - if ($min == $+{min}) { - return join(' ', $sec - $+{sec}, 'seconds'); - } - return join(' ', $min - $+{min}, 'minutes'); - } - return join(' ', $hour - $+{hour}, 'hours'); - } - return join(' ', $day - $+{day}, 'days'); - } - return join(' ', $month - $+{month}, 'months'); - } - - if ($year - $+{year} == 1) { - my $month_diff = $+{month} - $month; - if ($month_diff > 0) { - return join(' ', 12 - $month_diff, 'months'); - } - } - - return join(' ', $year - $+{year}, 'years'); - }; - - my $age = $lambda->(); - - if ($age =~ /^1\s/) { # singular mode - $age =~ s/s\z//; - } - - return $age; -} - -=head2 has_entries($result) - -Returns true if a given result has entries. - -=cut - -sub has_entries { - my ($self, $result) = @_; - - if (ref($result->{results}) eq 'HASH') { - - if (exists $result->{results}{comments}) { - return scalar @{$result->{results}{comments}} > 0; - } - - my $type = $result->{results}{type}//''; - - if ($type eq 'playlist') { - return $result->{results}{videoCount} > 0; - } - } - - if (ref($result->{results}) eq 'ARRAY') { - return scalar(@{$result->{results}}) > 0; - } - - return 1; # maybe? - #ref($result) eq 'HASH' and ($result->{results}{pageInfo}{totalResults} > 0); -} - -=head2 normalize_video_title($title, $fat32safe) - -Replace file-unsafe characters and trim spaces. - -=cut - -sub normalize_video_title { - my ($self, $title, $fat32safe) = @_; - - if ($fat32safe) { - $title =~ s/: / - /g; - $title =~ tr{:"*/?\\|}{;'+%!%%}; # " - $title =~ tr/<>//d; - } - else { - $title =~ tr{/}{%}; - } - - join(q{ }, split(q{ }, $title)); -} - -=head2 format_text(%opt) - -Formats a text with information from streaming and video info. - -The structure of C<%opt> is: - - ( - streaming => HASH, - info => HASH, - text => STRING, - escape => BOOL, - fat32safe => BOOL, - ) - -=cut - -sub format_text { - my ($self, %opt) = @_; - - my $streaming = $opt{streaming}; - my $info = $opt{info}; - my $text = $opt{text}; - my $escape = $opt{escape}; - my $fat32safe = $opt{fat32safe}; - - my %special_tokens = ( - ID => sub { $self->get_video_id($info) }, - AUTHOR => sub { $self->get_channel_title($info) }, - CHANNELID => sub { $self->get_channel_id($info) }, - DEFINITION => sub { $self->get_definition($info) }, - DIMENSION => sub { $self->get_dimension($info) }, - VIEWS => sub { $self->get_views($info) }, - VIEWS_SHORT => sub { $self->get_views_approx($info) }, - LIKES => sub { $self->get_likes($info) }, - DISLIKES => sub { $self->get_dislikes($info) }, - COMMENTS => sub { $self->get_comments($info) }, - DURATION => sub { $self->get_duration($info) }, - TIME => sub { $self->get_time($info) }, - TITLE => sub { $self->get_title($info) }, - FTITLE => sub { $self->normalize_video_title($self->get_title($info), $fat32safe) }, - CAPTION => sub { $self->get_caption($info) }, - PUBLISHED => sub { $self->get_publication_date($info) }, - AGE => sub { $self->get_publication_age($info) }, - AGE_SHORT => sub { $self->get_publication_age_approx($info) }, - DESCRIPTION => sub { $self->get_description($info) }, - - RATING => sub { - my $likes = $self->get_likes($info) // 0; - my $dislikes = $self->get_dislikes($info) // 0; - - my $rating = 0; - if ($likes + $dislikes > 0) { - $rating = $likes / ($likes + $dislikes) * 5; - } - - sprintf('%.2f', $rating); - }, - - ( - defined($streaming) - ? ( - RESOLUTION => sub { - $streaming->{resolution} =~ /^\d+\z/ - ? $streaming->{resolution} . 'p' - : $streaming->{resolution}; - }, - - ITAG => sub { $streaming->{streaming}{itag} }, - SUB => sub { $streaming->{srt_file} }, - VIDEO => sub { $streaming->{streaming}{url} }, - FORMAT => sub { $self->extension($streaming->{streaming}{type}) }, - - AUDIO => sub { - ref($streaming->{streaming}{__AUDIO__}) eq 'HASH' - ? $streaming->{streaming}{__AUDIO__}{url} - : q{}; - }, - - AOV => sub { - ref($streaming->{streaming}{__AUDIO__}) eq 'HASH' - ? $streaming->{streaming}{__AUDIO__}{url} - : $streaming->{streaming}{url}; - }, - ) - : () - ), - - URL => sub { sprintf($self->{youtube_url_format}, $self->get_video_id($info)) }, - ); - - my $tokens_re = do { - local $" = '|'; - qr/\*(@{[keys %special_tokens]})\*/; - }; - - my %special_escapes = ( - a => "\a", - b => "\b", - e => "\e", - f => "\f", - n => "\n", - r => "\r", - t => "\t", - ); - - my $escapes_re = do { - local $" = q{}; - qr/\\([@{[keys %special_escapes]}])/; - }; - - $text =~ s/$escapes_re/$special_escapes{$1}/g; - - $escape - ? $text =~ s/$tokens_re/\Q${\$special_tokens{$1}()}\E/gr - : $text =~ s/$tokens_re/${\$special_tokens{$1}()}/gr; -} - -=head2 set_thousands($num) - -Return the number with thousand separators. - -=cut - -sub set_thousands { # ugly, but fast - my ($self, $n) = @_; - - return 0 unless $n; - length($n) > 3 or return $n; - - my $l = length($n) - 3; - my $i = ($l - 1) % 3 + 1; - my $x = substr($n, 0, $i) . $self->{thousand_separator}; - - while ($i < $l) { - $x .= substr($n, $i, 3) . $self->{thousand_separator}; - $i += 3; - } - - return $x . substr($n, $i); -} - -=head2 get_video_id($info) - -Get videoID. - -=cut - -sub get_video_id { - my ($self, $info) = @_; - $info->{videoId}; - - #~ ref($info->{id}) eq 'HASH' ? $info->{id}{videoId} - #~ : exists($info->{snippet}{resourceId}{videoId}) ? $info->{snippet}{resourceId}{videoId} - #~ : exists($info->{contentDetails}{videoId}) ? $info->{contentDetails}{videoId} - #~ : exists($info->{contentDetails}{playlistItem}{resourceId}{videoId}) - #~ ? $info->{contentDetails}{playlistItem}{resourceId}{videoId} - #~ : exists($info->{contentDetails}{upload}{videoId}) ? $info->{contentDetails}{upload}{videoId} - #~ : do { - #~ my $id = $info->{id} // return undef; - - #~ if (length($id) != 11) { - #~ return undef; - #~ } - - #~ $id; - #~ }; -} - -sub get_playlist_id { - my ($self, $info) = @_; - $info->{playlistId}; -} - -sub get_playlist_video_count { - my ($self, $info) = @_; - $info->{videoCount}; -} - -=head2 get_description($info) - -Get description. - -=cut - -sub get_description { - my ($self, $info) = @_; - my $desc = $info->{description}; - (defined($desc) and $desc =~ /\S/) ? $desc : 'No description available...'; -} - -=head2 get_title($info) - -Get title. - -=cut - -sub get_title { - my ($self, $info) = @_; - $info->{title}; -} - -=head2 get_thumbnail_url($info;$type='default') - -Get thumbnail URL. - -=cut - -sub get_thumbnail_url { - my ($self, $info, $type) = @_; - - if (exists $info->{videoId}) { - $info->{type} = 'video'; - } - - if ($info->{type} eq 'playlist') { - return $info->{playlistThumbnail}; - } - - if ($info->{type} eq 'channel') { - ref($info->{authorThumbnails}) eq 'ARRAY' or return ''; - return $info->{authorThumbnails}[0]{url}; - } - - ref($info->{videoThumbnails}) eq 'ARRAY' or return ''; - - my @thumbs = @{$info->{videoThumbnails}}; - my @wanted = grep{$_->{quality} eq $type} @thumbs; - - if (@wanted) { - return $wanted[0]{url}; - } - - warn "[!] Couldn't find thumbnail of type <<$type>>..."; - $thumbs[0]{url}; -} - -sub get_channel_title { - my ($self, $info) = @_; - #$info->{snippet}{channelTitle} || $self->get_channel_id($info); - $info->{author}; -} - -sub get_author { - my ($self, $info) = @_; - $info->{author}; -} - -sub get_comment_id { - my ($self, $info) = @_; - $info->{commentId}; -} - -sub get_comment_content { - my ($self, $info) = @_; - $info->{content}; -} - -sub get_id { - my ($self, $info) = @_; - #$info->{id}; - $info->{videoId}; -} - -sub get_channel_id { - my ($self, $info) = @_; - #$info->{snippet}{resourceId}{channelId} // $info->{snippet}{channelId}; - $info->{authorId}; -} - -sub get_category_id { - my ($self, $info) = @_; - #$info->{snippet}{resourceId}{categoryId} // $info->{snippet}{categoryId}; - #"unknown"; - $info->{genre} // 'Unknown'; -} - -sub get_category_name { - my ($self, $info) = @_; - - state $categories = { - 1 => 'Film & Animation', - 2 => 'Autos & Vehicles', - 10 => 'Music', - 15 => 'Pets & Animals', - 17 => 'Sports', - 19 => 'Travel & Events', - 20 => 'Gaming', - 22 => 'People & Blogs', - 23 => 'Comedy', - 24 => 'Entertainment', - 25 => 'News & Politics', - 26 => 'Howto & Style', - 27 => 'Education', - 28 => 'Science & Technology', - 29 => 'Nonprofits & Activism', - }; - - #$categories->{$self->get_category_id($info) // ''} // 'Unknown'; - - $info->{genre} // 'Unknown'; -} - -sub get_publication_date { - my ($self, $info) = @_; - #$self->format_date($info->{snippet}{publishedAt}); - #$self->format_date - require Time::Piece; - my $time = Time::Piece->new($info->{published}); - $time->strftime("%d %B %Y"); -} - -sub get_publication_age { - my ($self, $info) = @_; - ($info->{publishedText} // '') =~ s/\sago\z//r;; -} - -sub get_publication_age_approx { - my ($self, $info) = @_; - - my $age = $self->get_publication_age($info) // ''; - - if ($age =~ /hour|min|sec/) { - return "0d"; - } - - if ($age =~ /^(\d+) day/) { - return "$1d"; - } - - if ($age =~ /^(\d+) week/) { - return "$1w"; - } - - if ($age =~ /^(\d+) month/) { - return "$1m"; - } - - if ($age =~ /^(\d+) year/) { - return "$1y"; - } - - return $age; -} - -sub get_duration { - my ($self, $info) = @_; - #$self->format_duration($info->{contentDetails}{duration}); - #$self->format_duration($info->{lengthSeconds}); - $info->{lengthSeconds}; -} - -sub get_time { - my ($self, $info) = @_; - - if ($info->{liveNow}) { - return 'LIVE'; - } - - $self->format_time($self->get_duration($info)); - - #$self->format_time($self->get_duration($info)); -} - -sub get_definition { - my ($self, $info) = @_; - #uc($info->{contentDetails}{definition} // '-'); - #...; - "unknown"; -} - -sub get_dimension { - my ($self, $info) = @_; - #uc($info->{contentDetails}{dimension}); - #...; - "unknown"; -} - -sub get_caption { - my ($self, $info) = @_; - #$info->{contentDetails}{caption}; - #...; - "unknown"; -} - -sub get_views { - my ($self, $info) = @_; - $info->{viewCount} // 0; -} - -sub get_views_approx { - my ($self, $info) = @_; - my $views = $self->get_views($info); - - if ($views < 1000) { - return $views; - } - - if ($views >= 10 * 1e9) { # ten billions - return sprintf("%dB", int($views / 1e9)); - } - - if ($views >= 1e9) { # billions - return sprintf("%.2gB", $views / 1e9); - } - - if ($views >= 10 * 1e6) { # ten millions - return sprintf("%dM", int($views / 1e6)); - } - - if ($views >= 1e6) { # millions - return sprintf("%.2gM", $views / 1e6); - } - - if ($views >= 10 * 1e3) { # ten thousands - return sprintf("%dK", int($views / 1e3)); - } - - if ($views >= 1e3) { # thousands - return sprintf("%.2gK", $views / 1e3); - } - - return $views; -} - -sub get_likes { - my ($self, $info) = @_; - $info->{likeCount} // 0; -} - -sub get_dislikes { - my ($self, $info) = @_; - $info->{dislikeCount} // 0; -} - -sub get_comments { - my ($self, $info) = @_; - #$info->{statistics}{commentCount}; - 1; -} - -{ - no strict 'refs'; - foreach my $pair ( - [playlist => {'playlist' => 1}], - [channel => {'channel' => 1}], - [video => {'video' => 1, 'playlistItem' => 1}], - [subscription => {'subscription' => 1}], - [activity => {'activity' => 1}], - ) { - - *{__PACKAGE__ . '::' . 'is_' . $pair->[0]} = sub { - my ($self, $item) = @_; - - if ($pair->[0] eq 'video') { - return 1 if exists $item->{videoId}; - } - - exists $pair->[1]{$item->{type} // ''}; - - #~ if (ref($item->{id}) eq 'HASH') { - #~ if (exists $pair->[1]{$item->{id}{kind}}) { - #~ return 1; - #~ } - #~ } - #~ elsif (exists $item->{kind}) { - #~ if (exists $pair->[1]{$item->{kind}}) { - #~ return 1; - #~ } - #~ } - - #~ return; - }; - - } -} - -sub is_channelID { - my ($self, $id) = @_; - $id || return; - $id eq 'mine' or $id =~ /^UC[-a-zA-Z0-9_]{22}\z/; -} - -sub is_videoID { - my ($self, $id) = @_; - $id || return; - $id =~ /^[-a-zA-Z0-9_]{11}\z/; -} - -sub period_to_date { - my ($self, $amount, $period) = @_; - - state $day = 60 * 60 * 24; - state $week = $day * 7; - state $month = $day * 30.4368; - state $year = $day * 365.242; - - my $time = $amount * ( - $period =~ /^d/i ? $day - : $period =~ /^w/i ? $week - : $period =~ /^m/i ? $month - : $period =~ /^y/i ? $year - : 0 - ); - - my $now = time; - my @time = gmtime($now - $time); - join('-', $time[5] + 1900, sprintf('%02d', $time[4] + 1), sprintf('%02d', $time[3])) . 'T' - . join(':', sprintf('%02d', $time[2]), sprintf('%02d', $time[1]), sprintf('%02d', $time[0])) . 'Z'; -} - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::Utils - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2012-2020 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::Utils diff --git a/lib/WWW/StrawViewer/VideoCategories.pm b/lib/WWW/StrawViewer/VideoCategories.pm deleted file mode 100644 index 30e2e6e..0000000 --- a/lib/WWW/StrawViewer/VideoCategories.pm +++ /dev/null @@ -1,97 +0,0 @@ -package WWW::StrawViewer::VideoCategories; - -use utf8; -use 5.014; -use warnings; - -=head1 NAME - -WWW::StrawViewer::VideoCategories - videoCategory resource handler. - -=head1 SYNOPSIS - - use WWW::StrawViewer; - my $obj = WWW::StrawViewer->new(%opts); - my $cats = $obj->video_categories(); - -=head1 SUBROUTINES/METHODS - -=cut - -sub _make_videoCategories_url { - my ($self, %opts) = @_; - - $self->_make_feed_url( - 'videoCategories', - hl => $self->get_hl, - %opts, - ); -} - -=head2 video_categories() - -Return video categories for a specific region ID. - -=cut - -sub video_categories { - my ($self) = @_; - - require File::Spec; - - my $region = $self->get_regionCode() // 'US'; - my $url = $self->_make_videoCategories_url(regionCode => $region); - my $file = File::Spec->catfile($self->get_config_dir, "categories-$region-" . $self->get_hl() . ".json"); - - my $json; - if (open(my $fh, '<:utf8', $file)) { - local $/; - $json = <$fh>; - close $fh; - } - else { - $json = $self->lwp_get($url, simple => 1); - open my $fh, '>:utf8', $file; - print {$fh} $json; - close $fh; - } - - return $self->parse_json_string($json); -} - -=head2 video_category_id_info($cagegory_id) - -Return info for the comma-separated specified category ID(s). - -=cut - -sub video_category_id_info { - my ($self, $id) = @_; - return $self->_get_results($self->_make_videoCategories_url(id => $id)); -} - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::VideoCategories - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2013-2015 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::VideoCategories diff --git a/lib/WWW/StrawViewer/Videos.pm b/lib/WWW/StrawViewer/Videos.pm deleted file mode 100644 index 11bf60b..0000000 --- a/lib/WWW/StrawViewer/Videos.pm +++ /dev/null @@ -1,230 +0,0 @@ -package WWW::StrawViewer::Videos; - -use utf8; -use 5.014; -use warnings; - -=head1 NAME - -WWW::StrawViewer::Videos - videos handler. - -=head1 SYNOPSIS - - use WWW::StrawViewer; - my $obj = WWW::StrawViewer->new(%opts); - my $info = $obj->video_details($videoID); - -=head1 SUBROUTINES/METHODS - -=cut - -sub _make_videos_url { - my ($self, %opts) = @_; - return $self->_make_feed_url('videos', %opts); -} - -{ - no strict 'refs'; - foreach my $part ( - qw( - id - snippet - contentDetails - fileDetails - player - liveStreamingDetails - processingDetails - recordingDetails - statistics - status - suggestions - topicDetails - ) - ) { - *{__PACKAGE__ . '::' . 'video_' . $part} = sub { - my ($self, $id) = @_; - return $self->_get_results($self->_make_videos_url(id => $id, part => $part)); - }; - } -} - -=head2 videos_from_category($category_id) - -Get videos from a category ID. - -=cut - -sub videos_from_category { - my ($self, $cat_id) = @_; - $self->_get_results( - $self->_make_videos_url( - chart => $self->get_chart, - videoCategoryId => $cat_id, - ) - ); -} - -=head2 trending_videos_from_category($category_id) - -Get popular videos from a category ID. - -=cut - -sub trending_videos_from_category { - my ($self, $cat_id) = @_; - - my $results = do { - local $self->{publishedAfter} = do { - state $yv_utils = WWW::StrawViewer::Utils->new; - $yv_utils->period_to_date(1, 'w'); - } if !defined($self->get_publishedAfter); - local $self->{videoCategoryId} = $cat_id; - local $self->{regionCode} = "US" if !defined($self->get_regionCode); - $self->search_videos(""); - }; - - return $results; -} - -=head2 popular_videos($channel_id) - -Get the most popular videos for a given channel ID. - -=cut - -sub popular_videos { - my ($self, $id) = @_; - - my $results = do { - local $self->{channelId} = $id; - local $self->{order} = 'viewCount'; - $self->search_videos(""); - }; - - return $results; -} - -=head2 my_likes() - -Get the videos liked by the authenticated user. - -=cut - -sub my_likes { - my ($self) = @_; - $self->get_access_token() // return; - $self->_get_results($self->_make_videos_url(myRating => 'like', pageToken => $self->page_token)); -} - -=head2 my_dislikes() - -Get the videos disliked by the authenticated user. - -=cut - -sub my_dislikes { - my ($self) = @_; - $self->get_access_token() // return; - $self->_get_results($self->_make_videos_url(myRating => 'dislike', pageToken => $self->page_token)); -} - -=head2 send_rating_to_video($videoID, $rating) - -Send rating to a video. $rating can be either 'like' or 'dislike'. - -=cut - -sub send_rating_to_video { - my ($self, $video_id, $rating) = @_; - - if ($rating eq 'none' or $rating eq 'like' or $rating eq 'dislike') { - my $url = $self->_simple_feeds_url('videos/rate', id => $video_id, rating => $rating); - return defined($self->lwp_post($url, $self->_auth_lwp_header())); - } - - return; -} - -=head2 like_video($videoID) - -Like a video. Returns true on success. - -=cut - -sub like_video { - my ($self, $video_id) = @_; - $self->send_rating_to_video($video_id, 'like'); -} - -=head2 dislike_video($videoID) - -Dislike a video. Returns true on success. - -=cut - -sub dislike_video { - my ($self, $video_id) = @_; - $self->send_rating_to_video($video_id, 'dislike'); -} - -=head2 videos_details($id, $part) - -Get info about a videoID, such as: channelId, title, description, -tags, and categoryId. - -Available values for I<part> are: I<id>, I<snippet>, I<contentDetails> -I<player>, I<statistics>, I<status> and I<topicDetails>. - -C<$part> string can contain more values, comma-separated. - -Example: - - part => 'snippet,contentDetails,statistics' - -When C<$part> is C<undef>, it defaults to I<snippet>. - -=cut - -sub video_details { - my ($self, $id, $fields) = @_; - $fields //= $self->basic_video_info_fields; - $self->_get_results($self->_make_feed_url("videos/$id", fields => $fields)); -} - -=head2 Return details - -Each function returns a HASH ref, with a key called 'results', and another key, called 'url'. - -The 'url' key contains a string, which is the URL for the retrieved content. - -The 'results' key contains another HASH ref with the keys 'etag', 'items' and 'kind'. -From the 'results' key, only the 'items' are relevant to us. This key contains an ARRAY ref, -with a HASH ref for each result. An example of the item array's content are shown below. - -=cut - -=head1 AUTHOR - -Trizen, C<< <echo dHJpemVuQHByb3Rvbm1haWwuY29tCg== | base64 -d> >> - - -=head1 SUPPORT - -You can find documentation for this module with the perldoc command. - - perldoc WWW::StrawViewer::Videos - - -=head1 LICENSE AND COPYRIGHT - -Copyright 2013-2015 Trizen. - -This program is free software; you can redistribute it and/or modify it -under the terms of either: the GNU General Public License as published -by the Free Software Foundation; or the Artistic License. - -See L<http://dev.perl.org/licenses/> for more information. - -=cut - -1; # End of WWW::StrawViewer::Videos |