#!/usr/bin/perl
# This program is free software. It comes without any warranty, to
# the extent permitted by applicable law. You can redistribute it
# and/or modify it under the terms of the Do What The Fuck You Want
# To Public License, Version 2, as published by Sam Hocevar. See
# http://sam.zoy.org/wtfpl/COPYING for more details.
use strict;
use URI;
use IO::Socket;
# Should really rewrite this using LWP, but this works despite being an evil
# hack.
my %ua = (
coremedia => "Apple iPhone v1.1.1 CoreMedia v1.0.0.3A110a",
safari => "Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/3A110a Safari/419.3",
);
my $id = $ARGV[0] || die "Usage: $0 id or url\n";
$id = $1 if $id =~ /\/(b.*?)\.shtml/;
my $page = "http://www.bbc.co.uk/iplayer/page/item/$id.shtml";
my $sock = iphone_req($page, "safari", "");
my($videourl, $video_id, $cookie, $title, $full, @vers);
while(<$sock>) {
print if $ENV{DEBUG};
if(/Location: (.*)/) {
$videourl = $1;
$sock = iphone_req($1, "coremedia", "Range: bytes=0-1\r\n");
} elsif(/Set-Cookie: (BBC-UID.*?);/i) {
$cookie = $1;
my $text = join "", <$sock>; # keepalive causes a pause here
@vers = ($text =~ /iplayer.versions.*?Original.*?'(\w+)/s);
push @vers, $text =~ /iplayer.versions(?:.*?pid\s*:\s*'(\w+)')+/sg;
if(@vers) {
$video_id = shift @vers;
if($text =~ m!
([^<]+)
(?:.*?(.*?)
)?!s) {
$title = "$1" . ($2 ? " - $2" : "");
$title =~ s/[^-A-Z0-9 ']/ /ig;
$title =~ s/(?:^\s+|\s+$)//g;
$title =~ s/\s{2,}/ /g;
} else {
$title = $video_id;
}
$sock = iphone_req("http://www.bbc.co.uk/mediaselector/3/auth/iplayer_streaming_http_mp4/$video_id?" . int(rand 1e6) . "%20", "coremedia", "Range: bytes=0-1\r\n");
} else {
die "video id not found\n";
}
} elsif(!$full && /^Content-Range: .*\/(\d+)\r?$/) {
$full = $1;
$sock = iphone_req($videourl, "coremedia", "Range: bytes=0-$1\r\n");
} elsif(/^\r?$/) {
if(!$full) {
if(@vers) { # try another version (maybe signed still available?)
$video_id = shift @vers;
$sock = iphone_req("http://www.bbc.co.uk/mediaselector/3/auth/iplayer_streaming_http_mp4/$video_id?" . int(rand 1e6) . "%20", "coremedia", "Range: bytes=0-1\r\n");
} else {
die "Didn't find anything to download!\n";
}
} else {
save_to_file($sock, $full);
exit;
}
}
}
my $prev_url;
sub iphone_req {
my($url, $ua, $add) = @_;
my $type = $prev_url ? "new_abs" : "new";
$url = URI->$type($url, $prev_url) unless ref $url;
$prev_url = $url;
my $path = $url->path . ($url->query ? "?" . $url->query : "");
my $host = $url->host;
print "--> $path\n";
my $req = <new("$host:80") or die $!;
print $sock $req;
return $sock;
}
sub save_to_file {
my($sock, $full) = @_;
my $file = "$ENV{HOME}/Videos/$title.mp4";
printf "\nSaving to $file (%0.1fMB)\n", $full/(1<<20);
open my $fh, ">$file" or die $!;
binmode $fh;
binmode $sock;
my $buf;
my($s,$p);
$|++;
my $start = time;
while($s=read $sock, $buf, 1<<16) {
$p+=$s;
printf "\r%0.2f%% (%0.2f KiB/s)",
($p/$full) * 100, $p / ((time - $start) || 1) / 1024;
print $fh $buf or die "HMM!";
}
}