App-get_flash_videos
view release on metacpan or search on metacpan
lib/FlashVideo/Site/Youtube.pm view on Meta::CPAN
# Part of get-flash-videos. See get_flash_videos for copyright.
package FlashVideo::Site::Youtube;
use strict;
use Encode;
use HTML::Entities;
use FlashVideo::Utils;
use FlashVideo::JSON;
use URI::Escape;
my @formats = (
{ id => 38, resolution => [4096, 2304] },
{ id => 37, resolution => [1920, 1080] },
{ id => 22, resolution => [1280, 720] },
{ id => 35, resolution => [854, 480] },
{ id => 34, resolution => [640, 360] },
{ id => 18, resolution => [480, 270] },
{ id => 5, resolution => [400, 224] },
{ id => 17, resolution => [176, 144] },
{ id => 13, resolution => [176, 144] },
);
sub find_video {
my ($self, $browser, $embed_url, $prefs) = @_;
if($embed_url !~ m!youtube\.com/watch!) {
$browser->get($embed_url);
if ($browser->response->header('Location') =~ m!/swf/.*video_id=([^&]+)!
|| $embed_url =~ m!/v/([-_a-z0-9]+)!i
|| $browser->uri =~ m!v%3D([-_a-z0-9]+)!i) {
# We ended up on a embedded SWF or other redirect page
$embed_url = "http://www.youtube.com/watch?v=$1";
$browser->get($embed_url);
}
}
if (!$browser->success) {
verify_age($browser, $prefs);
}
my $title = extract_info($browser)->{meta_title};
if (!$title and
$browser->content =~ /<div id="vidTitle">\s+<span ?>(.+?)<\/span>/ or
$browser->content =~ /<div id="watch-vid-title">\s*<div ?>(.+?)<\/div>/) {
$title = $1;
}
# If the page contains fmt_url_map, then process this. With this, we
# don't require the 't' parameter.
if ($browser->content =~ /["']fmt_url_map["']:\s{0,3}(["'][^"']+["'])/) {
debug "Using fmt_url_map method from page ($1)";
return $self->download_fmt_map($prefs, $browser, $title, {}, @{from_json $1});
}
my $video_id;
if ($browser->content =~ /(?:var pageVideoId =|(?:CFG_)?VIDEO_ID'?\s*:)\s*'(.+?)'/
|| $embed_url =~ /v=([^&]+)/) {
$video_id = $1;
} else {
check_die($browser, "Couldn't extract video ID");
}
my $t;
if ($browser->content =~ /\W['"]?t['"]?: ?['"](.+?)['"]/) {
$t = $1;
}
# Try to get Youtube's info for this video - needed for some types of
# video.
my $video_page_url = $browser->uri->as_string;
if (my %info = get_youtube_video_info($browser->clone, $video_id, $video_page_url, $t)) {
if($self->debug) {
require Data::Dumper;
debug Data::Dumper::Dumper(\%info);
}
# Check for rtmp downloads
if ($info{conn} =~ /^rtmp/) {
# Get season and episode
my ($season, $episode);
if ($browser->content =~ m{<span[^>]*>Season ?(\d+)}i) {
$season = $1;
}
if ($browser->content =~ m{<span[^>]*>[^<]+Ep\.?\w* ?(\d+)\W*\s*</span>}i) {
$episode = $1;
}
if ($season and $episode) {
$title .= sprintf " S%02dE%02d", $season, $episode;
}
# Need flash URL for SWF verification
my $swf_url;
if ($browser->content =~ /SWF_URL['"] ?: ?.{0,90}?(http:\/\/[^ ]+\.swf)/) {
$swf_url = $1;
lib/FlashVideo/Site/Youtube.pm view on Meta::CPAN
$title = $info->{title};
}
# Sort by quality...
my $preferred_quality = $prefs->quality->choose(map { $fmt_url_map->{$_->{id}}
? { resolution => $_->{resolution}, url => $fmt_url_map->{$_->{id}} }
: () } @formats);
$browser->allow_redirects;
return $preferred_quality->{url}, title_to_filename($title, "mp4");
}
sub download_get_video {
my($browser, $prefs, $video_id, $title, $t) = @_;
my $fetcher = sub {
my($url, $filename) = @_;
$url = url_exists($browser->clone, $url, 1);
return $url, $filename if $url;
return;
};
my @formats_to_try = @formats;
while(my $fmt = $prefs->quality->choose(@formats_to_try)) {
# Remove from the list
@formats_to_try = grep { $_ != $fmt } @formats_to_try;
# Try it..
my @ret = $fetcher->("http://www.youtube.com/get_video?fmt=$fmt->{id}&video_id=$video_id&t=$t",
title_to_filename($title, "mp4"));
return @ret if @ret;
}
# Otherwise try without an ID
my @ret = $fetcher->("http://www.youtube.com/get_video?video_id=$video_id&t=$t",
title_to_filename($title));
check_die($browser, "Unable to find video URL") unless @ret;
$browser->allow_redirects;
return @ret;
}
sub check_die {
my($browser, $message) = @_;
if($browser->content =~ m{class="yt-alert-content">([^<]+)}) {
my $alert = $1;
$alert =~ s/(^\s+|\s+$)//g;
$message .= "\nYouTube: $alert";
error $message;
exit 1;
} else {
die "$message\n";
}
}
sub verify_age {
my($browser, $prefs) = @_;
my $orig_uri = $browser->uri;
if ($browser->response->code == 303
&& $browser->response->header('Location') =~ m!/verify_age|/accounts/!) {
my $confirmation_url = $browser->response->header('Location');
$browser->get($confirmation_url);
if($browser->content =~ /has_verified=1/) {
my($verify_url) = $browser->content =~ /href="(.*?has_verified=1)"/;
$verify_url = decode_entities($verify_url);
$browser->get($verify_url);
# Great that worked, otherwise probably does want a login
return if $browser->response->code == 200;
}
# Lame age verification page - yes, we are grown up, please just give
# us the video!
my $account = $prefs->account("youtube", <<EOT);
Unfortunately, due to Youtube being lame, you have to have
an account to download this video. (See the documentation for how to configure
~/.netrc)
EOT
unless ($account->username and $account->password) {
error "You must supply Youtube account details.";
exit 1;
}
$browser->get("http://www.youtube.com/login");
if ($browser->response->code != 303) {
die "Unexpected response from Youtube login.\n";
}
my $real_login_url = $browser->response->header('Location');
$browser->get($real_login_url);
$browser->form_with_fields('Email', 'Passwd');
$browser->set_fields(
Email => $account->username,
Passwd => $account->password,
);
$browser->submit();
if ($browser->content =~ /your login was incorrect/) {
error "Couldn't log you in, check your username and password.";
exit 1;
} elsif ($browser->response->code == 302) {
# expected, next step in login process
my $check_cookie_url = $browser->response->header('Location');
$browser->get($check_cookie_url);
# and then another, html-only, non-http, redirection...
if ($browser->content =~ /<meta.*"refresh".*?url='(.*?)'"/i) {
my $redirected = decode_entities($1);
$browser->get($redirected);
# If we weren't redirected to YouTube we might have a regional Google
# site.
if(URI->new($redirected)->host !~ /youtube/i) {
if($browser->response->code == 302) {
$browser->get($browser->response->header("Location"));
} else {
die "Did not find expected redirection";
}
}
} else {
die "Did not find expected redirection";
}
}
else {
( run in 2.589 seconds using v1.01-cache-2.11-cpan-5837b0d9d2c )