diff options
-rw-r--r-- | MANIFEST | 4 | ||||
-rwxr-xr-x | bin/gtk-straw-viewer | 170 | ||||
-rwxr-xr-x | bin/straw-viewer | 59 | ||||
-rw-r--r-- | lib/WWW/StrawViewer.pm | 16 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/CommentThreads.pm | 13 | ||||
-rw-r--r-- | lib/WWW/StrawViewer/Utils.pm | 44 | ||||
-rw-r--r-- | share/gtk-straw-viewer.desktop | 2 | ||||
-rw-r--r-- | share/gtk-straw-viewer.glade (renamed from share/gtk3-straw-viewer.glade) | 0 | ||||
-rw-r--r-- | share/icons/gtk-straw-viewer.png (renamed from share/icons/logo.png) | bin | 121443 -> 121443 bytes |
9 files changed, 174 insertions, 134 deletions
@@ -27,11 +27,11 @@ META.json META.yml README.md share/gtk-straw-viewer.desktop -share/gtk3-straw-viewer.glade +share/gtk-straw-viewer.glade share/icons/default_thumb.jpg share/icons/feed.png share/icons/feed_gray.png -share/icons/logo.png +share/icons/gtk-straw-viewer.png share/icons/spinner.gif share/icons/user.png t/00-load.t diff --git a/bin/gtk-straw-viewer b/bin/gtk-straw-viewer index 821f202..26183ac 100755 --- a/bin/gtk-straw-viewer +++ b/bin/gtk-straw-viewer @@ -16,7 +16,7 @@ # GTK Straw Viewer # Fork: 14 February 2020 # Edit: 14 February 2020 -# https://github.com/trizen/youtube-viewer +# https://github.com/trizen/straw-viewer #------------------------------------------------------- # This is a fork of youtube-viewer: @@ -53,7 +53,7 @@ my $appname = 'GTK+ Straw Viewer'; my $version = $WWW::StrawViewer::VERSION; # Share directory -my $share_dir = $DEVEL ? '../share' : dist_dir('WWW-StrawViewer'); +my $share_dir = ($DEVEL and -d "../share") ? '../share' : dist_dir('WWW-StrawViewer'); sub VIDEO_PART () { 'contentDetails,statistics,snippet' } @@ -207,7 +207,10 @@ my %CONFIG = ( regionCode => undef, comments_width => 80, # wrap comments longer than `n` characters - comments_order => 'time', # valid values: time, relevance + comments_order => 'top', # valid values: time, relevance + + # API + api_host => "https://invidio.us", # URI options thumbnail_type => 'medium', @@ -240,8 +243,8 @@ my %CONFIG = ( web_browser => undef, # defaults to $ENV{WEBBROWSER} or xdg-open terminal => undef, # autodetect it later terminal_exec => q{-e '%s'}, - youtube_viewer => undef, - youtube_viewer_args => [], + straw_viewer => undef, + straw_viewer_args => [], youtube_users_file => $youtube_users_file, history => 1, history_limit => 100_000, @@ -287,7 +290,7 @@ local $SIG{TERM} = \&on_mainw_destroy; local $SIG{INT} = \&on_mainw_destroy; # Locating the .glade interface file and icons dir -my $glade_file = catfile($share_dir, "gtk3-straw-viewer.glade"); +my $glade_file = catfile($share_dir, "gtk-straw-viewer.glade"); my $icons_path = catdir($share_dir, 'icons'); # Defining GUI @@ -428,7 +431,7 @@ local $SIG{__DIE__} = sub { }; #---------------------- LOAD IMAGES ----------------------# -my $app_icon_pixbuf = 'Gtk3::Gdk::Pixbuf'->new_from_file(catfile($icons_path, "logo.png")); +my $app_icon_pixbuf = 'Gtk3::Gdk::Pixbuf'->new_from_file(catfile($icons_path, "gtk-straw-viewer.png")); my $user_icon_pixbuf = 'Gtk3::Gdk::Pixbuf'->new_from_file_at_size(catfile($icons_path, "user.png"), 16, 16); my $feed_icon_pixbuf = 'Gtk3::Gdk::Pixbuf'->new_from_file_at_size(catfile($icons_path, "feed.png"), 16, 16); my $feed_icon_gray_pixbuf = 'Gtk3::Gdk::Pixbuf'->new_from_file_at_size(catfile($icons_path, "feed_gray.png"), 16, 16); @@ -462,7 +465,7 @@ if (not defined $CONFIG{cache_dir}) { $cache_dir = catdir(curdir(), '.cache'); } - $CONFIG{cache_dir} = catdir($cache_dir, 'youtube-viewer'); + $CONFIG{cache_dir} = catdir($cache_dir, 'straw-viewer'); } # Create the cache directory (if needed) @@ -802,8 +805,8 @@ my %ResultsHistory = ( results => [], ); -# Locate youtube-viewer -$CONFIG{youtube_viewer} //= which_command('youtube-viewer') // 'youtube-viewer'; +# Locate CLI straw-viewer +$CONFIG{straw_viewer} //= which_command('straw-viewer') // 'straw-viewer'; my $yv_obj = WWW::StrawViewer->new( escape_utf8 => 1, @@ -860,6 +863,7 @@ sub apply_configuration { foreach my $option_name ( qw( + api_host videoSyndicated comments_order maxResults videoDimension videoEmbeddable videoLicense @@ -1342,11 +1346,11 @@ sub menu_popup { $menu->append($item); } - # Play with CLI youtube-viewer + # Play with CLI straw-viewer { my $item = 'Gtk3::ImageMenuItem'->new("Play in terminal"); - $item->signal_connect(activate => \&play_selected_video_with_cli_youtube_viewer); - $item->set_property(tooltip_text => "Play with youtube-viewer in a new terminal"); + $item->signal_connect(activate => \&play_selected_video_with_cli_straw_viewer); + $item->set_property(tooltip_text => "Play with straw-viewer in a new terminal"); $item->set_image('Gtk3::Image'->new_from_icon_name("computer", q{menu})); $item->show; $menu->append($item); @@ -1411,7 +1415,7 @@ $accel->connect(ord('l'), ['control-mask'], ['visible'], \&show_login_to_youtube $accel->connect(ord('p'), ['control-mask'], ['visible'], \&show_preferences_window); $accel->connect(ord('q'), ['control-mask'], ['visible'], \&on_mainw_destroy); $accel->connect(ord('u'), ['control-mask'], ['visible'], \&show_users_list_window); -$accel->connect(ord('y'), ['control-mask'], ['visible'], \&run_cli_youtube_viewer); +$accel->connect(ord('y'), ['control-mask'], ['visible'], \&run_cli_straw_viewer); $accel->connect(ord('d'), ['control-mask'], ['visible'], \&show_details_window); #$accel->connect(ord('c'), ['control-mask'], ['visible'], \&show_comments_window); @@ -2375,7 +2379,7 @@ sub get_code { } : $type eq 'video' ? ( $CONFIG{audio_only} - ? execute_cli_youtube_viewer("--id=$code") + ? execute_cli_straw_viewer("--id=$code") : play_video($yv_obj->parse_json_string($liststore->get($iter, 8))) ) : (); @@ -2520,8 +2524,8 @@ sub display_results { #my $info = $results->{results} // {}; my $items = $results->{results} // []; - use Data::Dump qw(pp); - pp $items; + #use Data::Dump qw(pp); + #pp $items; if (ref($items) eq 'HASH' and $items->{type} eq 'playlist') { $items = $items->{videos}; @@ -2842,11 +2846,7 @@ sub add_playlist_entry { . '</i>'); my $num_items_template = "<b>$symbols{numero}</b> %d items\n"; - my $num_items_text = ""; - - if (defined($playlist->{videoCount})) { - $num_items_text = sprintf($num_items_template, $playlist->{videoCount}); - } + my $num_items_text = sprintf($num_items_template, $yv_utils->get_playlist_video_count($playlist)); my $type_label = reflow_text("<b>$symbols{diamond}</b> " . 'Playlist' . "\n" . $num_items_text); @@ -3165,8 +3165,8 @@ sub clear_text { return 0; } -sub run_cli_youtube_viewer { - execute_cli_youtube_viewer('--interactive'); +sub run_cli_straw_viewer { + execute_cli_straw_viewer('--interactive'); } sub get_options_as_arguments { @@ -3248,22 +3248,22 @@ sub enqueue_video { sub play_enqueued_videos { if (@VIDEO_QUEUE) { - execute_cli_youtube_viewer('--video-ids=' . join(q{,}, splice @VIDEO_QUEUE)); + execute_cli_straw_viewer('--video-ids=' . join(q{,}, splice @VIDEO_QUEUE)); } return 1; } -sub play_selected_video_with_cli_youtube_viewer { +sub play_selected_video_with_cli_straw_viewer { my ($code, $iter) = get_selected_entry_code(); $code // return; my $type = $liststore->get($iter, 7); if ($type eq 'video') { - execute_cli_youtube_viewer("--video-id=$code"); + execute_cli_straw_viewer("--video-id=$code"); } elsif ($type eq 'playlist') { - execute_cli_youtube_viewer("--pp=$code"); + execute_cli_straw_viewer("--pp=$code"); } else { warn "Can't play $type: $code\n"; @@ -3272,7 +3272,7 @@ sub play_selected_video_with_cli_youtube_viewer { return 1; } -sub execute_cli_youtube_viewer { +sub execute_cli_straw_viewer { my @arguments = @_; my $command = join( @@ -3281,21 +3281,21 @@ sub execute_cli_youtube_viewer { sprintf( $CONFIG{terminal_exec}, join(q{ }, - $CONFIG{youtube_viewer}, get_options_as_arguments(), - @arguments, @{$CONFIG{youtube_viewer_args}}), + $CONFIG{straw_viewer}, get_options_as_arguments(), + @arguments, @{$CONFIG{straw_viewer_args}}), ) ); my $code = execute_external_program($command); say $command if $yv_obj->get_debug; - warn "youtube-viewer - exit code: $code\n" if $code != 0; + warn "straw-viewer - exit code: $code\n" if $code != 0; return 1; } sub download_video { my $code = get_selected_entry_code(type => 'video') // return; - execute_cli_youtube_viewer("--video-id=$code", '--download'); + execute_cli_straw_viewer("--video-id=$code", '--download'); return 1; } @@ -3382,27 +3382,33 @@ sub display_comments { return 1 if ref($results) ne 'HASH'; my $url = $results->{url}; - my $res = $results->{results} // {}; - my $comments = $res->{items} // []; + my $video_id = $results->{results}{videoId}; + my $comments = $results->{results}{comments} // []; foreach my $comment (@{$comments}) { - my $snippet = (($comment->{snippet} // next)->{topLevelComment} // next)->{snippet}; - my $comment_age = $yv_utils->date_to_age($snippet->{publishedAt}); + + #use Data::Dump qw(pp); + #pp $comment; + + #my $comment_age = $yv_utils->date_to_age($snippet->{publishedAt}); + my $comment_id = $yv_utils->get_comment_id($comment); + my $comment_age = $yv_utils->get_publication_age_approx($comment); + my $comment_text = reflow_text( "<big><b>" - . encode_entities($snippet->{authorDisplayName}) + . encode_entities($yv_utils->get_author($comment)) . "</b> (" . ( $comment_age =~ /sec|min|hour|day/ ? "$comment_age ago" - : $yv_utils->format_date($snippet->{publishedAt}) + : $yv_utils->get_publication_date($comment) ) . ") commented:</big>\n" . encode_entities( wrap_text( i_tab => "\t", s_tab => "\t", - text => [$snippet->{textDisplay} // 'Empty comment...'], + text => [$yv_utils->get_comment_content($comment) // 'Empty comment...'], ) ) ); @@ -3411,52 +3417,52 @@ sub display_comments { $feeds_liststore->set( $iter, 0 => $comment_text, - 3 => $snippet->{videoId}, - 4 => $comment->{snippet}{topLevelComment}{id}, + 3 => $video_id, + 4 => $comment_id, ); - if (exists $comment->{replies}) { - foreach my $reply (reverse @{$comment->{replies}{comments}}) { - my $reply_age = $yv_utils->date_to_age($reply->{snippet}{publishedAt}); - my $reply_text = reflow_text( - "\t<big><b>" - . encode_entities($reply->{snippet}{authorDisplayName}) - . "</b> (" - . ( - $reply_age =~ /sec|min|hour|day/ - ? "$reply_age ago" - : $yv_utils->format_date($reply->{snippet}{publishedAt}) - ) - . ") replied:</big>\n" - . encode_entities( - wrap_text( - i_tab => "\t\t", - s_tab => "\t\t", - text => [$reply->{snippet}{textDisplay} // 'Empty comment...'] - ) - ) - ); - - my $iter = $feeds_liststore->append; - $feeds_liststore->set( - $iter, - 0 => $reply_text, - 3 => $reply->{snippet}{videoId}, - 4 => $reply->{id}, - ); - } - } + #~ if (exists $comment->{replies}) { + #~ foreach my $reply (reverse @{$comment->{replies}{comments}}) { + #~ my $reply_age = $yv_utils->date_to_age($reply->{snippet}{publishedAt}); + #~ my $reply_text = reflow_text( + #~ "\t<big><b>" + #~ . encode_entities($reply->{snippet}{authorDisplayName}) + #~ . "</b> (" + #~ . ( + #~ $reply_age =~ /sec|min|hour|day/ + #~ ? "$reply_age ago" + #~ : $yv_utils->format_date($reply->{snippet}{publishedAt}) + #~ ) + #~ . ") replied:</big>\n" + #~ . encode_entities( + #~ wrap_text( + #~ i_tab => "\t\t", + #~ s_tab => "\t\t", + #~ text => [$reply->{snippet}{textDisplay} // 'Empty comment...'] + #~ ) + #~ ) + #~ ); + + #~ my $iter = $feeds_liststore->append; + #~ $feeds_liststore->set( + #~ $iter, + #~ 0 => $reply_text, + #~ 3 => $reply->{snippet}{videoId}, + #~ 4 => $reply->{id}, + #~ ); + #~ } + #~ } } - if (exists $res->{nextPageToken}) { - my $iter = $feeds_liststore->append; - $feeds_liststore->set( - $iter, - 0 => "<big><b>LOAD MORE</b></big>", - 1 => $url, - 2 => $res->{nextPageToken}, - ); - } + #~ if (exists $res->{nextPageToken}) { + #~ my $iter = $feeds_liststore->append; + #~ $feeds_liststore->set( + #~ $iter, + #~ 0 => "<big><b>LOAD MORE</b></big>", + #~ 1 => $url, + #~ 2 => $res->{nextPageToken}, + #~ ); + #~ } return 1; } diff --git a/bin/straw-viewer b/bin/straw-viewer index fbd0829..e444a35 100755 --- a/bin/straw-viewer +++ b/bin/straw-viewer @@ -205,7 +205,7 @@ my %CONFIG = ( publishedAfter => undef, order => undef, - comments_order => 'time', # valid values: time, relevance + comments_order => 'top', # valid values: top, new subscriptions_order => 'relevance', # valid values: alphabetical, relevance, unread hl => 'en_US', @@ -222,6 +222,9 @@ my %CONFIG = ( copy_caption => 0, cache_dir => undef, # auto-defined + # API + api_host => "https://invidio.us", + # Others autoplay_mode => 0, use_invidious_api => 0, @@ -1148,6 +1151,7 @@ sub apply_configuration { # ... YOUTUBE OPTIONS ... # foreach my $option_name ( qw( + api_host videoCaption maxResults order videoDefinition videoCategoryId videoDimension videoDuration @@ -2683,48 +2687,47 @@ sub print_comments { } my $url = $results->{url}; - my $info = $results->{results} // {}; - my $comments = $info->{items} // []; + my $comments = $results->{results}{comments} // []; my $i = 0; foreach my $comment (@{$comments}) { - my $snippet = (($comment->{snippet} // next)->{topLevelComment} // next)->{snippet}; - my $comment_age = $yv_utils->date_to_age($snippet->{publishedAt}); + my $comment_id = $yv_utils->get_comment_id($comment); + my $comment_age = $yv_utils->get_publication_age_approx($comment); printf( "\n%s (%s) commented:\n%s\n", - colored($snippet->{authorDisplayName}, 'bold'), + colored($yv_utils->get_author($comment), 'bold'), ( $comment_age =~ /sec|min|hour|day/ ? "$comment_age ago" - : $yv_utils->format_date($snippet->{publishedAt}) + : $yv_utils->get_publication_date($comment) ), wrap_text( i_tab => q{ } x 3, s_tab => q{ } x 3, - text => [$snippet->{textDisplay} // 'Empty comment...'] + text => [$yv_utils->get_comment_content($comment) // 'Empty comment...'] ), ); - if (exists $comment->{replies}) { - foreach my $reply (reverse @{$comment->{replies}{comments}}) { - my $reply_age = $yv_utils->date_to_age($reply->{snippet}{publishedAt}); - printf( - "\n %s (%s) replied:\n%s\n", - colored($reply->{snippet}{authorDisplayName}, 'bold'), - ( - $reply_age =~ /sec|min|hour|day/ - ? "$reply_age ago" - : $yv_utils->format_date($reply->{snippet}{publishedAt}) - ), - wrap_text( - i_tab => q{ } x 6, - s_tab => q{ } x 6, - text => [$reply->{snippet}{textDisplay} // 'Empty comment...'] - ), - ); - } - } + #~ if (exists $comment->{replies}) { + #~ foreach my $reply (reverse @{$comment->{replies}{comments}}) { + #~ my $reply_age = $yv_utils->date_to_age($reply->{snippet}{publishedAt}); + #~ printf( + #~ "\n %s (%s) replied:\n%s\n", + #~ colored($reply->{snippet}{authorDisplayName}, 'bold'), + #~ ( + #~ $reply_age =~ /sec|min|hour|day/ + #~ ? "$reply_age ago" + #~ : $yv_utils->format_date($reply->{snippet}{publishedAt}) + #~ ), + #~ wrap_text( + #~ i_tab => q{ } x 6, + #~ s_tab => q{ } x 6, + #~ text => [$reply->{snippet}{textDisplay} // 'Empty comment...'] + #~ ), + #~ ); + #~ } + #~ } } my @keywords = get_input_for_comments(); @@ -2740,7 +2743,7 @@ sub print_comments { sub => __SUB__, url => $url, res => $comments, - info => $info, + info => $results, mode => 'comments', args => [$videoID], ) diff --git a/lib/WWW/StrawViewer.pm b/lib/WWW/StrawViewer.pm index 336811e..42206d1 100644 --- a/lib/WWW/StrawViewer.pm +++ b/lib/WWW/StrawViewer.pm @@ -69,7 +69,7 @@ my %valid_options = ( safeSearch => {valid => [qw(none moderate strict)], default => undef}, videoType => {valid => [qw(any episode movie)], default => undef}, - comments_order => {valid => [qw(time relevance)], default => 'time'}, + comments_order => {valid => [qw(top new)], default => 'top'}, subscriptions_order => {valid => [qw(alphabetical relevance unread)], default => undef}, # Misc @@ -95,9 +95,10 @@ my %valid_options = ( refresh_token => {valid => [qr/^.{15}/], default => undef}, authentication_file => {valid => [qr/^./], default => undef}, + api_host => {valid => [qr{^https?://}], default => "https://invidio.us"}, # No input value allowed - feeds_url => {valid => q[], default => 'https://invidio.us/api/v1/'}, + api_path => {valid => q[], default => '/api/v1/'}, video_info_url => {valid => q[], default => 'https://www.youtube.com/get_video_info'}, oauth_url => {valid => q[], default => 'https://accounts.google.com/o/oauth2/'}, video_info_args => {valid => q[], default => '?video_id=%s&el=detailpage&ps=default&eurl=&gl=US&hl=en'}, @@ -457,9 +458,14 @@ sub _append_url_args { : $url; } +sub get_api_url { + my ($self) = @_; + join('', $self->get_api_host, $self->get_api_path); +} + sub _simple_feeds_url { - my ($self, $suburl, %args) = @_; - $self->get_feeds_url() . $suburl . '?' . $self->list_to_url_arguments(key => $self->get_key, %args); + my ($self, $path, %args) = @_; + $self->get_api_url . $path . '?' . $self->list_to_url_arguments(key => $self->get_key, %args); } =head2 default_arguments(%args) @@ -486,7 +492,7 @@ sub default_arguments { sub _make_feed_url { my ($self, $path, %args) = @_; my $extra_args = $self->default_arguments(%args); - my $url = $self->get_feeds_url() . $path; + my $url = $self->get_api_url . $path; if ($extra_args) { $url .= '?' . $extra_args; diff --git a/lib/WWW/StrawViewer/CommentThreads.pm b/lib/WWW/StrawViewer/CommentThreads.pm index 499d930..1eba143 100644 --- a/lib/WWW/StrawViewer/CommentThreads.pm +++ b/lib/WWW/StrawViewer/CommentThreads.pm @@ -36,15 +36,10 @@ Retrieve comments from a video ID. sub comments_from_video_id { my ($self, $video_id) = @_; - return - $self->_get_results( - $self->_make_commentThreads_url( - videoId => $video_id, - textFormat => 'plainText', - order => $self->get_comments_order, - part => 'snippet,replies' - ), - simple => 1, + $self->_get_results( + $self->_make_feed_url("comments/$video_id", + sort_by => $self->get_comments_order, + ), ); } diff --git a/lib/WWW/StrawViewer/Utils.pm b/lib/WWW/StrawViewer/Utils.pm index 87a7ac0..062bbe1 100644 --- a/lib/WWW/StrawViewer/Utils.pm +++ b/lib/WWW/StrawViewer/Utils.pm @@ -222,8 +222,15 @@ Returns true if a given result has entries. sub has_entries { my ($self, $result) = @_; - if (ref($result->{results}) eq 'HASH' and $result->{results}{type} eq 'playlist') { - return $result->{results}{videoCount} > 0; + if (ref($result->{results}) eq 'HASH') { + + if (exists $result->{results}{comments}) { + return scalar @{$result->{results}{comments}} > 0; + } + + if ($result->{results}{type} eq 'playlist') { + return $result->{results}{videoCount} > 0; + } } scalar(@{$result->{results}}) > 0; @@ -424,6 +431,11 @@ sub get_playlist_id { $info->{playlistId}; } +sub get_playlist_video_count { + my ($self, $info) = @_; + $info->{videoCount}; +} + =head2 get_description($info) Get description. @@ -488,6 +500,21 @@ sub get_channel_title { $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}; @@ -541,13 +568,13 @@ sub get_publication_date { sub get_publication_age { my ($self, $info) = @_; - $info->{publishedText} =~ s/\sago\z//r;; + ($info->{publishedText} // '') =~ s/\sago\z//r;; } sub get_publication_age_approx { my ($self, $info) = @_; - my $age = $self->get_publication_age($info); + my $age = $self->get_publication_age($info) // ''; if ($age =~ /hour|min|sec/) { return "0d"; @@ -650,17 +677,20 @@ sub get_views_approx { sub get_likes { my ($self, $info) = @_; - $info->{statistics}{likeCount}; + #$info->{statistics}{likeCount}; + 0; } sub get_dislikes { my ($self, $info) = @_; - $info->{statistics}{dislikeCount}; + #$info->{statistics}{dislikeCount}; + 0; } sub get_comments { my ($self, $info) = @_; - $info->{statistics}{commentCount}; + #$info->{statistics}{commentCount}; + 1; } { diff --git a/share/gtk-straw-viewer.desktop b/share/gtk-straw-viewer.desktop index 10d73d0..76136e4 100644 --- a/share/gtk-straw-viewer.desktop +++ b/share/gtk-straw-viewer.desktop @@ -1,5 +1,5 @@ [Desktop Entry] -Name=GTK Youtube Viewer +Name=GTK Straw Viewer Version=1.0 Comment=Search and play YouTube videos. Exec=gtk-straw-viewer diff --git a/share/gtk3-straw-viewer.glade b/share/gtk-straw-viewer.glade index 8d6cc4a..8d6cc4a 100644 --- a/share/gtk3-straw-viewer.glade +++ b/share/gtk-straw-viewer.glade diff --git a/share/icons/logo.png b/share/icons/gtk-straw-viewer.png Binary files differindex 656b637..656b637 100644 --- a/share/icons/logo.png +++ b/share/icons/gtk-straw-viewer.png |