#!/usr/bin/perl use strict; use warnings; =head1 This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. http://creativecommons.org/licenses/by-nc-sa/3.0/ Gets YouTube playlists and puts them in videoqueue.xml file, suitable for Roksbox. Reads playlist IDs and user names from playlists.txt Format: playlist_id name user_id Examples: D5066BDDA2629460 notjaph's playlist for trains notjaph Or, you can use as a CGI script to turn a playlist into a RSS feed for inclusion on Roksbox's videoqueue.xml file. See the sample XML file. Youtube playlist XML link: http://gdata.youtube.com/feeds/api/playlists/D5066BDDA2629460 Youtube users playlists: http://gdata.youtube.com/feeds/base/users/notjaph/playlists/ =cut my $debug=1; my $my_url = "http://www.thesatya.com/cgi-bin/rry/get_playlists.pl"; my $playlist_url = 'http://gdata.youtube.com/feeds/api/playlists/'; my $user_url = 'http://gdata.youtube.com/feeds/base/users/'; use Data::Dumper; use XML::LibXML; use LWP::Simple; use HTML::Template; use CGI; my $xpc = XML::LibXML::XPathContext->new; $xpc->registerNs('atom', 'http://www.w3.org/2005/Atom'); $xpc->registerNs('yt', 'http://gdata.youtube.com/schemas/2007'); $xpc->registerNs('media', 'http://search.yahoo.com/mrss/'); my $now = scalar(localtime).' -0500'; sub xml_escape($) { my $s=shift; $s=~s/&/&/g; return $s; } sub parse($) { my $s = shift; #my $dom=XML::LibXML->load_xml(string => $s); my $parser = XML::LibXML->new(); my $dom = $parser->parse_string($s); my $root = $dom->documentElement(); return $root; } sub get_value(@) { # Given a DOM root and an XPath, return the value, defaulting to $default my ($xpath, $root, $default) = @_; my @nodes = $xpc->findnodes($xpath, $root); foreach (@nodes) { $default = $_->textContent; } return $default; } sub get_attr(@) { # Given a DOM root and an XPath, return the attribute value, defaulting to $default my ($xpath, $root, $attrname, $default) = @_; my @nodes = $xpc->findnodes($xpath, $root); foreach my $n (@nodes) { foreach ($n->attributes()) { if($attrname eq $_->nodeName) { $default = $_->textContent; } } } return $default; } sub get_playlist(@) { my $playlists=shift; my $id=shift; my $desc=shift || ''; warn "playlist: $id, $desc\n" if $debug; my $data = get($playlist_url . $id); return unless $data; my $root = parse($data); my $genrename=xml_escape get_value('/atom:feed/atom:title', $root, $id); my $genre="[$desc$genrename]"; warn "$genre\n" if $debug; my $authorname=xml_escape get_value('/atom:feed/atom:author/atom:name', $root, ''); my @nodes = $xpc->findnodes('/atom:feed/atom:entry', $root); foreach my $item (@nodes) { unless( exists $playlists->{$genre} ) { $playlists->{$genre}={ 'items' => [], authorname => $authorname, }; } my $url = xml_escape get_attr('media:group/media:player', $item, 'url', ''); next unless $url; my %attrs=(url => $url, pubDate => $now); foreach ('atom:updated','atom:title','atom:author/atom:name') { $attrs{$_} = xml_escape(get_value($_, $item, '')); } my $video_id = $url; $video_id =~ s/.*\?//; $video_id = new CGI($video_id)->param('v'); $attrs{'video_id'} = $video_id; push @{ $playlists->{$genre}->{'items'} }, \%attrs; } } sub get_user(@) { my $playlists=shift; my $id=shift; warn "user: $id\n" if $debug; my $data = get($user_url . $id . '/playlists'); my $root = parse($data); my @nodes = $xpc->findnodes('/atom:feed/atom:entry/atom:id', $root); #print $root->toStringC14N; foreach (@nodes) { my $pl_id = $_->textContent; $pl_id=~s/^.*\///g; get_playlist($playlists, $pl_id, "$id/"); } } sub read_file() { open(I, "< playlists.txt") || die "Needs playlists.txt file"; my @lines=; close(I); my $playlists={}; foreach my $line (@lines) { chomp $line; $line=~s/\s+/ /g; # collapse multiple spaces next if $line=~/^#/ || $line=~/^\s*$/; # skip comments and empty lines $line =~ s/#.*//; # remove trailing comments my ($id, $desc) = split(/ /, $line, 2); if($desc) { get_playlist($playlists, $id); } else { get_user($playlists, $id); } } return $playlists; } sub write_file(@) { my $playlists=shift; my $tmpl_file=shift; my $data=[]; foreach my $genre (sort keys %$playlists) { my $urls = $playlists->{$genre}->{'items'}; my $urllist = ''; foreach my $u (@$urls) { next if $u->{'url'} eq ''; $urllist .= "" . $u->{'url'} . "\n"; } next if $urllist eq ''; push @$data, {urls => $urllist, genre => $genre}; } my $tmpl=HTML::Template->new(filename => $tmpl_file); $tmpl->param(videos => $data); print $tmpl->output; } sub rss_headers() { print "GData-Version: 1.0\n"; print "Content-type: application/rss+xml; charset=UTF-8\n\n"; } sub return_playlist_rss($$) { my $playlists=shift; my $id=shift; my $genre=(keys %$playlists)[0]; my $tmpl=HTML::Template->new(filename => 'rss.tmpl.xml'); my $pl = $playlists->{$genre}; $tmpl->param( description => "Playlist: $genre", buildDate => $now, items => $pl->{'items'}, playlist_id => "$my_url?id=$id", author => $pl->{'authorname'}, totalResults => $#{$pl->{'items'}} + 1, ); rss_headers; #print Dumper $playlists->{$genre}; print $tmpl->output; } sub send_file($) { my $file=shift; print "Content-type: text/plain\n\n"; open(I,"< $file"); print ; close(I); } sub print_page() { print< Playlist ID: Get this code Get queue file template Get RSS file template

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

HTML } if($ARGV[0] && $ARGV[0] eq 'cli') { my $playlists = read_file; write_file($playlists, 'videoqueue.tmpl.xml'); } else { my $q=new CGI; my $id=$q->param('id'); my $pl_id=$q->param('pl_id'); if($id) { if($id eq 'get_code') { send_file("get_playlists.pl"); } elsif($id eq 'get_queuefile') { send_file("videoqueue.sample"); } elsif($id eq 'get_rssfile') { send_file("rss.tmpl.xml"); } else { my $playlists={}; get_playlist($playlists, $id); return_playlist_rss($playlists, $id); } } elsif($pl_id) { print "Content-type: text/plain\n\nYour RSS feed URL is: $my_url?id=$pl_id\n"; } else { print_page; } }