<?php
/**
 *  Copyright (C) 2007 Ben Leto <undertoe@chemlab.org>
 * 
 *  Description: Apple Trailer Grabber for mythtv
 * 
 *  Version 0.4.2
 * 
 *  Apple Trailer Grabber is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 * 
 *  Apple Trailer Grabber is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 *  Usage: read the INSTALL file
 *  	Setup as a cron script to run as frequently as you want
 *
 *	----------- version: 0.4.1
 *	MODIFIED 9/12/2007 - Jarett Creason <jarett.code@gmail.com>
 * 	I modified this so that you now have the option of downloading the trailers
 *  OR stream them from the internet...  Also, when a trailer is in the RSS feed but
 *  fails to be found for (hopefully) whatever reason, that entry in the MythTV menu
 *  is preceeded by an "ERROR" warning, and there is not EXEC command.
 *  I also updated the script so that it works for trailers stored in the 
 *  http://movies.apple.com subdomain as well as http://images.apple.com...
 *  This works for me right now, but for how many different ways Apple seems to store
 *  it's trailers, who knows in the future...
 *
 *	----------- version: 0.4.2
 *  MODIFIED 12/10/2007 - Brian Johnson <mythtv@dogtoe.com>
 * 	Corrected special characters issue in movie titles where the & character
 *  would show up as &amp; etc.
 *
 */

/************************************************************************/
// CONFIGURATION

// Set your preferred format for trailers if available.
// leave blank to disable HD formated trailers.
// Valid options: 480 , 720, 1080 or leave blank to disable HD formated trailers
// ***WARNING*** Moving to 720 or 1080 requires a lot of horse power
$PREFERRED_HD_FORMAT 	= '480';

// whether you want to download the movies or stream them (stream is default)
//				stream	- stream all trailers via the internet (default) (you should use lower video quality) 
//				download - download the trailers to the $DOWNLOAD_FILES_DIR defined below
$STREAM_OR_DOWNLOAD		= "stream";

// where to save the downloaded files if we are to download them
$DOWNLOAD_FILES_DIR 		= "";

// Movie Player Command
// You can use mplayer, xine, totem, vlc your choice
// the command to use when playing content that is locally availble
$LOCAL_EXEC_CMD			= '/usr/bin/mplayer -fs -zoom -really-quiet';

// Nick Fox - Updated this command to add buffer for HD conent streaming
// command to use when streaming content from the internet - cache 50% before displaying and use 32MB of memory
$STREAMING_EXEC_CMD		= '/usr/bin/mplayer -fs -zoom -quiet -user-agent NSPlayer -cache-min 75 -cache 16384';

// where (if at all) to include the movies that are not collected correctly...
// if a link to a movie is not found, then by default the movie will be printed
// into the MythTV menu like this: "ERROR: <Movie Name>" (where Movie Name is the name of the movie)
// at the bottom...  use these options:
//				bottom  - print the movie name at the bottom with "ERROR:" preceeding it (default)
//				inline  - print the movie name in line where it get downloaded with "ERROR:" preceeding it
//				nowhere - don't show the movie name at all, anywhere, only show trailers we can watch
$PRINT_ERROR_MOVIE_NAME = "bottom";

/************************************************************************/
// Shouldn't Need To modify anything beyond here

// debug variable - will print output only for the screen, not for xml file!!
// *** DO NOT ENABLE THIS IF USE IN PRODUCTION ***
$DEBUG 						= false;

// Apple.com Trailer Search Patterns
define("HD", 		'hd');
define("Regular", 'reg');
define("Large", 	'lgr');
define("High",		'high');
define("Trailer1",'trl1');

/************************************************************************/

// charge!!!!!!
init_main();

// Function:  returns null | init_main () 
// Description: Outputs Apple Trailer RSS feed to watchable movie urls in MythTV menu XML format
function init_main()
{
	// RSS Feed Configuration
	$rss_url = 'http://images.apple.com/trailers/rss/newtrailers.rss'; //apple trailer rss feed
	$rss_tags = array('title','link','description','pubDate','content:encoded');
	$rss_item_tag = 'item';

	// Sleep in between movie fetch
	$sleep_sec = 2; 

	// Load Global Vars
	global $LOCAL_EXEC_CMD, $STREAMING_EXEC_CMD, $DOWNLOAD_FILES_DIR, $STREAM_OR_DOWNLOAD, $DEBUG, $PRINT_ERROR_MOVIE_NAME ;
	
	// Gather Array of Current Movie Trailers    
	$arr_rssfeed = rss_to_array($rss_item_tag, $rss_tags, $rss_url);

	// error movie text to be printed later (maybe)...
	$error_movie_text = "";

	// start printing the menu
	print "<mythmenu name=\"TRAILERS\">\n";

	// first check if the directory we're writing to exists
	if($STREAM_OR_DOWNLOAD == 'download' && !is_dir($DOWNLOAD_FILES_DIR)) {
		if(mkdir($DOWNLOAD_FILES_DIR)) {
			if($DEBUG) { print "The directory \"$DOWNLOAD_FILES_DIR\" didn't exist, but we created it for you...\n"; }
		} else {
			print "\t<button>\n";
			print "\t\t<type>TV_DELETE</type>\n";
			print "\t\t<text>ERROR: download directory doesn't exist!</text>\n";
			print "\t\t<action>EXEC xmessage -center -timeout 10 The directory: $DOWNLOAD_FILES_DIR; doesn't exist, and I can't create it!! This is where you told me to download videos to... Check this out, because I'm done...</action>\n";
			print "\t</button>\n\n";
			
			print "</mythmenu>\n";
			exit(1);
		}
	}

	// clean up old trailers; just delete them all
	// 9/12/07 - this needs to be updated so it's smarter, right now we just delete everything and
	// re-download whatever is currently in the RSS feed...
	// Nick Fox - Changed this to only remove the mov files to prevent issues
	if($STREAM_OR_DOWNLOAD == 'download') {
		system("/bin/rm -f ".$DOWNLOAD_FILES_DIR."/*.mov");
	}
	    
	// for every movie we found in the RSS feed
	foreach ($arr_rssfeed as $value) {
		$movieLink 			 = apple_getbestmovie( $value['link'] );
		$movieTitle 		 = apple_clean_title( $value['title'] );
		$mythtvPlayerCmd = "";

		// if there was an error collecting the movie, $movieLink will be false
		if($movieLink || $PRINT_ERROR_MOVIE_NAME == "inline") {
			if(!$movieLink) {
				$movieTitle = "ERROR: $movieTitle";
			}

			// download the trailer
			if($movieLink && $STREAM_OR_DOWNLOAD == 'download') {
				// change the directory the the place where we are to download the movies to
				chdir($DOWNLOAD_FILES_DIR);
				// download that movie!!
				system("/usr/bin/wget --quiet $movieLink");
				// command to use to play the local movie
				$mythtvPlayerCmd = "EXEC $LOCAL_EXEC_CMD ".$DOWNLOAD_FILES_DIR."/".basename($movieLink);

			// we're going to be streaming the trailer from the internet
			} else if($movieLink) {
				// command to use to play the local movie
				$mythtvPlayerCmd = "EXEC $STREAMING_EXEC_CMD $movieLink";
			}

			// print the text for the MythTV menu item
			print "\t<button>\n";
			print "\t\t<type>VIDEO_BROWSER</type>\n";
			print "\t\t<text>$movieTitle</text>\n";
			print "\t\t<action>$mythtvPlayerCmd</action>\n";
			print "\t</button>\n\n";
		
		// ERROR: we couldn't get the file to download, sad...
		} else if($PRINT_ERROR_MOVIE_NAME != "nowhere") {
			if($DEBUG) { print "ERROR: Woops! Couldn't download the movie: $movieTitle\n"; }
			$error_movie_text .= "\t<button>\n";
			$error_movie_text .= "\t\t<type>TV_DELETE</type>\n";
			$error_movie_text .= "\t\t<text>ERROR: $movieTitle</text>\n";
			$error_movie_text .= "\t\t<action>EXEC xmessage -center -timeout 10 There was an error accessing this Apple trailer...</action>\n";
			$error_movie_text .= "\t</button>\n\n";
		}
		
		if($DEBUG) { print "Sleeping now for $sleep_sec seconds...\n\n"; }
		sleep($sleep_sec);
	}

	// print out any errored movies that we couldn't find a download file for
	// (if it's alright with the configuration that is...)
	print $error_movie_text;
			
	print "</mythmenu>\n";
}

// Function:  returns string (movie url) | apple_getbestmovie ( var | trailer website) 
// Description: Gets the best Quicktime movie out of the many avalible formats (small, medium, large, hd)
function apple_getbestmovie($movielink) 
{
	global $PREFERRED_HD_FORMAT, $DEBUG, $STREAM_OR_DOWNLOAD;

	$movie_arr = apple_gather_movies($movielink);

	// Error getting the movie... return false
	if(!is_array($movie_arr) || count($movie_arr) == 0) {
		return false;
	} else {
		$set_movie_url = null;

		if($DEBUG) { 
			echo "All movies found by function \"apple_gather_movies\":\n";
			print_r($movie_arr);
			echo "\n";
		}
		
		// Check for Perfered HD Movie Format
		if(in_array($PREFERRED_HD_FORMAT , array('1080', '720', '480'))) {
			foreach ($movie_arr as $value) {
				if (preg_match("/_".$PREFERRED_HD_FORMAT."p/i", $value) && $PREFERRED_HD_FORMAT > 0) {
					$set_movie_url = $value;
				}
			}				
		}
		
		// Try to select non-HD content if only HD then get the lowest format 480p
		if($PREFERRED_HD_FORMAT == '') {
			foreach ($movie_arr as $value) {
				if (preg_match("/_(480|720|1080)p/i", $value)){
					if (preg_match("/_480p/i", $value)) {
						$backup_movie_url = $value; 
					}
				} else {
					$nonhd_movie_url = $value;
				}
			}
			
			if($nonhd_movie_url == '') {
				$set_movie_url = $backup_movie_url;
			} else {
				$set_movie_url = $nonhd_movie_url;
			}
		}

		// If no format is found select the best of the best!		
		if($set_movie_url == '') {
			$chosen = count($movie_arr) - 1;
			$set_movie_url = $movie_arr[$chosen];
		}

		// get the real movie to download (if told to do so), what we just got is only a pointer...
		if($STREAM_OR_DOWNLOAD == 'download') {
			$set_movie_url = apple_get_real_movie( $set_movie_url );
		}

		if($DEBUG) { echo "The movie we want to get is: $set_movie_url\n"; }

		return $set_movie_url;
	}
}
		
// Function:  returns array (valid movie urls) | apple_gather_movies ( var | trailer website) 
// Description: Gets the best Quicktime movie out of the many avalible formats (small, medium, large, hd)
function apple_gather_movies($movielink)
{
	global $PREFERRED_HD_FORMAT;		
	$arr_movie_url = array();
	
	// Gather Regular Trailers			
	$arr_basic_movie_url = apple_parse_site($movielink, Regular);
	if(is_array($arr_basic_movie_url)) {
		foreach ($arr_basic_movie_url as $value) {
			array_push($arr_movie_url, $value);
		}
	}
		
	// Gather HD Trailers if $PREFERRED_HD_FORMAT is set
	if($PREFERRED_HD_FORMAT == '1080' || $PREFERRED_HD_FORMAT == '720' || $PREFERRED_HD_FORMAT == '480') {
		$arr_hd_movie_url = apple_parse_site($movielink, HD);
		if(is_array($arr_hd_movie_url)) {
			foreach ($arr_hd_movie_url as $value) {
				array_push($arr_movie_url, $value);
			}
		}
	}
  
   // Collect from common large.html version of the trailer if not regular or HD version is found
	if(count($arr_movie_url) == 0 || $PREFERRED_HD_FORMAT == '') {
		$arr_large_movie_url = apple_parse_site($movielink, Large);
		if(is_array($arr_large_movie_url)) {
			foreach ($arr_large_movie_url as $value) {
				array_push($arr_movie_url, $value);
			}
		}
	}
	
	// Collect from sometimes seens Trailer 1 Site if nothing else is collected
	if(count($arr_movie_url) == 0 || $PREFERRED_HD_FORMAT == '') {
		$arr_last_shot = apple_parse_site($movielink, Trailer1);
		if(is_array($arr_last_shot)) {
			foreach ($arr_last_shot as $value) {
				array_push($arr_movie_url, $value);
			}
		}
	}
	
	// Collect from sometimes seens High Site if nothing else is collected
	if(count($arr_movie_url) == 0 || $PREFERRED_HD_FORMAT == '') {
		$arr_last_shot = apple_parse_site($movielink, High);
		if(is_array($arr_last_shot)) {
			foreach ($arr_last_shot as $value) {
				array_push($arr_movie_url, $value);
			}
		}
	}

	// Return all movie urls in format order (best to wrost)			
	return $arr_movie_url;
}


// Function:  returns array (valid movie urls) | apple_parse_site ( var | trailer website, var | search for format) 
// Description: Parses website of trailer and returns array of valid movies in specified format
function apple_parse_site($movie_url, $fmt)
{
	global $DEBUG;
	$collected_url 			= array();
	$search_pattern_array 	= array();
	
	if($fmt == 'hd') {
		$movie_url = apple_safeparse_url($movie_url) . 'hd/';
		$search_pattern_array[] = 'http:\/\/images.apple.com\/movies\/(.*?)\.mov';
		$search_pattern_array[] = 'http:\/\/movies.apple.com\/movies\/(.*?)\.mov';
	} elseif($fmt == 'reg') {
		$movie_url = $movie_url;
		$search_pattern_array[] = 'http:\/\/images.apple.com\/movies\/(.*?)\.mov';
		$search_pattern_array[] = 'http:\/\/movies.apple.com\/movies\/(.*?)\.mov';
	} elseif($fmt == 'lgr') {
		$movie_url = $movie_url . 'large.html';
		$search_pattern_array[] = 'http:\/\/images.apple.com\/(.*?)\.mov';
		$search_pattern_array[] = 'http:\/\/movies.apple.com\/(.*?)\.mov';
	} elseif($fmt == 'trl1') {
		$movie_url = $movie_url . 'trailer1_large.html';
		$search_pattern_array[] = 'http:\/\/images.apple.com\/movies\/(.*?)\.mov';
		$search_pattern_array[] = 'http:\/\/movies.apple.com\/movies\/(.*?)\.mov';
	} elseif($fmt == 'high') {
		$movie_url = $movie_url . 'high.html';
		$search_pattern_array[] = 'http:\/\/images.apple.com\/movies\/(.*?)\.mov';
		$search_pattern_array[] = 'http:\/\/movies.apple.com\/movies\/(.*?)\.mov';
	}
	
	// Check to see if valid url
	if(valid_url($movie_url)) {
		if($DEBUG) { print "YAY!!!  valid url: $movie_url\n"; }
		// Loop through our array lines on trailer page  
		$lines = file($movie_url);
		if(is_array($lines)) {
			foreach($lines as $line_num=>$line) {
				$matchFound = 0;
				foreach($search_pattern_array AS $search_pattern) {
					if(!$matchFound && preg_match("/$search_pattern/i", $line, $matches)) {
						//if($DEBUG) { print "PUSH THAT MATCH TO collected_url: $matches[0]\n"; }
						array_push($collected_url, $matches[0]);
						$matchFound = 1;
					}
				}
			}
		}
	} else {
		if($DEBUG) { print "Not valid url: $movie_url\n"; }
	}
			
	// Return Collection Array
	return $collected_url;
}

// Function:  returns string (actual movie url) | apple_get_real_movie ( var | captured url) 
// Description: Returns the URL of the actual .mov file for downloading
//					 By searching through the original .mov file for the name of the _real_ trailer
function apple_get_real_movie($url)
{
	global $PREFERRED_HD_FORMAT, $DEBUG;

	$preferredMatchName	= "";
	$directory 				= dirname($url);
	$movieFile 				= basename($url);

	// get just the name of the movie (ie: make "whydidigetmarried_720p.mov" into "whydidigetmarried")
	$truncatedMovieFile 	= preg_replace("/[_|\-][480|720|1080|h\.ref].*/", "", $movieFile);

	// OLD WAY - this is probably not reliable, but was _a lot_ simpler
	// replace the moviefile name and put an 'h' in front of the format
	// (ie: make "whydidigetmarried_720p.mov" = "whydidigetmarried_h720p.mov")
	// this is how apple stores the movies as far as I can tell as of September 12th, 2007
	//$url = preg_replace("//", "", $url);

	if ($DEBUG) { print "Opening up the URL for movie \"$movieFile\" to get the real download URL\n"; }
	$lines = file($url);
	$actualMovieFiles = array();

	if(is_array($lines) && count($lines) > 0) {
		foreach($lines AS $line) {
			$pattern = "/(".$truncatedMovieFile."_.*?[320|480|720|1080][p|i|w]*\.mov)/";
			if ($DEBUG) { print "Parsing through LINE: $line\nChecking with pattern: ".$pattern."\n"; }
			if (preg_match($pattern, $line, $matches)) {
				if ($DEBUG) { print "Found real movie match (we think): $matches[1]\n"; }
				$actualMovieFiles[] = $matches[1];
				if(preg_match("/$PREFERRED_HD_FORMAT/", $matches[1])) {
					$preferredMatchName = $matches[1];
				}
			}
		}

		// if we found any real movies
		if(count($actualMovieFiles) > 0) {
			// if we didn't find a good match, use the last one (usually best quality)
			if($preferredMatchName == "") {
				$preferredMatchName = $actualMovieFiles[(count($actualMovieFiles)-1)];
			}
			$url = $directory ."/". $preferredMatchName;
		} else {
			$url = false;
			if ($DEBUG) { print "ERROR: no matches found in: $url\n"; }
		}
	} else {
		$url = false;
		if ($DEBUG) { print "ERROR: no lines pulled from: $url\n"; }
	}

	return $url;
}

// Function:  returns string (clean movie url) | apple_safeparse_url ( var | captured url) 
// Description: Converts it to Non-Gay'd version of movie trailer site
// example: http://www.apple.com/trailers/lions_gate/310toyuma/
//          http://www.apple.com/trailers/iphone/lions_gate/310toyuma/
//          convert to production_comapny / movietag
function apple_safeparse_url($url)
{
	list($junk, $prod_tag) = split("/trailers/", $url, 2);
	list($prod, $movietag, $crap) = split("/", $prod_tag, 3);
	return 'http://www.apple.com/trailers/'.$prod.'/'.$movietag.'/';
}
		
// Function:  returns string (clean movie title) | apple_clean_title ( var | captured title) 
// Description: Removes junk after RSS'd movie title (ie. - Trailer 1, Teaser - 1)
function apple_clean_title($title)
{
	$title = strrev($title);
	list($junk, $newtitle) = split(" - ", $title, 2);
	$title = html_entity_decode(trim(strrev($newtitle)));
	// replace all amperstands with the word 'and', because they break the MythTV menus
	$title = preg_replace('/ & /', ' and ', $title);
	return $title;
}

// Function:  returns boolean | valid_url ( var | url to check) 
// Description: Checks to see if a url is a valid page    
function valid_url($str)
{
	if(@fopen($str, "r")) {
		return 1;
	} else {
		return 0;
	}
}

// Function:  returns 2 x array | elements of RSS ( var | url to feed) 
// Description: Converts each element in an RSS feed to an Array
// Notes: New version to not use php-xml, avoid dependancy problems. Should now work with php4 
function rss_to_array($tag, $tag_array, $url) 
{
	$lines 		= file($url);
	$rss_array 	= array();
	$tmp_array 	= array();
	$capture 	= False;
	
	foreach ($lines as $line) {
		$line = trim($line);
		
		if($capture == True) {
			foreach ($tag_array as $tageach) {
				if (preg_match("/<".$tageach.">(.*?)<\/".$tageach.">/i", $line, $matches)) {
					$tmp_array[$tageach] = $matches[1];
				}
			}
		}

		// Toggle RSS capture on and off
		if($line == '<'.$tag.'>') {
			$capture = True;
		}

		if($line == '</'.$tag.'>'){	
			$capture = False;
			array_push($rss_array, $tmp_array);
		}
	}
	
	return $rss_array;
}

?>
