#!/usr/bin/env perl ################################################# # # This file was automatically generated by utils/combine-perl.pl # You should edit the original files, not this # combined version. # # The original files are available at: # http://github.com/monsieurvideo/get-flash-videos # ################################################# # # get_flash_videos -- download all the Flash videos off a web page # # http://code.google.com/p/get-flash-videos/ # # Copyright 2009, zakflash and MonsieurVideo # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain a # copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Contributions are welcome and encouraged, but please take care to # maintain the JustWorks(tm) nature of the program. ##{ utils/combine-header { package main; $::SCRIPT_NAME = 'get_flash_videos'; } ##} utils/combine-header BEGIN { $INC{'FlashVideo/Site/4od.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. BEGIN { $INC{'FlashVideo/Utils.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Utils.pm { package FlashVideo::Utils; use strict; no warnings 'uninitialized'; use Exporter;use base 'Exporter'; use HTML::Entities; use HTML::TokeParser; use Encode; use constant FP_KEY => "Genuine Adobe Flash Player 001"; use constant EXTENSIONS => qr/\.(?:flv|mp4|mov|wmv|avi|m4v)/i; use constant MAX_REDIRECTS => 5; our @EXPORT = qw(debug info error extract_title extract_info title_to_filename get_video_filename url_exists swfhash swfhash_data EXTENSIONS get_user_config_dir get_win_codepage is_program_on_path get_terminal_width json_unescape convert_sami_subtitles_to_srt from_xml); sub debug(@) { my $string = "@_\n"; $string =~ s/\Q$ENV{HOME}\E/~/g; print STDERR $string if $App::get_flash_videos::opt{debug}; } sub info(@) { print STDERR "@_\n" unless $App::get_flash_videos::opt{quiet}; } sub error(@) { print STDERR "@_\n"; } sub extract_title { my($browser) = @_; return extract_info($browser)->{title}; } sub extract_info { my($browser) = @_; my($title, $meta_title); my $p = HTML::TokeParser->new(\$browser->content); while(my $token = $p->get_tag("title", "meta")) { my($tag, $attr) = @$token; if($tag eq 'meta' && $attr->{name} =~ /title/i) { $meta_title = $attr->{content}; } elsif($tag eq 'title') { $title = $p->get_trimmed_text; } } return { title => $title, meta_title => $meta_title, }; } sub swfhash { my($browser, $url) = @_; $browser->get($url); return swfhash_data($browser->content, $url); } sub swfhash_data { my ($data, $url) = @_; die "Must have Compress::Zlib and Digest::SHA for this RTMP download\n" unless eval { require Compress::Zlib; require Digest::SHA; }; $data = "F" . substr($data, 1, 7) . Compress::Zlib::uncompress(substr $data, 8); return swfsize => length $data, swfhash => Digest::SHA::hmac_sha256_hex($data, FP_KEY), swfUrl => $url; } sub url_exists { my($browser, $url) = @_; $browser->head($url); my $response = $browser->response; debug "Exists on $url: " . $response->code; return $url if $response->code == 200; my $redirects = 0; while ( ($response->code =~ /^30\d/) and ($response->header('Location')) and ($redirects < MAX_REDIRECTS) ) { $url = URI->new_abs($response->header('Location'), $url); $response = $browser->head($url); debug "Redirected to $url (" . $response->code . ")"; if ($response->code == 200) { return $url; } $redirects++; } return ''; } sub title_to_filename { my($title, $type) = @_; if($title =~ s/(@{[EXTENSIONS]})$//) { $type = substr $1, 1; } elsif ($type && $type !~ /^\w+$/) { $type = substr((URI->new($type)->path =~ /(@{[EXTENSIONS]})$/)[0], 1); } $type ||= "flv"; utf8::upgrade($title); if ($title =~ /&(?:\w+|#(?:\d+|x[A-F0-9]+));/) { $title = decode_entities($title); } $title =~ s/\s+/_/g; $title =~ s/[^\w\-,()&]/_/g; $title =~ s/^_+|_+$//g; # underscores at the start and end look bad return get_video_filename($type) unless $title; return "$title.$type"; } sub get_video_filename { my($type) = @_; $type ||= "flv"; return "video" . get_timestamp_in_iso8601_format() . "." . $type; } sub get_timestamp_in_iso8601_format { use Time::localtime; my $time = localtime; return sprintf("%04d%02d%02d%02d%02d%02d", $time->year + 1900, $time->mon + 1, $time->mday, $time->hour, $time->min, $time->sec); } sub get_vlc_exe_from_registry { if ($^O !~ /MSWin/i) { die "Doesn't make sense to call this except on Windows"; } my $HAS_WIN32_REGISTRY = eval { require Win32::Registry }; die "Win32::Registry required for JustWorks(tm) playing on Windows" unless $HAS_WIN32_REGISTRY; require Win32::Registry; Win32::Registry->import(); my $local_machine; { no strict 'vars'; $local_machine = $::HKEY_LOCAL_MACHINE; } my $key = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'; $local_machine->Open($key, my $reg); my @applications; $reg->GetKeys(\@applications); my $vlc_binary; foreach my $application (@applications) { next unless $application =~ /VLC Media Player/i; $reg->Open($application, my $details); my %app_properties; $details->GetValues(\%app_properties); if ($app_properties{DisplayIcon}->[-1] =~ /\.exe$/i) { $vlc_binary = $app_properties{DisplayIcon}->[-1]; last; } } return $vlc_binary; } sub get_win_codepage { require Win32::API; if (! %Win32::API::Type::Known) { %Win32::API::Type::Known = (int => 'i'); } Win32::API->Import("kernel32", "int GetACP()"); return "cp" . GetACP(); } sub get_user_config_dir { return $^O =~ /MSWin/i ? ($ENV{APPDATA} || 'c:/windows/application data') . "/get_flash_videos" : "$ENV{HOME}/.get_flash_videos"; } sub is_program_on_path { my($program) = @_; my $win = $^O =~ /MSWin/i; for my $dir(split($win ? ";" : ":", $ENV{PATH})) { return 1 if -f "$dir/$program" . ($win ? ".exe" : ""); } return 0; } sub get_terminal_width { if(eval { require Term::ReadKey } && (my($width) = Term::ReadKey::GetTerminalSize())) { return $width - 1 if $^O =~ /MSWin|cygwin/i; # seems to be off by 1 on Windows return $width; } elsif($ENV{COLUMNS}) { return $ENV{COLUMNS}; } else { return 80; } } sub json_unescape { my($s) = @_; $s =~ s/\\u([0-9a-f]{1,4})/chr hex $1/ge; $s =~ s{(\\[\\/rnt"])}{"\"$1\""}gee; return $s; } sub convert_sami_subtitles_to_srt { my ($sami_subtitles, $filename, $decrypt_callback) = @_; die "SAMI subtitles must be provided" unless $sami_subtitles; die "Output SRT filename must be provided" unless $filename; $sami_subtitles =~ s/[\r\n]//g; # flatten my @lines = split /| |g; s|&|&|g; s{&(?:nbsp|#160);}{ }g; ($begin, $sub) = ($1, $2) if m{[^>]*Start="(.+?)"[^>]*>(.*?)<\/Sync>}i; if (/^\s*Encrypted="true"\s*/i) { if ($decrypt_callback and ref($decrypt_callback) eq 'CODE') { $sub = $decrypt_callback->($sub); } } $sub =~ s@&@&@g; $sub =~ s@(?:]*>| | )@ @g; $sub =~ s{]*?>}{}g; # remove

and similar $sub =~ s{<(/)?([BI])>}{"<$1" . lc($2) . ">"}eg; decode_entities($sub); # in void context, this works in place if ($sub and ($begin or $begin == 0)) { my $seconds = int( $begin / 1000.0 ); my $ms = $begin - ( $seconds * 1000.0 ); $begin = sprintf("%02d:%02d:%02d,%03d", (gmtime($seconds))[2,1,0], $ms ); $sub =~ s/^\s*(.*?)\s*$/$1/; $sub =~ s/\s{2,}/ /g; $sub =~ s|
|\n|ig; $sub =~ s/^\s*|\s*$//mg; if ($count and !$subtitles[$count - 1]->{end}) { $subtitles[$count - 1]->{end} = $begin; } if (!$sub or $sub =~ /^\s+$/) { if ($count) { $last_proper_sub_end_time = $subtitles[$count - 1]->{end}; } next; # this is not a meaningful subtitle } push @subtitles, { start => $begin, text => $sub, }; $count++; } } $subtitles[$count - 1]->{end} = $last_proper_sub_end_time; open my $subtitle_fh, '>', $filename or die "Can't open subtitles file $filename: $!"; binmode $subtitle_fh, ':utf8'; $count = 1; foreach my $subtitle (@subtitles) { print $subtitle_fh "$count\n$subtitle->{start} --> $subtitle->{end}\n" . "$subtitle->{text}\n\n"; $count++; } close $subtitle_fh; return 1; } sub from_xml { my($xml, @args) = @_; if(!eval { require XML::Simple && XML::Simple::XMLin("") }) { die "Must have XML::Simple to download " . caller =~ /::([^:])+$/ . " videos\n"; } $xml = eval { XML::Simple::XMLin(ref $xml eq 'SCALAR' ? $xml : ref $xml ? $xml->content : $xml, @args); }; if($@) { die "$@ (from ", join("::", caller), ")\n"; } return $xml; } 1; } ##} blib/lib/FlashVideo/Utils.pm ##{ blib/lib/FlashVideo/Site/4od.pm { package FlashVideo::Site::4od; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *from_xml = \&FlashVideo::Utils::from_xml; } use URI::Escape; sub search { my ($self, $search, $type) = @_; unless(eval { from_xml("new(); $browser->get($search_url); if (!$browser->success) { die "Couldn't get YouTube search Atom XML: " . $browser->response->status_line(); } my $xml = from_xml($browser, KeyAttr => [], ForceArray => ['entry']); my @matches = map { _process_4od_result($_) } grep { $_->{author}->{name} =~ /^4oD\w+$/i } @{ $xml->{entry} }; return @matches; } sub _process_4od_result { my $feed_entry = shift; my $url = $feed_entry->{'media:group'}->{'media:player'}->{url}; $url =~ s/&feature=youtube_gdata//; my $published_date = $feed_entry->{published}; $published_date =~ s/T.*$//; # only care about date, not time my $title = $feed_entry->{'media:group'}->{'media:title'}->{content}; my $description = $feed_entry->{'media:group'}->{'media:description'}->{content}; my $result_name = "$title ($published_date)"; return { name => $result_name, url => $url, description => $description }; } 1; } ##} blib/lib/FlashVideo/Site/4od.pm BEGIN { $INC{'FlashVideo/Site/5min.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/5min.pm { package FlashVideo::Site::5min; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *info = \&FlashVideo::Utils::info; *extract_info = \&FlashVideo::Utils::extract_info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } sub find_video { my ($self, $browser) = @_; my $filename = title_to_filename(extract_info($browser)->{meta_title}); my $url = (FlashVideo::Generic->find_video($browser, $browser->uri))[0]; return $url, $filename; } 1; } ##} blib/lib/FlashVideo/Site/5min.pm BEGIN { $INC{'FlashVideo/Site/Abc.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Abc.pm { package FlashVideo::Site::Abc; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *from_xml = \&FlashVideo::Utils::from_xml; } sub find_video { my ($self, $browser, $embed_url) = @_; if ($browser->uri->as_string =~ m'/watch/clip/[\w\-]+/(\w+)/(\w+)/(\w+)') { my $show_id = $1; my $playlist_id = $2; my $video_id = $3; return handle_abc_clip($browser, $show_id, $playlist_id, $video_id); } my $video_id; if ($browser->uri->as_string =~ /\/watch\/[^\/]*\/[0-9]*\/([0-9]*)/) { $video_id = $1; } my $quality="432"; $browser->get("http://ll.static.abc.com/s/videoplatform/services/1001/getflashvideo?video=$video_id&h=$quality"); my $xml = from_xml($browser, KeyAttr => []); my $hosts = $xml->{resources}->{host}; my $host = ref $hosts eq 'ARRAY' ? (grep { $_->{name} == 'L3' } @$hosts)[0] : $hosts; my $rtmpurl = $xml->{protocol} . "://" . $host->{url} . "/" . $host->{app}; my $videos = $xml->{videos}->{video}; my $video = ref $videos eq 'ARRAY' ? (grep { $_->{src} =~ /^mp4:\// } @$videos)[0] : $videos; my $playpath = $video->{src}; $browser->get("http://ll.static.abc.com/s/videoplatform/services/1000/getVideoDetails?video=$video_id"); my $xml = from_xml($browser); my $title = $xml->{metadata}->{title}; return { rtmp => $rtmpurl, playpath => $playpath, flv => title_to_filename($title) }; } sub handle_abc_clip { my ($browser, $show_id, $playlist_id, $video_id) = @_; my $abc_clip_rss_url_template = "http://ll.static.abc.com/vp2/ws/s/contents/1000/videomrss?" . "brand=001&device=001&width=644&height=362&clipId=%s" . "&start=0&limit=1&fk=CATEGORIES&fv=%s"; my $abc_clip_rss_url = sprintf $abc_clip_rss_url_template, $video_id, $playlist_id; $browser->get($abc_clip_rss_url); if (!$browser->success) { die "Couldn't download ABC clip RSS: " . $browser->response->status_line; } my $xml = from_xml($browser); my $video_url = $xml->{channel}->{item}->{'media:content'}->{url}; my $type = $video_url =~ /\.mp4$/ ? 'mp4' : 'flv'; if (!$video_url) { die "Couldn't determine ABC clip URL"; } my $episode_name; if ($video_url =~ /FLF_\d+[A-Za-z]{0,5}_([^_]+)/) { $episode_name = $1; } my $category = $xml->{channel}->{item}->{category}; my $title = $xml->{channel}->{item}->{'media:title'}->{content}; if (ref($category) eq 'HASH' and ! keys %$category) { $category = ''; } my $description = $xml->{channel}->{item}->{'media:description'}->{content}; for ($category, $description, $title) { s/<\/?\w+>//g; } my $video_title = make_title($category, $episode_name, $title, $description); return $video_url, title_to_filename($video_title, $type); } sub make_title { return join " - ", grep /./, @_; } sub can_handle { my($self, $browser, $url) = @_; return $url && URI->new($url)->host =~ /\babc\.(?:go\.)?com$/; } 1; } ##} blib/lib/FlashVideo/Site/Abc.pm BEGIN { $INC{'FlashVideo/Site/About.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. BEGIN { $INC{'FlashVideo/Site/Brightcove.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Brightcove.pm { package FlashVideo::Site::Brightcove; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *info = \&FlashVideo::Utils::info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } use MIME::Base64; sub find_video { my ($self, $browser, $embed_url) = @_; my $metadata = { }; my ($video_id, $player_id); $video_id = ($browser->content =~ /(?:clip|video)Id["'\] ]*[:=]["' ]*(\d+)/i)[0]; $player_id = ($browser->content =~ /playerId["'\] ]*[:=]["' ]*(\d+)/i)[0]; $player_id ||= ($browser->content =~ /content =~ /content =~ /flashVars.*playerID=(\d+)/i)[0]; $video_id ||= ($browser->content =~ /flashVars.*video(?:Player|ID)=(\d+)/i)[0]; if(!$player_id && $browser->content =~ /brightcove.player.create\(['"]?(\d+)['"]?,\s*['"]?(\d+)/) { $video_id = $1; $player_id = $2; } for my $url($browser->uri->as_string, $embed_url) { if($url =~ /(?:videoID|bctid)=?(\d+)/i) { $video_id ||= $1; } if($url =~ /(?:playerID|bcpid)=?(\d+)/i) { $player_id ||= $1; } if($url =~ /(?:lineupID|bclid)=?(\d+)/i) { $metadata->{lineupId} ||= $1; } } debug "Extracted playerId: $player_id, videoId: $video_id, lineupID: $metadata->{lineupId}" if $player_id or $video_id; die "Unable to extract Brightcove IDs from page" unless $player_id; $metadata->{videoId} = $video_id; return $self->amfgateway($browser, $player_id, $metadata); } sub amfgateway { my($self, $browser, $player_id, $metadata) = @_; my $has_amf_packet = eval { require Data::AMF::Packet }; if (!$has_amf_packet) { die "Must have Data::AMF::Packet installed to download Brightcove videos"; } my $page_url = $browser->uri; my $packet = Data::AMF::Packet->deserialize(decode_base64(<messages->[0]->{value}->[0] = "$player_id"; } if (ref $metadata) { for(keys %$metadata) { $packet->messages->[0]->{value}->[1]->{$_} = "$metadata->{$_}"; } } my $data = $packet->serialize; $browser->post( "http://c.brightcove.com/services/amfgateway", Content_Type => "application/x-amf", Content => $data ); die "Failed to post to Brightcove AMF gateway" unless $browser->response->is_success; $packet = Data::AMF::Packet->deserialize($browser->content); if($self->debug) { require Data::Dumper; debug Data::Dumper::Dumper($packet); } if(ref $packet->messages->[0]->{value} ne 'ARRAY') { die "Unexpected data from AMF gateway"; } my @found; for (@{$packet->messages->[0]->{value}}) { if ($_->{data}->{videoDTO}) { push @found, $_->{data}->{videoDTO}; } if ($_->{data}->{videoDTOs}) { push @found, @{$_->{data}->{videoDTOs}}; } } my @rtmpdump_commands; for my $d (@found) { next if $metadata->{videoId} && $d->{id} != $metadata->{videoId}; my $host = ($d->{FLVFullLengthURL} =~ m!rtmp://(.*?)/!)[0]; my $file = ($d->{FLVFullLengthURL} =~ m!&([a-z0-9:]+/.*?)(?:&|$)!)[0]; my $app = ($d->{FLVFullLengthURL} =~ m!//.*?/(.*?)/&!)[0]; my $filename = ($d->{FLVFullLengthURL} =~ m!&.*?/([^/&]+)(?:&|$)!)[0]; $app .= "?videoId=$d->{id}&lineUpId=$d->{lineupId}&pubId=$d->{publisherId}&playerId=$player_id&playerTag=&affiliateId="; my $args = { app => $app, pageUrl => $page_url, swfUrl => "http://admin.brightcove.com/viewer/federated/f_012.swf?bn=590&pubId=$d->{publisherId}", tcUrl => "rtmp://$host:1935/$app", auth => ($d->{FLVFullLengthURL} =~ /^[^&]+&(.*)$/)[0], rtmp => "rtmp://$host/$app", playpath => $file, flv => "$filename.flv", }; if ($d->{publisherName} and $d->{displayName}) { $args->{flv} = title_to_filename("$d->{publisherName} - $d->{displayName}"); } if (!$d->{FLVFullLengthStreamed}) { info "Brightcove HTTP download detected"; return ($d->{FLVFullLengthURL}, $args->{flv}); } push @rtmpdump_commands, $args; } if (@rtmpdump_commands > 1) { return \@rtmpdump_commands; } else { return $rtmpdump_commands[-1]; } } sub can_handle { my($self, $browser, $url) = @_; return 1 if $url && URI->new($url)->host =~ /\.brightcove\.com$/; return $browser->content =~ /(playerI[dD]|brightcove.player.create)/ && $browser->content =~ /brightcove/i; } 1; } ##} blib/lib/FlashVideo/Site/Brightcove.pm ##{ blib/lib/FlashVideo/Site/About.pm { package FlashVideo::Site::About; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; } BEGIN { FlashVideo::Site::Brightcove->import(); } # (added by utils/combine-perl.pl) use base 'FlashVideo::Site::Brightcove'; my $JS_RE = qr/vdo_None\.js/; sub find_video { my($self, $browser, $embed_url) = @_; my($video_ref) = $browser->content =~ /zIvdoId=["']([^"']+)/; die "Unable to extract video ref" unless $video_ref; my($js_src) = $browser->content =~ /["']([^"']+$JS_RE)/; $browser->get($js_src); my($player_id) = $browser->content =~ /playerId.*?(\d+)/; die "Unable to extract playerId" unless $player_id; return $self->amfgateway($browser, $player_id, { videoRefId => $video_ref }); } sub can_handle { my($self, $browser, $url) = @_; return $browser->content =~ $JS_RE; } 1; } ##} blib/lib/FlashVideo/Site/About.pm BEGIN { $INC{'FlashVideo/Site/Amazon.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Amazon.pm { package FlashVideo::Site::Amazon; use strict; use Encode; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *from_xml = \&FlashVideo::Utils::from_xml; } use URI::Escape; my $playlist_url_template = 'http://%s/gp/mpd/getplaylist-v2/%s/%s'; sub find_video { my ($self, $browser) = @_; my $amazon_host = $browser->uri()->host(); if ($browser->content =~ /swfParams\.xmlUrl = ["'](http:.*?)["']/) { debug "Getting Amazon URL direct URL $1"; $browser->get($1); } else { my ($video_id, $session_id); if ($browser->content =~ /swfParams\.mediaObjectId = ["'](.*?)["']/) { $video_id = $1; } else { die "Couldn't find video ID / media object ID in Amazon page"; } if ($browser->content =~ /swfParams\.sessionId = ["'](.*?)["']/) { $session_id = $1; } else { die "Couldn't find session ID in Amazon page"; } my $playlist_url = sprintf($playlist_url_template, $amazon_host, $video_id, $session_id); $browser->get($playlist_url); } my ($title, @video_urls) = parse_smil_like_xml($browser->content); my $filename = title_to_filename($title); return $video_urls[0], $filename; } sub parse_smil_like_xml { my $smil = shift; my $parsed_smil = from_xml($smil); my $title; my $video_ref = $parsed_smil->{videoObject}->{smil}->{body}->{switch}->{video}; if (ref($video_ref) ne 'ARRAY') { my $id; my %videos = %{ $parsed_smil->{videoObject} }; foreach my $video (keys %videos) { next unless ref $videos{$video}; if ($videos{$video}->{index} == 0) { $id = $video; $title = $videos{$video}->{title}; last; } } $video_ref = $parsed_smil->{videoObject}->{$id}->{smil}->{body}->{switch}->{video}; } my @different_quality_videos = map { $_->{src} } sort { $b->{'system-bitrate'} <=> $a->{'system-bitrate'} } @$video_ref; $title ||= $parsed_smil->{videoObject}->{title}; if ($title !~ /\s/) { $title = uri_unescape($title); } return ($title, @different_quality_videos); } 1; } ##} blib/lib/FlashVideo/Site/Amazon.pm BEGIN { $INC{'FlashVideo/Site/Aniboom.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Aniboom.pm { package FlashVideo::Site::Aniboom; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } sub find_video { my ($self, $browser, $embed_url) = @_; my ($id, $url, $title); if ($browser->uri->as_string =~ /\/animation-video\/(\d*)\/([^\/]*)/) { $id = $1; $title = $2; $title =~ s/-/ /g; } else { die "Could not detect video ID!"; } $browser->get("http://www.aniboom.com/animations/player/handlers/animationDetails.aspx?mode=&movieid=$id"); if ($browser->content =~ /(?:mp4|flv)=([^&]*)/) { $url = $1; $url =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; } else { die "Could not get flv/mp4 location!"; } return $url, title_to_filename($title); } 1; } ##} blib/lib/FlashVideo/Site/Aniboom.pm BEGIN { $INC{'FlashVideo/Site/Apple.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Apple.pm { package FlashVideo::Site::Apple; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; } sub find_video { my ($self, $browser, $embed_url, $prefs) = @_; if(!FlashVideo::Downloader->check_file($browser->content)) { my @urls = $browser->content =~ /['"]([^'"]+\.mov)(?:\?[^'"]+)?['"]/g; die "No .mov URLs found on page" unless @urls; debug "Found URLs: @urls"; my $redirect_url = $prefs->quality->choose(map { /(\d+p?)\.mov/ && { url => $_, resolution => $prefs->quality->format_to_resolution($1) } } @urls )->{url}; $browser->get($redirect_url); } my $url = $self->handle_mov($browser); my $filename = ($url->path =~ m{([^/]+)$})[0]; return $url, $filename; } sub handle_mov { my ($self, $browser) = @_; $browser->agent("Apple iPhone OS v2.0.1 CoreMedia v1.0.0.5B108"); if($browser->content =~ /url\s*\0+[\1-,]*(.*?)\0/) { return URI->new_abs($1, $browser->uri) } else { die "Cannot find link in .mov"; } } sub can_handle { my($self, $browser, $url) = @_; return $url =~ m{apple\.com/trailers/} || $url =~ m{movies\.apple\.com}; } 1; } ##} blib/lib/FlashVideo/Site/Apple.pm BEGIN { $INC{'FlashVideo/Site/Arte.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Arte.pm { package FlashVideo::Site::Arte; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } sub find_video { my ($self, $browser, $embed_url) = @_; my ($lang, $xmlurl1, $xmlurl2, $filename, $videourl, $hash, $playerurl); debug "Arte::find_video called, embed_url = \"$embed_url\"\n"; my $pageurl = $browser->uri() . ""; if($pageurl =~ /videos\.arte\.tv\/(..)\//) { $lang = $1; } else { die "Unable to find language in original URL \"$pageurl\"\n"; } if($browser->content =~ /videorefFileUrl = "(.*)";/) { $xmlurl1 = $1; debug "found videorefFileUrl \"$xmlurl1\"\n"; ($filename = $xmlurl1) =~ s/-.*$//; $filename =~ s/^.*\///g; $filename = title_to_filename($filename); } else { die "Unable to find 'videorefFileUrl' in page\n"; } if($browser->content =~ /get($xmlurl1); if($browser->content =~ /

([^<]+)/; die "No video ID found" unless $id; $browser->get("http://ext.last.fm/1.0/video/getplaylist.php?&vid=$id&artist=$artist"); return $browser->content =~ /([^<]+)/, title_to_filename($title); } sub can_handle { my($self, $browser, $url) = @_; return $url =~ /last\.fm/ && $url =~ m{\+video/\d{2,}}; } 1; } ##} blib/lib/FlashVideo/Site/Last.pm BEGIN { $INC{'FlashVideo/Site/Liveleak.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Liveleak.pm { package FlashVideo::Site::Liveleak; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *extract_title = \&FlashVideo::Utils::extract_title; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } sub find_video { my ($self, $browser, $embed_url) = @_; my $file_embed_tag; if ($browser->content =~ /file_embed_tag(?:%3D|=)(\w+)\W/) { $file_embed_tag = $1; } else { die "Unable to get file_embed_tag"; } $browser->get("http://www.liveleak.com/playlist_new.php?file_embed_tag=$file_embed_tag"); if (!$browser->success) { die "Couldn't download LiveLeak playlist: " . $browser->response->status_line(); } my $video_url; if ($browser->content =~ m'(http://.*?)') { $video_url = $1; } else { die "Unable to extract LiveLeak video URL"; } if (my $redirected_url = $browser->head($video_url)->header('Location')) { $video_url = $redirected_url; } $browser->back(); my $title; if ($browser->content =~ m'

(.*?)

') { $title = $1; } else { $title = extract_title($browser); } return $video_url, title_to_filename($title); } 1; } ##} blib/lib/FlashVideo/Site/Liveleak.pm BEGIN { $INC{'FlashVideo/Site/Megaporn.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. BEGIN { $INC{'FlashVideo/Site/Megavideo.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Megavideo.pm { package FlashVideo::Site::Megavideo; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *info = \&FlashVideo::Utils::info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } use URI::Escape; my %sites = ( Megavideo => "megavideo.com", Megaporn => "megaporn.com/video", ); sub find_video { my ($self, $browser) = @_; my $site = $sites{($self =~ /::([^:]+)$/)[0]}; my $v; if ($browser->content =~ /\.v\s*=\s*['"]([^"']+)/ || $browser->uri =~ /v=([^&]+)/ || $browser->response->header("Location") =~ /v=([^&]+)/) { $v = $1; } else { die "Couldn't extract video ID from page"; } my $xml = "http://www.$site/xml/videolink.php?v=$v"; $browser->get($xml); die "Unable to get video infomation" unless $browser->response->is_success; my $k1 = ($browser->content =~ /k1="(\d+)/)[0]; my $k2 = ($browser->content =~ /k2="(\d+)/)[0]; my $un = ($browser->content =~ /un="([^"]+)/)[0]; my $s = ($browser->content =~ /\ss="(\d+)/)[0]; my $title = uri_unescape(($browser->content =~ /title="([^"]+)/)[0]); my $filename = title_to_filename($title); my $url = "http://www$s.$site/files/" . _decrypt($un, $k1, $k2) . "/"; return $url, $filename; } sub _decrypt { my($un, $k1, $k2) = @_; my @c = split //, join "", map { substr unpack("B8", pack "h", $_), 4 } split //, $un; my @iv; my $i = 0; while($i < 384) { $k1 = ($k1 * 11 + 77213) % 81371; $k2 = ($k2 * 17 + 92717) % 192811; $iv[$i] = ($k1 + $k2) % 128; $i++; } $i = 256; while($i >= 0) { my $a = $iv[$i]; my $b = $i-- % 128; ($c[$a], $c[$b]) = ($c[$b], $c[$a]); } $i = 0; while($i < 128) { $c[$i] ^= $iv[$i + 256] & 1; $i++; } $i = 0; my $c = ""; while($i < @c) { $c .= unpack "h", pack "B8", "0000" . join "", @c[$i .. ($i + 4)]; $i += 4; } return $c; } 1; } ##} blib/lib/FlashVideo/Site/Megavideo.pm ##{ blib/lib/FlashVideo/Site/Megaporn.pm { package FlashVideo::Site::Megaporn; use strict; BEGIN { FlashVideo::Site::Megavideo->import(); } # (added by utils/combine-perl.pl) use base 'FlashVideo::Site::Megavideo'; 1; } ##} blib/lib/FlashVideo/Site/Megaporn.pm BEGIN { $INC{'FlashVideo/Site/Metacafe.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Metacafe.pm { package FlashVideo::Site::Metacafe; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *extract_title = \&FlashVideo::Utils::extract_title; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } use URI::Escape; sub find_video { my ($self, $browser) = @_; if ($browser->response->header("Location") =~ /Openx/) { my $filter = "http://www.metacafe.com/f/index.php?inputType=filter&controllerGroup=user&filters=0&prevURL=" . $browser->uri->path; debug "Disabling family filter by getting $filter"; $browser->allow_redirects; $browser->get($filter); } my $url; if ($browser->content =~ m'mediaURL=(http.+?)&') { $url = uri_unescape($1); } else { die "Couldn't find mediaURL parameter."; } if ($browser->content =~ m'gdaKey=(.+?)&') { $url .= "?__gda__=" . uri_unescape($1); } else { } my $filename = title_to_filename(extract_title($browser)); return ($url, $filename); } 1; } ##} blib/lib/FlashVideo/Site/Metacafe.pm BEGIN { $INC{'FlashVideo/Site/Mitworld.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Mitworld.pm { package FlashVideo::Site::Mitworld; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *extract_title = \&FlashVideo::Utils::extract_title; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } sub find_video { my ($self, $browser) = @_; my($title) = $browser->content =~ m{id="video-meta">\s*

(.*?)

}s; if(!$title) { $title = extract_title($browser); $title =~ s/\|.*//; } my($host) = $browser->content =~ m{host:\s*"(.*?)"}; my($flv) = $browser->content =~ m{flv:\s*"(.*?)"}; return { rtmp => "rtmp://$host/ondemand/ampsflash/$flv?_fcs_vhost=$host", flv => title_to_filename($title) }; } 1; } ##} blib/lib/FlashVideo/Site/Mitworld.pm BEGIN { $INC{'FlashVideo/Site/Mofosex.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Mofosex.pm { package FlashVideo::Site::Mofosex; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } sub find_video { my ($self, $browser, $embed_url) = @_; my $filename = title_to_filename($browser->content =~ /(.*?)<\//); $browser->allow_redirects; my $playlist = ($browser->content =~ /videoPath=(.+?)%26page/)[0]; $browser->get($playlist); my $url = ($browser->content =~ /<url>(.+?)<\/url>/)[0]; return $url, $filename; } 1; } ##} blib/lib/FlashVideo/Site/Mofosex.pm BEGIN { $INC{'FlashVideo/Site/Msnbc.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Msnbc.pm { package FlashVideo::Site::Msnbc; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *from_xml = \&FlashVideo::Utils::from_xml; } sub find_video { my ($self, $browser, $embed_url) = @_; $browser->allow_redirects; my $id; my $location; if ($embed_url =~ /(.+\/id\/)([0-9]+)\/vp\/.+#([0-9]+)/) { $location = $1; $id = $3; } elsif ($embed_url =~ /(.+\/id\/)([0-9]+)\/vp\/([0-9]+)/) { $location = $1; $id = $3; } elsif ($embed_url =~ /(.+\/id\/)([0-9]+)\/.+#([0-9]+)/) { $location = $1; $id = $3; } elsif ($embed_url =~ /(.+\/id\/)([0-9]+)\/#([0-9]+)/) { $location = $1; $id = $3; } die "Unable to find location and videoid" unless $location and $id; $browser->get($location . $id . '/displaymode/1219/'); # http://today.msnbc.msn.com/id/$id/displaymode/1219/ my $xml = from_xml($browser->content); my $title; my $url; if ($xml->{video}->{docid} eq $id) { $title = $xml->{video}->{title}; foreach my $media (@{$xml->{video}->{media}}) { if ($media->{type} =~ /flashVideo$/i) { $url = $media->{content}; last; #prefer http get over rtmp } elsif ($media->{type} =~ /flashVideoStream$/i) { $browser->get($media->{content}); if ($browser->content =~ /<FlashLink>(.+)<\/FlashLink>/i) { $url = $1; #rtmp } } } } die "Unable to extract video url" unless $url; if ($url =~ /^rtmp/i) { return { rtmp => $url, flv => title_to_filename($title) }; } return $url, title_to_filename($title); } 1; } ##} blib/lib/FlashVideo/Site/Msnbc.pm BEGIN { $INC{'FlashVideo/Site/Msn.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Msn.pm { package FlashVideo::Site::Msn; use strict; BEGIN { FlashVideo::Site::Bing->import(); } # (added by utils/combine-perl.pl) use base 'FlashVideo::Site::Bing'; 1; } ##} blib/lib/FlashVideo/Site/Msn.pm BEGIN { $INC{'FlashVideo/Site/Mtvnservices.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Mtvnservices.pm { package FlashVideo::Site::Mtvnservices; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *swfhash = \&FlashVideo::Utils::swfhash; *from_xml = \&FlashVideo::Utils::from_xml; } use URI::Escape; my $MTVN_URL = qr{http://\w+.mtvnservices.com/(?:\w+/)?mgid:[a-z0-9:.\-_]+}; sub find_video { my ($self, $browser, $embed_url) = @_; my $page_url = $browser->uri->as_string; if($embed_url !~ $MTVN_URL) { if($browser->content =~ m!($MTVN_URL)!) { $embed_url = $1; } else { die "Unable to find embedding URL"; } } $browser->get($embed_url); die "Unable to get embed URL" unless $browser->response->code =~ /^30\d$/; my %param; my $location = $browser->response->header("Location"); for(split /&/, (split /\?/, $location)[-1]) { my($n, $v) = split /=/; $param{$n} = uri_unescape($v); } die "No config_url/id found\n" unless $param{CONFIG_URL}; $browser->get($param{CONFIG_URL}); my $xml = from_xml($browser); if($xml->{player}->{feed} && !ref $xml->{player}->{feed}) { my $feed = uri_unescape($xml->{player}->{feed}); $feed =~ s/\{([^}]+)\}/$param{$1}/g; $browser->get($feed); return $self->handle_feed($browser->content, $browser, $page_url, $param{uri}); } elsif(ref $xml->{player}->{feed}->{rss}) { return $self->handle_feed($xml->{player}->{feed}->{rss}, $browser, $page_url, $param{uri}); } else { die "Unable to find feed\n"; } } sub handle_full_episode { my($self, $items, $filename, $browser, $page_url, $uri) = @_; my @rtmpdump_commands; foreach (@$items) { my $item = $_; my $isepisodesegment = ref $item->{"media:group"}->{"media:category"} eq 'ARRAY' ? (grep { $_->{scheme} eq "urn:mtvn:playlist_uri" } @{$item->{"media:group"}->{"media:category"}})[0]->{content} : $item->{"media:group"}->{"media:category"}->{content} eq $uri; my $affect_counters = (grep { $_->{scheme} eq "urn:mtvn:affect_counters" } @{$item->{"media:group"}->{"media:category"}})[0]; my $iscommercial = 0; if (defined $affect_counters && $affect_counters->{content} eq 'false') { $iscommercial = 1; } if ($isepisodesegment && !$iscommercial) { my $mediagen_url = $item->{"media:group"}->{"media:content"}->{url}; die "Unable to find mediagen URL\n" unless $mediagen_url; $browser->get($mediagen_url); my $xml = from_xml($browser); my $rendition = (grep { $_->{rendition} } ref $xml->{video}->{item} eq 'ARRAY' ? @{$xml->{video}->{item}} : $xml->{video}->{item})[0]->{rendition}; $rendition = [ $rendition ] unless ref $rendition eq 'ARRAY'; my $url = (sort { $b->{bitrate} <=> $a->{bitrate} } @$rendition)[0]->{src}; $browser->allow_redirects; push @rtmpdump_commands, { flv => title_to_filename($item->{"media:group"}->{"media:title"}), rtmp => $url, pageUrl => $item->{"link"}, swfhash($browser, "http://media.mtvnservices.com/player/release/") }; } } return \@rtmpdump_commands; } sub handle_feed { my($self, $feed, $browser, $page_url, $uri) = @_; my $xml = ref $feed ? $feed : from_xml($feed); my $filename = title_to_filename($xml->{channel}->{title}); my $items = $xml->{channel}->{item}; my $categories = ref $items eq 'ARRAY' ? @$items[0]->{"media:group"}->{"media:category"} : @$items->{"media:group"}->{"media:category"}; if (ref $categories eq 'ARRAY' && (grep { $_->{scheme} eq "urn:mtvn:content_type" } @$categories)[0]->{content} eq "full_episode_segment") { return $self->handle_full_episode($items, $filename, $browser, $page_url, $uri); } my $item = ref $items eq 'ARRAY' ? (grep { $_->{guid}->{content} eq $uri } @$items)[0] : $items; my $mediagen_url = $item->{"media:group"}->{"media:content"}->{url}; die "Unable to find mediagen URL\n" unless $mediagen_url; $browser->get($mediagen_url); $xml = from_xml($browser); my $rendition = (grep { $_->{rendition} } ref $xml->{video}->{item} eq 'ARRAY' ? @{$xml->{video}->{item}} : $xml->{video}->{item})[0]->{rendition}; $rendition = [ $rendition ] unless ref $rendition eq 'ARRAY'; my $url = (sort { $b->{bitrate} <=> $a->{bitrate} } @$rendition)[0]->{src}; $browser->allow_redirects; if($url =~ /^rtmpe?:/) { return { flv => $filename, rtmp => $url, pageUrl => $page_url, swfhash($browser, "http://media.mtvnservices.com/player/release/") }; } return $url, $filename; } sub can_handle { my($self, $browser) = @_; return $browser->content =~ /mtvnservices\.com/i; } 1; } ##} blib/lib/FlashVideo/Site/Mtvnservices.pm BEGIN { $INC{'FlashVideo/Site/Muzu.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Muzu.pm { package FlashVideo::Site::Muzu; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *error = \&FlashVideo::Utils::error; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } use HTML::Entities; sub find_video { my ($self, $browser) = @_; if ($browser->response->code == 302) { $browser->allow_redirects; $browser->get($browser->response->header('Location')) } $browser->content =~ /id="trackHeading">(.*?)</; my $title = $1; if (!$title) { $browser->content =~ /id="videosPageMainTitleH1">(.*?)</s; $title = $1; } my $filename = title_to_filename(decode_entities($title)); my $flashvars = ($browser->content =~ m'flashvars:(?:\s+getPlayerData\(\)\s+\+\s+)?"([^"]+)')[0]; die "Unable to extract flashvars" unless $flashvars; my %map = ( networkId => "id", assetId => "assetId", vidId => "assetId", startChannel => "playlistId", ); my $playAsset = "http://www.muzu.tv/player/playAsset/?"; for(split /&/, $flashvars) { my($n, $v) = split /=/; $playAsset .= "$map{$n}=$v&" if exists $map{$n}; } $browser->get($playAsset); die "Unable to get $playAsset" if $browser->response->is_error; my $url = ($browser->content =~ /src="([^"]+)/)[0]; $url = decode_entities($url); die "Unable to find video URL" unless $url; return $url, $filename; } 1; } ##} blib/lib/FlashVideo/Site/Muzu.pm BEGIN { $INC{'FlashVideo/Site/Mylifetime.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Mylifetime.pm { package FlashVideo::Site::Mylifetime; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; } BEGIN { FlashVideo::Site::Brightcove->import(); } # (added by utils/combine-perl.pl) use base 'FlashVideo::Site::Brightcove'; my $JS_RE = qr/displayFlash\(/; sub find_video { my($self, $browser, $embed_url) = @_; my($player_id, $video_id) = $browser->content =~ /$JS_RE\s*"(\d+)",\s*"(\d+)"/; die "Unable to extract video ids" unless $video_id; return $self->amfgateway($browser, $player_id, { videoId => $video_id }); } sub can_handle { my($self, $browser, $url) = @_; return $browser->content =~ $JS_RE; } 1; } ##} blib/lib/FlashVideo/Site/Mylifetime.pm BEGIN { $INC{'FlashVideo/Site/Myvideo.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Myvideo.pm { package FlashVideo::Site::Myvideo; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } sub find_video { my ($self, $browser, $embed_url) = @_; my $video_url; if ($browser->content =~ m{<link rel='image_src' href='(http://[^'"]+)'}) { $video_url = $1; } $video_url =~ s|thumbs/||; $video_url =~ s|_\d\.jpg$|.flv|; my $title = (split /\//, $browser->uri->as_string)[-1]; return $video_url, title_to_filename($title); } 1; } ##} blib/lib/FlashVideo/Site/Myvideo.pm BEGIN { $INC{'FlashVideo/Site/Nbc.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Nbc.pm { package FlashVideo::Site::Nbc; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *from_xml = \&FlashVideo::Utils::from_xml; } use MIME::Base64; sub find_video { my ($self, $browser, $embed_url) = @_; my $has_amf_packet = eval { require Data::AMF::Packet }; if (!$has_amf_packet) { die "Must have Data::AMF::Packet installed to download Nbc videos"; } my $video_id; if ($browser->uri->as_string =~ /\/([0-9]+)\//) { $video_id = $1; } my $packet = Data::AMF::Packet->deserialize(decode_base64("AAAAAAABABZnZXRDbGlwSW5mby5nZXRDbGlwQWxsAAIvMQAAAB8KAAAABAIABzEyMjc2MTECAAJVUwIAAzYzMgIAAi0xCg==")); $packet->messages->[0]->{value}->[0] = $video_id; if($self->debug) { require Data::Dumper; debug Data::Dumper::Dumper($packet); } my $data = $packet->serialize; $browser->post( "http://video.nbcuni.com/amfphp/gateway.php", Content_Type => "application/x-amf", Content => $data ); die "Failed to post to Nbc AMF gateway" unless $browser->response->is_success; debug $browser->content; my($clipurl) = $browser->content =~ /clipurl.{0,5}(nbc[^\0]+)/; my($title) = $browser->content =~ /headline.{1,3}([^\0]+)/; debug $clipurl; debug $title; $browser->get("http://video.nbcuni.com/$clipurl"); my $xml = from_xml($browser); my $video_path = $xml->{body}->{switch}->{ref}->{src}; $browser->get("http://videoservices.nbcuni.com/player/config?configId=17010&clear=true"); # I don't know what configId means but it seems to be generic my $xml = from_xml($browser); my $app = $xml->{akamaiAppName}; my $host = $xml->{akamaiHostName}; $browser->get("http://$host/fcs/ident"); my $xml = from_xml($browser); my $ip = $xml->{ip}; my $port = "1935"; my $rtmpurl = "rtmp://$ip:$port/$app/$video_path"; return { rtmp => $rtmpurl, swfUrl => "http://www.nbc.com/[[IMPORT]]/video.nbcuni.com/outlet/extensions/inext_video_player/video_player_extension.swf?4.5.3", tcUrl => "rtmp://$ip:$port/$app?_fcs_vhost=$host", flv => title_to_filename($title) }; } 1; } ##} blib/lib/FlashVideo/Site/Nbc.pm BEGIN { $INC{'FlashVideo/Site/Nfb.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ################################################# # # This file was automatically generated by utils/combine-perl.pl # You should edit the original files, not this # combined version. # # The original files are available at: # http://github.com/monsieurvideo/get-flash-videos # ################################################# # Except the CCR bits, thanks to Fogerty for those. ##{ blib/lib/FlashVideo/Site/Nfb.pm { package FlashVideo::Site::Nfb; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *error = \&FlashVideo::Utils::error; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } use MIME::Base64; sub find_video { my ($self, $browser) = @_; my($mid) = $browser->content =~ /mID=(\w+)/; if (!eval { require Data::AMF::Packet; }) { die "Must have Data::AMF installed to download NFB videos"; } my $packet = decode_base64(<<EOF); AAAAAAADABFnZXRfbW92aWVfcGFja2FnZQACLzEAAAAiCgAAAAMCAAVBREFBUwIACElET0JKMjYw AgAHZGVmYXVsdAAJc2V0X3N0YXRzAAIvMgAAAEkKAAAAAwIAC3Rlc3RfZmxpZ2h0AgAISURPQkoy NjACAChpbmZvczogZmxhc2hQbGF5ZXJWZXJzaW9uPUxOWCAxMCwwLDMyLDE4AAlzZXRfc3RhdHMA Ai8zAAAASQoAAAADAgALdGVzdF9mbGlnaHQCAAhJRE9CSjI2MAIAKGluZm9zIDpzY3JlZW5SZXNv bHV0aW9uPTEwMjQsNzY4LCBkcGk9OTY= EOF my $data = Data::AMF::Packet->new->deserialize($packet); $data->messages->[0]->{value}->[1] = $data->messages->[1]->{value}->[1] = $mid; $data = $data->serialize; $browser->post( "http://www.nfb.ca/gwplayer/", Content_Type => "application/x-amf", Content => $data, ); if (!$browser->success) { die "Posting AMF to NFB failed: " . $browser->response->status_line(); } $data = $browser->content; my($title) = $data =~ m'title.{3}([^\0]+)'; my @rtmp_urls = sort { _get_quality_from_url($b) <=> _get_quality_from_url($a) } ($data =~ m'(rtmp://.*?)\0'g); if (!@rtmp_urls) { die "Didn't find any rtmp URLs in the packet, our hacky 'parsing' " . "code has probably broken"; } my $rtmp_url = $rtmp_urls[0]; my($host, $app, $playpath) = $rtmp_url =~ m'rtmp://([^/]+)/(\w+)(/[^?]+)'; if($host eq 'flash.onf.ca') { $playpath =~ s{^(/[^/]+)/}{}; $app .= $1; $playpath =~ s{\.\w+$}{}; } else { $playpath = "mp4:$playpath"; } return { flv => title_to_filename($title), rtmp => $rtmp_url, app => $app, playpath => $playpath }; } sub _get_quality_from_url { my($url) = @_; if ($url =~ m'/streams/[A-Z](\d+)([A-Z])') { my ($size, $units) = ($1, $2); $size *= 1024 if $units eq 'M'; return $size; } } 1; } ##} blib/lib/FlashVideo/Site/Nfb.pm BEGIN { $INC{'FlashVideo/Site/Nicovideo.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Nicovideo.pm { package FlashVideo::Site::Nicovideo; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } use URI::Escape; sub find_video { my ($self, $browser, $embed_url) = @_; my $id = ($embed_url =~ /([ns]m\d+)/)[0]; die "No ID found\n" unless $id; my $base = "http://ext.nicovideo.jp/thumb_watch/$id"; if($embed_url !~ /ext\.nicovideo\.jp\/thumb_watch/) { $embed_url = "$base?w=472&h=374&n=1"; } $browser->get($embed_url); my $playkey = ($browser->content =~ /thumbPlayKey: '([^']+)/)[0]; die "No playkey found\n" unless $playkey; my $title = ($browser->content =~ /title: '([^']+)'/)[0]; $title =~ s/\\u([a-f0-9]{1,5})/chr hex $1/eg; $browser->get($base . "/$playkey"); my $url = uri_unescape(($browser->content =~ /url=([^&]+)/)[0]); return $url, title_to_filename($title, $id =~ /^nm/ ? "swf" : "flv"); } 1; } ##} blib/lib/FlashVideo/Site/Nicovideo.pm BEGIN { $INC{'FlashVideo/Site/Pbs.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Pbs.pm { package FlashVideo::Site::Pbs; use strict; use warnings; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *from_xml = \&FlashVideo::Utils::from_xml; } use MIME::Base64 qw(decode_base64); sub find_video { my ($self, $browser, $embed_url, $prefs) = @_; die "Must have Crypt::Rijndael installed to download from PBS" unless eval { require Crypt::Rijndael }; my ($media_id) = $browser->uri->as_string =~ m[ ^http://video\.pbs\.org/video/(\d+) ]x; unless (defined $media_id) { ($media_id) = $browser->content =~ m[ http://video\.pbs\.org/widget/partnerplayer/(\d+) ]x; } unless (defined $media_id) { ($media_id) = $browser->content =~ m[ /embed-player[^"]+\bepisodemediaid=(\d+) ]x; } unless (defined $media_id) { ($media_id) = $browser->content =~ m[var videoUrl = "([^"]+)"]; } unless (defined $media_id) { my ($pap_id, $youtube_id) = $browser->content =~ m[ \bDetectFlashDecision\ \('([^']+)',\ '([^']+)'\); ]x; if ($youtube_id) { debug "Youtube ID found, delegating to Youtube plugin\n"; my $url = "http://www.youtube.com/v/$youtube_id"; require FlashVideo::Site::Youtube; return FlashVideo::Site::Youtube->find_video($browser, $url, $prefs); } } die "Couldn't find media_id\n" unless defined $media_id; debug "media_id: $media_id\n"; $browser->get("http://video.pbs.org/videoPlayerInfo/$media_id"); my $xml = $browser->content; $xml =~ s/&/&/g; my $href = from_xml($xml); my $release_url = $href->{releaseURL}; unless ($release_url =~ m[^https?://]) { debug "encrypted release url: $release_url\n"; my ($type, $iv, $ciphertext) = split '\$', $release_url, 3; $release_url = undef; my $key = 'RPz~i4p*FQmx>t76'; my $cipher = Crypt::Rijndael->new($key, Crypt::Rijndael->MODE_CBC); $iv = pack 'H*', $iv if 32 == length $iv; $cipher->set_iv($iv); $release_url = $cipher->decrypt(decode_base64($ciphertext)); $release_url =~ s/\s+$//; } debug "unencrypted release url: $release_url\n"; $browser->get($release_url); my $rtmp_url = $browser->res->header('location') || from_xml($browser->content)->{choice}{url} || die "Couldn't find stream url\n"; $rtmp_url =~ s/<break>//; my ($file) = $rtmp_url =~ m{([^/]+)$}; return { rtmp => $rtmp_url, pageUrl => $embed_url, swfUrl => 'http://www-tc.pbs.org/video/media/swf/PBSPlayer.swf?18809', flv => $file, }; } 1; } ##} blib/lib/FlashVideo/Site/Pbs.pm BEGIN { $INC{'FlashVideo/Site/Pennyarcade.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Pennyarcade.pm { package FlashVideo::Site::Pennyarcade; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } sub find_video { my ($self, $browser, $embed_url) = @_; my($id) = $browser->content =~ m{"http://api.indieclicktv.com/player/show/([a-f0-9/]*)/default/mplayer.js}; die "No Video Urls Found" unless $id; my $url = "http://ictv-pa-ec.indieclicktv.com/media/videos/$id/video.mp4"; my($title) = $browser->content =~ /<div class="title"><h1>([^<]*)<\/h1>/; return $url, title_to_filename($title, "mp4"); } 1; } ##} blib/lib/FlashVideo/Site/Pennyarcade.pm BEGIN { $INC{'FlashVideo/Site/Redbull.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Redbull.pm { package FlashVideo::Site::Redbull; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *info = \&FlashVideo::Utils::info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *from_xml = \&FlashVideo::Utils::from_xml; } use URI; use HTML::Entities; sub find_video { my ($self, $browser, $page_url) = @_; my $video_info_url; my $host = $browser->uri->host; if ( ($browser->content =~ /data_url:\s+'([^']+)'/) or ($browser->content =~ m{displayVideoPlayer\('([^']+)'\)})) { $video_info_url = $1; $video_info_url = "http://$host$video_info_url"; } if (!$video_info_url) { die "Couldn't find video info URL"; } $browser->get($video_info_url); if ($browser->response->is_redirect) { $browser->get($browser->response->header('Location')); } if (!$browser->success) { die "Couldn't download Red Bull video info XML: " . $browser->response->status_line; } my $xml = $browser->content; $xml =~ s/&//g; $xml = decode_entities($xml); my $video_info = from_xml($xml); my $file_type = "flv"; if ($video_info->{high_video_url} =~ /\.mp4$/) { $file_type = "mp4"; } return { flv => title_to_filename($video_info->{title}, $file_type), rtmp => $video_info->{high_video_url}, }; } 1; } ##} blib/lib/FlashVideo/Site/Redbull.pm BEGIN { $INC{'FlashVideo/Site/Redtube.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Redtube.pm { package FlashVideo::Site::Redtube; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } use URI::Escape; sub find_video { my($self, $browser, $embed_url) = @_; my($title) = $browser->content =~ /<h1 class="videoTitle">([^<]+)</; my($url) = $browser->content =~ /hashlink=([^&"]+)/; $url = uri_unescape($url); $browser->allow_redirects; return $url, title_to_filename($title); } 1; } ##} blib/lib/FlashVideo/Site/Redtube.pm BEGIN { $INC{'FlashVideo/Site/Ringtv.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Ringtv.pm { package FlashVideo::Site::Ringtv; use strict; BEGIN { FlashVideo::Site::Grindtv->import(); } # (added by utils/combine-perl.pl) use base 'FlashVideo::Site::Grindtv'; 1; } ##} blib/lib/FlashVideo/Site/Ringtv.pm BEGIN { $INC{'FlashVideo/Site/Seesaw.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Seesaw.pm { package FlashVideo::Site::Seesaw; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *info = \&FlashVideo::Utils::info; *error = \&FlashVideo::Utils::error; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *json_unescape = \&FlashVideo::Utils::json_unescape; *convert_sami_subtitles_to_srt = \&FlashVideo::Utils::convert_sami_subtitles_to_srt; } use HTML::Entities qw(decode_entities); use URI::Escape qw(uri_escape); my @res = ( { name => "lowResUrl", resolution => [ 512, 288 ] }, { name => "stdResUrl", resolution => [ 672, 378 ] }, { name => "highResUrl", resolution => [ 1024, 576 ] } ); sub find_video { my ($self, $browser, $page_url, $prefs) = @_; my $player_info = ($browser->content =~ m{(/videoplayerinfo/\d+[^"]+)"})[0]; (my $content = $browser->content) =~ s{\\/}{/}g; my %seen; # avoid duplication in filenames my %metadata = map { $_ => '' } qw(brandTitle seriesTitle programmeTitle); my ($series, $episode) = ($browser->content =~ /Series (\d+) - Ep(?:isode)?\.? (\d+)/); if ($series and $episode) { $metadata{series_and_episode} = sprintf "S%02dE%02d", $series, $episode; } foreach my $metadata_item (keys %metadata) { if (my $value = ($content =~ m{<$metadata_item>(.*?)</$metadata_item>}isg)[0]) { $value = decode_entities($value); next if $seen{$value}; $seen{$value}++; $metadata{$metadata_item} = $value; } } my $title = join "-", map { trim($_) } grep length, @metadata{qw(brandTitle series_and_episode seriesTitle programmeTitle)}; $browser->get($player_info); debug "Got player info URL $player_info"; if (!$browser->success) { die "Couldn't get player info: " . $browser->response->status_line; } my @urls; for my $res(@res) { if($browser->content =~ /$res->{name}":\["([^"]+)/) { push @urls, { %$res, url => $1 }; } } die "No video URLs found" unless @urls; my $rtmp = $prefs->quality->choose(@urls); my($app, $playpath, $query) = $rtmp->{url} =~ m{^\w+://[^/]+/(\w+/\w+)(/[^?]+)(\?.*)}; my $prefix = "mp4"; $prefix = "flv" if $playpath =~ /\.flv$/; if ($prefs->subtitles) { if ($browser->content =~ m{"subtitleLocation":\["([^"]+)"\]}) { my $subtitles_url = $1; if ($subtitles_url =~ m{^/}) { $subtitles_url = "http://www.seesaw.com$subtitles_url"; } debug "Got Seesaw subtitles URL: $subtitles_url"; $browser->get($subtitles_url); if ($browser->success) { my $srt_filename = title_to_filename($title, "srt"); convert_sami_subtitles_to_srt($browser->content, $srt_filename); info "Wrote subtitles to $srt_filename"; } else { info "Couldn't download subtitles: " . $browser->response->status_line; } } else { debug "No Seesaw subtitles available (or couldn't extract URL)"; } } return { flv => title_to_filename($title, $prefix), rtmp => $rtmp->{url}, app => $app, playpath => "$prefix:$playpath$query" } } sub search { my($self, $search, $type) = @_; my $series = $search =~ s/(?:series |\bs)(\d+)//i ? int $1 : ""; my $episode = $search =~ s/(?:episode |\be)(\d+)//i ? int $1 : ""; my $browser = FlashVideo::Mechanize->new; _update_with_content($browser, "http://www.seesaw.com/start.layout.searchsuggest:inputtextevent?search=" . uri_escape($search)); my @urls = map { chomp(my $name = $_->text); { name => $name, url => $_->url_abs->as_string } } $browser->find_all_links(text_regex => qr/.+/); my @words = split " ", $search; @urls = grep { my $a = $_; @words == grep { $a->{name} =~ /\Q$_\E/i } @words } @urls; if(@urls == 1) { $browser->get($urls[0]->{url}); my $main_title = ($browser->content =~ m{<h1>(.*?)</h1>}s)[0]; $main_title =~ s/<[^>]+>//g; $main_title =~ s/\s+/ /g; my $cur_series = ($browser->content =~ /<li class="current">.*?>\w+ (\d+)/i)[0]; if($main_title =~ s/\s*series (\d+)\s*//i && !$cur_series) { $cur_series = $1; } my %series = reverse( ($browser->content =~ m{<ul class="seriesList">(.*?)</ul>}i)[0] =~ /<li.*?href="\?([^"]+)".*?>\s*(?:series\s*)?([^<]+)/gi); my $episode_list; if($series && $cur_series ne $series) { if(!$series{$series}) { error "No such series number ($series)."; return; } _update_with_content($browser, $series{$series}); $episode_list = $browser->content; $cur_series = $series; } elsif(!$series && keys %series > 1) { my @series = sort { $a <=> $b } map { s/series\s+//i; $_ } keys %series; info "Viewing series $cur_series; series " . join(", ", @series) . " also available."; info "Search for 'seesaw $main_title series $series[0]' to view a specific series."; } if(!$episode_list) { $episode_list = ($browser->content =~ m{<table id="episodeListTble">(.*?)</table>}is)[0]; } @urls = (); for my $episode_html($episode_list =~ m{<tr.*?</tr>}gis) { my %info; for(qw(number date title action)) { my $class = "episode" . ucfirst; $episode_html =~ m{<td class=['"]$class['"]>(.*?)</td>}gis && ($info{$_} = $1); } $info{number} = ($info{number} =~ /ep\.?\w*\s*(\d+)/i)[0]; $info{date} = ($info{date} =~ />(\w+[^<]+)/)[0]; $info{number} ||= ($info{title} =~ /ep\.?\w*\s*(\d+)/i)[0]; $info{title} = ($info{title} =~ />\s*([^<].*?)\s*</s)[0]; $info{url} = ($info{action} =~ /href=['"]([^'"]+)/)[0]; my $title = join " - ", $main_title, ($cur_series ? sprintf("S%02dE%02d", $cur_series, $info{number}) : $info{number} ? sprintf("E%02d", $info{number}) : ()), $info{title}; my $result = { name => $title, url => URI->new_abs($info{url}, $browser->uri) }; if($episode && $info{number} == $episode) { return $result; } push @urls, $result; } } else { info "Please specify a more specific title to download a particular programme." if @urls > 1; } return @urls; } sub _update_with_content { my($browser, $url) = @_; $browser->get($url, X_Requested_With => 'XMLHttpRequest', X_Prototype_Version => '1.6.0.3'); my($content) = $browser->content =~ /content":\s*"(.*?)"\s*}/; $content = json_unescape($content); debug "Content is '$content'"; $browser->update_html($content); } sub trim { local $_ = shift; s/^\s+|\s+$//g; return $_; } 1; } ##} blib/lib/FlashVideo/Site/Seesaw.pm BEGIN { $INC{'FlashVideo/Site/Sevenload.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Sevenload.pm { package FlashVideo::Site::Sevenload; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *from_xml = \&FlashVideo::Utils::from_xml; } use HTML::Entities; use URI::Escape; sub find_video { my ($self, $browser) = @_; die "Could not find configPath" unless $browser->content =~ /configPath=([^"']+)/; my $configpath = uri_unescape(decode_entities($1)); $browser->get($configpath); my $config = from_xml($browser); my($title, $location); eval { my $item = $config->{playlists}->{playlist}->{items}->{item}; $title = title_to_filename($item->{title}); my $streams = $item->{videos}->{video}->{streams}->{stream}; $streams = [ $streams ] unless ref $streams eq 'ARRAY'; $location = (sort { $b->{width} <=> $a->{width} } @$streams)[0] ->{locations}->{location}->{content}; }; return $location, $title if $location; die "Unable to get stream location" . ($@ ? ": $@" : ""); } 1; } ##} blib/lib/FlashVideo/Site/Sevenload.pm BEGIN { $INC{'FlashVideo/Site/Spike.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Spike.pm { package FlashVideo::Site::Spike; use strict; BEGIN { FlashVideo::Site::Mtvnservices->import(); } # (added by utils/combine-perl.pl) use base 'FlashVideo::Site::Mtvnservices'; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *from_xml = \&FlashVideo::Utils::from_xml; } use URI::Escape; sub find_video { my ($self, $browser, $embed_url) = @_; my $page_url = $browser->uri->as_string; my $config_url; if($browser->content =~ /config_url\s*=\s*["']([^"']+)/) { $config_url = $1; } elsif($browser->content =~ /(?:ifilmId|flvbaseclip)=(\d+)/) { $config_url = "/ui/xml/mediaplayer/config.groovy?ifilmId=$1"; } die "No config_url/id found\n" unless $config_url; $browser->get(uri_unescape($config_url)); my $xml = from_xml($browser); my $feed = uri_unescape($xml->{player}->{feed}); die "Unable to find feed URL\n" unless $feed; $browser->get($feed); return $self->handle_feed($browser->content, $browser, $page_url); } 1; } ##} blib/lib/FlashVideo/Site/Spike.pm BEGIN { $INC{'FlashVideo/Site/Stagevu.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Stagevu.pm { package FlashVideo::Site::Stagevu; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } sub find_video { my ($self, $browser) = @_; my($title) = $browser->content =~ /<title>(.*?)<\/title>/; $title =~ s/\s*-\s*Stagevu.*?$//; my($url) = FlashVideo::Generic->find_video($browser); return $url, title_to_filename($title); } 1; } ##} blib/lib/FlashVideo/Site/Stagevu.pm BEGIN { $INC{'FlashVideo/Site/Starwars.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Starwars.pm { package FlashVideo::Site::Starwars; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *from_xml = \&FlashVideo::Utils::from_xml; } sub find_video { my ($self, $browser, $embed_url) = @_; my $video_id; if ($browser->uri->as_string =~ /view\/([0-9]+)\.html$/) { $video_id = $1; } my $page_url = $browser->uri->as_string; $browser->get("http://starwars.com/webapps/video/item/$video_id"); my $xml = from_xml($browser); my $items = $xml->{channel}->{item}; my $item = ref $items eq 'ARRAY' ? (grep { $_->{link}->{content} eq "/video/view/" . $video_id . ".html" } @$items)[0] : $items; debug $item->{enclosure}->{url}; my $rtmpurl = $item->{enclosure}->{url}; $rtmpurl =~ s/^rtmp:/rtmpe:/; # for some reason it only works with rtmpe my $title = $item->{title}; # is there a way to unencrypt <CDATA> tags? or does the xml handler do this for us? return { flv => $title, rtmp => title_to_filename($rtmpurl), playpath => $item->{content}->{url} }; } 1; } ##} blib/lib/FlashVideo/Site/Starwars.pm BEGIN { $INC{'FlashVideo/Site/Stickam.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Stickam.pm { package FlashVideo::Site::Stickam; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *info = \&FlashVideo::Utils::info; *error = \&FlashVideo::Utils::error; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *swfhash = \&FlashVideo::Utils::swfhash; } sub find_video { my($self, $browser, $embed_url, $prefs) = @_; my $perfomer_id; if ($browser->content =~ /profileUserId=(\d+)/) { $perfomer_id = $1; } else { die "Can't get performer ID"; } my $filename; if ($browser->content =~ /userName=([^&]+)/) { $filename = $1; } else { $filename = $perfomer_id; } my $stream_info_url = sprintf "http://player.stickam.com/servlet/flash/getChannel?" . "type=join&performerID=%d", $perfomer_id; $browser->get($stream_info_url); if (!$browser->success) { die "Couldn't get stream info: " . $browser->response->status_line; } my %stream_info; foreach my $pair (split /&/, $browser->content) { my ($name, $value) = split /=/, $pair; if ($name eq 'freeServerIP') { $value = (split /,/, $value)[0]; } $stream_info{$name} = $value; } if ($stream_info{errorCode}) { die "Stickam returned error $stream_info{errorCode}: $stream_info{errorMessage}"; } my $rtmp_stream_url = sprintf "rtmp://%s/video_chat2_stickam_peep/%d/public/mainHostFeed", $stream_info{freeServerIP}, $stream_info{channelID}; return { rtmp => $rtmp_stream_url, flv => title_to_filename($filename), live => '', conn => [ 'O:1', "NS:channel:$perfomer_id", 'O:1', ], swfhash($browser, "http://player.stickam.com/flash/stickam/stickam_simple_video_player.swf") }; } 1; } ##} blib/lib/FlashVideo/Site/Stickam.pm BEGIN { $INC{'FlashVideo/Site/Stupidvideos.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Stupidvideos.pm { package FlashVideo::Site::Stupidvideos; use strict; BEGIN { FlashVideo::Site::Grindtv->import(); } # (added by utils/combine-perl.pl) use base 'FlashVideo::Site::Grindtv'; 1; } ##} blib/lib/FlashVideo/Site/Stupidvideos.pm BEGIN { $INC{'FlashVideo/Site/Tbs.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Tbs.pm { package FlashVideo::Site::Tbs; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *from_xml = \&FlashVideo::Utils::from_xml; } sub find_video { my ($self, $browser, $embed_url) = @_; my $oid; if ($browser->uri->as_string =~ /oid=([0-9]*)/) { $oid = $1; } $browser->get("http://www.tbs.com/video/cvp/videoData.jsp?oid=$oid"); my $xml = from_xml($browser); my $headline = $xml->{headline}; my $akamai; if ($xml->{akamai}->{src} =~ /[^,]*,([^,]*)/){ $akamai = $1; } my $files = $xml->{files}->{file}; my $file = ref $files eq 'ARRAY' ? (grep { $_->{type} eq "standard" } @$files)[0] : $files; if($akamai) { my $rtmpurl = $akamai . $file->{content}; die "Unable to find RTMP URL\n" unless $rtmpurl; return { flv => title_to_filename($headline), rtmp => $rtmpurl }; } else { return $file->{content}, title_to_filename($headline); } } 1; } ##} blib/lib/FlashVideo/Site/Tbs.pm BEGIN { $INC{'FlashVideo/Site/Techcast.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Techcast.pm { package FlashVideo::Site::Techcast; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } use HTML::Entities; sub find_video { my ($self, $browser, $embed_url) = @_; my($clip_url) = $browser->content =~ /clip:\s*{\s*url:\s*['"]([^"']+)/; die "Unable to extract clip URL" unless $clip_url; $clip_url = URI->new_abs($clip_url, $browser->uri); my($talk) = $browser->content =~ /class="lecture_archive"[^>]+>([^<]+)/i; $talk = decode_entities($talk); my($author) = $browser->content =~ /class="speaker_archive"[^>]+>([^<]+)/i; $author = decode_entities($author); return $clip_url, title_to_filename($talk ? "$author - $talk" : $clip_url); } 1; } ##} blib/lib/FlashVideo/Site/Techcast.pm BEGIN { $INC{'FlashVideo/Site/Ted.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Ted.pm { package FlashVideo::Site::Ted; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *extract_title = \&FlashVideo::Utils::extract_title; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } sub find_video { my ($self, $browser) = @_; my $url; if($browser->content =~ m{<a href="(/talks[^"]+)">Watch high-res video}) { $url = URI->new_abs($1, $browser->uri); $browser->allow_redirects; } else { die "Unable to find download link"; } my $title = extract_title($browser); $title =~ s/\s*\|.*//; my $filename = title_to_filename($title, "mp4"); return $url, $filename; } 1; } ##} blib/lib/FlashVideo/Site/Ted.pm BEGIN { $INC{'FlashVideo/Site/Theonion.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Theonion.pm { package FlashVideo::Site::Theonion; # horrible casing :( use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *info = \&FlashVideo::Utils::info; *extract_info = \&FlashVideo::Utils::extract_info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } sub find_video { my ($self, $browser) = @_; if ($browser->response->is_redirect) { $browser->get( $browser->response->header('Location') ); if (!$browser->success) { die "Couldn't follow Onion redirect: " . $browser->response->status_line; } } my $title; if ($browser->content =~ /var video_title = "([^"]+)"/) { $title = $1; } else { $title = extract_info($browser)->{meta_title}; } my $filename = title_to_filename($title); my $url = (FlashVideo::Generic->find_video($browser, $browser->uri))[0]; return $url, $filename; } 1; } ##} blib/lib/FlashVideo/Site/Theonion.pm BEGIN { $INC{'FlashVideo/Site/Todaysbigthing.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Todaysbigthing.pm { package FlashVideo::Site::Todaysbigthing; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *extract_title = \&FlashVideo::Utils::extract_title; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *from_xml = \&FlashVideo::Utils::from_xml; } my $base = "http://www.todaysbigthing.com/betamax"; sub find_video { my ($self, $browser, $embed_url) = @_; my $id; if($browser->content =~ /item_id=(\d+)/) { $id = $1; } elsif($embed_url =~ m![/:](\d+)!) { $id = $1; } die "No ID found\n" unless $id; $browser->get("$base:$id"); my $xml = from_xml($browser); my $title = $xml->{title}; $title = extract_title($browser) if ref $title; my $filename = title_to_filename($title); my $url = $xml->{flv}; die "No FLV location" unless $url; return $url, $filename; } sub can_handle { my($self, $browser, $url) = @_; return $browser->content =~ $base; } 1; } ##} blib/lib/FlashVideo/Site/Todaysbigthing.pm BEGIN { $INC{'FlashVideo/Site/Tou.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ################################################# # # This file was automatically generated by utils/combine-perl.pl # You should edit the original files, not this # combined version. # # The original files are available at: # http://github.com/monsieurvideo/get-flash-videos # ################################################# # tou.tv # # Reverse-engineering details at http://store-it.appspot.com/tou/tou.html # by Sylvain Fourmanoit # # un grand merci a Sylvain qui a tout debrousaille! # # Stavr0 # ##{ blib/lib/FlashVideo/Site/Tou.pm { package FlashVideo::Site::Tou; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; } use URI::Escape; sub find_video { my ($self, $browser) = @_; my $video_id; if ($browser->content =~ /,"pid":"(\w+)"/) { $video_id = $1; } debug "Video ID = " . $video_id; die "Couldn't find TOU.TV video ID" unless $video_id; my $filename; if ($browser->content =~ /,"titleId":"([^"]+)"/) { $filename = $1 ; } debug "Filename = " . $filename; $browser->get("http://release.theplatform.com/content.select?pid=$video_id"); die "Couldn't download TOU.TV XML: " . $browser->response->status_line if !$browser->success; my $url; if ($browser->content =~ /(rtmp:[^\<]+)/) { $url = uri_unescape($1); } debug "URL = " . $url; my $auth; if ($url =~ /auth=([^&]+)/ ) { $auth = uri_unescape($1); } debug "AUTH = " . $auth; my $app; if ($url =~ /(ondemand\/.+)/ ) { $app = uri_unescape($1); } debug "APP = " . $app; my $playpath; if ($url =~ /<break>(.+)/ ) { $playpath = uri_unescape($1); } debug "PLAYPATH = " . $playpath; return { app => $app, pageUrl => $url, swfUrl => "http://static.tou.tv/lib/ThePlatform/4.1.2/swf/flvPlayer.swf", tcUrl => $url, auth => $auth, rtmp => $url, playpath => $playpath, flv => "$filename.flv", }; } sub search { my($self, $search, $type) = @_; my $browser = FlashVideo::Mechanize->new; $browser->get("http://www.tou.tv/recherche?q=" . uri_escape($search)); return unless $browser->success; my $results = $browser->content; my @emissions; my @links; while($results =~ /<a\s+href="([^"]+)"\s+id="[^"]+"\s+class="([^"]+)/g) { debug $1; if($2 eq "tousEpisodes") { push @emissions, $1; } } for my $emission (@emissions) { $browser->get($emission); my $liste = $browser->content; while($liste =~ /<a.+class="episode".+href="([^"]+)".+>(.+)<\/a>/g) { push @links, { name => $1, url => "http://www.tou.tv$1", description => $2 }; } } return @links; } 1; } ##} blib/lib/FlashVideo/Site/Tou.pm BEGIN { $INC{'FlashVideo/Site/Traileraddict.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Traileraddict.pm { package FlashVideo::Site::Traileraddict; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *info = \&FlashVideo::Utils::info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } use URI::Escape; sub find_video { my ($self, $browser) = @_; my $video_id; if ($browser->content =~ m'/em[db]/(\d+)') { $video_id = $1; } else { die "Unable to get Traileraddict video ID"; } my $video_info_url = "http://www.traileraddict.com/fvar.php?tid=$video_id"; $browser->get($video_info_url); if (!$browser->success) { die "Couldn't download Traileraddict video info URL: " . $browser->response->status_line; } my %info = parse_video_info($browser->content); die "Couldn't find Traileraddict video URL" unless $info{fileurl}; $browser->head($info{fileurl}); if ($browser->response->is_redirect()) { $info{fileurl} = $browser->response->header('Location'); } my $type = $info{fileurl} =~ /\.mp4/i ? 'mp4' : 'flv'; return $info{fileurl}, title_to_filename($info{title}, $type); } sub parse_video_info { my $raw_video_info = shift; my %info; foreach my $pair (split /&/, $raw_video_info) { $pair = uri_unescape($pair); my ($name, $value) = split /=/, $pair; $info{$name} = $value; } return %info; } 1; } ##} blib/lib/FlashVideo/Site/Traileraddict.pm BEGIN { $INC{'FlashVideo/Site/Truveo.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Truveo.pm { package FlashVideo::Site::Truveo; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *url_exists = \&FlashVideo::Utils::url_exists; } sub find_video { my($self, $browser, $embed_url, $prefs) = @_; my($videourl) = $browser->content =~ /var videourl = "(.*?)"/; $videourl = $embed_url if !$videourl && $browser->uri->host eq 'xml.truveo.com'; die "videourl not found" unless $videourl; $browser->get($videourl); if($browser->content =~ /url=(http:.*?)["']/) { my $redirect = url_exists($browser, $1); $browser->get($redirect); my($package, $possible_url) = FlashVideo::URLFinder->find_package($redirect, $browser); die "Recursion detected" if $package eq __PACKAGE__; return $package->find_video($browser, $possible_url, $prefs); } else { die "Redirect URL not found"; } } 1; } ##} blib/lib/FlashVideo/Site/Truveo.pm BEGIN { $INC{'FlashVideo/Site/Tudou.pm'}++; } ##{ blib/lib/FlashVideo/Site/Tudou.pm { package FlashVideo::Site::Tudou; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *info = \&FlashVideo::Utils::info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *from_xml = \&FlashVideo::Utils::from_xml; } sub find_video { my ($self, $browser, $embed_url) = @_; my $check_response = sub { my ( $message ) = @_; return if $browser->success; die sprintf $message, $browser->response->code; }; my $videoID = 0; if ( $embed_url =~ m`hd.tudou.com/program/\w+` ) { ( $videoID ) = ( $browser->content =~ /iid: "(\w+)"/ ); } else { if ( $embed_url =~ m`tudou.com/programs/view/(.+)$` ) { $embed_url = sprintf "http://www.tudou.com/v/%s", $1; $browser->get( $embed_url ); } if ( $browser->response->code eq 302 and $embed_url =~ m`tudou.com/v/(.+)$` ) { $embed_url = $browser->response->header( 'Location' ); } ( $videoID ) = ( $embed_url =~ m`tudou.com/player/outside/player_outside.swf\?iid=(\d+)` ); } die "Couldn't extract video ID, we are out probably out of date" unless $videoID; debug "Using video ID $videoID"; $browser->get( sprintf "http://v2.tudou.com/v2/kili?safekey=%s&id=%s&noCatch=%d", 'YouNeverKnowThat', $videoID, rand( 10000 ) ); if ( not $browser->success ) { debug 'Using fallback tudou link for video info'; $browser->get( sprintf "http://v2.tudou.com/v2/cdn?safekey=%s&id=%s&noCatch=%d", 'YouNeverKnowThat', $videoID, rand( 10000 ) ); } $check_response->( "Couldn't grab video informaton from tudou, server response was %s" ); return parse_video_info( $browser->content ); } sub parse_video_info { my ( $raw_xml ) = @_; my $xml = from_xml($raw_xml, forcearray => [ 'f' ] ); my %streams; foreach my $file ( @{$xml->{b}->{f}} ) { my $url = $file->{content}; my ( $format ) = ( $url =~ m`http://[^/]+/([^/]+)/` ); debug "Unable to extract file format for $url" and next unless $format; push @{$streams{$format}{urls}}, $url; $streams{$format}{size} = $file->{size}; } my $stream = ( exists $streams{mp4} ? 'mp4' : exists $streams{m4v} ? 'm4v' : exists $streams{flv} ? 'flv' : exists $streams{phoneMp4} ? 'phoneMp4' : '' ); my $stream_formats = join ', ', ( keys %streams ); die "Video is only available in unknown file formats ($stream_formats)", unless $stream; debug "Choosing to use the $stream stream (available: $stream_formats)"; my $stream_choice = int rand( 1 + $#{$streams{$stream}{urls}} ); my $url = @{$streams{$stream}{urls}}[$stream_choice]; my $sourceID = ( $stream eq 'flv' ? '11000' : '18000' ); $url =~ s/\?key=/?$sourceID&key=/; my $title = $xml->{title}; my $filename = title_to_filename( $title, 'flv' ); my $stream_duration = $xml->{time}; my $stream_size = $streams{$stream}{size}; debug sprintf "%s, %d seconds, %s bytes", $title, $stream_duration / 1000, $stream_size if ( $title and $stream_duration and $stream_size ); return ( $url, $filename ); } 1; } ##} blib/lib/FlashVideo/Site/Tudou.pm BEGIN { $INC{'FlashVideo/Site/Tva.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Tva.pm { package FlashVideo::Site::Tva; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; } BEGIN { FlashVideo::Site::Brightcove->import(); } # (added by utils/combine-perl.pl) use base 'FlashVideo::Site::Brightcove'; sub find_video { my ($self, $browser, $embed_url) = @_; my $video_id = ($browser->content =~ /CanoeVideoStandalone.SetVideo.(\d+)/i)[0]; my $player_id = ($browser->content =~ /CanoeVideoStandalone.SetPlayer.(\d+)/i)[0]; debug "Extracted playerId: $player_id, videoId: $video_id" if $player_id or $video_id; if(!$video_id) { my $video_offset = ($browser->content =~ /CanoeVideoStandalone.SetVideo.\w+\[(\d+)/i)[0]; $video_id = ($browser->content =~ /videos\[$video_offset\].+'(\d+)'\s*\]/)[0]; } die "Unable to extract Brightcove IDs from page" unless $player_id and $video_id; return $self->amfgateway($browser, $player_id, { videoId => $video_id, } ); } sub can_handle { my($self, $browser, $url) = @_; return $browser->content =~ /CanoeVideoStandalone\.GeneratePlayer\(\);/i; } 1; } ##} blib/lib/FlashVideo/Site/Tva.pm BEGIN { $INC{'FlashVideo/Site/Ustream.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Ustream.pm { package FlashVideo::Site::Ustream; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } use MIME::Base64; sub find_video { my ($self, $browser, $embed_url) = @_; unless(eval { require Data::AMF::Packet }) { die "Must have Data::AMF::Packet installed to download ustream videos"; } my $packet = Data::AMF::Packet->deserialize(decode_base64(<<EOF)); AAAAAAABAA9WaWV3ZXIuZ2V0VmlkZW8AAi8xAAAAiAoAAAABAwAIYXV0b3BsYXkBAQAEcnBpbgIA GHJwaW4uMC4xODM2MDk4NTkzMTY0Njg5OAAHdmlkZW9JZAIABzIzNTU3MzYAB3BhZ2VVcmwCACZo dHRwOi8vd3d3LnVzdHJlYW0udHYvcmVjb3JkZWQvMjM1NTczNgAHYnJhbmRJZAIAATEAAAkK EOF my($title) = $browser->content =~ /<h2[^>]*>([^<]+)/; my($video_id) = $browser->uri =~ m{recorded/(\d+)}; $video_id ||= $browser->content =~ m{vid\s*=\s*["']?(\d+)}; $packet->messages->[0]->{value}->[0]->{videoId} = $video_id; my $data = $packet->serialize; $browser->post( "http://216.52.240.138/gateway.php", Content_Type => "application/x-amf", Content => $data ); die "Failed to post to Ustream AMF gateway" unless $browser->response->is_success; my($flv_url) = $browser->content =~ /flv.{3,5}(http:[^\0]+)/; return $flv_url, title_to_filename($title); } 1; } ##} blib/lib/FlashVideo/Site/Ustream.pm BEGIN { $INC{'FlashVideo/Site/Videojug.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Videojug.pm { package FlashVideo::Site::Videojug; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *from_xml = \&FlashVideo::Utils::from_xml; } my $playlist_url = "http://www.videojug.com/views/film/playlist.aspx?items=&userName=&ar=16_9&id="; sub find_video { my ($self, $browser) = @_; my $interview_clip; if ($browser->uri->as_string =~ m'/interview/'i) { $playlist_url = "http://www.videojug.com/views/interview/playlist.aspx?ar=16_9&id="; $interview_clip = $browser->uri->fragment; } my $video_id; if ($browser->content =~ /<meta name=["']video-id["'] content="([A-F0-9a-f\-]+)"/) { $video_id = $1; } else { die "Couldn't find video ID in Videojug page"; } $browser->get($playlist_url . $video_id); my($video_url, $filename); eval { my $xml = from_xml($browser); my $shape = $xml->{Shapes}->{Shape}->[-1]; my $location = (grep { $shape->{Locations} =~ /\Q$_->{Name}\E/ } @{$xml->{Locations}->{Location}})[0]; my ($prefix, $title); if ($interview_clip) { ($prefix, $title) = get_prefix_and_title($xml, $interview_clip); } else { $prefix = $xml->{Items}->{Media}->{Prefix}; $title = $xml->{Items}->{Media}->{Title}; } $video_url = sprintf "%s%s__%sENG.flv", $location->{Url}, $prefix, $shape->{Code}; $filename = title_to_filename($title); }; die "Unable to retrieve/parse Videojug playlist. $@" if $@; die "Couldn't find video URL" unless $video_url; return $video_url, $filename; } sub get_prefix_and_title { my ($xml, $video_name) = @_; foreach my $media (@{ $xml->{Items}->{Media} }) { my $title = lc $media->{Title}; $title =~ s/ /-/g; $title =~ s/[^a-z0-9\-]//g; if ($title eq $video_name) { return $media->{Prefix}, $media->{Title}; } } die "Couldn't find prefix for video '$video_name'"; } 1; } ##} blib/lib/FlashVideo/Site/Videojug.pm BEGIN { $INC{'FlashVideo/Site/Videolectures.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Videolectures.pm { package FlashVideo::Site::Videolectures; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } sub find_video { my ($self, $browser) = @_; my $author = ($browser->content =~ /author:\s*<a [^>]+>([^<]+)/s)[0]; my $title = ($browser->content =~ /<h2>([^<]+)/)[0]; my $streamer = ($browser->content =~ /streamer:\s*["']([^"']+)/)[0]; my $playpath = ($browser->content =~ /file:\s*["']([^"']+)/)[0]; $playpath =~ s/\.flv$//; my $data = { app => (split m{/}, $streamer)[-1], rtmp => $streamer, playpath => $playpath, flv => title_to_filename("$author - $title") }; return $data; } 1; } ##} blib/lib/FlashVideo/Site/Videolectures.pm BEGIN { $INC{'FlashVideo/Site/Vimeo.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Vimeo.pm { package FlashVideo::Site::Vimeo; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *from_xml = \&FlashVideo::Utils::from_xml; } sub find_video { my ($self, $browser, $embed_url) = @_; my $base = "http://vimeo.com/moogaloop"; my $id; if($embed_url =~ /clip_id=(\d+)/) { $id = $1; } elsif($embed_url =~ m!/(\d+)!) { $id = $1; } die "No ID found\n" unless $id; $browser->get("$base/load/clip:$id/embed?param_fullscreen=1¶m_clip_id=$id¶m_show_byline=0¶m_server=vimeo.com¶m_color=cc6600¶m_show_portrait=0¶m_show_title=1"); my $xml = from_xml($browser); my $filename = title_to_filename($xml->{video}->{caption}); my $request_signature = $xml->{request_signature}; my $request_signature_expires = $xml->{request_signature_expires}; $browser->allow_redirects; my $url = "$base/play/clip:$id/$request_signature/$request_signature_expires/?q=sd&type=embed"; return $url, $filename; } 1; } ##} blib/lib/FlashVideo/Site/Vimeo.pm BEGIN { $INC{'FlashVideo/Site/Wat.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Wat.pm { package FlashVideo::Site::Wat; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *json_unescape = \&FlashVideo::Utils::json_unescape; } use HTML::Entities; use URI::Escape; sub find_video { my ($self, $browser) = @_; $browser->content =~ /videoid\s*:\s*["'](\d+)/i || die "No video ID found"; my $video_id = $1; $browser->get("http://www.wat.tv/interface/contentv2/$video_id"); my $title = json_unescape(($browser->content =~ /title":"(.*?)",/)[0]); my $url = json_unescape(($browser->content =~ /files.*?url":"(.*?)",/)[0]); $url .= "?context=swf2&getURL=1&version=WIN%2010,0,45,2"; my $file_type = 'flv'; $browser->head($url); if (!$browser->success) { die "Couldn't do HEAD request $url: " . $browser->response->status_line; } my $content_type = $browser->response->header('Content-Type'); if ($content_type =~ /text/) { $browser->get($url); if (!$browser->success) { die "Couldn't get $url: " . $browser->response->status_line; } if ($browser->content =~ m'^(http://\S+)') { $url = $1; if ($url =~ /\.h264/) { $file_type = 'mp4'; } } } else { die "Unexpected Content-Type ($content_type) from Wat server."; } my $filename = title_to_filename($title, $file_type); $browser->allow_redirects; return $url, $filename; } 1; } ##} blib/lib/FlashVideo/Site/Wat.pm BEGIN { $INC{'FlashVideo/Site/Xhamster.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Xhamster.pm { package FlashVideo::Site::Xhamster; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *extract_title = \&FlashVideo::Utils::extract_title; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } sub find_video { my ($self, $browser) = @_; my $server; if ($browser->content =~ m{'srv': '(http://[^'"]+)'}) { $server = $1; } else { die "Couldn't determine xhamster server"; } my $video_file; if ($browser->content =~ m{'file': '([^'"]+\.flv)'}) { $video_file = $1; } else { die "Couldn't determine xhamster video filename"; } my $filename = title_to_filename(extract_title($browser)); my $url = sprintf "%s/flv2/%s", $server, $video_file; $browser->allow_redirects; return $url, $filename; } 1; } ##} blib/lib/FlashVideo/Site/Xhamster.pm BEGIN { $INC{'FlashVideo/Site/Xnxx.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Xnxx.pm { package FlashVideo::Site::Xnxx; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *title_to_filename = \&FlashVideo::Utils::title_to_filename; } use URI::Escape; sub find_video { my ($self, $browser, $embed_url) = @_; my $url = ($browser->content =~ /flv_url=(.+?)&/)[0]; $url = uri_unescape($url); die "Unable to extract url" unless $url; $browser->content =~ /(?:<span class="style5">|<td style="font-size: 20px;">\s*)<strong>([^<]+)/; my $filename = title_to_filename($1); return $url, $filename; } 1; } ##} blib/lib/FlashVideo/Site/Xnxx.pm BEGIN { $INC{'FlashVideo/Site/Xvideos.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Site/Xvideos.pm { package FlashVideo::Site::Xvideos; use strict; BEGIN { FlashVideo::Site::Xnxx->import(); } # (added by utils/combine-perl.pl) use base 'FlashVideo::Site::Xnxx'; 1; } ##} blib/lib/FlashVideo/Site/Xvideos.pm BEGIN { $INC{'FlashVideo/Site/Youku.pm'}++; } ##{ blib/lib/FlashVideo/Site/Youku.pm { package FlashVideo::Site::Youku; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *info = \&FlashVideo::Utils::info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *get_video_filename = \&FlashVideo::Utils::get_video_filename; } sub find_video { my ($self, $browser, $embed_url) = @_; my $check_response = sub { my ( $message ) = @_; return if $browser->success; die sprintf $message, $browser->response->code; }; if ( $embed_url !~ m`^http://v.youku.com/v_show/` ) { die "Don't recognise the youku link" unless $embed_url =~ m`player.php/sid/(.+)/v\.swf` or $embed_url =~ m`qplayer\.swf\?VideoIDS=([^&]+)`; $embed_url = sprintf "http://v.youku.com/v_show/id_%s.html", $1; $browser->get( $embed_url ); } $check_response->( "Can't load the youku page, server response was %s" ); my ( $videoID ) = ( $browser->content =~ /var videoId = '(.+?)';/ ); die "Couldn't extract video ID from youku page, we are probably out of date" unless $videoID; debug "Using video ID $videoID"; $browser->get( sprintf "http://v.youku.com/player/getPlayList/VideoIDS/%s/version/5/source/video/password/?ran=%d&n=%d", $videoID, rand( 10000 ), 3 ); $check_response->( "Couldn't grab video informaton from youku, server response was %s" ); return parse_video_info( $browser, $browser->content ); } sub parse_video_info { my ( $browser, $json ) = @_; debug "Video data: $json"; my ( $shuffle_seed ) = ( $json =~ /"seed":(\d+)/ ); die "Can't find the seed value in the video info JSON" unless $shuffle_seed; my ( $streams ) = ( $json =~ /"streamtypes":\[([^\]]+)\]/ ); my $stream = ( index $streams, 'mp4' ) > 0 ? 'mp4' : 'flv'; debug "Choosing to use the $stream stream (available: $streams)"; my $fileID = ''; if ($json =~ /"streamfileids":{([^}]+)}/) { my $streamfileids = $1; ( $fileID ) = ( $streamfileids =~ /"$stream":"([^"]+)"/ ); } ( $fileID ) = ( $json =~ /"fileid":"([^"]+)"/ ) if not $fileID; die "Can't find the encrypted file ID in the video info JSON" unless $fileID; debug "Encrypted file ID: $fileID"; my @lookup_table = shuffle_table( $shuffle_seed ); $fileID =~ s/(\d+)\*/$lookup_table[$1]/eg; debug "Decrypted file ID: $fileID (seed is $shuffle_seed)"; my $sID = sprintf "%s1%07d_00", time, rand( 10000000 ) ; my ( $keyA ) = ( $json =~ /"key1":"([^"]+)"/ ); my ( $keyB ) = ( $json =~ /"key2":"([^"]+)"/ ); my $key = sprintf "%s%x", $keyB, hex( $keyA ) ^ hex( 'a55aa5a5' ); my ( $title ) = ( $json =~ /"title":"([^"]+)"/ ); $title =~ s/\\u([a-f0-9]{4})/chr(hex $1)/egi; my $filename = get_video_filename( $stream ); $filename = title_to_filename( $title, $stream ) if $title; my ( $stream_info ) = ( $json =~ /"segs":{"$stream":\[([^\]]+)\]/ ); my @urls; my $part_count = 0; while ($stream_info =~ /\G{"no":"?(\d+)"?,([^}]+)},?/g) { my ( $segment_number, $segment_info ) = ( $1, $2 ); my ( $segment_duration ) = ( $segment_info =~ /"seconds":"([^"]+)"/ ); my ( $segment_size ) = ( $segment_info =~ /"size":"([^"]+)"/ ); my $segment_number_str = sprintf '%02X', $segment_number; my $segment_fileID = $fileID; substr $segment_fileID, 8, 2, $segment_number_str; $browser->get( sprintf "http://f.youku.com/player/getFlvPath/sid/%s/st/%s/fileid/%s?K=%s&myp=null", $sID, $stream, $segment_fileID, $key ); my $url = $browser->response->header( 'Location' ); die "Youku rejected our attempt to get the video, we're probably out of date" unless $browser->response->code eq 302 and $url; debug "Video location for segment $segment_number is $url"; $url = "$url.$stream" unless $url =~ /$stream$/; debug sprintf "%s, segment %d, %s seconds, %s bytes", $title, $segment_number, $segment_duration, $segment_size if ( $title and $segment_duration and $segment_size ); push @urls, [$url, ++$part_count, 0, $segment_size]; } $_->[2] = $part_count for @urls; return ( \@urls, $filename ); } sub shuffle_table { my ( $seed ) = @_; my @lookup = split //, q`abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\:._-1234567890`; my @shuffled; while ( $#lookup > 0 ) { $seed = ( 211 * $seed + 30031 ) % 2**16; my $x = int( $seed / 2**16 * ( $#lookup + 1 ) ); push @shuffled, splice( @lookup, $x, 1 ); } return @shuffled; } 1; } ##} blib/lib/FlashVideo/Site/Youku.pm BEGIN { $INC{'FlashVideo/Site/Youtubenocookie.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. BEGIN { $INC{'FlashVideo/Site/Youtube.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. BEGIN { $INC{'FlashVideo/JSON.pm'}++; } ##{ blib/lib/FlashVideo/JSON.pm { package FlashVideo::JSON; use strict; use Exporter;use base 'Exporter'; our @EXPORT = qw(from_json); my $number = qr{(?:-?\b(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b)}; my $oneChar = qr{(?:[^\0-\x08\x0a-\x1f\"\\]|\\(?:["/\\bfnrt]|u[0-9A-Fa-f]{4}))}; my $string = qr{(?:"$oneChar*")}; my $jsonToken = qr{(?:false|true|null|[\{\}\[\]]|$number|$string)}; my $escapeSequence = qr{\\(?:([^u])|u(.{4}))}; my %escapes = ( '\\' => '\\', '"' => '"', '/' => '/', 'b' => "\b", 'f' => "\f", 'n' => "\xA", 'r' => "\xD", 't' => "\t" ); sub from_json { my($in) = @_; my @tokens = $in =~ /$jsonToken/go; my $result = $tokens[0] eq '{' ? {} : []; shift @tokens if $tokens[0] =~ /^[\[\{]/; my $key; # key to use for next value my @stack = $result; for my $t(@tokens) { my $ft = substr $t, 0, 1; my $cont = $stack[0]; if($ft eq '"') { my $s = substr $t, 1, length($t) - 2; $s =~ s/$escapeSequence/$1 ? $escapes{$1} : chr hex $2/geo; if(!defined $key) { if(ref $cont eq 'ARRAY') { $cont->[@$cont] = $s; } else { $key = $s; next; # need to save $key } } else { $cont->{$key} = $s; } } elsif($ft eq '[' || $ft eq '{') { unshift @stack, (ref $cont eq 'ARRAY' ? $cont->[@$cont] : $cont->{$key}) = $ft eq '[' ? [] : {}; } elsif($ft eq ']' || $ft eq '}') { shift @stack; } else { (ref $cont eq 'ARRAY' ? $cont->[@$cont] : $cont->{$key}) = $ft eq 'f' ? 0 # false : $ft eq 'n' ? undef # null : $ft eq 't' ? 1 # true : $t; # sign or digit } undef $key; } return $result; } 1; } ##} blib/lib/FlashVideo/JSON.pm ##{ blib/lib/FlashVideo/Site/Youtube.pm { package FlashVideo::Site::Youtube; use strict; use Encode; use HTML::Entities; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *info = \&FlashVideo::Utils::info; *error = \&FlashVideo::Utils::error; *extract_info = \&FlashVideo::Utils::extract_info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *url_exists = \&FlashVideo::Utils::url_exists; *swfhash = \&FlashVideo::Utils::swfhash; *json_unescape = \&FlashVideo::Utils::json_unescape; } BEGIN { FlashVideo::JSON->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *from_json = \&FlashVideo::JSON::from_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) { $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 ($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; } 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); } if ($info{conn} =~ /^rtmp/) { 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; } my $swf_url; if ($browser->content =~ /SWF_URL['"] ?: ?.{0,90}?(http:\/\/[^ ]+\.swf)/) { $swf_url = $1; } elsif($browser->content =~ /swfConfig\s*=\s*(\{.*?\});/ && (my $swf = from_json($1))) { $swf_url = $swf->{url}; } elsif($browser->content =~ /src=\\['"]([^'"]+\.swf)/) { $swf_url = json_unescape($1); } else { die "Couldn't extract SWF URL"; } my $rtmp_url = $info{conn}; if($info{fmt_stream_map}) { my $fmt_stream_map = parse_youtube_format_url_map($info{fmt_stream_map}, 1); my $preferred_quality = $prefs->quality->choose(map { $fmt_stream_map->{$_->{id}} ? { resolution => $_->{resolution}, url => $fmt_stream_map->{$_->{id}} } : () } @formats); $rtmp_url = $preferred_quality->{url}; } return { flv => title_to_filename($title), rtmp => $rtmp_url, swfhash($browser, $swf_url) }; } elsif($info{fmt_url_map}) { debug "Using fmt_url_map method from info"; return $self->download_fmt_map($prefs, $browser, $title, \%info, $info{fmt_url_map}); } } return download_get_video($browser, $prefs, $video_id, $title, $t); } sub download_fmt_map { my($self, $prefs, $browser, $title, $info, $fmt_map) = @_; my $fmt_url_map = parse_youtube_format_url_map($fmt_map); if (!$title and $browser->uri->as_string =~ m'/user/.*?#') { my $video_id = (split /\//, $browser->uri->fragment)[-1]; my %info = get_youtube_video_info($browser->clone, $video_id); $title = $info->{title}; } 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)) { @formats_to_try = grep { $_ != $fmt } @formats_to_try; 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; } 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); return if $browser->response->code == 200; } 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) { my $check_cookie_url = $browser->response->header('Location'); $browser->get($check_cookie_url); if ($browser->content =~ /<meta.*"refresh".*?url='(.*?)'"/i) { my $redirected = decode_entities($1); $browser->get($redirected); 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 { die "Unexpected response during login"; } $browser->get($orig_uri); if ($browser->response->code == 303) { my $real_confirmation_url = $browser->response->header('Location'); $browser->get($real_confirmation_url); if ($browser->form_with_fields('next_url', 'action_confirm')) { $browser->field('action_confirm' => 'Confirm Birth Date'); $browser->click_button(name => "action_confirm"); if ($browser->response->code != 303) { die "Unexpected response from Youtube"; } $browser->get($browser->response->header('Location')); } } } else { if ($browser->response->code == 302) { $browser->get($browser->response->header('Location')); } if ($browser->response->code == 303) { debug "Video not available (303), trying " . $browser->response->header('Location'); $browser->get($browser->response->header('Location')); } if (!$browser->success) { die "Couldn't download URL: " . $browser->response->status_line; } } } sub get_youtube_video_info { my ($browser, $video_id, $url, $t) = @_; $url ||= "http://www.youtube.com/watch?v=$video_id"; for my $el(qw(profilepage detailpage)) { my $video_info_url_template = "http://www.youtube.com/get_video_info?&video_id=%s&el=$el&ps=default&eurl=%s&hl=en_US&t=%s"; my $video_info_url = sprintf $video_info_url_template, uri_escape($video_id), uri_escape($url), uri_escape($t); debug "get_youtube_video_info: $video_info_url"; $browser->get($video_info_url); next unless $browser->success; my %info = parse_youtube_video_info($browser->content); next if $info{status} eq 'fail'; return %info; } error "Unable to get YouTube video information."; } sub parse_youtube_video_info { my $raw_info = shift; my %video_info; foreach my $raw_pair (split /&/, $raw_info) { my ($key, $value) = split /=/, $raw_pair; $value = uri_unescape($value); $value =~ s/\+/ /g; $video_info{$key} = $value; } return %video_info; } sub parse_youtube_format_url_map { my($raw_map, $param_idx) = @_; $param_idx = 0 unless defined $param_idx; my $map = {}; foreach my $pair (split /,/, $raw_map) { my ($format, @params) = split /\|/, $pair; my $url = $params[$param_idx]; $url = uri_unescape($url); $map->{$format} = $url; } return $map; } 1; } ##} blib/lib/FlashVideo/Site/Youtube.pm ##{ blib/lib/FlashVideo/Site/Youtubenocookie.pm { package FlashVideo::Site::Youtubenocookie; use strict; BEGIN { FlashVideo::Site::Youtube->import(); } # (added by utils/combine-perl.pl) use base 'FlashVideo::Site::Youtube'; 1; } ##} blib/lib/FlashVideo/Site/Youtubenocookie.pm ##{ .sitemodules { package main; } ##} .sitemodules #!/usr/bin/perl BEGIN { $INC{'FlashVideo/URLFinder.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. BEGIN { $INC{'FlashVideo/Generic.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Generic.pm { package FlashVideo::Generic; use strict; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *info = \&FlashVideo::Utils::info; *extract_title = \&FlashVideo::Utils::extract_title; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *get_video_filename = \&FlashVideo::Utils::get_video_filename; *url_exists = \&FlashVideo::Utils::url_exists; *EXTENSIONS = \&FlashVideo::Utils::EXTENSIONS; } use URI; use URI::Escape qw(uri_unescape); my $video_re = qr!http[-:/a-z0-9%_.?=&]+@{[EXTENSIONS]} (?:\?[-:/a-z0-9%_.?=&]+)?!xi; sub find_video { my ($self, $browser, $embed_url, $prefs) = @_; if (!$browser->success) { $browser->get($browser->response->header('Location')); die "Couldn't download URL: " . $browser->response->status_line unless $browser->success; } my ($possible_filename, $actual_url, $title); $title = extract_title($browser); my @flv_urls = map { (m{http://.+?(http://.+?@{[EXTENSIONS]})}i) ? $1 : $_ } ($browser->content =~ m{($video_re)}gi); if (@flv_urls) { require LWP::Simple; require Memoize; Memoize::memoize("LWP::Simple::head"); @flv_urls = sort { (LWP::Simple::head($a))[1] <=> (LWP::Simple::head($b))[1] } @flv_urls; $possible_filename = (split /\//, $flv_urls[-1])[-1]; if ($flv_urls[-1] =~ /^http%3a%2f%2f/) { $flv_urls[-1] = uri_unescape($flv_urls[-1]) } $actual_url = url_exists($browser->clone, $flv_urls[-1]); } my $filename_is_reliable; if(!$actual_url) { RE: for my $regex( qr{(?si)<embed.*?flashvars=["']?([^"'>]+)}, qr{(?si)<embed.*?src=["']?([^"'>]+)}, qr{(?si)<a[^>]* href=["']?([^"'>]+?@{[EXTENSIONS]})}, qr{(?si)<object[^>]*>.*?<param [^>]*value=["']?([^"'>]+)}, qr{(?si)<object[^>]*>(.*?)</object>}, qr{(?si)<script[^>]*>(.*?)</script>}) { for my $param($browser->content =~ /$regex/gi) { (my $url, $possible_filename, $filename_is_reliable) = find_file_param($browser->clone, $param, $prefs); if($url) { my $resolved_url = url_exists($browser->clone, $url); if($resolved_url) { $actual_url = $resolved_url; last RE; } } } } if(!$actual_url) { for my $iframe($browser->content =~ /<iframe[^>]+src=["']?([^"'>]+)/gi) { $iframe = URI->new_abs($iframe, $browser->uri); debug "Found iframe: $iframe"; my $sub_browser = $browser->clone; $sub_browser->get($iframe); ($actual_url) = eval { $self->find_video($sub_browser, undef, $prefs) }; } } } my @filenames; return $actual_url, $possible_filename if $filename_is_reliable; $possible_filename =~ s/\?.*//; push @filenames, $possible_filename if $possible_filename && $possible_filename !~ /^[0-9_.]+@{[EXTENSIONS]}$/; my $ext = substr(($actual_url =~ /(@{[EXTENSIONS]})$/)[0], 1); push @filenames, title_to_filename($title, $ext) if $title && $title !~ /\Q$possible_filename\E/i; push @filenames, get_video_filename() if !@filenames; return $actual_url, @filenames if $actual_url; my %swf_urls; if (eval { require URI::Find }) { my $finder = URI::Find->new( sub { $swf_urls{$_[1]}++ if $_[1] =~ /\.swf$/i } ); $finder->find(\$browser->content); } else { while ($browser->content =~ m{(http://[^ "']+?\.swf)}ig) { $swf_urls{$_[1]}++; } } if (%swf_urls) { foreach my $swf_url (keys %swf_urls) { if (my ($flv_url, $title) = search_for_flv_in_swf($browser, $swf_url)) { return $flv_url, title_to_filename($title); } } } die "No URLs found"; } sub search_for_flv_in_swf { my ($browser, $swf_url) = @_; $browser = $browser->clone(); $browser->get($swf_url); if (!$browser->success) { die "Couldn't download SWF URL $swf_url: " . $browser->response->status_line(); } my $swf_data = $browser->content; if ('C' eq substr $swf_data, 0, 1) { if (eval { require Compress::Zlib }) { $swf_data = Compress::Zlib::uncompress(substr $swf_data, 8); } else { die "Compress::Zlib is required to uncompress compressed SWF files.\n"; } } if ($swf_data =~ m{(http://.{10,300}?\.flv)}i) { my $flv_url = $1; my $filename = uri_unescape(File::Basename::basename(URI->new($flv_url)->path())); $filename =~ s/\.flv$//i; return ($flv_url, $filename); } return; } sub find_file_param { my($browser, $param, $prefs) = @_; for my $file($param =~ /(?:video|movie|file|path)_?(?:href|src|url)?['"]?\s*[=:,]\s*['"]?([^&'" ]+)/gi, $param =~ /(?:config|playlist|options)['"]?\s*[,:=]\s*['"]?(http[^'"&]+)/gi, $param =~ /['"=](.*?@{[EXTENSIONS]})/gi, $param =~ /([^ ]+@{[EXTENSIONS]})/gi, $param =~ /SWFObject\(["']([^"']+)/) { debug "Found $file"; my ($actual_url, $filename, $filename_is_reliable) = guess_file($browser, $file, '', $prefs); if(!$actual_url && $file =~ /\?(.*)/) { debug "Trying query param on $1"; for my $query_param(split /[;&]/, $1) { my($query_key, $query_value) = split /=/, $query_param; debug "Found $query_value from $query_key"; ($actual_url, $filename, $filename_is_reliable) = guess_file($browser, $query_value, '', $prefs); last if $actual_url; } } if($actual_url) { my $possible_filename = $filename || (split /\//, $actual_url)[-1]; return $actual_url, $possible_filename, $filename_is_reliable; } } if($param =~ m{(rtmp://[^ &"']+)}) { info "This looks like RTMP ($1), no generic support yet.."; } return; } sub guess_file { my($browser, $file, $once, $prefs) = @_; $file = uri_unescape($file) if scalar(() = $file =~ /%[A-F0-9]{2}/gi) > 3; my $orig_uri = URI->new_abs($file, $browser->uri); info "Guessed $orig_uri trying..."; if($orig_uri) { my $uri = url_exists($browser->clone, $orig_uri); if($uri) { my ($package, $url) = FlashVideo::URLFinder->find_package($uri, $browser->clone); if($package && $package ne __PACKAGE__) { debug "$uri is supported by $package."; (my $browser_on_supported_site = $browser->clone())->get($uri); return $package->find_video($browser_on_supported_site, $uri, $prefs), 1; } my $content_type = $browser->response->header("Content-type"); if($content_type =~ m!^(text|application/xml)!) { $browser->add_header("Range", "bytes=0-10000"); $browser->get($uri); $browser->delete_header("Range"); if(FlashVideo::Downloader->check_magic($browser->content) || $uri =~ m!$video_re!) { debug "Found a video at $uri"; return $uri; } return if $browser->content =~ /<html[^>]*>/i; if($browser->content =~ m!($video_re)!) { return $1; } elsif(!defined $once && $browser->content =~ m!(http[-:/a-zA-Z0-9%_.?=&]+)!i) { return guess_file($browser, $1, 1, $prefs); } else { info "Tried $uri, but no video URL found"; } } elsif($content_type =~ m!application/! && $uri ne $orig_uri) { return((find_file_param($browser, $uri))[0]); } else { return $uri->as_string; } } elsif(not defined $once) { if($browser->content =~ /["']([^ ]+\.swf)/) { my $swf_uri = URI->new_abs($1, $browser->uri); if($swf_uri) { my $new_uri = URI->new_abs($file, $swf_uri); debug "Found SWF: $swf_uri -> $new_uri"; if($new_uri ne $uri) { return guess_file($browser, $new_uri, 1, $prefs); } } } } } return; } 1; } ##} blib/lib/FlashVideo/Generic.pm ##{ blib/lib/FlashVideo/URLFinder.pm { package FlashVideo::URLFinder; use strict; BEGIN { FlashVideo::Mechanize->import(); } # (added by utils/combine-perl.pl) BEGIN { FlashVideo::Generic->import(); } # (added by utils/combine-perl.pl) BEGIN { FlashVideo::Site->import(); } # (added by utils/combine-perl.pl) BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *info = \&FlashVideo::Utils::info; } use URI; my @extra_can_handle = qw(Brightcove Mtvnservices Gawker); sub find_package { my($class, $url, $browser) = @_; my $package = _find_package_url($url, $browser); if(!defined $package) { for my $possible_url($browser->content =~ m!(?:<object[^>]+>.*?|<(?:script|embed|iframe|param) [^>]*(?:src=["']?|name=["']src["']\ value=["']))(http://[^"'> ]+)!gixs) { $package = _find_package_url($possible_url, $browser); return _found($package, $possible_url) if defined $package; } } if(!defined $package) { for(@extra_can_handle) { my $possible_package = _load($_); my $r = $possible_package->can_handle($browser, $url); if($r) { $package = $possible_package; last; } } } if(!defined $package) { $package = "FlashVideo::Generic"; } return _found($package, $url); } sub _find_package_url { my($url, $browser) = @_; my $package; foreach my $host_part (split /\./, URI->new($url)->host) { $host_part = lc $host_part; $host_part =~ s/[^a-z0-9]//i; my $possible_package = _load($host_part); if($possible_package->can("find_video")) { if($possible_package->can("can_handle")) { next unless $possible_package->can_handle($browser, $url); } $package = $possible_package; last; } } return $package; } sub _found { my($package, $url) = @_; info "Using method '" . lc((split /::/, $package)[-1]) . "' for $url"; return $package, $url; } sub _load { my($site) = @_; my $package = "FlashVideo::Site::" . ucfirst lc $site; if(eval "require $package") { no strict 'refs'; push @{$package . "::ISA"}, "FlashVideo::Site"; } return $package; } 1; } ##} blib/lib/FlashVideo/URLFinder.pm BEGIN { $INC{'FlashVideo/RTMPDownloader.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/RTMPDownloader.pm { package FlashVideo::RTMPDownloader; use strict; BEGIN { FlashVideo::Downloader->import(); } # (added by utils/combine-perl.pl) use base 'FlashVideo::Downloader'; use IPC::Open3; use Fcntl (); use Symbol qw(gensym); BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *info = \&FlashVideo::Utils::info; *error = \&FlashVideo::Utils::error; *swfhash = \&FlashVideo::Utils::swfhash; *is_program_on_path = \&FlashVideo::Utils::is_program_on_path; } use constant LATEST_RTMPDUMP => 2.2; sub download { my ($self, $rtmp_data) = @_; $self->{printable_filename} = $rtmp_data->{flv}; my $file = $rtmp_data->{flv} = $self->get_filename($rtmp_data->{flv}); if (-s $file && !$rtmp_data->{live}) { info "RTMP output filename '$self->{printable_filename}' already " . "exists, asking to resume..."; $rtmp_data->{resume} = ''; } if(my $socks = FlashVideo::Mechanize->new->get_socks_proxy) { $rtmp_data->{socks} = $socks; } my($r_fh, $w_fh); # So Perl doesn't close them behind our back.. if ($rtmp_data->{live} && $self->action eq 'play') { pipe($r_fh, $w_fh); my $pid = fork; die "Fork failed" unless defined $pid; if(!$pid) { fcntl $r_fh, Fcntl::F_SETFD(), ~Fcntl::FD_CLOEXEC(); exec $self->replace_filename($self->player, "/dev/fd/" . fileno $r_fh); die "Exec failed\n"; } fcntl $w_fh, Fcntl::F_SETFD(), ~Fcntl::FD_CLOEXEC(); $rtmp_data->{flv} = "/dev/fd/" . fileno $w_fh; $self->{stream} = undef; } my $prog = $self->get_rtmp_program; if($prog eq 'flvstreamer' && ($rtmp_data->{rtmp} =~ /^rtmpe:/ || $rtmp_data->{swfhash})) { error "FLVStreamer does not support " . ($rtmp_data->{swfhash} ? "SWF hashing" : "RTMPE streams") . ", please install rtmpdump."; exit 1; } if($self->debug) { $rtmp_data->{verbose} = undef; } my($return, @errors) = $self->run($prog, $rtmp_data); if($return != 0 && "@errors" =~ /failed to connect/i) { info "Couldn't connect on RTMP port, trying port 443 instead"; $rtmp_data->{port} = 443; ($return, @errors) = $self->run($prog, $rtmp_data); } if($file ne '-' && (-s $file < 100 || !$self->check_file($file))) { error "Download failed, no valid file downloaded"; unlink $rtmp_data->{flv}; return 0; } if($return == 2) { info "\nDownload incomplete -- try running again to resume."; return 0; } elsif($return) { info "\nDownload failed."; return 0; } return -s $file; } sub get_rtmp_program { if(is_program_on_path("rtmpdump")) { return "rtmpdump"; } elsif(is_program_on_path("flvstreamer")) { return "flvstreamer"; } return "rtmpdump"; } sub get_command { my($self, $rtmp_data, $debug) = @_; return map { my $arg = $_; (ref $rtmp_data->{$arg} eq 'ARRAY' ? (map { ("--$arg" => $debug ? $self->shell_escape($_) : $_) } @{$rtmp_data->{$arg}}) : ("--$arg" => (($debug && $rtmp_data->{$arg}) ? $self->shell_escape($rtmp_data->{$arg}) : $rtmp_data->{$arg}) || ())) } keys %$rtmp_data; } sub run { my($self, $prog, $rtmp_data) = @_; debug "Running $prog", join(" ", $self->get_command($rtmp_data, 1)); my($in, $out, $err); $err = gensym; my $pid = open3($in, $out, $err, $prog, $self->get_command($rtmp_data)); local $SIG{INT}; if ($^O =~ /mswin/i) { $SIG{INT} = sub { kill 'TERM', $pid; exit; }; } my $complete = 0; my $buf = ""; my @error; while(sysread($err, $buf, 128, length $buf) > 0) { $buf =~ s/\015\012/\012/g; my @parts = split /\015/, $buf; $buf = ""; for(@parts) { if(/^((?:DEBUG:|WARNING:|Closing connection|ERROR: No playpath found).*)\n/) { debug "$prog: $1"; } elsif(/^(ERROR: .*)\012/) { push @error, $1; info "$prog: $1"; } elsif(/^([0-9.]+) kB(?:\s+\/ \S+ sec)?(?: \(([0-9.]+)%\))?/i) { $self->{downloaded} = $1 * 1024; my $percent = $2; if($self->{downloaded} && $percent != 0) { $self->{content_length} = $self->{downloaded} / ($percent / 100); } $self->progress; } elsif(/\012$/) { for my $l(split /\012/) { if($l =~ /^[A-F0-9]{,2}(?:\s+[A-F0-9]{2})*\s*$/) { debug $l; } elsif($l =~ /Download complete/) { $complete = 1; } elsif($l =~ /\s+filesize\s+(\d+)/) { $self->{content_length} = $1; } elsif($l =~ /\w/) { print STDERR "\r" if $self->{downloaded}; info $l; if($l =~ /^RTMPDump v([0-9.]+)/ && $1 < LATEST_RTMPDUMP) { error "==== Using the latest version of RTMPDump (version " . LATEST_RTMPDUMP . ") is recommended. ===="; } } } if(/open3/) { error "\nMake sure you have 'rtmpdump' or 'flvstreamer' installed and available on your PATH."; return 0; } } else { $buf = $_; } } if(defined $self->{stream} && $self->{downloaded} > 300_000) { $self->{stream}->(); } } waitpid $pid, 0; return $? >> 8, @error; } 1; } ##} blib/lib/FlashVideo/RTMPDownloader.pm BEGIN { $INC{'FlashVideo/Search.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/Search.pm { package FlashVideo::Search; use strict; use Carp; BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *info = \&FlashVideo::Utils::info; } my @sites_with_search = ('4oD', 'GoogleVideoSearch'); sub search { my ($class, $search, $max_per_site, $max_results) = @_; my @search_sites = map { FlashVideo::URLFinder::_load($_) } @sites_with_search; if ($search =~ /^(\w+) \w+/) { my $possible_site = ucfirst lc $1; debug "Checking to see if '$possible_site' in '$search' is a search-supported site."; my $possible_package = FlashVideo::URLFinder::_load($possible_site); if ($possible_package->can("search")) { debug "Search for '$search' will only search $possible_site."; $search =~ s/^\w+ //; return search_site($possible_package, $search, "site", $max_results); } } my @plugins = App::get_flash_videos::get_installed_plugins(); foreach my $plugin (@plugins) { $plugin =~ s/\.pm$//; my $plugin_package = FlashVideo::URLFinder::_load($plugin); if ($plugin_package->can("search")) { debug "Plugin '$plugin' has a search method."; unshift @search_sites, $plugin_package; } else { debug "Plugin '$plugin' doesn't have a search method."; } } my @results = map { search_site($_, $search, "all", $max_per_site) } @search_sites; trim_resultset(\@results, $max_results); return @results; } sub search_site { my($search_site, $search, $type, $max) = @_; debug "Searching '$search_site' for '$search'."; if (my @site_results = eval { $search_site->search($search, $type) }) { debug "Found " . @site_results . " results for $search."; trim_resultset(\@site_results, $max); return @site_results; } elsif($@) { info "Searching '$search_site' failed with: $@"; } else { debug "No results found for '$search'."; } return (); } sub trim_resultset { my ($results, $max) = @_; croak "Must be supplied a reference to resultset" unless ref($results) eq 'ARRAY'; croak "No max supplied" unless $max; if (@$results > $max) { debug "Found " . @$results . " results, trimming to $max."; splice @$results, $max; } } 1; } ##} blib/lib/FlashVideo/Search.pm BEGIN { $INC{'FlashVideo/VideoPreferences.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. BEGIN { $INC{'FlashVideo/VideoPreferences/Quality.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/VideoPreferences/Quality.pm { package FlashVideo::VideoPreferences::Quality; use strict; my %format_map = ( "240p" => [320, 240, "low"], "240w" => [427, 240, "low"], "480p" => [640, 480, "medium"], "480w" => [854, 480, "medium"], "576p" => [720, 576, "medium"], "720p" => [1280, 720, "high"], "1080p" => [1920, 1080, "high"], ); sub new { my($class, $quality) = @_; return bless \$quality, $class; } sub name { my($self) = @_; return $$self; } sub choose { my($self, @available) = @_; my $max_preferred_res = $self->quality_to_resolution($self->name); my $max_preferred_size = $max_preferred_res->[0] * $max_preferred_res->[1]; my @sorted = sort { $a->[0] <=> $b->[0] } map { my $r = $_->{resolution}; $r = $r->[0] * $r->[1]; [$r, $_] } @available; if(my @at_or_under_preferred = grep { $_->[0] <= $max_preferred_size } @sorted) { return $at_or_under_preferred[-1]->[1]; } else { return $sorted[0]->[1]; } } sub format_to_resolution { my($self, $name) = @_; $name .= "p" if $name !~ /[a-z]$/i; if(my $resolution = $format_map{lc $name}) { return $resolution; } elsif(my $num = ($name =~ /(\d+)/)[0]) { my $resolution = [($num) x 2]; return [@$resolution, $self->resolution_to_quality($resolution)]; } die "Unknown format '$name'"; } sub quality_to_resolution { my($self, $quality) = @_; if($quality =~ /^(\d+)x(\d+)$/) { my $resolution = [$1, $2]; return [@$resolution, $self->resolution_to_quality($resolution)]; } elsif(my $resolution = eval { $self->format_to_resolution($quality) }) { return $resolution; } else { for my $r(sort { ($b->[0]*$b->[1]) <=> ($a->[0]*$a->[1]) } values %format_map) { if($r->[2] eq lc $quality) { return $r; } } } die "Unknown quality '$quality'"; } sub resolution_to_quality { my($self, $resolution) = @_; my $quality = "high"; for my $r(sort { ($b->[0]*$b->[1]) <=> ($a->[0]*$a->[1]) } values %format_map) { $quality = $r->[2] if $r->[0] >= $resolution->[0]; } return $quality; } 1; } ##} blib/lib/FlashVideo/VideoPreferences/Quality.pm BEGIN { $INC{'FlashVideo/VideoPreferences/Account.pm'}++; } # Part of get-flash-videos. See get_flash_videos for copyright. ##{ blib/lib/FlashVideo/VideoPreferences/Account.pm { package FlashVideo::VideoPreferences::Account; use strict; sub new { my($class, $site, $prompt) = @_; require Net::Netrc; # Core since 5.8 my $record = Net::Netrc->lookup($site); my($user, $pass) = $record ? $record->lpa : (); if(!$user) { print $prompt; print "Username: "; chomp($user = <STDIN>); } if(!$pass) { print "Ok, need your password"; if(eval { require Term::ReadKey }) { print ": "; Term::ReadKey::ReadMode(2); chomp($pass = <STDIN>); Term::ReadKey::ReadMode(0); print "\n"; } else { print " (will be displayed): "; chomp($pass = <STDIN>); } } return bless { username => $user, password => $pass, }, $class; } sub username { my($self) = @_; return $self->{username}; } sub password { my($self) = @_; return $self->{password}; } 1; } ##} blib/lib/FlashVideo/VideoPreferences/Account.pm ##{ blib/lib/FlashVideo/VideoPreferences.pm { package FlashVideo::VideoPreferences; use strict; BEGIN { FlashVideo::VideoPreferences::Quality->import(); } # (added by utils/combine-perl.pl) BEGIN { FlashVideo::VideoPreferences::Account->import(); } # (added by utils/combine-perl.pl) sub new { my($class, %opt) = @_; return bless { quality => $opt{quality} || "high", subtitles => $opt{subtitles} || 0, }, $class; } sub quality { my($self) = @_; return FlashVideo::VideoPreferences::Quality->new($self->{quality}); } sub subtitles { my($self) = @_; return $self->{subtitles}; } sub account { my($self, $site, $prompt) = @_; return FlashVideo::VideoPreferences::Account->new($site, $prompt); } 1; } ##} blib/lib/FlashVideo/VideoPreferences.pm ##{ bin/get_flash_videos { package main; $::SCRIPT_NAME = "get_flash_videos"; $::INSTALL_TYPE = "cpan-manual"; package App::get_flash_videos; use strict; use Encode (); use File::Basename qw(basename); use File::stat; use Getopt::Long; use Text::Wrap; BEGIN { if(!$::SCRIPT_NAME) { require Cwd; require File::Spec; my($vol, $dir) = (File::Spec->splitpath(Cwd::realpath($0)))[0, 1]; unshift @INC, File::Spec->catpath($vol, File::Spec->catdir($dir, "lib")); } } BEGIN { FlashVideo::URLFinder->import(); } # (added by utils/combine-perl.pl) BEGIN { FlashVideo::Mechanize->import(); } # (added by utils/combine-perl.pl) BEGIN { FlashVideo::Downloader->import(); } # (added by utils/combine-perl.pl) BEGIN { FlashVideo::RTMPDownloader->import(); } # (added by utils/combine-perl.pl) BEGIN { FlashVideo::Search->import(); } # (added by utils/combine-perl.pl) BEGIN { FlashVideo::Utils->import(); } # (added by utils/combine-perl.pl) BEGIN { no strict 'refs'; *debug = \&FlashVideo::Utils::debug; *info = \&FlashVideo::Utils::info; *error = \&FlashVideo::Utils::error; *get_user_config_dir = \&FlashVideo::Utils::get_user_config_dir; *get_win_codepage = \&FlashVideo::Utils::get_win_codepage; *is_program_on_path = \&FlashVideo::Utils::is_program_on_path; *get_terminal_width = \&FlashVideo::Utils::get_terminal_width; } BEGIN { FlashVideo::VideoPreferences->import(); } # (added by utils/combine-perl.pl) unshift @INC, \&plugin_loader; our $VERSION; BEGIN { $VERSION = "1.24"; } our %opt; BEGIN { my $player = "mplayer -really-quiet"; $player = "VLC" if $^O =~ /MSWin/i; $player = "open" if $^O =~ /darwin/ && !is_program_on_path("mplayer"); if(is_program_on_path("xdg-open") && !is_program_on_path("mplayer")) { $player = "xdg-open"; } elsif(is_program_on_path("gnome-open") && !is_program_on_path("mplayer")) { $player = "gnome-open"; } elsif(is_program_on_path("kde-open") && !is_program_on_path("mplayer")) { $player = "kde-open"; } %opt = ( yes => 0, filename => '', version => 0, update => 0, play => 0, player => $player, proxy => '', debug => 0, quiet => 0, quality => "high", subtitles => 0, ); } use constant VER_INFO => <<EOF; get_flash_videos version $VERSION (http://code.google.com/p/get-flash-videos/) EOF use constant USAGE => VER_INFO . <<EOF; Usage: $0 [OPTION]... URL... $0 [OPTION]... search string Downloads videos from the web pages given in URL or searches Google Video Search for 'search string'. If the URL contains characters such as '&' you will need to quote it. Options: --add-plugin Add a plugin from a URL. -d --debug Print extra debugging information. -f --filename Filename to save the video as. -p --play Start playing the video once enough has been downloaded. --player Player to use for the video (default: $opt{player}). --proxy Proxy to use, use host:port for SOCKS, or URL for HTTP. --subtitles Download subtitles where available. -q --quiet Be quiet (only print errors). -r --quality Quality to download at (high|medium|low, or site specific). -u --update Update to latest version. -v --version Print version. -y --yes Say yes to any questions (don't prompt for any information). EOF use constant REQ_INFO => <<EOF; A required Perl module for downloading this video is not installed. EOF use constant FRIENDLY_FAILURE => <<EOF; Couldn't extract Flash movie URL. This site may need specific support adding, or fixing. Please confirm the site is using Flash video and if you have Flash available check that the URL really works(!). Check for updates by running: $0 --update If the latest version does not support this please open a bug (or contribute a patch!) at http://code.google.com/p/get-flash-videos/ make sure you include the output with --debug enabled. EOF read_conf(); GetOptions( "yes|y" => \$opt{yes}, "filename|f=s" => \$opt{filename}, "version|v" => \$opt{version}, "update|u" => \$opt{update}, "help|h" => \$opt{help}, "play|p" => \$opt{play}, "player=s" => \$opt{player}, "proxy=s" => \$opt{proxy}, "debug|d" => \$opt{debug}, "quiet|q" => \$opt{quiet}, "add-plugin=s" => \$opt{add_plugin}, "quality|r=s" => \$opt{quality}, "subtitles" => \$opt{subtitles}, ) or die "Try $0 --help for more information.\n"; if($opt{version}) { die VER_INFO; } elsif($opt{update}) { exit update(); } elsif($opt{help}) { die USAGE; } elsif($opt{add_plugin}) { exit add_plugin($opt{add_plugin}); } if ($opt{debug}) { if(my @plugins = get_installed_plugins()) { debug @plugins . " plugin" . (@plugins != 1 && "s") . " installed:"; debug "- $_" for @plugins; } else { debug "No plugins installed"; } } if($^O =~ /MSWin/i) { $opt{filename} = Encode::decode(get_win_codepage(), $opt{filename}); binmode STDERR, ":encoding(" . get_win_codepage() . ")"; binmode STDOUT, ":encoding(" . get_win_codepage() . ")"; } else { $opt{filename} = Encode::decode("utf-8", $opt{filename}); binmode STDERR, ":utf8"; binmode STDOUT, ":utf8"; } my (@urls) = @ARGV; @urls > 0 or die USAGE; my $search; if ( ((@urls == 1) and $urls[0] !~ m'\.') or ( (@urls > 1) and ! grep /^http:\/\/|^[\w\-]+\.[\w\-]+/, @urls)) { $search = join ' ', @urls; } my @download_urls; if ($search) { if (my @results = FlashVideo::Search->search($search, 10, 20)) { if ($opt{yes} or @results == 1) { my $message = (@results == 1) ? "Downloading only match for '$search': '$results[0]->{name}'" : "Downloading first match for '$search': '$results[0]->{name}'" ; info $message; push @download_urls, $results[0]->{url}; } else { print "Search for '$search' found these results:\n"; my $columns = get_terminal_width() - 5; local $Text::Wrap::columns = $columns; my $count = 1; for my $result(@results) { printf "[%2d] %s\n", $count, $result->{name}; if ($result->{description}) { print wrap(" ", " ", substr($result->{description}, 0, $columns * 2)), "\n"; } $count++; } print "Enter the number(s) or range (e.g. 1-3) of the videos to download " . "(separate multiple with comma or space): "; chomp(my $choice = <STDIN>); $choice ||= 1; for(split /[ ,]+/, $choice) { if (/-/) { my ($lower, $upper) = split /-/, $choice; if ($upper > $lower and $upper > 0) { push @download_urls, map { $results[$_]->{url} } $lower - 1 .. $upper - 1; next; } else { print STDERR "Search range '$_' is invalid.\n"; exit 1; } } $_--; if (!$results[$_]) { print STDERR "'$_' is an invalid choice.\n"; exit 1; } push @download_urls, $results[$_]->{url}; } } } else { print STDERR "No results found for '$search'.\n"; exit 1; } } else { @download_urls = @urls; } my $download_count = 0; my $prefs = FlashVideo::VideoPreferences->new(%opt); foreach my $url (@download_urls) { if (download($url, $prefs, @download_urls - $download_count)) { $download_count++; } } if($download_count == 0) { info "Couldn't download any videos."; exit 1; } elsif($download_count != @download_urls) { info "Problems downloading some videos."; exit 2; } exit 0; sub download { my($url, $prefs, $remaining) = @_; $url = "http://$url" if $url !~ m!^\w+:!; info "Downloading $url"; my $browser = FlashVideo::Mechanize->new; $browser->get($url); if (!$browser->success and !$browser->response->is_redirect) { error "Couldn't download '$url': " . $browser->response->status_line; } my($package, $possible_url) = FlashVideo::URLFinder->find_package($url, $browser); my($actual_url, @suggested_fnames) = eval { $package->find_video($browser, $possible_url, $prefs); }; if(!$actual_url) { if($@ =~ /^Must have | requires /) { my $error = "$@"; $error =~ s/at $0.*//; print STDERR "$error" . REQ_INFO; return 0; } else { print STDERR "Error: $@" . FRIENDLY_FAILURE; return 0; } } my $suggested_filename = $suggested_fnames[-1]; if (!$opt{play}) { if (!$opt{yes} && @suggested_fnames > 1) { print "There are different suggested filenames, please choose:\n"; my $count; foreach my $filename (@suggested_fnames) { $count++; print "$count - $filename\n"; } print "\nWhich filename would you like to use?: "; chomp(my $chosen_fname = <STDIN>); $suggested_filename = $suggested_fnames[$chosen_fname - 1] || $suggested_fnames[-1]; } } my $save_as = $opt{filename} || $suggested_filename; my $action = $opt{play} ? "play" : "download"; for my $data((ref($actual_url) eq 'ARRAY' ? @$actual_url : $actual_url)) { my $downloader; my $file = $save_as; if(ref $data eq 'HASH') { $downloader = FlashVideo::RTMPDownloader->new; $file ||= $data->{flv}; } else { $downloader = FlashVideo::Downloader->new; } if (ref $data eq 'ARRAY') { my ($url, $part_number, $part_count, $part_size) = @$data; $data = $url; if (defined $part_number && defined $part_count) { my $part_suffix = sprintf('.part%02d_of_%02d', $part_number, $part_count); substr $file, rindex($file, '.'), 0, $part_suffix if $part_count > 1; } if (defined $part_size && -f $file && -s $file == $part_size) { info "Already downloaded $file ($part_size bytes)"; next; } } my $size = $downloader->$action($data, $file, $browser) || return 0; info "\n" . ($remaining == 1 ? "Done. " : "") . "Saved $size bytes to $downloader->{printable_filename}"; } return 1; } sub read_conf { for my $file("/etc/get_flash_videosrc", "$ENV{HOME}/.get_flash_videosrc") { open my $fh, "<", $file or next; while(<$fh>) { s/\r?\n//; next if /^\s*(#|$)/; my($n, $v) = split /\s*=\s*/; $v = 1 unless defined $v; $opt{$n} = $v; } } } sub add_plugin { my($plugin_url) = @_; my $uri = URI->new($plugin_url); unless(-d get_plugin_dir()) { require File::Path; File::Path::mkpath(get_plugin_dir()) or die "Unable to create plugin dir: $!"; } my $filename = get_plugin_dir() . "/" . basename($uri->path); if($filename !~ /\.pm$/) { die "Plugins must have a file extension of '.pm'\n"; } if(!$uri->scheme) { require File::Copy; File::Copy::copy($plugin_url => $filename) || die "Unable to copy plugin to '$filename': $!\n"; info "Plugin installed."; return 0; } else { my $browser = FlashVideo::Mechanize->new; return !install_plugin($browser, $plugin_url, $filename); } } sub update { my %update_types = ( 'cpan-cpan' => [1, "cpan " . __PACKAGE__], 'cpan-cpanp' => [1, "cpanp i " . __PACKAGE__], 'cpan-cpanm' => [1, "cpanm " . __PACKAGE__], 'cpan-manual' => [0, "Manual install"], ); if($::SCRIPT_NAME) { my $browser = FlashVideo::Mechanize->new; $browser->get("http://get-flash-videos.googlecode.com/svn/wiki/Version.wiki"); if(!$browser->response->is_success) { die "Unable to retrieve version data: " . $browser->response->status_line . "\n"; } my $version = ($browser->content =~ /version: (\S+)/)[0]; my $base = ($browser->content =~ /from: (\S+)/)[0]; my $info = ($browser->content =~ /info: (\S+)/)[0]; my $url = $base . $::SCRIPT_NAME . "-" . $version; die "Unable to parse version data" unless $version and $base; my @v = split /\./, $version; my @V = split /\./, $VERSION; my $newer = 0; my $i = 0; for(@v) { $newer = 1 if !defined $V[$i] || $_ > $V[$i]; last if $V[$i] > $v[$i]; $i++; } if($newer) { info "Newer version ($version) available"; debug "(Install type: $::INSTALL_TYPE)"; if($::INSTALL_TYPE =~ /^cpan-/) { my $update_method = $update_types{$::INSTALL_TYPE}; if($update_method->[0]) { info "This was installed via CPAN, you may upgrade by running:"; info $update_method->[1]; my $run_cpan = $opt{yes} || do { info "Shall I run that for you? (Y/n)"; <STDIN> =~ /(?:^\s*$|y)/i; }; if($run_cpan) { system $update_method->[1]; } } else { info "Please visit http://code.google.com/p/get-flash-videos to upgrade"; } } else { update_script($browser, $url, $info); } } else { print STDERR "You already have the latest version.\n"; } } else { info "Development version, not updated"; } update_plugins(); return 0; # exit code } sub update_script { my($browser, $url, $info) = @_; info "Downloading new version..."; die "Cannot update -- unable to write to $0\n" unless -w $0; my $new_file = $0 . ".new"; $browser->mirror($url, $new_file); if($browser->response->is_success && -f $new_file) { rename $0, "$0.old" or die "Unable to rename $0 to $0.old: $!"; rename $new_file, $0 or die "Unable to rename $new_file to $0: $!"; chmod 0755, $0; info "New version installed as $0"; info "(previous version backed up to $0.old)."; info $info; } else { die "Download failed: " . $browser->response->status_line; } } sub update_plugins { my $browser = FlashVideo::Mechanize->new; foreach my $plugin(get_installed_plugins()) { debug "Seeing if there is an update for $plugin.."; my $file = get_plugin_dir() . "/$plugin"; require $file; my $package = "FlashVideo::Site::" . ($plugin =~ /(.*)\.pm$/)[0]; if($package->can("update")) { $package->update(); } else { no strict 'refs'; my $downloaded = 0; my $newer_found = 0; foreach my $update_url (@{ "$package\::update_urls" }) { $browser->head($update_url); if (!$browser->response->is_success) { debug "Couldn't retrieve $update_url for $plugin: " . $browser->response->status_line; next; } my $file_mtime = stat($file)->mtime; my $remote_plugin_mtime = $browser->response->last_modified; if ($remote_plugin_mtime > $file_mtime) { info "Newer version of plugin $plugin found at $update_url, trying to download and install"; $newer_found = 1; if ($downloaded = install_plugin($browser, $update_url, $file)) { last; } } else { debug "Plugin $plugin is already the lastest version."; debug "(Remote: " . $browser->response->header("Last-Modified") . "; Local: " . gmtime($file_mtime) . " GMT)"; } } if ($newer_found and !$downloaded) { die "Couldn't install $plugin plugin"; } } } } sub install_plugin { my ($browser, $url, $file) = @_; my $plugin_exists = -f $file; my $new_file = $plugin_exists ? "$file.new" : $file; $browser->mirror($url, $new_file); if ($browser->response->is_success && -f $new_file) { my $short_name = basename($file); if ($plugin_exists) { rename $file, "$file.old" or die "Unable to rename $file to $file.old: $!"; rename $new_file, $file or die "Unable to rename $new_file to $file: $!"; info "New version of $short_name installed as $file"; info "(previous version backed up to $file.old)."; } else { info "New plugin $short_name installed as $file"; } return 1; } else { warn "Download failed: " . $browser->response->status_line; } return 0; } sub plugin_loader { my (undef, $module) = @_; if ($module =~ m'^FlashVideo/Site/(.*)') { my $plugin_name = $1; my $plugin_dir = get_plugin_dir(); debug "Trying to open plugin $plugin_dir/$plugin_name"; if (open my $plugin_fh, '<', "$plugin_dir/$plugin_name") { return $plugin_fh; # Perl then reads the plugin from the FH } } return; } sub get_installed_plugins { my $plugin_dir = get_plugin_dir(); my @plugins; if (opendir my $plugin_dir_dh, $plugin_dir) { @plugins = grep /\.pm$/i, readdir $plugin_dir_dh; closedir $plugin_dir_dh; } return @plugins; } sub get_plugin_dir { return get_user_config_dir() . "/plugins"; } } ##} bin/get_flash_videos