<?php

###############################################################################
#
# Автор (Author): Brigadir (forum.mydune.ru)
# Дата (Date): 30-12-2019
# Последнее обновление (Latest update): 19-03-2025
#
###############################################################################

require_once dirname(__FILE__) . '/default_epg_parser.php';

///////////////////////////////////////////////////////////////////////////////

class YandexEpgParser extends DefaultEpgParser
{
	///////////////////////////////////////////////////////////////////////////

	const	API_ROOT_URL 		= 'https://tv.yandex.ru/api';
	const	SCHEDULE_QUERY_URN 	= '/channel/%s/schedule?selectedDay=%s';
	const	PROGRAM_EVENT_URN	= '/event?eventId=%s&programCoId=%s';

	///////////////////////////////////////////////////////////////////////////

	private	$epg;

	///////////////////////////////////////////////////////////////////////////

	private function get_data_by_uri($uri, $region_id)
	{
		static $initial_sk_key, $initial_sk_expire;

		if (!empty($uri))
		{
			$t = microtime(true);
			$region_id = empty($region_id)? '' : "/$region_id";
			$opts =
				array
				(
					CURLOPT_SSL_VERIFYPEER => false,
					CURLOPT_CONNECTTIMEOUT => 5,
					CURLOPT_TIMEOUT => 20,
					CURLOPT_FOLLOWLOCATION => true,
					CURLOPT_COOKIEJAR => '/tmp/tv_yandex_ru_cookie',
					CURLOPT_COOKIEFILE => '/tmp/tv_yandex_ru_cookie',
					CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36',
				);

			if (empty($initial_sk_key) || ($initial_sk_expire <= (time() + 3600)))
			{
				try
				{
					$doc = http_get_document(str_replace('/api', '', self::API_ROOT_URL), $opts, true);

					if (preg_match('/__INITIAL_SK__\s*=\s*(.*?);/ms', $doc, $matches))
					{
						$initial_sk = json_decode($matches[1]);
						$initial_sk_key = $initial_sk->key;
						$initial_sk_expire = $initial_sk->expire;
					}
				}
				catch (Exception $e)
				{
					hd_print("Warning! Can\'t load data. Error $e");
					return false;
				}
			}

			try
			{
				$opts[CURLOPT_HTTPHEADER] =
					array
					(
						"content-type: application/json",
						"x-requested-with: XMLHttpRequest",
						"x-tv-sk: $initial_sk_key",
					);

				$doc = http_get_document(self::API_ROOT_URL . $region_id . $uri, $opts);
				hd_print('Data successfully loaded in ' . round(microtime(true) - $t, 4) . ' sec');
			}
			catch (Exception $e)
			{
				hd_print("Warning! Can\'t load data. Error $e");
				return false;
			}

			return
				empty($doc)? false : json_decode($doc);
		}

		return false;
	}

	private function get_parsed_event($event, $timeshift_secs)
	{
		$start = new DateTime($event->start);
		$finish = new DateTime($event->finish);
		$e_title = isset($event->episode->title)? rtrim(trim($event->episode->title), ' .,:;') : '';
		$e_desc = isset($event->episode->description)? $event->episode->description : '';
		$p_title = isset($event->program->title)? rtrim(trim($event->program->title), ' .,:;') : '';
		$p_desc = isset($event->program->description)? $event->program->description : '';
		$icon_urls = array();

		if (isset($event->program->images) && is_array($event->program->images))
			foreach($event->program->images as $item)
			{
				$icon_size = 0;
				$icon_src = '';

				foreach($item->sizes as $size => $image)
					if (($size > $icon_size) && !preg_match('/alice_/', $image->src))
					{
						$icon_size = $size;
						$icon_src = $image->src;
					}

				if ($icon_size >= 300)
					$icon_urls[] = preg_replace('/\/\d+x\d+$/U', '/400x300', "http:$icon_src");
			}

			if (empty($icon_urls) && !empty($icon_src))
				$icon_urls[] = preg_replace('/\/\d+x\d+$/U', '/400x300', "http:$icon_src");

		if (empty($icon_urls) && isset($event->program->mainImageBaseUrl))
			$icon_urls[] = "http:{$event->program->mainImageBaseUrl}/400x300";

		$persons = array();

		if (isset($event->program->persons) && is_array($event->program->persons))
			foreach($event->program->persons as $person)
			{
				$name = trim($person->name);

				if (!empty($name))
					$persons[$person->role][] = $name;
			}

		$countries = array();

		if (isset($event->program->countries) && is_array($event->program->countries))
			foreach($event->program->countries as $country)
				$countries[] = trim($country);

		$program_type = preg_match('/^(.)(.*)$/us', trim($event->program->type->name), $matches)? mb_strtoupper($matches[1], "UTF-8") . mb_strtolower($matches[2], "UTF-8") : '';
		$cine = ($program_type == 'Сериалы')? 'Т/с ' : (($program_type == 'Фильмы')? 'Х/ф ' : '');

		switch($program_type)
		{
			case 'Сериалы':
				$genre = 'Телесериал';
				break;

			case 'Фильмы':
				$genre = 'Художественный фильм';
				break;

			case 'Досуг':
				$genre = 'ТВ-Шоу';
				break;

			case 'Инфо':
				$genre = 'Новости';
				break;

			default:
				$genre = $program_type;
		}

		$start_ts = $start->getTimestamp() + $timeshift_secs;
		$finish_ts = $finish->getTimestamp() + $timeshift_secs;
		$desc = trim(($e_desc <> $p_desc)? (empty($e_desc)? $p_desc : (empty($p_desc)? $e_desc : "$p_desc\n$e_desc")) : $p_desc);

		return
			array
			(
				ExtEpgProgram::id => $event->id,
				ExtEpgProgram::start_tm => $start_ts,
				ExtEpgProgram::end_tm => $finish_ts,
				ExtEpgProgram::title => preg_replace('/([!\(,.:;<?\[{])\s*\.+/uU', '$1', $cine . (preg_match('/^"/u', $p_title)? preg_replace('/"/u', '', $p_title) : $p_title)),
				ExtEpgProgram::sub_title => trim(($e_title <> $p_title)? $e_title : ''),
				ExtEpgProgram::desc => empty($desc)? '' : (preg_match('/[,.:;!?]$/', $desc)? $desc : "$desc."),
				ExtEpgProgram::main_category => trim("[" . (is_null($age_censor = isset($event->program->ageRestriction)? $event->program->ageRestriction : null)? '0' : $age_censor) . "+] $genre"),
				ExtEpgProgram::main_icon => isset($icon_urls[0])? $icon_urls[0] : '',
				ExtEpgProgram::icon_urls => array_slice($icon_urls, 1),
				ExtEpgProgram::director => isset($persons['director'])? $persons['director'] : array(),
				ExtEpgProgram::producer => isset($persons['producer'])? $persons['producer'] : array(),
				ExtEpgProgram::actor => isset($persons['actor'])? $persons['actor'] : array(),
				ExtEpgProgram::presenter => isset($persons['presenter'])? $persons['presenter'] : array(),
				ExtEpgProgram::writer => isset($persons['writer'])? $persons['writer'] : array(),
				ExtEpgProgram::operator => isset($persons['cameraman'])? $persons['cameraman'] : array(),
				ExtEpgProgram::composer => isset($persons['composer'])? $persons['composer'] : array(),
				ExtEpgProgram::country => empty($countries)? '' : trim(implode(', ', $countries)),
				ExtEpgProgram::year => isset($event->program->year)? trim($event->program->year) : '',
			);
	}

	///////////////////////////////////////////////////////////////////////////

	public function __construct(EpgEngine &$epg)
	{
		$this->epg = $epg;
	}

	public function get_parser_id()
	{
		return 'yandex';
	}

	public function get_parser_name()
	{
		return 'Яндекс.Телепрограмма';
	}

	public function thumbnails_support()
	{
		return true;
	}

	public function get_future_days()
	{
		return max(1, (7 - date('N', $this->epg->get_day_start_ts())));
	}

	public function get_past_days()
	{
		return 7;
	}

	public function get_day_epg_by_id($channel_id, $day_start_ts, $timeshift_hours)
	{
		$data = array();

		if (preg_match_all('/^(.+)@(\d+)$/', $channel_id, $matches))
		{
			$ya_channel_id = trim($matches[1][0]);
			$ya_region_id = trim($matches[2][0]);
			$timeshift_secs = empty($timeshift_hours)? 0 : $timeshift_hours * 3600;

			if (!empty($ya_channel_id) && !empty($ya_region_id))
			{
				if ($json = $this->get_data_by_uri(sprintf(self::SCHEDULE_QUERY_URN, $ya_channel_id, date('Y-m-d', $day_start_ts - $this->epg->time_zone_offset)), $ya_region_id))
					foreach($json->schedule->events as $event)
						if ($p_event = $this->get_parsed_event($event, $timeshift_secs))
							$data[$p_event[ExtEpgProgram::start_tm] + $timeshift_secs] = $p_event;

				ksort($data);
			}
		}

		return $data;
	}

	public function get_epg_event_by_id($channel_id, $event_id, $timeshift_hours)
	{
		if (preg_match_all('/^(.*)@(.*)$/', $channel_id, $matches))
		{
			$ya_channel_id = trim($matches[1][0]);
			$ya_region_id = trim($matches[2][0]);
		}
		else
			return null;

		$timeshift_secs = empty($timeshift_hours)? 0 : $timeshift_hours * 3600;

		if (!empty($ya_channel_id) && !empty($ya_region_id))
			if ($event = $this->get_data_by_uri(sprintf(self::PROGRAM_EVENT_URN, $event_id, ''), $ya_region_id))
				if ($p_event = $this->get_parsed_event($event, $timeshift_secs))
				{
					$p_event[ExtEpgProgram::id] = null;
					return $p_event;
				}

		return null;
	}
}

?>
