diff options
-rwxr-xr-x | bin/fair-viewer | 84 | ||||
-rwxr-xr-x | bin/gtk-fair-viewer | 101 | ||||
-rw-r--r-- | lib/WWW/FairViewer.pm | 19 | ||||
-rw-r--r-- | lib/WWW/FairViewer/Channels.pm | 5 | ||||
-rw-r--r-- | lib/WWW/FairViewer/GuideCategories.pm | 2 | ||||
-rw-r--r-- | lib/WWW/FairViewer/VideoCategories.pm | 48 | ||||
-rw-r--r-- | lib/WWW/FairViewer/Videos.pm | 31 | ||||
-rw-r--r-- | 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 <property name="can_focus">False</property> <property name="orientation">vertical</property> <child> - <object class="GtkExpander" id="expander2"> + <object class="GtkFrame" id="frame14"> <property name="visible">True</property> - <property name="can_focus">True</property> + <property name="can_focus">False</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> <child> <object class="GtkAlignment" id="alignment11"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="left_padding">12</property> <child> - <object class="GtkBox" id="vbox22"> + <object class="GtkBox" id="vbox9"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child> - <object class="GtkCheckButton" id="channels_checkbox"> - <property name="label" translatable="yes">Channels</property> + <object class="GtkComboBoxText" id="comboboxtext10"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="draw_indicator">True</property> + <property name="can_focus">False</property> + <property name="active">0</property> + <items> + <item translatable="yes">video</item> + <item translatable="yes">playlist</item> + <item translatable="yes">channel</item> + <item translatable="yes">all</item> + </items> </object> <packing> - <property name="expand">True</property> - <property name="fill">True</property> + <property name="expand">False</property> + <property name="fill">False</property> <property name="position">0</property> </packing> </child> - <child> - <object class="GtkCheckButton" id="playlists_checkbox"> - <property name="label" translatable="yes">Playlists</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="draw_indicator">True</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="videos_checkbox"> - <property name="label" translatable="yes">Videos</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="draw_indicator">True</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> </object> </child> </object> @@ -953,7 +931,8 @@ Author: Jesus E. https://framagit.org/heckyel <object class="GtkLabel" id="label6"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="label" translatable="yes">Search for:</property> + <property name="label" translatable="yes"><b>Type:</b></property> + <property name="use_markup">True</property> </object> </child> </object> @@ -1383,48 +1362,6 @@ Unless the author name is valid, this field is ignored.</property> </packing> </child> <child> - <object class="GtkFrame" id="frame25"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label_xalign">0</property> - <property name="shadow_type">none</property> - <child> - <object class="GtkAlignment" id="alignment24"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="left_padding">12</property> - <child> - <object class="GtkEntry" id="category_id_entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="tooltip_text" translatable="yes">Search in videos uploaded in a specific category. -Unless the categoryID is valid, this field is ignored.</property> - <property name="invisible_char">•</property> - <property name="text" translatable="yes">Insert a valid category ID...</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <signal name="activate" handler="search" swapped="no"/> - <signal name="button-press-event" handler="clear_text" swapped="no"/> - </object> - </child> - </object> - </child> - <child type="label"> - <object class="GtkLabel" id="label33"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"><b>CategoryID:</b></property> - <property name="use_markup">True</property> - </object> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">4</property> - </packing> - </child> - <child> <object class="GtkFrame" id="frame1"> <property name="visible">True</property> <property name="can_focus">False</property> |