mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-03 01:39:24 +02:00
392 lines
11 KiB
PHP
392 lines
11 KiB
PHP
<?php
|
|
|
|
namespace buibr\xmlepg;
|
|
|
|
use XMLReader;
|
|
use SimpleXMLElement;
|
|
use RuntimeException;
|
|
use DateTimeZone;
|
|
|
|
class EpgParser {
|
|
|
|
// Source datas.
|
|
private $file;
|
|
private $url;
|
|
private $content;
|
|
// for temporary file if from content or from url.
|
|
private $isTemp;
|
|
public $temp_dir = '/tmp'; //unix, change this for windows.
|
|
// channel settings
|
|
private $channels;
|
|
private $channels_groupby = '@id';
|
|
// programmes settings.
|
|
private $epgdata;
|
|
private $epgdata_groupby = '@id';
|
|
// filter
|
|
private $channelfilter = [];
|
|
private $ignoreDescr = [];
|
|
// zone.
|
|
private $targetTimeZone;
|
|
// callbacks
|
|
public $onError;
|
|
|
|
public function __construct() {
|
|
$this->targetTimeZone = \date_default_timezone_get();
|
|
}
|
|
|
|
/**
|
|
* @param mixed $file`
|
|
*/
|
|
public function setFile($file): void {
|
|
$this->file = $file;
|
|
}
|
|
|
|
/**
|
|
* @param mixed $url - url
|
|
*/
|
|
public function setUrl($url): void {
|
|
$this->url = $url;
|
|
}
|
|
|
|
/**
|
|
* @param mixed $content = xml parsed string.
|
|
*/
|
|
public function setContent($content): void {
|
|
$this->content = $content;
|
|
}
|
|
|
|
/**
|
|
* @param mixed $channelfilter
|
|
*/
|
|
public function setChannelfilter($channelfilter): void {
|
|
$this->channelfilter[$channelfilter] = 1;
|
|
}
|
|
|
|
/**
|
|
* @param string $descr
|
|
*/
|
|
public function setIgnoreDescr(string $descr): void {
|
|
$this->ignoreDescr[$descr] = 1;
|
|
}
|
|
|
|
/**
|
|
* @param mixed $targetTimeZone
|
|
*/
|
|
public function setTargetTimeZone($targetTimeZone): void {
|
|
$this->targetTimeZone = $targetTimeZone;
|
|
}
|
|
|
|
/**
|
|
* Set group by for channels must be channels atribute.
|
|
* @param $group - channel will be grouped with. must be @id or pgram attribute.
|
|
*/
|
|
public function setChannelGroup($group) {
|
|
$this->channels_groupby = $group;
|
|
}
|
|
|
|
/**
|
|
* Set group by for channels must be channels atribute.
|
|
* @id = array index starting from 0
|
|
* @param $group - programes will be grouped with. must be @id or pgram attribute.
|
|
*/
|
|
public function setProgrammGroup($group) {
|
|
$this->epgdata_groupby = $group;
|
|
}
|
|
|
|
/**
|
|
* Parse the date from string in posible formats.
|
|
*/
|
|
public function getDate(string $date) {
|
|
|
|
try {
|
|
$dt = \DateTime::createFromFormat('YmdHis P', $date, new DateTimeZone('UTC'));
|
|
$dt->setTimezone(new DateTimeZone($this->targetTimeZone));
|
|
return $dt->format('Y-m-d H:i:s');
|
|
} catch (\Exception $e) {
|
|
|
|
} catch (\Error $e) {
|
|
|
|
}
|
|
|
|
try {
|
|
$dt = \DateTime::createFromFormat('YmdHis', $date, new DateTimeZone('UTC'));
|
|
$dt->setTimezone(new DateTimeZone($this->targetTimeZone));
|
|
return $dt->format('Y-m-d H:i:s');
|
|
} catch (\Exception $e) {
|
|
|
|
} catch (\Error $e) {
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
$ex = explode(' ', $date);
|
|
$sd = $ex[0];
|
|
$ed = $ex[1];
|
|
|
|
if (strlen($sd) == 13) {
|
|
$sd = "{$sd}0";
|
|
}
|
|
|
|
$date = $sd . " " . $ed;
|
|
|
|
$dt = \DateTime::createFromFormat('YmdHis P', $date, new DateTimeZone('UTC'));
|
|
$dt->setTimezone(new DateTimeZone($this->targetTimeZone));
|
|
return $dt->format('Y-m-d H:i:s');
|
|
} catch (\Exception $e) {
|
|
|
|
} catch (\Error $e) {
|
|
|
|
}
|
|
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param $descr
|
|
*
|
|
* @return string
|
|
*/
|
|
private function filterDescr($descr): string {
|
|
if (array_key_exists($descr, $this->ignoreDescr)) {
|
|
return '';
|
|
}
|
|
return $descr;
|
|
}
|
|
|
|
/**
|
|
* @return mixed
|
|
*/
|
|
public function getChannels() {
|
|
return $this->channels;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getEpgdata() {
|
|
return $this->epgdata;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public function resetChannelfilter(): void {
|
|
$this->channelfilter = [];
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
private function channelMatchFilter(string $channel): bool {
|
|
return array_key_exists($channel, $this->channelfilter);
|
|
}
|
|
|
|
/**
|
|
* @throws \RuntimeException
|
|
* @throws \Exception
|
|
*/
|
|
public function parseFile(): void {
|
|
|
|
if (!$this->file) {
|
|
throw new \RuntimeException('missing file: please use setFile before parse');
|
|
}
|
|
|
|
if (!file_exists($this->file)) {
|
|
throw new \RuntimeException('file does not exists: ' . $this->file);
|
|
}
|
|
|
|
//
|
|
$xml = new XMLReader();
|
|
|
|
// compress.zlib://'
|
|
$xml->open($this->file);
|
|
|
|
/** @noinspection PhpStatementHasEmptyBodyInspection */
|
|
/** @noinspection LoopWhichDoesNotLoopInspection */
|
|
/** @noinspection MissingOrEmptyGroupStatementInspection */
|
|
while ($xml->read() && $xml->name !== 'channel') {
|
|
|
|
}
|
|
|
|
$i = 0;
|
|
while ($xml->name === 'channel') {
|
|
$element = new SimpleXMLElement($xml->readOuterXML());
|
|
|
|
/** @noinspection PhpUndefinedFieldInspection */
|
|
$group_by = $this->channels_groupby === '@id' ? (@$i++) : (string) $element->attributes()->{$this->epgdata_groupby};
|
|
|
|
// se the id
|
|
$channel_id = $group_by ?: 1;
|
|
|
|
/** @noinspection PhpUndefinedFieldInspection */
|
|
$this->channels[$channel_id] = [
|
|
'id' => (string) $element->attributes()->id,
|
|
'display-name' => (string) $element->{'display-name'},
|
|
'url' => (string) $element->{'url'},
|
|
'email' => (string) $element->{'email'},
|
|
'icon' => null,
|
|
];
|
|
if (isset($element->{'icon'}) && $element->{'icon'} instanceof SimpleXMLElement) {
|
|
$attributes = $element->{'icon'}->attributes();
|
|
$pathinfo = pathinfo($attributes->src);
|
|
|
|
if (empty($attributes->src)) {
|
|
$this->channels[$channel_id]['icon'] = (string) $element->{'icon'};
|
|
$xml->next('channel');
|
|
unset($element);
|
|
} elseif (!filter_var($attributes->src, FILTER_VALIDATE_URL)) {
|
|
$this->channels[$channel_id]['icon'] = (string) $element->{'icon'};
|
|
$xml->next('channel');
|
|
unset($element);
|
|
} elseif (empty($pathinfo['extension'])) {
|
|
$this->channels[$channel_id]['icon'] = (string) $element->{'icon'};
|
|
$xml->next('channel');
|
|
unset($element);
|
|
continue;
|
|
} else {
|
|
$this->channels[$channel_id]['icon'] = (string) $attributes->src;
|
|
}
|
|
}
|
|
|
|
$xml->next('channel');
|
|
unset($element);
|
|
}
|
|
|
|
$xml->close();
|
|
$xml->open($this->file);
|
|
|
|
/** @noinspection PhpStatementHasEmptyBodyInspection */
|
|
/** @noinspection LoopWhichDoesNotLoopInspection */
|
|
/** @noinspection MissingOrEmptyGroupStatementInspection */
|
|
while ($xml->read() && $xml->name !== 'programme') {
|
|
|
|
}
|
|
|
|
while ($xml->name === 'programme') {
|
|
$element = new SimpleXMLElement($xml->readOuterXML());
|
|
|
|
/** @noinspection PhpUndefinedFieldInspection */
|
|
if (!\count($this->channelfilter) || (\count($this->channelfilter) && $this->channelMatchFilter((string) $element->attributes()->channel))) {
|
|
|
|
/** @noinspection PhpUndefinedFieldInspection */
|
|
$startString = $this->getDate((string) $element->attributes()->start);
|
|
|
|
/** @noinspection PhpUndefinedFieldInspection */
|
|
$stopString = $this->getDate((string) $element->attributes()->stop);
|
|
|
|
/** @noinspection PhpUndefinedFieldInspection */
|
|
$grouper = $this->epgdata_groupby === '@id' ? (@$i++) : (string) $element->attributes()->{$this->epgdata_groupby};
|
|
|
|
$icon = (string) $element->icon;
|
|
if (empty($icon) && !empty($element->icon->attributes()->src)) {
|
|
$icon = (string) $element->icon->attributes()->src;
|
|
}
|
|
|
|
/** @noinspection PhpUndefinedFieldInspection */
|
|
$this->epgdata[$grouper ?: 0] = [
|
|
'start' => $startString,
|
|
'start_raw' => (string) $element->attributes()->start,
|
|
'channel' => (string) $element->attributes()->channel,
|
|
'stop' => $stopString,
|
|
'title' => (string) $element->title,
|
|
'sub-title' => (string) $element->{'sub-title'},
|
|
'desc' => $this->filterDescr((string) $element->desc),
|
|
'date' => (int) (string) $element->date,
|
|
'category' => (string) $element->category,
|
|
'credits' => (string) $element->credits,
|
|
'country' => (string) $element->country,
|
|
'icon' => $icon,
|
|
'episode-num' => (string) $element->{'episode-num'},
|
|
];
|
|
}
|
|
|
|
$xml->next('programme');
|
|
unset($element);
|
|
}
|
|
|
|
$xml->close();
|
|
|
|
if ($this->isTemp) {
|
|
@unlink($this->file);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @throws \RuntimeException
|
|
* @throws \Exception
|
|
*/
|
|
public function parseUrl(): void {
|
|
|
|
if (!$this->url) {
|
|
throw new \RuntimeException('Url missing: please use setUrl before parseUrl');
|
|
}
|
|
|
|
if (!filter_var($this->url, FILTER_VALIDATE_URL)) {
|
|
throw new \RuntimeException('Url invalid: ' . $this->url);
|
|
}
|
|
|
|
$this->content = @\file_get_contents($this->url);
|
|
|
|
if (!strpos($http_response_header[0], "200")) {
|
|
throw new \RuntimeException("Invalid response headers: " . $http_response_header[0], 1);
|
|
}
|
|
|
|
$this->checkXml();
|
|
$this->saveTemp(); // will save the file.
|
|
$this->parseFile();
|
|
}
|
|
|
|
/**
|
|
* @throws \RuntimeException
|
|
* @throws \Exception
|
|
*/
|
|
public function parseContent(): void {
|
|
|
|
if (!$this->content) {
|
|
throw new \RuntimeException('Url missing: please use setUrl before parseUrl');
|
|
}
|
|
|
|
$this->checkXml();
|
|
$this->saveTemp(); // will save the file.
|
|
$this->parseFile();
|
|
}
|
|
|
|
/**
|
|
* Save content to temp file from ulr or from content.
|
|
*/
|
|
public function saveTemp(): void {
|
|
if (empty($this->temp_dir)) {
|
|
$this->temp_dir = '/tmp/';
|
|
}
|
|
|
|
$length = 15;
|
|
$filename = substr(str_shuffle(str_repeat($x = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length / strlen($x)))), 1, $length);
|
|
|
|
$this->file = $this->temp_dir . '/' . $filename . '.xml';
|
|
|
|
if (!\file_put_contents($this->file, $this->content)) {
|
|
throw new \RuntimeException("Writing to {$this->file} is not possible.");
|
|
}
|
|
|
|
$this->isTemp = true;
|
|
$this->content = null;
|
|
}
|
|
|
|
/**
|
|
* Check content of response from xml f is xml
|
|
* @throws \RuntimeException
|
|
*/
|
|
public function checkXml() {
|
|
libxml_use_internal_errors(true);
|
|
$doc = simplexml_load_string($this->content);
|
|
if (!$doc) {
|
|
$errors = libxml_get_errors();
|
|
@$errors = $errors[0];
|
|
|
|
throw new \RuntimeException("Content of this request its not XML: $errors");
|
|
}
|
|
}
|
|
|
|
}
|