package WWW::FairViewer::Search; use utf8; use 5.014; use warnings; =head1 NAME WWW::FairViewer::Search - Search functions for Fair API v3 =head1 SYNOPSIS use WWW::FairViewer; my $obj = WWW::FairViewer->new(%opts); $obj->search_videos(@keywords); =head1 SUBROUTINES/METHODS =cut sub _make_search_url { my ($self, %opts) = @_; my @features; if (defined(my $vd = $self->get_videoDefinition)) { if ($vd eq 'high') { push @features, 'hd'; } } if (defined(my $vc = $self->get_videoCaption)) { if ($vc eq 'true' or $vc eq '1') { push @features, 'subtitles'; } } if (defined(my $vd = $self->get_videoDimension)) { if ($vd eq '3d') { push @features, '3d'; } } if (defined(my $license = $self->get_videoLicense)) { if ($license eq 'creative_commons') { push @features, 'creative_commons'; } } return $self->_make_feed_url( 'search', region => $self->get_region, sort_by => $self->get_order, date => $self->get_date, channelId => $self->get_channelId, pageToken => $self->page_token, duration => $self->get_videoDuration, (@features ? (features => join(',', @features)) : ()), %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, $videoID) = @_; my %info = $self->_get_video_info($videoID); my $watch_next_response = $self->parse_json_string($info{watch_next_response}); my $related = eval { $watch_next_response->{contents}{twoColumnWatchNextResults}{secondaryResults}{secondaryResults}{results} } // return {results => []}; #use Data::Dump qw(pp); #pp $related; my @results; foreach my $entry (@$related) { my $info = $entry->{compactVideoRenderer} // next; my $title = $info->{title}{simpleText} // next; my $viewCount = 0; if (($info->{viewCountText}{simpleText} // '') =~ /^([\d,]+) views/) { $viewCount = ($1 =~ tr/,//dr); } elsif (($info->{viewCountText}{simpleText} // '') =~ /Recommended for you/i) { next; # filter out recommended videos from related videos } my $lengthSeconds = 0; if (($info->{lengthText}{simpleText} // '') =~ /([\d:]+)/) { my $time = $1; my @fields = split(/:/, $time); my $seconds = pop(@fields) // 0; my $minutes = pop(@fields) // 0; my $hours = pop(@fields) // 0; $lengthSeconds = 3600 * $hours + 60 * $minutes + $seconds; } my $published = 0; if (exists $info->{publishedTimeText} and $info->{publishedTimeText}{simpleText} =~ /(\d+)\s+(\w+)\s+ago/) { my $quantity = $1; my $period = $2; $period =~ s/s\z//; # make it singural my %table = ( year => 31556952, # seconds in a year month => 2629743.83, # seconds in a month week => 604800, # seconds in a week day => 86400, # seconds in a day hour => 3600, # seconds in a hour minute => 60, # seconds in a minute second => 1, # seconds in a second ); if (exists $table{$period}) { $published = int(time - $quantity * $table{$period}); } else { warn "BUG: cannot parse: <<$quantity $period>>"; } } push @results, { type => "video", title => $title, videoId => $info->{videoId}, author => $info->{longBylineText}{runs}[0]{text}, authorId => $info->{longBylineText}{runs}[0]{navigationEndpoint}{browseEndpoint}{browseId}, #authorUrl => $info->{longBylineText}{runs}[0]{navigationEndpoint}{browseEndpoint}{browseId}, description => $info->{accessibility}{accessibilityData}{label}, descriptionHtml => undef, viewCount => $viewCount, published => $published, publishedText => $info->{publishedTimeText}{simpleText}, lengthSeconds => $lengthSeconds, liveNow => ($lengthSeconds == 0), # maybe it's live if lengthSeconds == 0? paid => 0, premium => 0, videoThumbnails => [ map { scalar { quality => 'medium', url => $_->{url}, width => $_->{width}, height => $_->{height}, } } @{$info->{thumbnail}{thumbnails}} ], }; } return scalar { url => undef, results => \@results, }; } =head1 AUTHOR Trizen, C<< >> Jesus, C<< >> =head1 SUPPORT You can find documentation for this module with the perldoc command. perldoc WWW::FairViewer::Search =head1 LICENSE AND COPYRIGHT Copyright 2013-2015 Trizen. Copyright 2020 Jesus E. 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 for more information. =cut 1; # End of WWW::FairViewer::Search