|
|
@@ -0,0 +1,544 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace Moinax\TvDb;
|
|
|
+
|
|
|
+use Moinax\TvDb\CurlException;
|
|
|
+use Moinax\TvDb\Http\CurlClient;
|
|
|
+use Moinax\TvDb\Http\HttpClient;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Base TVDB library class, provides universal functions and variables
|
|
|
+ *
|
|
|
+ * @package TvDb
|
|
|
+ * @author Jérôme Poskin <moinax@gmail.com>
|
|
|
+ **/
|
|
|
+class Client
|
|
|
+{
|
|
|
+ const POST = 'post';
|
|
|
+ const GET = 'get';
|
|
|
+
|
|
|
+ const MIRROR_TYPE_XML = 1;
|
|
|
+ const MIRROR_TYPE_BANNER = 2;
|
|
|
+ const MIRROR_TYPE_ZIP = 4;
|
|
|
+
|
|
|
+ const DEFAULT_LANGUAGE = 'en';
|
|
|
+
|
|
|
+ const FORMAT_XML = 'xml';
|
|
|
+ const FORMAT_ZIP = 'zip';
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Base url for TheTVDB
|
|
|
+ *
|
|
|
+ * @var string
|
|
|
+ */
|
|
|
+ protected $baseUrl = '';
|
|
|
+
|
|
|
+ /**
|
|
|
+ * API key for thetvdb.com
|
|
|
+ *
|
|
|
+ * @var string
|
|
|
+ */
|
|
|
+ protected $apiKey = '';
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Array of available mirrors
|
|
|
+ *
|
|
|
+ * @var array
|
|
|
+ */
|
|
|
+ protected $mirrors = array();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Array of available languages
|
|
|
+ *
|
|
|
+ * @var array
|
|
|
+ */
|
|
|
+ protected $languages = array();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Default language.
|
|
|
+ *
|
|
|
+ * @var string
|
|
|
+ */
|
|
|
+ protected $defaultLanguage = 'en';
|
|
|
+
|
|
|
+ /**
|
|
|
+ * HttpClient used to query the API.
|
|
|
+ *
|
|
|
+ * @var HttpClient
|
|
|
+ */
|
|
|
+ protected $httpClient;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param string $baseUrl Domain name of the api without trailing slash
|
|
|
+ * @param string $apiKey Api key provided by http://thetvdb.com
|
|
|
+ */
|
|
|
+ public function __construct($baseUrl, $apiKey)
|
|
|
+ {
|
|
|
+ $this->baseUrl = $baseUrl;
|
|
|
+ $this->apiKey = $apiKey;
|
|
|
+ $this->httpClient = new CurlClient();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Set the HttpClient used to query the API.
|
|
|
+ *
|
|
|
+ * @param HttpClient $client
|
|
|
+ */
|
|
|
+ public function setHttpClient(HttpClient $client)
|
|
|
+ {
|
|
|
+ $this->httpClient = $client;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get a language information
|
|
|
+ *
|
|
|
+ * @param string $abbreviation
|
|
|
+ * @return array
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ public function getLanguage($abbreviation)
|
|
|
+ {
|
|
|
+ if (empty($this->languages)) {
|
|
|
+ $this->getLanguages();
|
|
|
+ }
|
|
|
+ if (!isset($this->languages[$abbreviation])) {
|
|
|
+ throw new \Exception('This language is not available');
|
|
|
+ }
|
|
|
+
|
|
|
+ return $this->languages[$abbreviation];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Set the default query language. It must be a valid language code.
|
|
|
+ *
|
|
|
+ * @param string $language
|
|
|
+ */
|
|
|
+ public function setDefaultLanguage($language) {
|
|
|
+ $this->defaultLanguage = $language;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get the default query language.
|
|
|
+ *
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+ public function getDefaultLanguage() {
|
|
|
+ return $this->defaultLanguage;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get the server time for further updates
|
|
|
+ *
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+ public function getServerTime()
|
|
|
+ {
|
|
|
+ return (string)$this->fetchXml('Updates.php?type=none')->Time;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Searches for tv serie based on series name
|
|
|
+ *
|
|
|
+ * @param string $seriesName
|
|
|
+ * @param string $language
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public function getSeries($seriesName, $language = null)
|
|
|
+ {
|
|
|
+ $language = $language ? : $this->defaultLanguage;
|
|
|
+
|
|
|
+ $data = $this->fetchXml('GetSeries.php?seriesname=' . urlencode($seriesName) . '&language=' . $language);
|
|
|
+ $series = array();
|
|
|
+ foreach ($data->Series as $serie) {
|
|
|
+ $series[] = new Serie($serie);
|
|
|
+ }
|
|
|
+ return $series;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Find a tv serie by the id from thetvdb.com
|
|
|
+ *
|
|
|
+ * @var int $serieId
|
|
|
+ * @var string $language
|
|
|
+ *
|
|
|
+ * @return Serie A serie object or false if not found
|
|
|
+ **/
|
|
|
+ public function getSerie($serieId, $language = null)
|
|
|
+ {
|
|
|
+ $language = $language ? : $this->defaultLanguage;
|
|
|
+
|
|
|
+ $data = $this->fetchXml('series/' . $serieId . '/' . $language . '.xml');
|
|
|
+
|
|
|
+ return new Serie($data->Series);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Find a tv serie by a remote id
|
|
|
+ *
|
|
|
+ * @param array $remoteId
|
|
|
+ * @param string $language
|
|
|
+ *
|
|
|
+ * @example <pre><code>$obj->getSerieByRemoteId(array('imdbid' => '<imdbid>'));</code></pre>
|
|
|
+ * @example <pre><code>$obj->getSerieByRemoteId(array('zap2it' => '<zap2it>'));</code></pre>
|
|
|
+ *
|
|
|
+ * @return Serie
|
|
|
+ */
|
|
|
+ public function getSerieByRemoteId(array $remoteId, $language = null)
|
|
|
+ {
|
|
|
+ $language = $language ? : $this->defaultLanguage;
|
|
|
+
|
|
|
+ $data = $this->fetchXml('GetSeriesByRemoteID.php?' . http_build_query($remoteId) . '&language=' . $language);
|
|
|
+
|
|
|
+ return new Serie($data->Series);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Find all banners related to a serie
|
|
|
+ *
|
|
|
+ * @param int $serieId
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+ public function getBanners($serieId)
|
|
|
+ {
|
|
|
+ $data = $this->fetchXml('series/' . $serieId . '/banners.xml');
|
|
|
+ $banners = array();
|
|
|
+ foreach ($data->Banner as $banner) {
|
|
|
+ $banners[] = new Banner($banner);
|
|
|
+ }
|
|
|
+
|
|
|
+ return $banners;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Find banners that match a filter string
|
|
|
+ * Eg: poster or season
|
|
|
+ */
|
|
|
+ public function getBannersFiltered($serieId,$filter) {
|
|
|
+ $data = $this->getBanners($serieId);
|
|
|
+ $banners = array();
|
|
|
+ foreach($data as $image) {
|
|
|
+ if($image->type == $filter) {
|
|
|
+ $banners[] = $image;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return $banners;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Find all actors related to a serie
|
|
|
+ *
|
|
|
+ * @param int $serieId
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public function getActors($serieId)
|
|
|
+ {
|
|
|
+ $data = $this->fetchXml('series/'. $serieId . '/actors.xml');
|
|
|
+ $actors = array();
|
|
|
+ foreach ($data->Actor as $actor) {
|
|
|
+ $actors [] = new Actor($actor);
|
|
|
+ }
|
|
|
+
|
|
|
+ return $actors;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get all episodes for a serie
|
|
|
+ *
|
|
|
+ * @param int $serieId
|
|
|
+ * @param string $language
|
|
|
+ * @param string $format
|
|
|
+ * @return array
|
|
|
+ * @throws \ErrorException
|
|
|
+ */
|
|
|
+ public function getSerieEpisodes($serieId, $language = null, $format = self::FORMAT_XML)
|
|
|
+ {
|
|
|
+ $language = $language ? : $this->defaultLanguage;
|
|
|
+
|
|
|
+ switch ($format) {
|
|
|
+ case self::FORMAT_XML:
|
|
|
+ $data = $this->fetchXml('series/' . $serieId . '/all/' . $language . '.' . $format);
|
|
|
+ break;
|
|
|
+ case self::FORMAT_ZIP:
|
|
|
+ if (!in_array('zip', stream_get_wrappers())) {
|
|
|
+ throw new \ErrorException('Your PHP does nort support ZIP stream wrappers');
|
|
|
+ }
|
|
|
+ $data = $this->fetchZIP('series/' . $serieId . '/all/' . $language . '.' . $format, array(), self::GET, $language.".xml");
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new \ErrorException('Unsupported format');
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ $serie = new Serie($data->Series);
|
|
|
+ $episodes = array();
|
|
|
+ foreach ($data->Episode as $episode) {
|
|
|
+ $episodes[(int)$episode->id] = new Episode($episode);
|
|
|
+ }
|
|
|
+ return array('serie' => $serie, 'episodes' => $episodes);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get a specific episode by season and episode number
|
|
|
+ *
|
|
|
+ * @var int $serieId required the id of the serie
|
|
|
+ * @var int $season required the season number
|
|
|
+ * @var int $episode required the episode number
|
|
|
+ * @var string $language language abbreviation
|
|
|
+ *
|
|
|
+ * @return Episode
|
|
|
+ **/
|
|
|
+ public function getEpisode($serieId, $season, $episode, $language = null)
|
|
|
+ {
|
|
|
+ $language = $language ? : $this->defaultLanguage;
|
|
|
+
|
|
|
+ $data = $this->fetchXml('series/' . $serieId . '/default/' . $season . '/' . $episode . '/' . $language . '.xml');
|
|
|
+
|
|
|
+ return new Episode($data->Episode);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get a specific episode by his id
|
|
|
+ *
|
|
|
+ * @var int $episodeId required the id of the episode
|
|
|
+ * @var string $language
|
|
|
+ * @return Episode
|
|
|
+ **/
|
|
|
+ public function getEpisodeById($episodeId, $language = null)
|
|
|
+ {
|
|
|
+ $language = $language ? : $this->defaultLanguage;
|
|
|
+
|
|
|
+ $data = $this->fetchXml('episodes/' . $episodeId . '/' . $language . '.xml');
|
|
|
+
|
|
|
+ return new Episode($data->Episode);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param $serieId
|
|
|
+ * @param $airdate
|
|
|
+ * @param null $language
|
|
|
+ *
|
|
|
+ * @return Episode
|
|
|
+ */
|
|
|
+ public function getEpisodeByAirDate($serieId, $airdate, $language = null)
|
|
|
+ {
|
|
|
+ $language = $language ? : $this->defaultLanguage;
|
|
|
+ $data = $this->fetchXml('GetEpisodeByAirDate.php?apikey=' . $this->apiKey . '&seriesid=' . $serieId . '&airdate=' . $airdate . '&language=' . $language );
|
|
|
+ return new Episode($data->Episode);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get updates list based on previous time you got data
|
|
|
+ *
|
|
|
+ * @param int $previousTime
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public function getUpdates($previousTime)
|
|
|
+ {
|
|
|
+ $data = $this->fetchXml('Updates.php?type=all&time=' . $previousTime);
|
|
|
+
|
|
|
+ $series = array();
|
|
|
+ foreach ($data->Series as $serieId) {
|
|
|
+ $series[] = (int)$serieId;
|
|
|
+ }
|
|
|
+ $episodes = array();
|
|
|
+ foreach ($data->Episode as $episodeId) {
|
|
|
+ $episodes[] = (int)$episodeId;
|
|
|
+ }
|
|
|
+ return array('series' => $series, 'episodes' => $episodes);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Fetch banner raw jpeg data from banner mirror.
|
|
|
+ *
|
|
|
+ * @param string $banner the banner to fetch like graphical/79349-g7.jpg
|
|
|
+ * @return string raw jpeg data
|
|
|
+ */
|
|
|
+ public function fetchBanner($banner)
|
|
|
+ {
|
|
|
+ $url = $this->getMirror(self::MIRROR_TYPE_BANNER) . '/banners/' . $banner;
|
|
|
+ return $this->httpClient->fetch($url, array(), self::GET);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Fetches data via curl and returns result
|
|
|
+ *
|
|
|
+ * @access protected
|
|
|
+ * @param string $function The function used to fetch data in xml
|
|
|
+ * @param array $params
|
|
|
+ * @param string $method
|
|
|
+ * @return string The data
|
|
|
+ */
|
|
|
+ protected function fetchXml($function, $params = array(), $method = self::GET)
|
|
|
+ {
|
|
|
+ if (strpos($function, '.php') > 0) { // no need of api key for php calls
|
|
|
+ $url = $this->getMirror(self::MIRROR_TYPE_XML) . '/api/' . $function;
|
|
|
+ } else {
|
|
|
+ $url = $this->getMirror(self::MIRROR_TYPE_XML) . '/api/' . $this->apiKey . '/' . $function;
|
|
|
+ }
|
|
|
+
|
|
|
+ $data = $this->httpClient->fetch($url, $params, $method);
|
|
|
+
|
|
|
+ $simpleXml = $this->getXml($data);
|
|
|
+
|
|
|
+ return $simpleXml;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Fetches data via curl and returns result
|
|
|
+ *
|
|
|
+ * @access protected
|
|
|
+ * @param string $function The function used to fetch data in zip
|
|
|
+ * @param array $params
|
|
|
+ * @param string $method
|
|
|
+ * @param string $file The file to extract from the ZIP
|
|
|
+ * @return string The data
|
|
|
+ */
|
|
|
+ protected function fetchZIP($function, $params = array(), $method = self::GET, $file=null)
|
|
|
+ {
|
|
|
+ if (strpos($function, '.php') > 0) { // no need of api key for php calls
|
|
|
+ $url = $this->getMirror(self::MIRROR_TYPE_ZIP) . '/api/' . $function;
|
|
|
+ } else {
|
|
|
+ $url = $this->getMirror(self::MIRROR_TYPE_ZIP) . '/api/' . $this->apiKey . '/' . $function;
|
|
|
+ }
|
|
|
+
|
|
|
+ $zipName = tempnam(sys_get_temp_dir(), "tvdb-");
|
|
|
+ $zip = fopen($zipName, "w");
|
|
|
+ fwrite($zip, $this->httpClient->fetch($url, $params, $method));
|
|
|
+ fclose($zip);
|
|
|
+ if (is_null($file)) {
|
|
|
+ $file = $this->getDefaultLanguage().".xml";
|
|
|
+ }
|
|
|
+ $dataPath = "zip://".$zipName."#".$file;
|
|
|
+ $data = file_get_contents($dataPath);
|
|
|
+
|
|
|
+ $simpleXml = $this->getXml($data);
|
|
|
+
|
|
|
+ return $simpleXml;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Fetch data with curl
|
|
|
+ *
|
|
|
+ * @param string $url
|
|
|
+ * @param array $params
|
|
|
+ * @param string $method
|
|
|
+ * @throws CurlException
|
|
|
+ * @return bool|string
|
|
|
+ */
|
|
|
+ protected function fetch($url, array $params = array(), $method = self::GET)
|
|
|
+ {
|
|
|
+ return $this->httpClient->fetch($url, $params, $method);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Convert xml string to SimpleXMLElement
|
|
|
+ *
|
|
|
+ * @param string $data
|
|
|
+ * @return \SimpleXMLElement
|
|
|
+ * @throws \ErrorException|\Exception
|
|
|
+ */
|
|
|
+ protected function getXml($data)
|
|
|
+ {
|
|
|
+ if (extension_loaded('libxml')) {
|
|
|
+ libxml_use_internal_errors(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ $simpleXml = simplexml_load_string($data);
|
|
|
+ if (!$simpleXml) {
|
|
|
+ if (extension_loaded('libxml')) {
|
|
|
+ $xmlErrors = libxml_get_errors();
|
|
|
+ $errors = array();
|
|
|
+ foreach ($xmlErrors as $error) {
|
|
|
+ $errors[] = sprintf('Error in file %s on line %d with message : %s', $error->file, $error->line, $error->message);
|
|
|
+ }
|
|
|
+ if (count($errors) > 0) {
|
|
|
+
|
|
|
+ throw new XmlException(implode("\n", $errors));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ throw new XmlException('Xml file could not be loaded');
|
|
|
+ }
|
|
|
+
|
|
|
+ return $simpleXml;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get a list of mirrors available to fetchXml the data from the api
|
|
|
+ * @return void
|
|
|
+ */
|
|
|
+ protected function getMirrors()
|
|
|
+ {
|
|
|
+ $data = $this->httpClient->fetch($this->baseUrl . '/api/' . $this->apiKey . '/mirrors.xml');
|
|
|
+ $mirrors = $this->getXml($data);
|
|
|
+
|
|
|
+ foreach ($mirrors->Mirror as $mirror) {
|
|
|
+ $typeMask = (int)$mirror->typemask;
|
|
|
+ $mirrorPath = (string)$mirror->mirrorpath;
|
|
|
+
|
|
|
+ if ($typeMask & self::MIRROR_TYPE_XML) {
|
|
|
+ $this->mirrors[self::MIRROR_TYPE_XML][] = $mirrorPath;
|
|
|
+ }
|
|
|
+ if ($typeMask & self::MIRROR_TYPE_BANNER) {
|
|
|
+ $this->mirrors[self::MIRROR_TYPE_BANNER][] = $mirrorPath;
|
|
|
+ }
|
|
|
+ if ($typeMask & self::MIRROR_TYPE_ZIP) {
|
|
|
+ $this->mirrors[self::MIRROR_TYPE_ZIP][] = $mirrorPath;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get a random mirror from the list of available mirrors
|
|
|
+ *
|
|
|
+ * @param int $typeMask
|
|
|
+ * @return string
|
|
|
+ * @access protected
|
|
|
+ */
|
|
|
+ public function getMirror($typeMask = self::MIRROR_TYPE_XML)
|
|
|
+ {
|
|
|
+ if (empty($this->mirrors)) {
|
|
|
+ $this->getMirrors();
|
|
|
+ }
|
|
|
+ return $this->mirrors[$typeMask][array_rand($this->mirrors[$typeMask], 1)];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get a list of languages available for the content of the api
|
|
|
+ * @return \SimpleXMLElement
|
|
|
+ */
|
|
|
+ protected function getLanguages()
|
|
|
+ {
|
|
|
+ $languages = $this->fetchXml('languages.xml');
|
|
|
+
|
|
|
+ foreach ($languages->Language as $language) {
|
|
|
+ $this->languages[(string)$language->abbreviation] = array(
|
|
|
+ 'name' => (string)$language->name,
|
|
|
+ 'abbreviation' => (string)$language->abbreviation,
|
|
|
+ 'id' => (int)$language->id,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Removes indexes from an array if they are zero length after trimming
|
|
|
+ *
|
|
|
+ * @param array $array The array to remove empty indexes from
|
|
|
+ * @return array An array with all empty indexes removed
|
|
|
+ **/
|
|
|
+ public static function removeEmptyIndexes($array)
|
|
|
+ {
|
|
|
+
|
|
|
+ $length = count($array);
|
|
|
+
|
|
|
+ for ($i = $length - 1; $i >= 0; $i--) {
|
|
|
+ if (trim($array[$i]) == '') {
|
|
|
+ unset($array[$i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ sort($array);
|
|
|
+ return $array;
|
|
|
+ }
|
|
|
+}
|