aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Sainty <dave@dtsp.co.nz>2012-11-27 11:31:09 +1300
committerDavid Sainty <dave@dtsp.co.nz>2012-11-27 11:31:09 +1300
commite686556227621ae79dc80f277c13e14c5f27a34b (patch)
tree742e374d9c4fbaca1dfafbd706051447772d984a
parentbd46aaba3cbe2b9ecfae597d528d60ec9ac8f926 (diff)
downloadget-flash-videos-e686556227621ae79dc80f277c13e14c5f27a34b.tar.gz
TVNZ module (Subversion r16004)
-rw-r--r--lib/FlashVideo/Site/Tvnz.pm237
1 files changed, 237 insertions, 0 deletions
diff --git a/lib/FlashVideo/Site/Tvnz.pm b/lib/FlashVideo/Site/Tvnz.pm
new file mode 100644
index 0000000..919b2c7
--- /dev/null
+++ b/lib/FlashVideo/Site/Tvnz.pm
@@ -0,0 +1,237 @@
+# Part of get-flash-videos. See get_flash_videos for copyright.
+package FlashVideo::Site::Tvnz;
+
+use strict;
+
+use FlashVideo::Utils;
+use URI;
+
+my $encode_rates = {
+ "low" => 250000,
+ "medium" => 700000,
+ "high" => 1500000
+ };
+
+sub find_video {
+ my ($self, $browser, $embed_url, $prefs) = @_;
+
+ my $videoPlayer = ($browser->content =~ m/\<param\s+value=\"ref:(\d+)\"\s+name=\"\@videoPlayer\"\s*\/\>/s)[0];
+ my $player_id = ($browser->content =~ /\<param value=\"(\d+)\" name=\"playerID\" \/\>/i)[0];
+
+ debug "Extracted playerId: $player_id, videoPlayer: $videoPlayer"
+ if $player_id or $videoPlayer;
+
+ my $metadata = {
+ videoplayer => $videoPlayer
+ };
+
+ # The "session ID" appears to be constant.
+ my $sessionId = "f86d6617a68b38ee0f400e1f4dc603d6e3b4e4ed";
+ $metadata->{sessionId} = $sessionId;
+
+ debug "Extracted playerId: $player_id, sessionId: $metadata->{sessionId} videoplayer: $videoPlayer"
+ if $player_id or $videoPlayer;
+
+ die "Unable to extract Brightcove IDs from page"
+ unless $player_id && $videoPlayer && $sessionId;
+
+ return $self->amfgateway($browser, $player_id, $metadata, $prefs);
+}
+
+sub amfrequest($$$$) {
+ my($self, $base_url, $player_id, $metadata) = @_;
+
+ if ($player_id ne "1029272630001") {
+ die "Player ID looks botched";
+ }
+
+ my $has_amf_packet = eval { require Data::AMF::Packet };
+ if (!$has_amf_packet) {
+ die "Must have Data::AMF::Packet installed to download Brightcove videos";
+ }
+
+# AMF3 incompatable between Data::AMF and Brightcove
+# results in Brightcove rejecting message
+# create message without deserialize/serialize.
+
+ my $amf0_formatter = Data::AMF::Formatter->new(version =>0);
+ my $amf3_formatter = Data::AMF::Formatter->new(version =>3);
+
+ my @amf_pkt = ();
+
+ push(@amf_pkt, pack("H*", "0003000000010046636f6d2e627269676874636f76652e657870657269656e63652e457870657269656e636552756e74696d654661636164652e67657444617461466f72457870657269656e636500022f310000"));
+
+ push(@amf_pkt, "");
+ my $lengthIndex = $#amf_pkt;
+
+ push(@amf_pkt, pack("H*", "0a00000002"));
+
+ push(@amf_pkt, $amf0_formatter->format($metadata->{sessionId}));
+
+ push(@amf_pkt, pack("H*", "110a6363636f6d2e627269676874636f76652e657870657269656e63652e566965776572457870657269656e63655265717565737413706c617965724b657921636f6e74656e744f76657272696465731154544c546f6b656e1964656c6976657279547970650755524c19657870657269656e6365496406010903010a810353636f6d2e627269676874636f76652e657870657269656e63652e436f6e74656e744f76657272696465156665617475726564496413636f6e74656e7449641b6665617475726564526566496415636f6e74656e7449647319636f6e74656e74526566496417636f6e74656e74547970651b636f6e74656e745265664964730d746172676574057fffffffe0000000057fffffffe00000000101"));
+
+ sub amf3_string($$) {
+ my $self = $_[0]->new;
+ $self->io->write_u8($self->STRING_MARKER);
+ $self->write_string($_[1]);
+ return $self->io->data;
+ }
+
+ push(@amf_pkt, amf3_string($amf3_formatter, $metadata->{videoplayer}));
+
+ push(@amf_pkt,
+ pack("H*","0400010617766964656f506c617965720601057fffffffe0000000"));
+
+ push(@amf_pkt, amf3_string($amf3_formatter, $base_url));
+
+ push(@amf_pkt, $amf3_formatter->format($player_id));
+
+ $amf_pkt[$lengthIndex] = pack('n', length(
+ join('',@amf_pkt[$lengthIndex .. $#amf_pkt])));
+
+ return join('', @amf_pkt);
+}
+
+sub amfresponse($$$$$$) {
+ my ($self, $page_url, $player_id, $metadata, $prefs, $content) = @_;
+
+ my $packet = Data::AMF::Packet->deserialize($content);
+
+ if ($self->debug) {
+ require Data::Dumper;
+ debug Data::Dumper::Dumper($packet);
+ }
+
+ #require Data::Dumper;
+ #print Data::Dumper::Dumper($packet);
+
+ my @found = ();
+ {
+ # renditions Array contains the rtmpe URL.
+ my $renditions = $packet->messages->[0]->{value}->{programmedContent}->{videoPlayer}->{mediaDTO}->{renditions};
+ if (ref($renditions) ne 'ARRAY') {
+ die "Unexpected data from AMF gateway";
+ }
+
+ foreach my $rendition (@{$renditions}) {
+ if ($rendition->{defaultURL}) {
+ push(@found, $rendition);
+ }
+ }
+ }
+
+ # other information returned in message.
+ my $detail = $packet->messages->[0]->{value}->{programmedContent}->{videoPlayer};
+
+ my $mediaId = $detail->{mediaId};
+
+ my $publisherId = $detail->{mediaDTO}->{publisherId};
+ die "Publisher ID not determined" if !defined($publisherId);
+
+ my $customFields = $detail->{mediaDTO}->{customFields};
+
+ my $programme = $customFields->{programme};
+ die "Programme number not determined" if !defined($programme);
+
+ my $seriesnumber = $customFields->{seriesnumber};
+ die "Series number not determined" if !defined($seriesnumber);
+
+ my $episodenumber = $customFields->{episodenumber};
+ die "Episode number not determined" if !defined($episodenumber);
+
+ my $episodename = $customFields->{episodename};
+ die "Episode name not determined" if !defined($episodename);
+
+ my $filehead = $programme .
+ sprintf("_S\%02dE\%02d_", $seriesnumber, $episodenumber) .
+ $episodename;
+
+ my $encode_rate = $encode_rates->{$prefs->{quality}};
+ if (!defined($encode_rate)) {
+ $encode_rate = $prefs->{quality};
+ }
+
+ my @rtmpdump_commands = ();
+
+ for my $d (@found) {
+ my $rate = $d->{encodingRate};
+
+ # The service returns encoding rates that are close to, but not
+ # exactly, the published rates of 250k, 700k, 1500k etc. For
+ # example, 1499998 instead 1500k. Round the rate to the nearest
+ # 1k.
+ {
+ use integer;
+ $rate = (($rate + 500) / 1000) * 1000;
+ }
+
+ #print "Saw: " . $d->{defaultURL} . " @ " . $rate . "\n";
+
+ next if $encode_rate != $rate;
+
+ my $host = ($d->{defaultURL} =~ m!rtmpe://(.*?)/!)[0];
+ my $file = ($d->{defaultURL} =~ /^[^&]+&(.*)$/)[0];
+ my $app = ($d->{defaultURL} =~ m!//.*?/(.*?)/&!)[0];
+ my $filenamePrefix = $filehead . "_" . $rate;
+
+ my $filename = title_to_filename($filenamePrefix);
+ $filename ||= get_video_filename();
+
+ $app .= "?videoId=" . $mediaId .
+ "&lineUpId=&pubId=" . $publisherId .
+ "&playerId=" . $player_id . "&affiliateId=";
+
+ my $args = {
+ app => $app,
+ pageUrl => $page_url,
+ swfVfy => "http://admin.brightcove.com/viewer/us1.24.04.08.2011-01-14072625/connection/ExternalConnection_2.swf",
+ tcUrl => "rtmpe://$host:1935/$app",
+ rtmp => "$d->{defaultURL}",
+ playpath => $file,
+ flv => $filename,
+ };
+
+ push @rtmpdump_commands, $args;
+ }
+
+ return \@rtmpdump_commands;
+}
+
+sub amfgateway {
+ my($self, $browser, $player_id, $metadata, $prefs) = @_;
+
+ my $page_url = $browser->uri;
+ my $base_url = "" . $page_url;
+
+ my $data = $self->amfrequest($base_url, $player_id, $metadata);
+
+ $browser->post(
+ "http://c.brightcove.com/services/messagebroker/amf?playerid=$player_id",
+ Content_Type => "application/x-amf",
+ Content => $data
+ );
+
+ die "Failed to post to Brightcove AMF gateway"
+ unless $browser->response->is_success;
+
+ my $content = $browser->content;
+
+ #open(F, "> response.data");print F $content;close(F);
+
+ my $commands = $self->amfresponse($page_url, $player_id, $metadata,
+ $prefs, $content);
+
+ if ($#$commands > 0) {
+ return $commands;
+ } else {
+ return ${$commands}[0];
+ }
+}
+
+sub can_handle {
+ my($self, $browser, $url) = @_;
+
+ return $url && URI->new($url)->host =~ m/(?:^|\.)tvnz\.co\.nz$/;
+}
+
+1;