From 9838963cf2aab02241a37cee7d305025dc694d90 Mon Sep 17 00:00:00 2001 From: trizen Date: Mon, 5 Oct 2020 22:00:21 +0300 Subject: - Implemented support for trending categories. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example: fair-viewer --trending # trending videos fair-viewer --trending=music # trending music videos fair-viewer -c # list trending categories In the Gtk version, the trending categories are listed in the "Categories" tab. - Several internal code tweaks and improvements. Signed-off-by: Jesús --- bin/fair-viewer | 84 +++------------------------- bin/gtk-fair-viewer | 101 ++++++---------------------------- lib/WWW/FairViewer.pm | 19 +++---- lib/WWW/FairViewer/Channels.pm | 5 ++ lib/WWW/FairViewer/GuideCategories.pm | 2 +- lib/WWW/FairViewer/VideoCategories.pm | 48 +++------------- lib/WWW/FairViewer/Videos.pm | 31 +++-------- share/gtk-fair-viewer.glade | 99 ++++++--------------------------- 8 files changed, 76 insertions(+), 313 deletions(-) diff --git a/bin/fair-viewer b/bin/fair-viewer index c72db85..a9f412b 100755 --- a/bin/fair-viewer +++ b/bin/fair-viewer @@ -197,7 +197,6 @@ my %CONFIG = ( comments_order => 'top', # valid values: top, new subscriptions_order => 'relevance', # valid values: alphabetical, relevance, unread - hl => 'en_US', region => undef, # URI options @@ -661,7 +660,6 @@ usage: $execname [options] ([url] | [keywords]) * Categories -c --categories : display the available YouTube categories - -hl --catlang=s : language for categories (default: en_US) * Region --region=s : set the region code (default: US) @@ -687,7 +685,8 @@ usage: $execname [options] ([url] | [keywords]) -ua --activities:s : show activity events for a given channel * Trending - --trending:s : show trending videos in a given category ID or name + --trending:s : show trending videos in a given category + valid categories: music gaming news movies * Channels -sc --channels : search for YouTube channels @@ -1110,7 +1109,7 @@ sub apply_configuration { videoDefinition videoDimension videoDuration date order - channelId region debug hl + channelId region debug http_proxy page comments_order subscriptions_order user_agent cookie_file timeout ytdl ytdl_cmd @@ -1129,20 +1128,6 @@ sub apply_configuration { } } - if (defined $opt->{category_id}) { - - my $str = delete $opt->{category_id}; - my $category = extract_category($str); - - if (ref($category) eq 'HASH') { - say ":: Category selected: $category->{snippet}{title}" if $yv_obj->get_debug; - $yv_obj->set_videoCategoryId($category->{id}); - } - else { - warn_invalid('category', $str); - } - } - if (defined $opt->{hd}) { $yv_obj->set_videoDefinition(delete($opt->{hd}) ? 'high' : 'any'); } @@ -1306,19 +1291,7 @@ sub apply_configuration { } if (defined $opt->{trending}) { - - my $str = delete $opt->{trending}; - my $category = extract_category($str); - my $cat_id = undef; - - if (ref($category) eq 'HASH') { - say ":: Category selected: $category->{snippet}{title}" if $yv_obj->get_debug; - $cat_id = $category->{id}; - } - elsif ($str) { - warn_invalid('category', $str); - } - + my $cat_id = delete $opt->{trending}; print_videos($yv_obj->trending_videos_from_category($cat_id)); } @@ -1519,8 +1492,6 @@ sub parse_arguments { 'user-agent|agent=s' => \$opt{user_agent}, 'http-proxy|https-proxy|proxy=s' => \$opt{http_proxy}, - 'catlang|cl|hl=s' => \$opt{hl}, - 'category|cat-id|cat=s' => \$opt{category_id}, 'r|region|region-code=s' => \$opt{region}, 'order|order-by|sort|sort-by=s' => \$opt{order}, @@ -1715,34 +1686,6 @@ sub get_valid_playlist_id { return $id; } -sub extract_category { - my ($str) = @_; - - $str || return; - - state $results = $yv_obj->video_categories; - return if ref($results) ne 'HASH'; - - my $categories = $results->{items}; - return if ref($categories) ne 'ARRAY'; - - foreach my $category (@$categories) { - if ($category->{id} eq $str) { - return $category; - } - } - - my $str_re = qr/\Q$str\E/i; - - foreach my $category (@$categories) { - if ($yv_utils->get_title($category) =~ /$str_re/) { - return $category; - } - } - - return; -} - sub extract_channel_id { my ($str) = @_; @@ -2448,10 +2391,9 @@ sub valid_num { sub adjust_width { my ($str, $len, $prepend) = @_; - $len > 0 or do { - warn "[WARN] Insufficient space for the title: increase your terminal width!\n"; + if ($len <= 0) { return $str; - }; + } state $pkg = ( eval { @@ -2749,18 +2691,14 @@ sub print_comments { sub print_categories { my ($results) = @_; - return if ref($results) ne 'HASH'; - my $categories = $results->{items}; + my $categories = $results; return if ref($categories) ne 'ARRAY'; my $i = 0; print "\n" if @{$categories}; - # Filter out nonassignable categories - @$categories = grep { $_->{snippet}{assignable} } @$categories; - foreach my $category (@{$categories}) { - printf "%s. %-40s (id: %s)\n", colored(sprintf('%2d', ++$i), 'bold'), $yv_utils->get_title($category), $category->{id}; + printf "%s. %-40s\n", colored(sprintf('%2d', ++$i), 'bold'), $category->{title}; } my @keywords = get_input_for_categories(); @@ -2796,11 +2734,7 @@ sub print_categories { elsif (valid_num($key, $categories)) { my $category = $categories->[$key - 1]; my $cat_id = $category->{id}; - my $videos = $yv_obj->videos_from_category($cat_id); - - if (not $yv_utils->has_entries($videos)) { - $videos = $yv_obj->trending_videos_from_category($cat_id); - } + my $videos = $yv_obj->trending_videos_from_category($cat_id); print_videos($videos); } diff --git a/bin/gtk-fair-viewer b/bin/gtk-fair-viewer index 3274ce2..bafdc12 100755 --- a/bin/gtk-fair-viewer +++ b/bin/gtk-fair-viewer @@ -186,20 +186,19 @@ my %CONFIG = ( # Fair options dash_support => 1, dash_mp4_audio => 1, - dash_segmented => 1, # may load slow + dash_segmented => 1, # may load slow prefer_mp4 => 0, prefer_av1 => 0, ignore_av1 => 0, maxResults => 10, - hfr => 1, # true to prefer high frame rate (HFR) videos + hfr => 1, # true to prefer high frame rate (HFR) videos resolution => 'best', videoDimension => undef, videoLicense => undef, - hl => 'en_US', region => undef, - comments_width => 80, # wrap comments longer than `n` characters - comments_order => 'top', # valid values: time, relevance + comments_width => 80, # wrap comments longer than `n` characters + comments_order => 'top', # valid values: time, relevance # API api_host => "auto", @@ -338,7 +337,6 @@ my %objects = ( 'treeviewcolumn2' => \my $thumbs_column, 'textview2' => \my $textview_help, 'from_author_entry' => \my $from_author_entry, - 'category_id_entry' => \my $category_id_entry, 'more_options_expander' => \my $more_options_expander, 'notebook1' => \my $notebook, 'comboboxtext9' => \my $resolution_combobox, @@ -352,9 +350,7 @@ my %objects = ( 'comboboxtext6' => \my $panel_account_type_combobox, 'comboboxtext2' => \my $order_combobox, 'comboboxtext7' => \my $channel_type_combobox, - 'videos_checkbox' => \my $search_for_videos_checkbox, - 'playlists_checkbox' => \my $search_for_playlists_checkbox, - 'channels_checkbox' => \my $search_for_channels_checkbox, + 'comboboxtext10' => \my $search_for_combobox, 'spinbutton1' => \my $spin_results, 'spinbutton2' => \my $spin_start_with_page, 'thumbs_checkbutton' => \my $thumbs_checkbutton, @@ -822,7 +818,6 @@ my $yv_obj = WWW::FairViewer->new( config_dir => $config_dir, ytdl => $CONFIG{ytdl}, ytdl_cmd => $CONFIG{ytdl_cmd}, - hl => $CONFIG{hl}, env_proxy => $CONFIG{env_proxy}, cache_dir => $CONFIG{cache_dir}, cookie_file => $CONFIG{cookie_file}, @@ -852,9 +847,6 @@ $order_combobox->set_active(0); # Spin button start with page $spin_start_with_page->set_value(1); -# Set search for videos -$search_for_videos_checkbox->set_active(1); - sub apply_configuration { # Fullscreen mode @@ -875,14 +867,11 @@ sub apply_configuration { foreach my $option_name ( qw( - api_host videoSyndicated comments_order - maxResults videoDimension - videoLicense - region videoCategoryId - debug http_proxy user_agent + maxResults videoDimension videoLicense + region debug http_proxy user_agent timeout cookie_file ytdl ytdl_cmd - prefer_mp4 prefer_av1 + api_host prefer_mp4 prefer_av1 ) ) { @@ -1703,6 +1692,7 @@ sub combobox_definition_changed { } sub combobox_published_within_changed { + my $period = $published_within_combobox->get_active_text; if ($period =~ /^any/) { @@ -1781,17 +1771,15 @@ sub add_category_header { sub append_categories { my ($categories, $type) = @_; - foreach my $category (@{$categories->{items}}) { + foreach my $category (@$categories) { - # Ignore nonassignable categories - $category->{snippet}{assignable} || next; - - my $label = $yv_utils->get_title($category); + my $label = $category->{title}; my $id = $category->{id}; $label =~ s{&}{&}g; my $iter = $cats_liststore->append; + $cats_liststore->set( $iter, 0 => $label, @@ -1803,33 +1791,7 @@ sub append_categories { return 1; } -#<<< -#~ { - # Standard categories: - #~ add_category_header("Categories"); - - #~ my $cats = $yv_obj->video_categories(); - #~ if (ref($cats) eq 'HASH' and ref($cats->{items}) eq 'ARRAY') { - - #~ my $help_text = ''; - #~ foreach my $cat (sort { $a->{id} <=> $b->{id} } @{$cats->{items}}) { - #~ $cat->{snippet}{assignable} || next; - #~ $help_text .= sprintf("%2d - %s\n", $cat->{id}, $yv_utils->get_title($cat)); - #~ } - - #~ # Set tooltip text for "CategoryID" entry - #~ chomp($help_text); - #~ $category_id_entry->set_tooltip_text($help_text); - - #~ # Append the categories to the "Categories" tab - #~ append_categories($cats, 'cat'); - #~ } - - # EDU categories: - #add_category_header("EDU Categories"); - #append_categories($yv_obj->get_educategories(), 'edu-cat'); -#~ } -#>>> +append_categories($yv_obj->video_categories, 'cat'); my $tops_liststore = $gui->get_object('liststore6'); my $tops_treeview = $gui->get_object('treeview4'); @@ -2285,29 +2247,7 @@ sub search { $yv_obj->set_channelId(); } - # Set the category ID - my $category_id = $category_id_entry->get_text; - if ($category_id =~ /^\d+\z/) { - $yv_obj->set_videoCategoryId($category_id); - } - else { - $yv_obj->set_videoCategoryId(); - } - - my @types; - if ($search_for_playlists_checkbox->get_active) { - push @types, 'playlist'; - } - - if ($search_for_channels_checkbox->get_active) { - push @types, 'channel'; - } - - if ($search_for_videos_checkbox->get_active) { - push @types, 'video'; - } - - my $type = @types ? join(',', @types) : 'video'; + my $type = $search_for_combobox->get_active_text; display_results($yv_obj->search_for($type, $keywords)); return 1; @@ -3127,18 +3067,12 @@ sub play_video { } sub list_category { + my $iter = $cat_treeview->get_selection->get_selected; - my $cat_id = $cats_liststore->get($iter, 1) // return; + my $cat_id = $cats_liststore->get($iter, 1); my $type = $cats_liststore->get($iter, 3); - my $videos = - $type eq 'edu-cat' - ? $yv_obj->get_video_lectures_from_category($cat_id) - : $yv_obj->videos_from_category($cat_id); - - if (not $yv_utils->has_entries($videos)) { - $videos = $yv_obj->trending_videos_from_category($cat_id); - } + my $videos = $yv_obj->trending_videos_from_category($cat_id); if ($yv_utils->has_entries($videos)) { $liststore->clear if $CONFIG{clear_search_list}; @@ -3150,6 +3084,7 @@ sub list_category { } sub list_tops { + my $iter = $tops_treeview->get_selection->get_selected; my %top_opts; diff --git a/lib/WWW/FairViewer.pm b/lib/WWW/FairViewer.pm index d7be54c..8d8abff 100644 --- a/lib/WWW/FairViewer.pm +++ b/lib/WWW/FairViewer.pm @@ -50,7 +50,6 @@ my %valid_options = ( v => {valid => q[], default => 3}, page => {valid => qr/^(?!0+\z)\d+\z/, default => 1}, http_proxy => {valid => qr/./, default => undef}, - hl => {valid => qr/^\w+(?:[\-_]\w+)?\z/, default => undef}, maxResults => {valid => [1 .. 50], default => 10}, order => {valid => [qw(relevance rating upload_date view_count)], default => undef}, date => {valid => [qw(hour today week month year)], default => undef}, @@ -283,7 +282,7 @@ sub set_lwp_useragent { // do { require LWP::UserAgent; 'LWP::UserAgent' } ); - $self->{lwp} = $lwp->new( + my $agent = $lwp->new( cookie_jar => {}, # temporary cookies timeout => $self->get_timeout, @@ -329,7 +328,6 @@ sub set_lwp_useragent { HTTP::Message::decodable(); }; - my $agent = $self->{lwp}; $agent->ssl_opts(Timeout => $self->get_timeout); $agent->default_header('Accept-Encoding' => $accepted_encodings); $agent->conn_cache($cache); @@ -366,8 +364,9 @@ sub set_lwp_useragent { $agent->cookie_jar($cookies); } - push @{$self->{lwp}->requests_redirectable}, 'POST'; - return $self->{lwp}; + push @{$agent->requests_redirectable}, 'POST'; + $self->{lwp} = $agent; + return $agent; } =head2 prepare_access_token() @@ -399,7 +398,7 @@ sub _auth_lwp_header { sub _warn_reponse_error { my ($resp, $url) = @_; - warn sprintf("[%s] Error occurred on URL: %s\n", $resp->status_line, $url =~ s/([&?])key=(.*?)&/${1}key=[...]&/r); + warn sprintf("[%s] Error occurred on URL: %s\n", $resp->status_line, $url); } =head2 lwp_get($url, %opt) @@ -463,7 +462,7 @@ sub lwp_get { $opt{depth} ||= 0; # Try again on 500+ HTTP errors - if ( $opt{depth} <= 3 + if ( $opt{depth} < 3 and $response->code() >= 500 and $response->status_line() =~ /(?:Temporary|Server) Error|Timeout|Service Unavailable/i) { return $self->lwp_get($url, %opt, depth => $opt{depth} + 1); @@ -581,7 +580,7 @@ sub select_good_invidious_instances { my %ignored = ( 'yewtu.be' => 1, - 'invidiou.site' => 0, + 'invidiou.site' => 1, 'invidious.xyz' => 1, 'vid.mint.lgbt' => 1, 'invidious.ggc-project.de' => 1, @@ -704,7 +703,6 @@ sub _extract_from_invidious { require List::Util; @instances = List::Util::shuffle(map { $_->[0] } @instances); push @instances, 'invidious.snopyta.org'; - push @instances, 'invidious.13ad.de'; } else { @instances = qw( @@ -712,7 +710,6 @@ sub _extract_from_invidious { invidious.site invidious.fdn.fr invidious.snopyta.org - invidious.13ad.de ); } @@ -1240,6 +1237,8 @@ sub previous_page { local $ENV{HTTP_PROXY} = $self->{lwp}->proxy('http'); local $ENV{HTTPS_PROXY} = $self->{lwp}->proxy('https'); + local $" = " "; + $name eq 'exec' ? exec(@args) : $name eq 'system' ? system(@args) : $name eq 'stdout' ? qx(@args) diff --git a/lib/WWW/FairViewer/Channels.pm b/lib/WWW/FairViewer/Channels.pm index c554044..3ee44d4 100644 --- a/lib/WWW/FairViewer/Channels.pm +++ b/lib/WWW/FairViewer/Channels.pm @@ -41,6 +41,11 @@ Get the most popular videos for a given channel ID. sub popular_videos { my ($self, $channel_id) = @_; + + if (not defined($channel_id)) { # trending popular videos + return $self->_get_results($self->_make_feed_url('popular')); + } + return $self->_get_results($self->_make_feed_url("channels/$channel_id/videos", sort_by => 'popular')); } diff --git a/lib/WWW/FairViewer/GuideCategories.pm b/lib/WWW/FairViewer/GuideCategories.pm index a348abb..cead9f6 100644 --- a/lib/WWW/FairViewer/GuideCategories.pm +++ b/lib/WWW/FairViewer/GuideCategories.pm @@ -25,7 +25,7 @@ sub _make_guideCategories_url { $opts{region} //= $self->get_region; } - $self->_make_feed_url('guideCategories', hl => $self->get_hl, %opts); + $self->_make_feed_url('guideCategories', %opts); } =head2 guide_categories(;$region_id) diff --git a/lib/WWW/FairViewer/VideoCategories.pm b/lib/WWW/FairViewer/VideoCategories.pm index 85045fd..4dfc125 100644 --- a/lib/WWW/FairViewer/VideoCategories.pm +++ b/lib/WWW/FairViewer/VideoCategories.pm @@ -18,16 +18,6 @@ WWW::FairViewer::VideoCategories - videoCategory resource handler. =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. @@ -37,37 +27,13 @@ Return video categories for a specific region ID. sub video_categories { my ($self) = @_; - require File::Spec; - - my $region = $self->get_region() // 'US'; - my $url = $self->_make_videoCategories_url(region => $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)); + return [{id => "music", title => "Music"}, + {id => "gaming", title => "Gaming"}, + {id => "news", title => "News"}, + {id => "movies", title => "Movies"}, + {id => "trending", title => "Trending"}, + {id => "popular", title => "Popular"}, + ]; } =head1 AUTHOR diff --git a/lib/WWW/FairViewer/Videos.pm b/lib/WWW/FairViewer/Videos.pm index aaaacc7..4acd866 100644 --- a/lib/WWW/FairViewer/Videos.pm +++ b/lib/WWW/FairViewer/Videos.pm @@ -48,22 +48,6 @@ sub _make_videos_url { } } -=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. @@ -71,14 +55,17 @@ Get popular videos from a category ID. =cut sub trending_videos_from_category { - my ($self, $cat_id) = @_; + my ($self, $category) = @_; + + if (defined($category) and $category eq 'popular') { + return $self->popular_videos; + } - my $results = do { - local $self->{videoCategoryId} = $cat_id; - $self->search_videos(""); - }; + if (defined($category) and $category eq 'trending') { + $category = undef; + } - return $results; + return $self->_get_results($self->_make_feed_url('trending', (defined($category) ? (type => $category) : ()))); } =head2 my_likes() diff --git a/share/gtk-fair-viewer.glade b/share/gtk-fair-viewer.glade index 48b3a05..09ecfe5 100644 --- a/share/gtk-fair-viewer.glade +++ b/share/gtk-fair-viewer.glade @@ -890,61 +890,39 @@ Author: Jesus E. https://framagit.org/heckyel False vertical - + True - True + False + 0 + none True False 12 - + True False vertical - - Channels + True - True - False - True + False + 0 + + video + playlist + channel + all + - True - True + False + False 0 - - - Playlists - True - True - False - True - - - True - True - 1 - - - - - Videos - True - True - False - True - - - True - True - 2 - - @@ -953,7 +931,8 @@ Author: Jesus E. https://framagit.org/heckyel True False - Search for: + <b>Type:</b> + True @@ -1382,48 +1361,6 @@ Unless the author name is valid, this field is ignored. 3 - - - True - False - 0 - none - - - True - False - 12 - - - True - True - Search in videos uploaded in a specific category. -Unless the categoryID is valid, this field is ignored. - - Insert a valid category ID... - False - False - - - - - - - - - True - False - <b>CategoryID:</b> - True - - - - - True - True - 4 - - True -- cgit v1.2.3