Created
May 20, 2013 21:05
-
-
Save ispedals/5615556 to your computer and use it in GitHub Desktop.
Mojolicious::Lite app that explores a youtube video by both displaying both the author's video and related videos recursively
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#perl | |
use v5.16; | |
use warnings; | |
use XML::Smart; | |
use Mojolicious::Lite; | |
my %videos; #videos to be saved; structured <id:[title, author_url, id, duration, [thumbnails]]> | |
my %seen; #videos that have been seen and either saved or rejected; keys are video ids | |
my %authors; #authors whose uploads have been seen; keys are author urls | |
my @to_process = ( | |
[ | |
] | |
); #initialize with videos | |
$seen{$_->[2]} = 1 for @to_process; | |
sub get_videos_from_feed { | |
my $url = shift; | |
my $ret = []; | |
#actually 97% faster than Mojo::DOM | |
my $x = XML::Smart->new($url); | |
for ( @{ $x->{feed}->{entry} } ) { | |
my $title = $_->{title}->content; | |
my $author = $_->{author}->{uri}->content . '/uploads?v=2'; | |
my $id = $_->{'media:group'}->{'yt:videoid'}->content; | |
my @thumbs = | |
map { $_->{url} } | |
@{ $_->{'media:group'}->{'media:thumbnail'} }[ -3 .. -1 ]; #last three thumbs are start, middle and end of video | |
my $duration = $_->{'media:group'}->{'yt:duration'}->{seconds}; | |
next if $videos{$id} || $seen{$id}; | |
push( @$ret, [ $title, $author, $id, $duration, \@thumbs ] ); | |
} | |
if ( $x->{feed}->{link}( 'rel', 'eq', 'next' ) ) { | |
$url = $x->{feed}{link}( 'rel', 'eq', 'next' )->{href}->content; | |
my @next = get_videos_from_feed($url); | |
return @$ret, @next; | |
} | |
else { | |
return @$ret; | |
} | |
#use Mojo::UserAgent; | |
#state $ua = Mojo::UserAgent->new; | |
#my $dom = $ua->get($url)->res->dom; | |
#for ( $dom->find('media\:group')->each ) { | |
# my $title = $_->at('media\:title')->text; | |
# my $author = $_->at('yt\:uploaderId')->text; | |
# $author = | |
# sprintf 'http://gdata.youtube.com/feeds/api/users/%s/uploads?v=2', | |
# $author; | |
# my $id = $_->at('yt\:videoid')->text; | |
# my @thumbs = | |
# map { $_->{url} } ( $_->find('media\:thumbnail')->each )[ -3 .. -1 ]; | |
# my $duration = $_->at('yt\:duration')->{seconds}; | |
# next if $videos{$id} || $seen{$id}; | |
# push( @$ret, [ $title, $author, $id, $duration, \@thumbs ] ); | |
#} | |
#return @$ret unless ( $url = $dom->at('link[rel="next"]') ); | |
#my @next = get_videos_from_feed( $url->{href} ); | |
#return @$ret, @next; | |
} | |
sub process { | |
my $vid = shift @to_process; | |
my @ret; | |
my @related_videos = | |
get_videos_from_feed "http://gdata.youtube.com/feeds/api/videos/" | |
. $vid->[2] | |
. "/related?v=2"; | |
my @author_uploads = | |
$authors{ $vid->[1] } ? () : get_videos_from_feed $vid->[1]; | |
for ( ( @related_videos, @author_uploads ) ) { | |
next if $videos{ $_->[2] } || $seen{ $_->[2] } || $authors{ $_->[1] }; | |
$seen{ $_->[2] } = 1; | |
push @ret, $_; | |
} | |
$authors{ $vid->[1] } = 1; | |
return @ret; | |
} | |
my @to_be_selected; #contains all the videos derived from a single video, filtered in post | |
get '/' => sub { | |
my $self = shift; | |
@to_be_selected = process; | |
my $picked = scalar keys %videos; | |
my $processed = scalar keys %seen; | |
my $out = "<!html><head><title>Picked $picked out of $processed</title></head><body><form method='post'>"; | |
for (@to_be_selected) { | |
my ( $title, $id, $duration ) = @{$_}[ 0, 2, 3 ]; | |
$out .= | |
"<div><p>$title ($duration seconds)</p><input type='checkbox' name='$id'>"; | |
$out .= "<img src='$_'>" for @{ $_->[4] }; | |
$out .= '</div>'; | |
} | |
$out .= '<input type="submit" value="Submit">'; | |
$self->render( text => $out ); | |
}; | |
post '/' => sub { | |
my $self = shift; | |
my %wanted = map { $_ => 1 } $self->param; #params contains the ids of the video that were selected to be saved | |
@to_be_selected = grep { $wanted{ $_->[2] } } @to_be_selected; | |
$videos{ $_->[2] } = $_ for @to_be_selected; | |
push @to_process, @to_be_selected; | |
@to_be_selected = (); | |
$self->redirect_to('/'); | |
}; | |
get '/view' => sub { | |
my $self = shift; | |
my $picked = scalar keys %videos; | |
my $processed = scalar keys %seen; | |
my $out = "<!html><head><title>Picked $picked out of $processed</title></head><body>"; | |
for ( values %videos ) { | |
my ( $title, $id, $duration ) = @{$_}[ 0, 2, 3 ]; | |
$out .= "<div><p><a href='https://www.youtube.com/watch?v=$id'>$title</a> ($duration seconds)</p>"; | |
$out .= '<img src="' . $_ . '">' for @{ $_->[4] }; | |
$out .= '</div>'; | |
} | |
$out .= "http://www.youtube.com/watch?v=$_<br>" for keys %videos; | |
$out .= '<hr><h1>Seen</h1>'; | |
$out .= "http://www.youtube.com/watch?v=$_<br>" for keys %seen; | |
$out .= '<hr><h1>Authors</h1>'; | |
$out .= "$_<br>" for keys %authors; | |
$self->render( text => $out ); | |
}; | |
app->start; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment