TVDBgrabber
TVDBgrabber is a PHP script that is used to generate png and xml files from TV shows based on their filename for use with the ATVFiles plugin on the Apple TV. It utilizes TheTVDB.com.
SYSTEM REQUIREMENTS
- PHP5 (This script utilizes SimpleXML.)
- MPlayer (Required only for screenshots.)
- Mac OS X, Linux, Solaris, or any other UNIX flavor.
(If you are running TVDBgrabber on Windows, please let me know.) - Internet Connection
SUPPORTED FILENAMES
- SHOWNAME S00E00whatever.ext
(Examples: Entourage S01E01 - Pilot.avi, Entourage S01E01.avi)
TO DO
- Support more filename/directory structures.
- Implement better data validation and error checking.
- I’m currently looking for a solution that will run on all Apple TV boxes and not require PHP5. (As of Mac OS X 10.5, PHP5 is standard.)
- Use TheTVDB.com banners instead of screenshots
USAGE
Create the xml/png data for new files:
*TVDBgrabber will recursively traverse through the given file path.
DISCUSSION
For a current discussion of the script, please visit the AwkwardTV Forums.
DOWNLOAD
Download the latest version of TVDBgrabber.
- Version 0.1.5 (18 October 2007)
- Version 0.1.4 (20 August 2007)
- Version 0.1.3 (27 July 2007)
- Version 0.1.2 (9 July 2007)
VIEW
The current code is as follows:
/**********************************************************************
tvdbgrabber.php
VERSION: 0.1.5
DATE: 10 October 2007
AUTHOR: Joshua Glemza
CONTACT: josh uknow hwarf.com
WEBSITE: http://hwarf.com/
LICENSE
This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/3.0/us/ or send a letter to
Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
AKNOWLEDGEMENTS:
This script utilizes the TheTVDB.com website. Many thanks to them!
http://www.thetvdb.com/
Thanks to sdaniels on the AwkwardTV forums for his suggestions.
OVERVIEW:
This script will take the information from a filename and create the
preview image and xml data needed by ATVFiles. From a given path it
will recursively traverse the directory and add xml to all video files.
CURRENT LIMITATIONS
*The file structure must be in this format: SHOWNAME S00E00whatever.ext
Example: Entourage S03E20 - Adios Amigos.avi
*Does not currently work if you need to go through an HTTP proxy server.
*Requires that mplayer be installed if png images are created.
USAGE:
php -q tvdbgrabber.php /path/to/folder/root ACTION
An action can be:
NEW: Create png/xml for files that do not have them
ALL: Recreate all jpg/xml data
THINGS TO DO:
*Support more filename formats. ie, how they're posted.
*Give an error if no tv show is found.
*Input validation for multiple tv show names.
*Some png screens are blank.
**********************************************************************/
//////////// Configuration Variables ////////////
$createPng = 'y'; //y for yes, n for no
$pngSeconds = 450; //seconds into video file to create png
$pngWidth = 400; //Width of the png file
$mirrorUrl = ''; //tvdb.com mirror url, blank for default
////////////////////////////////////////////////
# Takes a show name and returns the seriesid from tvdb
function getSeriesId($show_name, $mirrorUrl)
{
$show_name = urlencode($show_name);
// Get seriesid
$seriesId_xml = file_get_contents("$mirrorUrl/GetSeries.php?seriesname=$show_name");
$seriesXml = new SimpleXMLElement($seriesId_xml);
// Display all the shows found and select the correct one
$counter = 1;
$selection = 1;
$output = '';
foreach($seriesXml->Item as $Item)
{
$output .= $counter . ". " . $Item->SeriesName . "\n";
$counter++;
}
if($counter > 2)
{
print "####################################\n";
print "Multiple TV Shows found:\n";
print $output;
print "####################################\n\n";
print "Please choose the correct show: ";
$selection = fread(STDIN, 80);
print "\n\n";
}
$seriesid = $seriesXml->Item[$selection-1]->id;
return $seriesid;
}
# Takes a seriesid, season number, episode number and return xml data
function getEpisodeInfo ($seriesid, $season, $episode, $mirrorUrl)
{
// Get episodeid
$episodeId_xml = file_get_contents("$mirrorUrl/GetEpisodes.php?seriesid=$seriesid&season=$season&episode=$episode");
$episodeXml = new SimpleXMLElement($episodeId_xml);
$episodeid = $episodeXml->Item[1]->id;
// Get episode info
$episodeInfo_xml = file_get_contents("$mirrorUrl/EpisodeUpdates.php?lasttime=0&idlist=$episodeid");
$episodeInfoXml = new SimpleXMLElement($episodeInfo_xml);
return $episodeInfoXml;
}
# Takes episodeInfo and returns ATVFiles formatted XML
function createXmlData($episodeInfo, $show_name)
{
$atv_xml = "<media type=\"TV Show\">\n";
$atv_xml .= " <title>" . $episodeInfo->Item[1]->EpisodeNumber . ". " . $episodeInfo->Item[1]->EpisodeName . "</title>\n";
$atv_xml .= " <artist>" . $show_name . "</artist>\n";
$atv_xml .= " <summary>" . $episodeInfo->Item[1]->EpisodeName . "</summary>\n";
$atv_xml .= " <description>\n";
$atv_xml .= " " . $episodeInfo->Item[1]->Overview . "\n";
$atv_xml .= " </description>\n";
$atv_xml .= " <episode>" . $episodeInfo->Item[1]->EpisodeNumber . "</episode>\n";
$atv_xml .= " <season>" . $episodeInfo->Item[1]->SeasonNumber . "</season>\n";
$atv_xml .= " <published>" . $episodeInfo->Item[1]->FirstAired . "</published>\n";
$atv_xml .= "</media>";
return $atv_xml;
}
# This function takes a directory and returns all of the files within
# the directory that match a given extension.
# Taken from: http://www.hawkee.com/snippet/1281/
function directoryToArray($directory, $extension="", $full_path = true)
{
$array_items = array();
if ($handle = opendir($directory))
{
while (false !== ($file = readdir($handle)))
{
if ($file != "." && $file != "..")
{
if (is_dir($directory. "/" . $file))
$array_items = array_merge($array_items, directoryToArray($directory. "/" . $file, $extension, $full_path));
else
{
if(!$extension || (ereg("." . $extension, $file)))
{
if($full_path)
$array_items[] = $directory . "/" . $file;
else
$array_items[] = $file;
}
}
}
}
}
closedir($handle);
return $array_items;
}
// Was the path and action given on the command line?
if ($argv[1] == '' || $argv[2] == '')
die("tvdbgrabber.php\nError: Incorrect usage.\nExample: php -q tvdb_grabber.php /path/to/root/folder NEW|ALL\n\n");
// Get a list of all video files from the given path based on extension
$root_path = $argv[1];
$vid_extensions = array ("mpg", "avi", "vob", "mp4", "mkv", "mpg", "m4v", "mov");
$video_files = array();
foreach ($vid_extensions as $ext)
$video_files = array_merge($video_files, directoryToArray($root_path, $ext, true));
sort($video_files);
// Loop through the returned video files based on desired action
$action = $argv[2];
switch($action)
{
// Create a jpg/xml pair for the given path only if they don't exist
case "NEW":
case "ALL":
$show_name = '';
//Loop through all of the video files found
foreach ($video_files as $path)
{
// Strip the file extension from the video path
$current_path = preg_replace("/\.[0-9A-Za-z][0-9A-Za-z][0-9A-Za-z]$/", "", $path);
// Remember the orginal path and the stripped extension path
$original_path = $path;
$path = $current_path;
// Does the xml/png file exist for this video
if (file_exists($current_path . ".xml") && file_exists($current_path . ".png") && $action != "ALL");
// The file doesn't exists. Create it.
else
{
// Parse the video information
preg_match("/[Ss][0-9][0-9][Ee][0-9][0-9]/", $current_path, $matches);
if ($matches[0] != '')
$season_episode = trim($matches[0]);
else
print "Could not be parsed: $current_path\n";
// Is the season and episode info there?
if ($season_episode != '')
{
$current_path = preg_replace("/[Ss][0-9][0-9][Ee][0-9][0-9].*$/", "", $current_path);
$current_show = preg_replace("/^.*\//", "", $current_path);
$current_show = preg_replace("/\./", " ", $current_show);
$current_show = trim($current_show);
// Is the show name present?
if ($current_show != '')
{
// Is this the same show that we already looked up?
if ($show_name == $current_show);
else
{
// Get a tvdb mirror
if ($mirrorUrl == '')
{
// Query tvdb for a list of mirrors
$mirrors_xml = file_get_contents("http://thetvdb.com/interfaces/GetMirrors.php");
$mirrorsXml = new SimpleXMLElement($mirrors_xml);
$mirrorUrl = $mirrorsXml->Item[0]->interface;
}
$series_id = getSeriesId($current_show, $mirrorUrl);
$show_name = $current_show;
}
// Parse out the Season number
preg_match("/[Ss][0-9][0-9]/", $season_episode, $matches);
$season = $matches[0];
$season = str_ireplace("S0", "", $season);
$season = str_ireplace("S", "", $season);
// Parse out the Episode number
preg_match("/[Ee][0-9][0-9]/", $season_episode, $matches);
$episode = $matches[0];
$episode = str_ireplace("E0", "", $episode);
$episode = str_ireplace("E", "", $episode);
// Get the episode information
$episodeInfo = getEpisodeInfo($series_id, $season, $episode, $mirrorUrl);
// Create the ATVFiles XML data
print "Creating XML for $show_name $season_episode\n";
$episodeXml = createXmlData($episodeInfo, $show_name);
file_put_contents($path . ".xml", $episodeXml);
// Create the screenshot png
if ($createPng == 'y')
{
$original_path = escapeshellarg($original_path);
print "Creating PNG for $show_name $season_episode\n";
exec("/usr/bin/mplayer -really-quiet -nosound -ss $pngSeconds -frames 2 -vf scale=$pngWidth:-2 -vo png $original_path > /dev/null 2>&1");
// If two screenshots exist, delete the second one
if (file_exists("./00000002.png"))
{
rename("00000002.png", $path . ".png");
unlink("./00000001.png");
}
else
rename("00000001.png", $path . ".png");
}
print "Created PNG/XML for $show_name $season_episode\n\n";
}
}
}
}
break;
default:
print "An invalid action was chosen.\nValid choices are: NEW and ALL.";
}
?>
CHANGELOG
Version 0.1.5
- Fixed a bug that didn’t allow filenames with numbers in it to be parsed correctly. Thanks sdaniels.
Version 0.1.4
- Changed the default URL for TheTVDB.com mirrors. This should accommodate their server transition.
- The files that need xml/png files are now sorted to be created in order.
Version 0.1.3
- If two screenshots are created, the second one is used. Some screenshots were coming up black.
Version 0.1.2
- Fixed the bug where two screenshots were created, but only one was deleted on certain environments.
LICENSE
This work is licensed under a
Creative Commons Attribution-ShareAlike 3.0 License.