<?php
/**************************************************
* NZBirc v1
* Copyright (c) 2006 Harry Bragg
* tiberious.org
* Module: tvrage
**************************************************
*
* Full GPL License: <http://www.gnu.org/licenses/gpl.txt>
*
* This program 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.
*
* This program 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
*/
class Net_SmartIRC_module_tvrage extends Net_SmartIRC_module_base
{
// default module variable
var $name = 'tvrage';
var $version = 'v0.1';
var $description = 'searches and gets information from tvrage.com';
/**
* Lists the definitions for tvrage website
*
* @var array of regular expressions
* @access public
*/
var $_def = array(
'findShow' => "/<a href='http:\/\/www.tvrage.com\/([^']+)' >([^<]+)<\/a>/i",
'findEpisodeFromSeries' => "/<a href=\'.+\/(\d+)\/0*%dx%02d\'>.+<\/a>/i",
'findEpisodeFromName' => "/<a href=\'.+\/(\d+)\/0*\d+x\d+\'>.*?%s.*?<\/a>/i",
'show' => array(
'name' => '/<h5 class=\'nospace\'><a name=\'summary\'> <\/a>"(.+?)" Summary<\/h5>/i',
'genre' => '/<tr><td width=\'\d+\' valign=\'top\'><b>Genre: <\/b><\/td><td>(.+)<\/td>/i',
'classification' => '/<tr><td width=\'\d+\' valign=\'top\'><b>Classification: <\/b><\/td><td>(.+)<\/td>/i',
'status' => '/<tr><td width=\'\d+\' valign=\'top\'><b>Status: <\/b><\/td><td>(.+)<\/td>/i',
'episodes' => '/<tr><td width=\'\d+\' valign=\'top\'><b>(First|Last|Latest|Next) Episode: <\/b><\/td><td>(?:.+?)?<a\s+href=\'.+\'>(?:\d+): (\d+)x(\d+) -- (.+)<\/a>(?:\s\(([a-z0-9\/]+)\))?/i',
'airtime' => '/<tr><td width=\'\d+\' valign=\'top\'><b>Airs on: <\/b><\/td><td>(.+) \((.+)\)<\/td>/i',
),
'episode' => array(
'title' => '/<b>Title: <\/b><\/td><td [^>]+>(.+)<\/td>/i',
'epnum' => '/<b>Episode Number: <\/b><\/td><td [^>]+>(\d+)<\/td>/i',
'series' => '/<b>Season: <\/b><\/td><td [^>]+>(\d+)<\/td>/i',
'sepnum' => '/<b>Season Episode #.: <\/b><\/td><td [^>]+>(\d+)<\/td>/i',
'prodnum' => '/<b>Production Number: <\/b><\/td><td [^>]+>(\d+)<\/td>/i',
'airdate' => '/<b>Original Airdate: <\/b><\/td><td [^>]+>(.+)<\/td>/i'
),
'findEpisodeFromDate' => "/<td width='\d+' align='center' class='b1'><table align='center' cellspacing='0'><tr>\s+<td width='15' align='left'>%02d<\/td><td>\/<\/td>\s+<td width='10'>%s<\/td><td>\/<\/td>\s+<td width='10'>%d<\/td><\/table><\/td>\s+<td style='padding-left: 6px;' class='b1'>\s*<a href='.+\/(\d+)\/0*(\d+)x(\d+)'>(.+)<\/a>\s*<\/td>/i",
'command' => array(
'showEp' => '/^(.+) s?(\d+)(?:x|e)(\d+)/i',
'showEpName' => '/^(.+), (.+)$/i'
),
'error' => '/Unknown Error/i'
);
/**
* TVRage module
*
* STARTCONFIG
* @var array publicCommand - Lists the commands that trigger this module, and return publicly
* @var array privateCommand - Lists the commands that trigger this module privatly (notice you)
* @var double dataCache - The amount of time (in days) to keep the actual data cache for (this does not include tvrage id caching, which is forever)
* @var double cacheChange - Probability of using the cache instead of re-querying (between 0 and 1, 0.9 = 90%)
* ENDCONFIG
*/
var $_config = array(
'publicCommand' => array( '^tvrage', '^!tvrage'),
'privateCommand' => array( '^@tvrage' ),
'dataCache' => 0.5,
'cacheChance' => 0.95
);
/**
* Template
*
* @var array
* @access private
*/
var $_template = array(
'episode' => '{show} - {series}x{sepnum} - {title} | {url} [{genre}, ep:{epnum}, air:{date}]',
'show' => '{name} | {url} [Status:{status}, Genre:{genre}, Class:{classification}] {showEps}',
'showEps' => '{type}: {series}x{episode} - {title} ({date})',
'help' => array(
'{command} show - list information about a show',
'{command} show SxE - S=series, E=episode, list information about a particular episode',
'{command} show, episode - search for an episode matching \'epsiode\' and list information' ),
'error' => '**Error:** {errormsg}',
);
function _registerCommands()
{
global $irc;
$this->_setHandler( '.*', $this, 'tvrage' );
$irc->_modules['store']->addVariable( '_tvrageShowCache' );
$irc->_modules['store']->addVariable( '_tvrageEpisodeCache' );
$irc->_modules['store']->addVariable( '_tvrageShowDataCache' );
$irc->_modules['store']->addVariable( '_tvrageEpisodeDataCache' );
}
/*****************************************************
* Bot Calls
*****************************************************/
function tvrage( &$irc, &$data, $notice = false)
{
if ( isset( $data->messageex[1] ) )
{
$query = $data->strippedMessage;
// help
if ( $query == 'help' )
{
$this->parseTemplate( $msg, 'help', array( 'command' => $data->trigger ) );
$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
// preg match checking
// single tv episode
}
else if ( preg_match( $this->_def['command']['showEp'], $query, $match ) )
{
if ( ( $show = $this->getFShow( $match[1] ) ) !== false )
{
if ( ( $ep = $this->getFEpisode( $show['tvrage'], $match[2], $match[3] ) ) !== false )
{
$ep['show'] = $show['name'];
$ep['genre'] = $show['genre'];
$ep['date'] = $irc->_modules['func']->snicedate( $ep['airdate'] );
$ep['sepnum'] = sprintf( '%02d', $ep['sepnum'] );
$this->parseTemplate( $msg, 'episode', $ep );
}
else
{
$this->parseTemplate( $msg, 'error', array( 'errormsg' => sprintf( 'No Episode found for show: %s, %dx%02d', $show['name'], $match[2], $match[3] ) ) );
}
}
else
{
$this->parseTemplate( $msg, 'error', array( 'errormsg' => sprintf( 'No program found for: %s', $match[1] ) ) );
}
$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
// tv series, with episode name
}
else if ( preg_match( $this->_def['command']['showEpName'], $query, $match ) )
{
if ( ( $show = $this->getFShow( $match[1] ) ) !== false )
{
if ( ( $ep = $this->getFEpisodeName( $show['tvrage'], $match[2] ) ) !== false )
{
$ep['show'] = $show['name'];
$ep['genre'] = $show['genre'];
$ep['date'] = $irc->_modules['func']->snicedate( $ep['airdate'] );
$ep['sepnum'] = sprintf( '%02d', $ep['sepnum'] );
$this->parseTemplate( $msg, 'episode', $ep );
}
else
{
$this->parseTemplate( $msg, 'error', array( 'errormsg' => sprintf( 'No Episode found for show: %s, %s', $show['name'], $match[2] ) ) );
}
}
else
{
$this->parseTemplate( $msg, 'error', array( 'errormsg' => sprintf( 'No program found for: %s', $match[1] ) ) );
}
$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
} else {
// show
if ( ( $show = $this->getFShow( $query ) ) !== false )
{
$show['classification'] = $show['class'];
foreach( $show as $key => $value )
{
if ( in_array( $key, array( 'First', 'Last', 'Latest', 'Next' ) ) ) {
$value['type'] = $key;
$value['date'] = $irc->_modules['func']->snicedate( $value['date'] );
$value['episode'] = sprintf( '%02d', $value['episode'] );
$show['showEps'][] = $value;
}
}
$this->parseTemplate( $msg, 'show', $show );
} else {
$this->parseTemplate( $msg, 'error', array( 'errormsg' => sprintf( 'No program found for: %s', $query ) ) );
}
$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
}
}
else
{
// help
$this->parseTemplate( $msg, 'help', array( 'command' => $data->trigger ) );
$irc->_modules['func']->reply( $irc, $data, $msg, $notice );
}
}
/*****************************************************
* Main functions
*****************************************************/
/**
* Get URL
*
* @params string $url - url to get
* @return contents of the page
* @access public
*/
function getUrl( $url )
{
global $irc;
$req =& new HTTP_Request( );
$req->setMethod(HTTP_REQUEST_METHOD_GET);
if ( isset( $irc->config->proxy ) )
{
if ( strlen($irc->config->proxy['host']) > 0 )
{
if ( strlen($irc->config->proxy['username']) > 0)
{
$req->setProxy($irc->config->proxy['host'],
$irc->config->proxy['port'],
$irc->config->proxy['username'],
$irc->config->proxy['password']);
}
else
{
$req->setProxy($irc->config->proxy['host'],
$irc->config->proxy['port']);
}
}
}
$req->setURL( $url, array( 'timeout' => '30', 'readTimeout' => 30, 'allowRedirects' => true ) );
$irc->modDebug( 'tvrage', 'request sent for: '.$url, __FILE__, __LINE__ );
$request = $req->sendRequest();
if (PEAR::isError($request)) {
$irc->modDebug( 'tvrage', 'failed to get '.$url.', error: '.$request->getMessage(), __FILE__, __LINE__ );
unset( $req, $request );
return false;
} else {
$body = $req->getResponseBody();
unset( $req, $request );
return $body;
}
}
/**
* look for a show
*
* @params string $query - Show search query
* @return int - tvrage.com showid
* @access public
*/
function findShow( $query )
{
global $irc;
$irc->_modules['store']->manualUpdate( '_tvrageShowCache' );
// check the cache
if ( ( isset( $irc->_tvrageShowCache[$query] ) ) &&
( mt_rand(1, 100) <= (100 * $this->_config['cacheChance'] * 0.7) ) ) // more likely to requery as cache doesnt go away
{
$irc->modDebug( 'tvrage', sprintf( 'Show search: %s, using cache: %s', $query, $irc->_tvrageShowCache[$query] ), __FILE__, __LINE__ );
return $irc->_tvrageShowCache[$query];
}
// find tv show
$tmp = $this->getUrl( 'http://www.tvrage.com/search.php?search='.urlencode(strtolower($query)) );
if ( preg_match($this->_def['findShow'], $tmp, $tvurl) )
{
$irc->_modules['store']->setVariable( '_tvrageShowCache', $tvurl[1], $query );
$irc->modDebug( 'tvrage', sprintf( 'Show search: %s, Found: %s', $query, $tvurl[1] ), __FILE__, __LINE__ );
return $tvurl[1];
}
else
{
$irc->modDebug( 'tvrage', sprintf( 'Show search: %s Failed', $query ), __FILE__, __LINE__ );
return false;
}
}
/**
* look for a episode
*
* @param string showID - tvrage.com showid
* @param int $series - Series Number
* @param int $episode - Episode in $series number
* @return int - episodeID
* @access public
*/
function findEpisode( $show, $series, $episode )
{
global $irc;
$irc->_modules['store']->manualUpdate( '_tvrageEpisodeCache' );
// check cache
if ( ( isset( $irc->_tvrageEpisodeCache[$show][$series.'x'.$episode] ) ) &&
( mt_rand(1, 100) <= (100 * $this->_config['cacheChance'] * 0.7) ) )
{
$irc->modDebug( 'tvrage', sprintf( 'Show %s, Episode search: %dx%02d, using cache: %s', $show, $series, $episode, $irc->_tvrageEpisodeCache[$show][$series.'x'.$episode]), __FILE__, __LINE__ );
return $irc->_tvrageEpisodeCache[$show][$series.'x'.$episode];
}
// download series page
$url = sprintf('http://www.tvrage.com/%s/episode_list/%d', $show, ltrim($series, '0') );
$tmp = $this->getUrl( $url );
// get episode id
if ( preg_match( sprintf( $this->_def['findEpisodeFromSeries'], $series, $episode ), $tmp, $epid ) )
{
$irc->_tvrageEpisodeCache[$show][$series.'x'.$episode] = $epid[1];
$irc->_modules['store']->manualUpdate( '_tvrageEpisodeCache' );
$irc->modDebug( 'tvrage', sprintf( 'Show: %s Episode search: %dx%02d, Found: %s', $show, $series, $episode, $epid[1] ), __FILE__, __LINE__ );
return $epid[1];
}
else
{
$irc->modDebug( 'tvrage', sprintf( 'Show: %s Episode search: %dx%02d Failed', $show, $series, $episode ), __FILE__, __LINE__ );
return false;
}
}
/**
* @param string $show - tvrage.com showid
* @param string $query - episode name
* @access public
*/
function findEpisodeName( $show, $query )
{
global $irc;
$irc->_modules['store']->manualUpdate( '_tvrageEpisodeCache' );
// check cache
if ( ( isset( $irc->_tvrageEpisodeCache[$show][$query] ) ) &&
( mt_rand(1, 100) <= (100 * $this->_config['cacheChance'] * 0.7) ) )
{
$irc->modDebug( 'tvrage', sprintf( 'Show %s, Episode search: %s, using cache: %s', $show, $query, $irc->_tvrageEpisodeCache[$show][$query]), __FILE__, __LINE__ );
return $irc->_tvrageEpisodeCache[$show][$uqery];
}
// download series page
$url = sprintf('http://www.tvrage.com/%s/episode_list/all', $show );
$tmp = $this->getUrl( $url );
// get episode id
if ( preg_match( sprintf( $this->_def['findEpisodeFromName'], str_replace( ' ', '.*', $query ) ), $tmp, $epid ) )
{
$this->_tvrageEpisodeCache[$show][$query] = $epid[1];
$irc->_modules['store']->manualUpdate( '_tvrageEpisodeCache' );
$irc->modDebug( 'tvrage', sprintf( 'Show: %s Name search: %s, Found: %s', $show, $query, $epid[1] ), __FILE__, __LINE__ );
return $epid[1];
} else
{
$irc->modDebug( 'tvrage', sprintf( 'Show: %s Name search: %s Failed', $show, $query ), __FILE__, __LINE__ );
return false;
}
}
function getFShow( $query, $ignoreCache = false )
{
if ( ( $showid = $this->findShow( $query ) ) !== false )
{
return $this->getShow( $showid, $ignoreCache );
}
else
return false;
}
/**
* @param string $tvin - tvrage showID
* @return array - Show information
* @access public
*/
function getShow( $tvin, $ignoreCache = false )
{
global $irc;
$irc->_modules['store']->manualUpdate( '_tvrageShowDataCache' );
// check cache
if ( is_array( $irc->_tvrageShowDataCache ) )
{
$tmpKeys = array_keys( $irc->_tvrageShowDataCache );
if ( ( !$ignoreCache ) && ( in_array( $tvin, $tmpKeys ) ) ) {
// found cache
if ( ( $irc->_tvrageShowDataCache[$tvin]['searched'] > time() - ( $this->_config['dataCache'] * 3600 * 24 ) ) &&
( mt_rand(1, 100) <= (100 * $this->_config['cacheChance']) ) )
{
$irc->modDebug( 'tvrage', 'show ID: '.$tvin.', using cache', __FILE__, __LINE__ );
return $irc->_tvrageShowDataCache[$tvin];
}
$isCache = true;
}
}
$url = sprintf( 'http://www.tvrage.com/%s/', $tvin );
if ( ( $page = $this->getUrl( $url ) ) !== false )
{
if ( preg_match( $this->_def['error'], $page ) )
{
return false;
}
preg_match( $this->_def['show']['name'], $page, $name );
preg_match( $this->_def['show']['genre'], $page, $genre );
preg_match( $this->_def['show']['classification'], $page, $class );
preg_match( $this->_def['show']['status'], $page, $status );
preg_match( $this->_def['show']['airtime'], $page, $airtime );
$at = strtotime( '1970/01/01 '.$airtime[1] );
$show = array(
'tvrage' => $tvin,
'name' => $this->stringDecode( $name[1] ),
'genre' => $this->stringDecode( $genre[1] ),
'class' => $this->stringDecode( $class[1] ),
'status' => $this->stringDecode( $status[1] ),
'airday' => $this->stringDecode( $airtime[1] ),
'airtime' => $this->stringDecode( $airtime[2] ),
'url' => sprintf( 'http://www.tvrage.com/%s/', $tvin ) );
if ( empty( $show['name'] ) )
{
if ( $isCache )
{
$irc->modDebug( 'tvrage', 'tvrage.com broken, show ID: '.$tvin.', using cache', __FILE__, __LINE__ );
return $irc->_tvrageShowDataCache[$tvin];
}
else
{
return false;
}
}
if ( preg_match_all( $this->_def['show']['episodes'], $page, $eps ) )
{
for( $i=0; $i < count( $eps[0] ); $i++ )
{
$show[$eps[1][$i]] = array(
'series' => $eps[2][$i],
'episode' => $eps[3][$i],
'title' => $this->stringDecode( $eps[4][$i] ),
'date' => ($eps[5][$i])? strtotime( str_replace( '/', ' ', $eps[5][$i] ) ):0 );
}
}
$show['searched'] = time();
$irc->_tvrageShowDataCache[$tvin] = $show;
$this->_checkCache();
return $show;
} else {
return false;
}
}
function getFEpisode( $showid, $series, $episode )
{
if ( ( $epid = $this->findEpisode( $showid, $series, $episode ) ) !== false )
{
return $this->getEpisode( $showid, $epid );
}
else
return false;
}
function getFEpisodeName( $showid, $query )
{
if ( ( $epid = $this->findEpisodeName( $showid, $query ) ) !== false )
{
return $this->getEpisode( $showid, $epid );
}
else
return false;
}
/**
* @param string $show - tvrage showID
* @param int $id - episodeID
* @return array - Episode information
* @access public
*/
function getEpisode( $show, $id )
{
global $irc;
$irc->_modules['store']->manualUpdate( '_tvrageEpisodeDataCache' );
// check cache
if ( isset( $irc->_tvrageEpisodeDataCache[$show] ) )
{
$tmpKeys = array_keys( $irc->_tvrageEpisodeDataCache[$show] );
if ( in_array( $id, $tmpKeys ) ) {
// found cache
if ( ( $irc->_tvrageEpisodeDataCache[$show][$id]['searched'] > ( time() - ( $this->_config['dataCache'] * 3600 * 24 ) ) ) &&
( mt_rand(1, 100) <= (100 * $this->_config['cacheChance']) ) )
{
$irc->modDebug( 'tvrage', 'show ID: '.$show.', episode ID: '.$id.', using cache', __FILE__, __LINE__ );
return $irc->_tvrageEpisodeDataCache[$show][$id];
}
$isCache = true;
}
unset( $tmpKeys );
}
$url = sprintf( 'http://www.tvrage.com/%s/episodes/%s/', $show, $id );
if ( ( $page = $this->getUrl( $url ) ) !== false )
{
if ( preg_match( $this->_def['error'], $page ) )
{
return false;
}
preg_match( $this->_def['episode']['title'], $page, $title );
preg_match( $this->_def['episode']['epnum'], $page, $epnum );
preg_match( $this->_def['episode']['series'], $page, $series );
preg_match( $this->_def['episode']['sepnum'], $page, $sepnum );
preg_match( $this->_def['episode']['prodnum'], $page, $prodnum );
preg_match( $this->_def['episode']['airdate'], $page, $airdate );
$ep = array(
'tvrage' => $id,
'title' => $this->stringDecode( $title[1] ),
'epnum' => $epnum[1],
'series' => $series[1],
'sepnum' => $sepnum[1],
'prod' => $prodnum[1],
'airdate' => strtotime( $airdate[1] ),
'url' => sprintf( 'http://www.tvrage.com/%s/episodes/%d/%dx%02d/', $show, $id, $series[1], $sepnum[1] ) );
if ( empty( $ep['title'] ) )
{
if ( $isCache )
{
$irc->modDebug( 'tvrage', 'tvrage.com is broken, show ID: '.$show.', episode ID: '.$id.', using cache', __FILE__, __LINE__ );
return $irc->_tvrageEpisodeDataCache[$show][$id];
}
else
{
return false;
}
}
$ep['searched'] = time();
$irc->_tvrageEpisodeDataCache[$show][$id] = $ep;
$this->_checkCache();
return $ep;
} else {
return false;
}
}
/**
* @param string $showid - tvrage showid
* @param int $date - date to start looking on
* @param int days - number of days to look for, default: 0
* @return array - array of Episode information
* @access public
*/
function findEpisodesFromDate( $showid, $date, $days = 0 )
{
global $irc;
// a bit of cheap caching
if ( ( isset( $irc->_tvrageLastList ) ) &&
( $irc->_tvrageLastList['showid'] == $showid ) &&
( $irc->_tvrageLastList['searched'] > ( time() - 3600 ) ) )
{
$irc->modDebug( 'tvrage', 'using Episodelist Cache from previous search, showid: '.$showid, __FILE__, __LINE__ );
$multiep = $irc->_tvrageLastList['multiep'];
}
else
{
$url = sprintf('http://www.tvrage.com/%s/episode_list/all', $showid );
$multiep = $this->getUrl($url);
$irc->_tvrageLastList = array(
'showid' => $showid,
'multiep' => $multiep,
'searched' => time()
);
}
$date2 = $date + ( 3600 * 24 * $days );
for ( $dtmp = $date; $dtmp <= $date2; $dtmp += 86400 ) {
$reg = sprintf( $this->_def['findEpisodeFromDate'], date( 'd', $dtmp ), date( 'M', $dtmp ), date( 'Y', $dtmp ) );
preg_match_all($reg, $multiep, $matches);
for ($i=0; $i < count($matches[1]); $i++ )
{
$eps[] = $matches[1][$i];
}
}
// $irc->modDebug( 'tvrage', sprintf( 'FindEpisodesByDate: show: %s, date: %s, days: %s', $showid, $date, $days ), __FILE__, __LINE__ );
if ( is_array( $eps ) ) {
return $eps;
} else {
return false;
}
}
/**
* Check the data cache, for old data
*
* @return void
*/
function _checkCache()
{
global $irc;
if ( is_array( $irc->_tvrageShowDataCache ) )
{
// show Data Cache
foreach( $irc->_tvrageShowDataCache as $id => $show )
{
if ( $show['searched'] < ( time() - ( $this->_config['dataCache'] * 3600 * 24 ) ) )
{
$irc->_modules['store']->unsetVariable( '_tvrageShowDataCache', $id );
}
}
}
if ( is_array( $irc->_tvrageEpisodeDataCache ) )
{
// episode Data Cache
foreach( $irc->_tvrageEpisodeDataCache as $id => $show )
{
if ( is_array( $show ) )
{
foreach( $show as $eID => $ep )
{
if ( $ep['searched'] < ( time() - ( $this->_config['dataCache'] * 3600 * 24 ) ) )
{
unset( $irc->_tvrageEpisodeDataCache[$id][$eID] );
$eDel = 1;
}
}
}
}
}
$irc->_modules['store']->manualUpdate( array( '_tvrageEpisodeDataCache', '_tvrageShowDataCache' ), true );
}
}
?>