Skip to content

Commit 3a53d23

Browse files
committed
- Simplified --save-playlist=s to take a playlist ID and automatically extract the playlist name from YouTube.
- Added the `--save-channel=s` option, to save a channel locally in `-lc`, given a channel ID or username. - Added the `:remove=i` (aliased as `:r=i`) to remove a locally saved playlist from `-lp`. - Display the playlist ID in `:info=i` in playlist results.
1 parent 98072e8 commit 3a53d23

File tree

1 file changed

+100
-42
lines changed

1 file changed

+100
-42
lines changed

bin/youtube-viewer

Lines changed: 100 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ my $playlists_help = <<"PLAYLISTS_HELP" . $general_help;
355355
<number> : list videos from the selected playlist
356356
:pp=i,i : play videos from the selected playlists
357357
:s=i :save=i : save the playlist locally (see -lp)
358+
:r=i :remove=i : remove a locally saved playlist from -lp
358359
PLAYLISTS_HELP
359360

360361
my $channels_help = <<"CHANNELS_HELP" . $general_help;
@@ -745,8 +746,7 @@ usage: $execname [options] ([url] | [keywords])
745746
--ps=s : add video by ID or URL to a post-selected playlist
746747
or in a given playlist ID specified with `--pid`
747748
--position=i : position in the playlist where to add the video
748-
--save-playlist=s : save a playlist locally given a filename
749-
playlist ID must be specified with `--pid`
749+
--save-playlist=s : save a playlist locally given a playlist ID
750750
-lp --local-playlists : display saved local playlists
751751
-lp=s : display a local playlist by name
752752
@@ -759,9 +759,11 @@ usage: $execname [options] ([url] | [keywords])
759759
760760
* Channels
761761
-sc --channels : search for YouTube channels
762+
--save-channel=s : save a channel locally given a channel ID
762763
-lc --local-channels : display the list of local channels
763764
-lc=s : display videos from a local channel by name
764765
766+
765767
* Comments
766768
--comments=s : display comments for a video by ID or URL
767769
--comments-order=s : change the order of YouTube comments
@@ -1340,16 +1342,13 @@ sub apply_configuration {
13401342
}
13411343

13421344
if (defined $opt->{save_playlist}) {
1345+
my $playlistID = get_valid_playlist_id(delete($opt->{save_playlist})) // return;
1346+
save_playlist_to_file($playlistID);
1347+
}
13431348

1344-
if (not defined $opt->{playlist_id}) {
1345-
warn_no_thing_selected('playlist (--pid=ID)');
1346-
return;
1347-
}
1348-
1349-
my $playlistID = get_valid_playlist_id(delete($opt->{playlist_id})) // return;
1350-
my $filename = catfile($local_playlists_dir, delete($opt->{save_playlist}) // $playlistID);
1351-
1352-
save_playlist_to_file($playlistID, $filename);
1349+
if (defined $opt->{save_channel}) {
1350+
my $channelID = delete($opt->{save_channel});
1351+
save_channel_to_file($channelID);
13531352
}
13541353

13551354
if (defined $opt->{playlist_id}) {
@@ -1610,7 +1609,9 @@ sub parse_arguments {
16101609
'lc|fc|local-channels:s' => \$opt{local_channels},
16111610
'lp|local-playlists:s' => \$opt{local_playlist},
16121611
'wv|watched-videos' => \$opt{watched_videos},
1613-
'save-playlist=s' => \$opt{save_playlist},
1612+
1613+
'save-channel=s' => \$opt{save_channel},
1614+
'save-playlist=s' => \$opt{save_playlist},
16141615

16151616
'search-videos|search|sv!' => \$opt{search_videos},
16161617
'search-channels|channels|sc!' => \$opt{search_channels},
@@ -2090,7 +2091,7 @@ sub get_user_input {
20902091
sub logout {
20912092

20922093
unlink $authentication_file
2093-
or warn "Can't unlink: `$authentication_file' -> $!";
2094+
or warn "[!] Can't unlink: `$authentication_file' -> $!";
20942095

20952096
$yv_obj->set_access_token();
20962097
$yv_obj->set_refresh_token();
@@ -2128,7 +2129,7 @@ INFO
21282129
if ($remember_me) {
21292130
$yv_obj->set_authentication_file($authentication_file);
21302131
$yv_obj->save_authentication_tokens()
2131-
or warn "Can't store the authentication tokens: $!";
2132+
or warn "[!] Can't store the authentication tokens: $!";
21322133
}
21332134
else {
21342135
$yv_obj->set_authentication_file();
@@ -2832,12 +2833,7 @@ sub print_channels {
28322833
foreach my $id (@nums) {
28332834
my $channel_id = $yv_utils->get_channel_id($channels->[$id]);
28342835
my $channel_title = $yv_utils->get_title($channels->[$id]);
2835-
if (save_channel_to_file($channel_id, $channel_title)) {
2836-
say "\n:: Saved channel <<$channel_title>> (id: $channel_id) to file.";
2837-
}
2838-
else {
2839-
say "\n[!] Unable to save channel <<$channel_title>> to file...";
2840-
}
2836+
save_channel_to_file($channel_id, $channel_title);
28412837
}
28422838
}
28432839
else {
@@ -2997,22 +2993,49 @@ sub print_comments {
29972993
sub save_channel_to_file {
29982994
my ($channel_id, $channel_title) = @_;
29992995

3000-
$yv_utils->is_channelID($channel_id) || return;
2996+
$channel_id // return;
2997+
2998+
if ($channel_id = extract_channel_id($channel_id)) {
2999+
if (not $yv_utils->is_channelID($channel_id)) {
3000+
$channel_id = $yv_obj->channel_id_from_username($channel_id) // do {
3001+
warn_invalid("username or channel ID", $channel_id);
3002+
undef;
3003+
};
3004+
}
3005+
3006+
if ($channel_id eq 'mine') {
3007+
$channel_id = $yv_obj->my_channel_id() // do {
3008+
warn_invalid("username or channel ID", $channel_id);
3009+
undef;
3010+
};
3011+
}
3012+
}
3013+
3014+
$channel_id // return;
3015+
3016+
if (not defined($channel_title)) {
3017+
$channel_title = $yv_utils->get_title($yv_obj->channel_from_id($channel_id, 'snippet')->{results}{items}[0]);
3018+
}
3019+
3020+
say "\n:: Saving channel <<$channel_title>> (id: $channel_id) to file...";
30013021

30023022
open(my $fh, '>>:utf8', $opt{youtube_users_file}) or do {
3003-
warn "Can't open file <<$opt{youtube_users_file}>> for appending: $!\n";
3023+
warn "[!] Can't open file <<$opt{youtube_users_file}>> for appending: $!\n";
30043024
return;
30053025
};
30063026

30073027
say $fh "$channel_id $channel_title";
30083028
close $fh;
3029+
3030+
say ":: Done.";
3031+
return 1;
30093032
}
30103033

30113034
sub update_channel_file {
30123035
my (%channels) = @_;
30133036

30143037
open(my $fh, '>:utf8', $opt{youtube_users_file}) or do {
3015-
warn "Can't open file <<$opt{youtube_users_file}>> for writing: $!\n";
3038+
warn "[!] Can't open file <<$opt{youtube_users_file}>> for writing: $!\n";
30163039
return;
30173040
};
30183041

@@ -3027,7 +3050,7 @@ sub remove_saved_channels {
30273050
my (@channel_ids) = @_;
30283051

30293052
open(my $fh, '<:utf8', $opt{youtube_users_file}) or do {
3030-
warn "Can't open file <<$opt{youtube_users_file}>> for reading: $!\n";
3053+
warn "[!] Can't open file <<$opt{youtube_users_file}>> for reading: $!\n";
30313054
return;
30323055
};
30333056

@@ -3041,7 +3064,7 @@ sub remove_saved_channels {
30413064
$channels{$channel_id} = $channel_title;
30423065
}
30433066
else {
3044-
warn "Invalid channel ID: $channel_id\n";
3067+
warn "[!] Invalid channel ID: $channel_id\n";
30453068
}
30463069
}
30473070
}
@@ -3134,20 +3157,35 @@ sub extract_video_ids_from_playlist {
31343157
}
31353158

31363159
sub save_playlist_to_file {
3137-
my ($playlistID, $filename) = @_;
3160+
my ($playlistID, $title) = @_;
3161+
3162+
$playlistID // return;
3163+
3164+
if (not defined($title)) {
3165+
$title = $yv_utils->get_title($yv_obj->playlist_from_id($playlistID, 'snippet')->{results}{items}[0]);
3166+
}
3167+
3168+
my $basename = $title . ' -- ' . $playlistID . '.txt';
3169+
$basename =~ s{/}{%}g;
3170+
3171+
my $filename = catfile($local_playlists_dir, $basename);
3172+
say ":: Saving playlist <<$title>> (id: $playlistID) to file...";
31383173

31393174
if (-e $filename) {
3140-
warn ":: File <<$filename>> already exists!\n";
3175+
warn "[!] File <<$filename>> already exists!\n";
31413176
return;
31423177
}
31433178

31443179
open(my $fh, '>', $filename) or do {
3145-
warn "Can't open file <<$filename>> for writing: $!";
3180+
warn "[!] Can't open file <<$filename>> for writing: $!";
31463181
return;
31473182
};
31483183

31493184
extract_video_ids_from_playlist($playlistID, sub { say $fh $_[0] });
31503185
close $fh;
3186+
3187+
say ":: Done.";
3188+
return 1;
31513189
}
31523190

31533191
sub print_local_playlist {
@@ -3421,23 +3459,45 @@ sub print_playlists {
34213459
elsif ($opt =~ /^(?:r|return)\z/) {
34223460
return;
34233461
}
3462+
3463+
# :s=i, :save=i
34243464
elsif ($opt =~ /^(?:s|save)${digit_or_equal_re}(.*)/) {
34253465
if (my @ids = get_valid_numbers($#{$playlists}, $1)) {
34263466
foreach my $id (@ids) {
34273467
my $playlist = $playlists->[$id];
34283468
my $playlistID = $yv_utils->get_playlist_id($playlist);
34293469
my $title = $yv_utils->get_title($playlist);
3430-
my $basename = $title . ' -- ' . $playlistID . '.txt';
3431-
$basename =~ s{/}{%}g;
3432-
my $filename = catfile($local_playlists_dir, $basename);
3433-
say ":: Saving playlist <<$title>> as <<$filename>>";
3434-
save_playlist_to_file($playlistID, $filename);
3470+
save_playlist_to_file($playlistID, $title);
3471+
}
3472+
}
3473+
else {
3474+
warn_no_thing_selected('playlist');
3475+
}
3476+
}
3477+
3478+
# :r=i, :rm=i, :remove=i
3479+
elsif ($opt =~ /^(?:r|rm|remove)${digit_or_equal_re}(.*)/) {
3480+
if (my @nums = get_valid_numbers($#{$playlists}, $1)) {
3481+
3482+
foreach my $i (@nums) {
3483+
my $id = $yv_utils->get_playlist_id($playlists->[$i]);
3484+
my $title = $yv_utils->get_title($playlists->[$i]);
3485+
3486+
if ($id =~ m{^/} and -f $id) {
3487+
say ":: Removing local playlist <<$title>>";
3488+
say(unlink($id) ? ":: Done." : ":: Error: $!");
3489+
}
3490+
else {
3491+
say ":: Playlist <<$title>> is not saved locally";
3492+
}
34353493
}
34363494
}
34373495
else {
34383496
warn_no_thing_selected('playlist');
34393497
}
34403498
}
3499+
3500+
# :i=i, :info=i
34413501
elsif ($opt =~ /^(?:i|info)${digit_or_equal_re}(.*)/) {
34423502
if (my @ids = get_valid_numbers($#{$playlists}, $1)) {
34433503
foreach my $id (@ids) {
@@ -3449,6 +3509,8 @@ sub print_playlists {
34493509
warn_no_thing_selected('playlist');
34503510
}
34513511
}
3512+
3513+
# :pp=i
34523514
elsif ($opt =~ /^pp${digit_or_equal_re}(.*)/) {
34533515
if (my @ids = get_valid_numbers($#{$playlists}, $1)) {
34543516
my $arg = "--pp=" . join(q{,}, map { $yv_utils->get_playlist_id($_) } @{$playlists}[@ids]);
@@ -4114,9 +4176,10 @@ sub print_playlist_info {
41144176
(
41154177
map { sprintf(q{-> } . "%-*s: %s\n", $opt{_colors} ? 18 : 10, _bold_color($_->[0]), $_->[1]) }
41164178
grep { defined($_->[1]) } (
4117-
['Title' => $yv_utils->get_title($playlist)],
4118-
['Channel' => $yv_utils->get_channel_title($playlist)],
4119-
['ChannelID' => $yv_utils->get_channel_id($playlist)],
4179+
['Title' => $yv_utils->get_title($playlist)],
4180+
['Author' => $yv_utils->get_channel_title($playlist)],
4181+
['ChannelID' => $yv_utils->get_channel_id($playlist)],
4182+
['PlaylistID' => ($id =~ m{^/} ? undef : $id)],
41204183
['Videos' => $yv_utils->set_thousands($yv_utils->get_playlist_item_count($playlist))],
41214184
['Published' => $yv_utils->get_publication_date($playlist)],
41224185
)
@@ -4590,12 +4653,7 @@ sub print_videos {
45904653
foreach my $id (@nums) {
45914654
my $channel_id = $yv_utils->get_channel_id($videos->[$id]);
45924655
my $channel_title = $yv_utils->get_channel_title($videos->[$id]);
4593-
if (save_channel_to_file($channel_id, $channel_title)) {
4594-
say "\n:: Saved channel <<$channel_title>> (id: $channel_id) to file.";
4595-
}
4596-
else {
4597-
say "\n[!] Unable to save channel <<$channel_title>> to file...";
4598-
}
4656+
save_channel_to_file($channel_id, $channel_title);
45994657
}
46004658
}
46014659
else {

0 commit comments

Comments
 (0)