#!/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://code.google.com/p/get-flash-videos/source/checkout
#
#################################################
#
# 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/5min.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.
##{ ./FlashVideo/Utils.pm
{
package FlashVideo::Utils;
use strict;
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)/;
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);
sub debug(@) {
my $string = "@_\n";
$string =~ s/\Q$ENV{HOME}\E/~/g;
print STDERR $string if $::opt{debug};
}
sub info(@) {
print STDERR "@_\n" unless $::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) = @_;
$type ||= "flv";
$type = substr $1, 1 if $title =~ s/(@{[EXTENSIONS]})$//;
$type = substr $1, 1 if $type =~ s/(@{[EXTENSIONS]})$//;
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;
}
1;
}
##} ./FlashVideo/Utils.pm
##{ ./FlashVideo/Site/5min.pm
{
package FlashVideo::Site::5min;
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; *extract_title = \&FlashVideo::Utils::extract_title; *extract_info = \&FlashVideo::Utils::extract_info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *get_video_filename = \&FlashVideo::Utils::get_video_filename; *url_exists = \&FlashVideo::Utils::url_exists; *swfhash = \&FlashVideo::Utils::swfhash; *swfhash_data = \&FlashVideo::Utils::swfhash_data; *EXTENSIONS = \&FlashVideo::Utils::EXTENSIONS; *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; }
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;
}
##} ./FlashVideo/Site/5min.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.
##{ ./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; *error = \&FlashVideo::Utils::error; *extract_title = \&FlashVideo::Utils::extract_title; *extract_info = \&FlashVideo::Utils::extract_info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *get_video_filename = \&FlashVideo::Utils::get_video_filename; *url_exists = \&FlashVideo::Utils::url_exists; *swfhash = \&FlashVideo::Utils::swfhash; *swfhash_data = \&FlashVideo::Utils::swfhash_data; *EXTENSIONS = \&FlashVideo::Utils::EXTENSIONS; *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; }
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 unless $metadata->{lineupId};
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($::opt{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;
}
##} ./FlashVideo/Site/Brightcove.pm
##{ ./FlashVideo/Site/About.pm
{
package FlashVideo::Site::About;
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; *extract_title = \&FlashVideo::Utils::extract_title; *extract_info = \&FlashVideo::Utils::extract_info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *get_video_filename = \&FlashVideo::Utils::get_video_filename; *url_exists = \&FlashVideo::Utils::url_exists; *swfhash = \&FlashVideo::Utils::swfhash; *swfhash_data = \&FlashVideo::Utils::swfhash_data; *EXTENSIONS = \&FlashVideo::Utils::EXTENSIONS; *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; }
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;
}
##} ./FlashVideo/Site/About.pm
BEGIN { $INC{'FlashVideo/Site/Amazon.pm'}++; }
# Part of get-flash-videos. See get_flash_videos for copyright.
##{ ./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; *info = \&FlashVideo::Utils::info; *error = \&FlashVideo::Utils::error; *extract_title = \&FlashVideo::Utils::extract_title; *extract_info = \&FlashVideo::Utils::extract_info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *get_video_filename = \&FlashVideo::Utils::get_video_filename; *url_exists = \&FlashVideo::Utils::url_exists; *swfhash = \&FlashVideo::Utils::swfhash; *swfhash_data = \&FlashVideo::Utils::swfhash_data; *EXTENSIONS = \&FlashVideo::Utils::EXTENSIONS; *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; }
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;
die "Must have XML::Simple installed to parse SMIL"
unless eval { require XML::Simple };
my $parsed_smil = eval { XML::Simple::XMLin($smil) };
if ($@) {
die "Couldn't parse 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;
}
##} ./FlashVideo/Site/Amazon.pm
BEGIN { $INC{'FlashVideo/Site/Apple.pm'}++; }
# Part of get-flash-videos. See get_flash_videos for copyright.
##{ ./FlashVideo/Site/Apple.pm
{
package FlashVideo::Site::Apple;
use strict;
sub find_video {
my ($self, $browser) = @_;
if(!FlashVideo::Downloader->check_file($browser->content)) {
my @urls = sort
{ ($b =~ /(\d+)p\.mov/)[0] <=> ($a =~ /(\d+)p\.mov/)[0] }
$browser->content =~ /['"]([^'"]+\.mov)['"]/g;
die "No .mov URLs found on page" unless @urls;
$browser->get($urls[0]);
}
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;
}
##} ./FlashVideo/Site/Apple.pm
BEGIN { $INC{'FlashVideo/Site/Bbc.pm'}++; }
# Part of get-flash-videos. See get_flash_videos for copyright.
##{ ./FlashVideo/Site/Bbc.pm
{
package FlashVideo::Site::Bbc;
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; *extract_title = \&FlashVideo::Utils::extract_title; *extract_info = \&FlashVideo::Utils::extract_info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *get_video_filename = \&FlashVideo::Utils::get_video_filename; *url_exists = \&FlashVideo::Utils::url_exists; *swfhash = \&FlashVideo::Utils::swfhash; *swfhash_data = \&FlashVideo::Utils::swfhash_data; *EXTENSIONS = \&FlashVideo::Utils::EXTENSIONS; *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; }
sub find_video {
my ($self, $browser, $page_url) = @_;
my $has_xml_simple = eval { require XML::Simple };
if(!$has_xml_simple) {
die "Must have XML::Simple installed to download BBC videos";
}
my $playlist_xml;
if ($browser->content =~ /content =~ /empDivReady\s*\(([^)]+)/) {
my @params = split /,\s*/, $1;
my $id = $params[3];
my $path = $params[4];
$id =~ s/['"]//g;
$path =~ s/['"]//g;
$playlist_xml = URI->new_abs($path, $browser->uri) . "/media/emp/playlists/$id.xml";
}
elsif($browser->content =~ /setPlaylist\s*\(([^)]+)/) {
my $path = $1;
$path =~ s/['"]//g;
$playlist_xml = URI->new_abs($path, $browser->uri);
}
elsif($browser->content =~ /EmpEmbed.embed\s*\((.*?)\);/) {
my $path = (split /,/, $1)[3];
$path =~ s/"//g;
$playlist_xml = URI->new_abs($path, $browser->uri);
}
elsif($browser->uri =~ m!/(b[0-9a-z]{7})(?:/|$)!) {
my @gi_cmd = (qw(get_iplayer -g --pid), $1);
if($browser->content =~ /buildAudioPlayer/) {
push @gi_cmd, "--type=radio";
}
error "get_flash_videos does not support iplayer, but get_iplayer does..";
info "Attempting to run '@gi_cmd'";
exec @gi_cmd;
error "Please download get_iplayer from http://linuxcentre.net/getiplayer/\n" .
"and install in your PATH";
exit 1;
}
else {
die "Couldn't find BBC XML playlist URL in " . $browser->uri->as_string;
}
$browser->get($playlist_xml);
if (!$browser->success) {
die "Couldn't download BBC XML playlist $playlist_xml: " .
$browser->response->status_line;
}
my $playlist = eval {
XML::Simple::XMLin($browser->content, KeyAttr => {item => 'kind'})
};
if ($@) {
eval {
my $content = $browser->content;
if ($content !~ m{}) {
$content .= "\n\n";
}
$playlist = XML::Simple::XMLin($content, KeyAttr => {item => 'kind'})
};
if ($@) {
die "Couldn't parse BBC XML playlist: $@";
}
}
my $sound = ($playlist->{item}->{guidance} !~ /has no sound/);
my $info = ref $playlist->{item}->{media} eq 'ARRAY'
? $playlist->{item}->{media}->[0]->{connection}
: $playlist->{item}->{media}->{connection};
$info = $playlist->{item}->{programme}->{media}->{connection} unless $info;
$info->{application} ||= "ondemand";
my $data = {
app => $info->{application},
tcUrl => "rtmp://$info->{server}/$info->{application}",
swfUrl => "http://news.bbc.co.uk/player/emp/2.11.7978_8433/9player.swf",
pageUrl => $page_url,
rtmp => "rtmp://" . $info->{server} . "/$info->{application}",
playpath => $info->{identifier},
flv => title_to_filename('BBC - ' . $playlist->{title} .
($sound ? '' : ' (no sound)'))
};
if ($info->{identifier} =~ /^secure/ or $info->{tokenIssuer}) {
my $url = "http://www.bbc.co.uk/mediaselector/4/gtis?server=$info->{server}" .
"&identifier=$info->{identifier}&kind=$info->{kind}" .
"&application=$info->{application}&cb=123";
debug "Got BBC auth URL for 'secure' video: $url";
$browser->get($url);
if (my $redirect = $browser->response->header('Location')) {
debug "BBC auth URL redirects to: $url";
$browser->get($redirect);
}
my $stream_auth = eval {
XML::Simple::XMLin($browser->content);
};
if ($@) {
die "Couldn't parse BBC stream auth XML for 'secure' stream.\n" .
"XML is apparently:\n" .
$browser->content() . "\n" .
"XML::Simple said: $@";
}
my $token = $stream_auth->{token};
if (!$token) {
die "Couldn't get token for 'secure' video download";
}
$data->{app} = "$info->{application}?_fcs_vhost=$info->{server}"
. "&auth=$token"
. "&aifp=v001&slist=" . $info->{identifier};
$data->{tcUrl} = "rtmp://$info->{server}/$info->{application}?_fcs_vhost=$info->{server}"
. "&auth=$token"
. "&aifp=v001&slist=" . $info->{identifier};
$data->{playpath} .= "?auth=$token&aifp=v0001";
if($info->{application} eq 'live') {
$data->{subscribe} = $data->{playpath};
$data->{live} = 1;
}
}
return $data;
}
1;
}
##} ./FlashVideo/Site/Bbc.pm
BEGIN { $INC{'FlashVideo/Site/Blip.pm'}++; }
# Part of get-flash-videos. See get_flash_videos for copyright.
##{ ./FlashVideo/Site/Blip.pm
{
package FlashVideo::Site::Blip;
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; *extract_title = \&FlashVideo::Utils::extract_title; *extract_info = \&FlashVideo::Utils::extract_info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *get_video_filename = \&FlashVideo::Utils::get_video_filename; *url_exists = \&FlashVideo::Utils::url_exists; *swfhash = \&FlashVideo::Utils::swfhash; *swfhash_data = \&FlashVideo::Utils::swfhash_data; *EXTENSIONS = \&FlashVideo::Utils::EXTENSIONS; *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; }
sub find_video {
my ($self, $browser, $embed_url) = @_;
my $base = "http://blip.tv";
my $has_xml_simple = eval { require XML::Simple };
if(!$has_xml_simple) {
die "Must have XML::Simple installed to download Blip videos";
}
my $id;
if($embed_url =~ m{flash/(\d+)}) {
$id = $1;
} else {
$browser->get($embed_url);
if($browser->response->is_redirect
&& $browser->response->header("Location") =~ m!(?:/|%2f)(\d+)!i) {
$id = $1;
} else {
$id = ($browser->content =~ m!/rss/flash/(\d+)!)[0];
}
}
die "No ID found\n" unless $id;
$browser->get("$base/rss/flash/$id");
my $xml = eval {
XML::Simple::XMLin($browser->content)
};
if ($@) {
die "Couldn't parse Blip XML : $@";
}
my $content = $xml->{channel}->{item}->{"media:group"}->{"media:content"};
my $url = ref $content eq 'ARRAY' ? $content->[0]->{url} : $content->{url};
my $filename = title_to_filename($xml->{channel}->{item}->{title}, $url);
$browser->allow_redirects;
return $url, $filename;
}
1;
}
##} ./FlashVideo/Site/Blip.pm
BEGIN { $INC{'FlashVideo/Site/Break.pm'}++; }
# Part of get-flash-videos. See get_flash_videos for copyright.
##{ ./FlashVideo/Site/Break.pm
{
package FlashVideo::Site::Break;
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; *extract_title = \&FlashVideo::Utils::extract_title; *extract_info = \&FlashVideo::Utils::extract_info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *get_video_filename = \&FlashVideo::Utils::get_video_filename; *url_exists = \&FlashVideo::Utils::url_exists; *swfhash = \&FlashVideo::Utils::swfhash; *swfhash_data = \&FlashVideo::Utils::swfhash_data; *EXTENSIONS = \&FlashVideo::Utils::EXTENSIONS; *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; }
use URI::Escape;
sub find_video {
my($self, $browser, $embed_url) = @_;
if(URI->new($embed_url)->host eq "embed.break.com") {
$browser->get($embed_url);
}
if($browser->uri->host eq "embed.break.com") {
if(!$browser->success && $browser->response->header('Location') !~ /sVidLoc/) {
$browser->get($browser->response->header('Location'));
}
if($browser->response->header("Location") =~ /sVidLoc=([^&]+)/) {
my $url = uri_unescape($1);
my $filename = title_to_filename((split /\//, $url)[-1]);
return $url, $filename;
}
}
my $path = ($browser->content =~ /sGlobalContentFilePath='([^']+)'/)[0];
my $filename = ($browser->content =~ /sGlobalFileName='([^']+)'/)[0];
die "Unable to extract path and filename" unless $path and $filename;
my $video_path = ($browser->content =~ /videoPath\s*(?:',|=)\s*['"]([^'"]+)/)[0];
$browser->allow_redirects;
return $video_path . $path . "/" . $filename . ".flv",
title_to_filename($filename);
}
1;
}
##} ./FlashVideo/Site/Break.pm
BEGIN { $INC{'FlashVideo/Site/Channel4.pm'}++; }
# Part of get-flash-videos. See get_flash_videos for copyright.
##{ ./FlashVideo/Site/Channel4.pm
{
package FlashVideo::Site::Channel4;
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; *extract_title = \&FlashVideo::Utils::extract_title; *extract_info = \&FlashVideo::Utils::extract_info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *get_video_filename = \&FlashVideo::Utils::get_video_filename; *url_exists = \&FlashVideo::Utils::url_exists; *swfhash = \&FlashVideo::Utils::swfhash; *swfhash_data = \&FlashVideo::Utils::swfhash_data; *EXTENSIONS = \&FlashVideo::Utils::EXTENSIONS; *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; }
use URI::Escape;
sub find_video {
my ($self, $browser, $embed_url) = @_;
my $has_xml_simple = eval { require XML::Simple };
if(!$has_xml_simple) {
die "Must have XML::Simple installed to download Channel4/4oD videos";
}
my $page_url = $browser->uri->as_string;
my $asset_number;
if($page_url =~ /catch-up#(\d+)/) {
$asset_number = $1;
} else {
die "Couldn't get Channel 4 asset number";
}
my $metadata_url = URI->new(
'http://www.channel4.com/services/catchup-availability/asset-info/'
. $asset_number . '?' . time);
my $host = $metadata_url->host;
my $path = $metadata_url->path_query;
my $request = join "\r\n",
"GET $path HTTP/1.1",
"Host: $host",
"User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.0.1) Gecko/2008072820 Firefox/3.0.1",
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language: en-gb,en;q=0.5",
"Accept-Encoding: gzip,deflate",
"Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7",
"Connection: close",
"\r\n";
my $sock = IO::Socket::INET->new(PeerAddr => $host, PeerPort => 80) or die $!;
print $sock $request;
my $response = HTTP::Response->parse(join '', <$sock>);
if(!$response->is_success) {
error "Couldn't download: " . $response->status_line;
error "Content:\n" . $response->content;
}
return _process_xml($browser, $response->content);
}
sub _process_xml {
my($browser, $xml_string) = @_;
$xml_string =~ s/(?:\r\n)?\s*[a-f0-9]{1,5}\r\n//igm;
$xml_string =~ s/^\s*//s;
debug $xml_string;
my $xml = eval { XML::Simple::XMLin($xml_string) };
die "Couldn't parse XML: $@" if $@;
my($server, $playpath);
my $uriData = $xml->{assetInfo}->{uriData};
if($uriData->{streamUri} =~ m{rtmpe://([^/]+)/\w+/(.*)}) {
$server = $1;
$playpath = $2;
}
my $app = "ondemand?_fcs_vhost=$server&ovpfv=1.1&auth=$uriData->{token}&"
. "aifp=$uriData->{fingerprint}&slist=$uriData->{slist}";
$server = Socket::inet_ntoa(scalar gethostbyname($server));
my $url = "rtmpe://$server:1935/$app";
return {
rtmp => $url,
tcUrl => $url,
app => $app,
playpath => $playpath,
auth => $uriData->{token},
pageUrl => (split /#/, $browser->uri->as_string)[0],
flv => _generate_filename($xml->{assetInfo}),
swfhash($browser,
"http://www.channel4.com/static/programmes/asset/flash/swf/4odplayer-4.3.2.swf"),
};
}
sub _generate_filename {
my($asset) = @_;
my $title = $asset->{brandTitle};
if($asset->{episodeTitle} =~ /\Q$title\E/i) {
$title = $asset->{episodeTitle};
} else {
$title .= " - $asset->{episodeTitle}";
}
my $episode = "";
if($asset->{seriesNumber}) {
$episode = sprintf "S%02d", $asset->{seriesNumber};
}
if($asset->{episodeNumber}) {
$episode .= sprintf "E%02d", $asset->{episodeNumber};
}
$title .= " - $episode" if $episode;
return title_to_filename($title);
}
sub can_handle {
my($self, $browser) = @_;
return $browser->content =~ /catch-up.*(\d+)/;
}
1;
}
##} ./FlashVideo/Site/Channel4.pm
BEGIN { $INC{'FlashVideo/Site/Collegehumor.pm'}++; }
# Part of get-flash-videos. See get_flash_videos for copyright.
##{ ./FlashVideo/Site/Collegehumor.pm
{
package FlashVideo::Site::Collegehumor;
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; *extract_title = \&FlashVideo::Utils::extract_title; *extract_info = \&FlashVideo::Utils::extract_info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *get_video_filename = \&FlashVideo::Utils::get_video_filename; *url_exists = \&FlashVideo::Utils::url_exists; *swfhash = \&FlashVideo::Utils::swfhash; *swfhash_data = \&FlashVideo::Utils::swfhash_data; *EXTENSIONS = \&FlashVideo::Utils::EXTENSIONS; *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; }
sub find_video {
my ($self, $browser, $embed_url) = @_;
my $base = "http://www.collegehumor.com/moogaloop";
my $has_xml_simple = eval { require XML::Simple };
if(!$has_xml_simple) {
die "Must have XML::Simple installed to download Collegehumor videos";
}
my $id;
if($browser->content =~ /clip_id=(\d+)/) {
$id = $1;
} elsif($embed_url =~ m!) {
$id = $1;
}
die "No ID found\n" unless $id;
$browser->get("$base/video:$id");
my $xml = eval {
XML::Simple::XMLin($browser->content)
};
if ($@) {
die "Couldn't parse Collegehumor XML: $@";
}
my $title = $xml->{video}->{caption};
$title = extract_title($browser) if ref $title;
my $filename = title_to_filename($title);
my $url = $xml->{video}->{file};
return $url, $filename;
}
1;
}
##} ./FlashVideo/Site/Collegehumor.pm
BEGIN { $INC{'FlashVideo/Site/Dailymotion.pm'}++; }
# Part of get-flash-videos. See get_flash_videos for copyright.
##{ ./FlashVideo/Site/Dailymotion.pm
{
package FlashVideo::Site::Dailymotion;
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; *extract_title = \&FlashVideo::Utils::extract_title; *extract_info = \&FlashVideo::Utils::extract_info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *get_video_filename = \&FlashVideo::Utils::get_video_filename; *url_exists = \&FlashVideo::Utils::url_exists; *swfhash = \&FlashVideo::Utils::swfhash; *swfhash_data = \&FlashVideo::Utils::swfhash_data; *EXTENSIONS = \&FlashVideo::Utils::EXTENSIONS; *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; }
use URI::Escape;
sub find_video {
my ($self, $browser, $embed_url) = @_;
if ($browser->content =~ /content.is.not.available.for.your.country/i) {
error "Can't (yet) download this video because it's not available " .
"in your area";
exit 1;
}
$browser->allow_redirects;
$browser->content =~ /]*>(.*?)<\//;
my $filename = title_to_filename($1);
my $video;
if ($browser->content =~ /"video", "([^"]+)/) {
$video = uri_unescape($1);
} else {
if ($embed_url !~ m!/swf/!) {
$browser->uri =~ m!video(?:%2F|/)([^_]+)!;
$embed_url = "http://www.dailymotion.com/swf/$1";
}
$browser->get($embed_url);
die "Must have Compress::Zlib for embedded Dailymotion videos\n"
unless eval { require Compress::Zlib; };
my $data = Compress::Zlib::uncompress(substr $browser->content, 8);
$data =~ /\{\{video\}\}\{\{(.*?)\}\}/;
$video = $1;
if($data =~ /videotitle=([^&]+)/) {
$filename = title_to_filename(uri_unescape($1));
}
}
die "Couldn't find video parameter." unless $video;
my @streams;
for(split /\|\|/, $video) {
my($path, $type) = split /@@/;
my($width, $height) = $path =~ /(\d+)x(\d+)/;
push @streams, {
width => $width,
height => $height,
url => URI->new_abs($path, $browser->uri)->as_string
};
}
my $url = (sort { $b->{width} <=> $a->{width} } @streams)[0]->{url};
return $url, $filename;
}
1;
}
##} ./FlashVideo/Site/Dailymotion.pm
BEGIN { $INC{'FlashVideo/Site/Ehow.pm'}++; }
# Part of get-flash-videos. See get_flash_videos for copyright.
##{ ./FlashVideo/Site/Ehow.pm
{
package FlashVideo::Site::Ehow;
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; *extract_title = \&FlashVideo::Utils::extract_title; *extract_info = \&FlashVideo::Utils::extract_info; *title_to_filename = \&FlashVideo::Utils::title_to_filename; *get_video_filename = \&FlashVideo::Utils::get_video_filename; *url_exists = \&FlashVideo::Utils::url_exists; *swfhash = \&FlashVideo::Utils::swfhash; *swfhash_data = \&FlashVideo::Utils::swfhash_data; *EXTENSIONS = \&FlashVideo::Utils::EXTENSIONS; *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; }
use URI::Escape;
sub find_video {
my ($self, $browser) = @_;
my $video_id;
if ($browser->content =~ /flashvars=(?:"|'|")id=(\w+)&/) {
$video_id = $1;
}
else {
die "Couldn't extract video ID from page";
}
my $embed_url =
"http://www.ehow.com/embedvars.aspx?isEhow=true&show_related=true&" .
"from_url=" . uri_escape($browser->uri->as_string) .
"&id=" . $video_id;
my $title;
if ($browser->content =~ /