Syfy: Add preliminary support

This adds support for the Syfy site and its allied sites like defiance.  It
works with video hosted on syfy.com (full episodes and clips) and with video
on defiance.com.  It looks like there are quite a few other syfy related
sites, so the site list could be expanded somewhat.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
diff --git a/lib/FlashVideo/Site/Syfy.pm b/lib/FlashVideo/Site/Syfy.pm
new file mode 100644
index 0000000..71ba6bf
--- /dev/null
+++ b/lib/FlashVideo/Site/Syfy.pm
@@ -0,0 +1,142 @@
+# Part of get-flash-videos. See get_flash_videos for copyright.
+package FlashVideo::Site::Syfy;
+use strict;
+use FlashVideo::Utils;
+use FlashVideo::JSON;
+
+sub make_guid {
+    my($self, $len) = @_;
+
+    my @chars = ('A' .. 'Z');
+    return join "", map { $chars[rand @chars] } 1 .. $len;
+}
+
+sub find_video {
+    my ($self, $browser, $embed_url, $prefs) = @_;
+
+    ##
+    # For the Syfy website, the video player is embedded in an iframe
+    # the current player is from player.theplatform.com
+    #
+    # Note too that there are several other iframes in the page, mostly
+    # for adverts.  Currently the player iframe is first but if the regexp
+    # below stops working, likely theres an advert iframe above it
+    ##
+
+    my $url;
+    if ($browser->content =~ /<iframe\n\s*class=.*\n\s*src="([^"]*)"/) {
+	$browser->get($1);
+	if (!$browser->success) {
+	    die "Couldn't download iframe $url: " . $browser->response->status_line;
+	}
+	if ($browser->content =~ /tp:releaseUrl="([^"]*)"/) {
+	    $url = $1;
+	} else {
+	    die "Couln't find SMIL url in " . $browser->uri->as_string;
+	}
+	$url .= 'feed=All%20Videos&defaultm3ubitrate=1000&manifest=f4m&format=SMIL&Tracking=true&Embedded=true';
+    } elsif ($browser->uri->as_string =~ m,(http://[^/]*)/.*vid:(\d+),) {
+	my ($host,$vid) = ($1, $2);
+	##
+	# This is a lot of heuristics, so if anything goes wrong it's
+	# probably in here
+	##
+	# First heuristic, the nbcParams.js file exists and gives the 
+	# base URLs for most of the downloads
+	##
+	$browser->get($host.'/_utils/video/codebase/js/nbcParams.js');
+	info $browser->uri;
+	die "Looks like nbcParams heuristic no longer works" if (!$browser->success);
+	my ($json) = from_json($browser->content);
+	$host =~ m,https?://www\.([^\.]*).com,;
+	$host = $1;
+	my $feedurl;
+	foreach (@{$json->{channels}}) {
+	    info "found name ".$_->{name};
+	    if ($_->{name} eq $host) {
+		$feedurl = $_->{feed}->{url};
+		last;
+	    }
+	}
+	die "Couldn't find feed URL for $host" if (!defined($feedurl));
+	##
+	# another nasty hairy heuristic
+	##
+	$feedurl .= '?&form=json&fields=guid,title,description,:subtitle,content,thumbnails,categories,:fullEpisode,:disallowSharing%20&fileFields=url,duration,width,height,contentType,fileSize,format,isDefault&byGuid='.$vid;
+	$browser->get($feedurl);
+	die "Feed URL heuristic failed" if (!$browser->success);
+
+	$json = from_json($browser->content);
+	##
+	# the reason for this bizzarre sequence is that the json seems
+	# to contain a set of media descriptors and resolutions, but
+	# the link file urls are all for the same f4m manifest, so
+	# just pull out the firs one
+	##
+	$url = $json->{entries}->[0]->{'media$content'}->[0]->{'plfile$url'};
+	die "Failed to parse feedurl json" if (!$url);
+	$url .= '&manifest=f4m&format=SMIL&Tracking=true&Embedded=true';
+    } else {    
+	die "Couln't recognise Syfy video in " . $browser->uri->as_string;
+    }
+
+    ##
+    # OK, now we have the url, try to find the F4V manifest.  First we
+    # need the SMIL data
+    ##
+
+    $browser->get($url);
+    if (!$browser->success) {
+	die "Couldn't download SMIL $url: " . $browser->response->status_line;
+    }
+
+    ##
+    # SMIL is really just XML with a certain schema
+    ##
+    my $xml = from_xml($browser->content);
+
+    my $seq = $xml->{body}->{seq};
+
+    my $par = $seq;
+
+    $par = $seq->{par} if (defined($seq->{par}));
+
+    my $vid;
+
+    if (ref $par eq 'HASH') {
+	$vid = $par->{video};
+    } else {
+	foreach (@{$par}) {
+	    if (defined($_->{video})) {
+		$vid = $_->{video};
+		last;
+	    }
+	}
+    }
+
+    die "Failed to find video tag in ".$url if (!defined($vid));
+
+    
+
+  ##
+  # now we have to slap on a 12 letter GUID and the version of hdcore
+  ##
+  my $auth = 'g='.$self->make_guid(12);
+  $url = $vid->{src}.'?'.$auth."&hdcore=3.0.3";
+  my $title = $vid->{title};
+
+  return {
+      downloader => 'f4m',
+      manifest => $url,
+      flv => title_to_filename($title),
+  };
+}
+
+sub can_handle {
+  my($self, $browser, $url) = @_;
+
+  return $url =~ m,http://www\.defiance\.com/,i
+      || $url =~ m,http://www\.syfy\.com/,i;
+}
+
+1;