#!perl
use strict;
use warnings;
use URI;
use URI::QueryParam;
use XML::TreePP;
use WWW::YouTube::Download;
use Getopt::Long qw(GetOptions :config bundling);
use Pod::Usage qw(pod2usage);
my $api_url = 'http://gdata.youtube.com/feeds/api/playlists';
my $proxy = undef;
GetOptions(
'C|no-colors!' => \my $disable_colors,
'p|proxy=s' => \$proxy,
'v|verbose' => \my $verbose,
'h|help' => sub { pod2usage(exitval => 0, -noperldoc => 1, -verbose => 2) },
'V|version' => sub { show_version() },
) or pod2usage(2);
pod2usage() unless @ARGV;
my $ua = WWW::YouTube::Download->new->ua;
my $tpp = XML::TreePP->new;
if($proxy){
$ua->proxy(['http','https'] => $proxy);
print "--> Using proxy $proxy\n";
}
main: {
for my $id_or_url (@ARGV) {
chatty("--> Working on %s\n", $id_or_url);
my $id = find_playlist_id($id_or_url);
throw('%s is not supported arguments', $id_or_url) unless $id;
my $xml = fetch_playlist_xml($id);
my $urls = find_video_urls($xml);
local $\ = "\n"; # auto print @_, "\n"
print join "\n", @$urls;
}
}
sub fetch_playlist_xml {
my $id = shift;
my $url = sprintf '%s/%s?v=2', $api_url, $id;
chatty('Fetching %s ... ', $url);
my $res = $ua->get($url);
unless ($res->is_success) {
throw('%s: %s', $id, $res->status_line);
}
chatty(pcolor(['green'], "done\n"));
return $res->decoded_content;
}
sub find_video_urls {
my $xml = shift;
my $urls = [];
chatty('Parsing XML ... ');
my $tree = $tpp->parse($xml);
for my $entry (@{ $tree->{feed}{entry} || [] }) {
my $uri = URI->new($entry->{'media:group'}{'media:player'}{-url});
$uri->query_param_delete('feature');
push @$urls, $uri->as_string;
}
chatty(pcolor(['green'], "done\n"));
return $urls;
}
sub find_playlist_id {
my $id_or_url = shift || return;
if ($id_or_url =~ /^http/) {
($id_or_url) = $id_or_url =~ m/list=PL([0-9A-F]+)/;
}
else {
$id_or_url =~ s/^PL//;
}
return $id_or_url;
}
sub throw {
my $format = shift;
die pcolor(['red'], sprintf($format, @_)), "\n";
}
sub chatty {
return unless $verbose;
my $format = shift;
print STDERR sprintf $format, @_;
}
sub pcolor {
my ($color, @msg) = @_;
if ($^O eq 'MSWin32' || $disable_colors || !-t STDOUT) {
return @msg;
}
eval { require Term::ANSIColor };
return @msg if $@; # module not available
return Term::ANSIColor::colored($color, @msg);
}
sub show_version {
print "youtube-playlists (WWW::YouTube::Download) version $WWW::YouTube::Download::VERSION\n";
exit;
}
__END__
=head1 NAME
youtube-playlists - Find a YouTube video urls from playlis(s)
=head1 SYNOPSIS
# print the list of video urls
$ youtube-playlists http://www.youtube.com/playlist?list=PLB199169FA7413767
$ youtube-playlists PLB199169FA7413767
$ youtube-playlists B199169FA7413767
# with youtube-download
$ youtube-playlists B199169FA7413767 | youtube-download
# with socks proxy
$ youtube-playlists -p socks://<some IP>:<some port>/ B199169FA7413767
=head1 OPTIONS
=over
=item -C, --no-colors
Force disable colored output
=item -p, --proxy
Use the given proxy. Note that using a socks proxy requires LWP::protocol::socks to be installed.
=item -v, --verbose
truns on chatty output
=item -h, --help
display help
=item -V, --version
display version
=back
=head1 AUTHOR
Yuji Shiamda (xaicron)