diff --git a/app/src/XmlTv.php b/app/src/XmlTv.php index 9f0d101..44f0c0c 100644 --- a/app/src/XmlTv.php +++ b/app/src/XmlTv.php @@ -3,29 +3,66 @@ class XmlTv { private string $file; + private string $cacheFile; private string $timezone; - public function __construct(string $file, string $timezone = 'Europe/Vienna') - { + public function __construct( + string $file, + string $timezone = 'Europe/Vienna', + string $cacheFile = '/var/www/cache/guide.json' + ) { $this->file = $file; $this->timezone = $timezone; + $this->cacheFile = $cacheFile; + } + + public function synchronizeChannels(Config $config): void + { + $data = $this->getCachedData(); + $storedChannels = $config->getChannels(); + + $maxNumber = 0; + foreach ($storedChannels as $stored) { + $maxNumber = max($maxNumber, (int)($stored['number'] ?? 0)); + } + + foreach ($data['channels'] ?? [] as $id => $channel) { + if (!isset($storedChannels[$id])) { + $maxNumber++; + + $storedChannels[$id] = [ + 'uuid' => bin2hex(random_bytes(16)), + 'name' => $channel['name'] ?? $id, + 'icon' => $channel['icon'] ?? '', + 'country' => '', + 'language' => '', + 'enabled' => true, + 'favorite' => false, + 'number' => $maxNumber, + 'group' => 'Unknown', + ]; + } else { + $storedChannels[$id]['name'] = $channel['name'] ?? $storedChannels[$id]['name']; + $storedChannels[$id]['icon'] = $channel['icon'] ?? ($storedChannels[$id]['icon'] ?? ''); + } + } + + uasort($storedChannels, function ($a, $b) { + return ((int)($a['number'] ?? 999999)) <=> ((int)($b['number'] ?? 999999)); + }); + + $config->saveChannels($storedChannels); } public function load(?Config $config = null): array { + $data = $this->getCachedData(); + + $channelConfig = $config ? $config->getChannels() : []; $channels = []; $programmes = []; - $channelConfig = $config ? $config->getChannels() : []; - - if (!file_exists($this->file)) { - return [$channels, $programmes]; - } - - $xml = simplexml_load_file($this->file); - - foreach ($xml->channel as $channel) { - $id = (string)$channel['id']; + foreach ($data['channels'] ?? [] as $id => $channel) { $cfg = $channelConfig[$id] ?? []; if (($cfg['enabled'] ?? true) !== true) { @@ -34,16 +71,60 @@ class XmlTv $channels[$id] = [ 'id' => $id, - 'name' => $cfg['name'] ?? (string)$channel->{'display-name'}, - 'icon' => $cfg['icon'] ?? (isset($channel->icon) ? (string)$channel->icon['src'] : null), + 'name' => $cfg['name'] ?? $channel['name'] ?? $id, + 'icon' => $cfg['icon'] ?? $channel['icon'] ?? '', 'number' => (int)($cfg['number'] ?? 999999), 'favorite' => (bool)($cfg['favorite'] ?? false), ]; + + foreach ($channel['programmes'] ?? [] as $p) { + $programmes[$id][] = [ + 'title' => $p['title'] ?? '', + 'desc' => $p['description'] ?? '', + 'category' => $p['category'] ?? '', + 'start' => (new DateTime())->setTimestamp((int)$p['start'])->setTimezone(new DateTimeZone($this->timezone)), + 'stop' => (new DateTime())->setTimestamp((int)$p['stop'])->setTimezone(new DateTimeZone($this->timezone)), + ]; + } + } + + uasort($channels, function ($a, $b) { + return ($a['number'] ?? 999999) <=> ($b['number'] ?? 999999); + }); + + return [$channels, $programmes]; + } + + public function rebuildCache(): array + { + $data = [ + 'updated' => date('c'), + 'source' => $this->file, + 'channels' => [], + ]; + + if (!file_exists($this->file)) { + $this->writeCache($data); + return $data; + } + + $xml = simplexml_load_file($this->file); + + foreach ($xml->channel as $channel) { + $id = (string)$channel['id']; + + $data['channels'][$id] = [ + 'id' => $id, + 'name' => (string)$channel->{'display-name'}, + 'icon' => isset($channel->icon) ? (string)$channel->icon['src'] : '', + 'programmes' => [], + ]; } foreach ($xml->programme as $programme) { $channelId = (string)$programme['channel']; - if (!isset($channels[$channelId])) { + + if (!isset($data['channels'][$channelId])) { continue; } @@ -54,66 +135,62 @@ class XmlTv continue; } - $programmes[$channelId][] = [ + $data['channels'][$channelId]['programmes'][] = [ 'title' => (string)$programme->title, - 'desc' => (string)$programme->desc, + 'subtitle' => (string)$programme->{'sub-title'}, + 'description' => (string)$programme->desc, 'category' => (string)$programme->category, - 'start' => $start, - 'stop' => $stop, + 'start' => $start->getTimestamp(), + 'stop' => $stop->getTimestamp(), + 'duration' => $stop->getTimestamp() - $start->getTimestamp(), + 'year' => (string)$programme->date, + 'rating' => isset($programme->rating->value) ? (string)$programme->rating->value : '', + 'episode' => isset($programme->{'episode-num'}) ? (string)$programme->{'episode-num'} : '', + 'icon' => isset($programme->icon) ? (string)$programme->icon['src'] : '', + 'country' => (string)$programme->country, ]; } - uasort($channels, function ($a, $b) { - return ($a['number'] ?? 999999) <=> ($b['number'] ?? 999999); - }); + foreach ($data['channels'] as &$channel) { + usort($channel['programmes'], function ($a, $b) { + return $a['start'] <=> $b['start']; + }); + } - return [$channels, $programmes]; + unset($channel); + + $this->writeCache($data); + return $data; } - public function synchronizeChannels(Config $config): void + public function getCachedData(): array { - if (!file_exists($this->file)) { - return; + if (!file_exists($this->cacheFile)) { + return $this->rebuildCache(); } - $xml = simplexml_load_file($this->file); - $storedChannels = $config->getChannels(); + $xmlMtime = file_exists($this->file) ? filemtime($this->file) : 0; + $cacheMtime = filemtime($this->cacheFile); - $maxNumber = 0; - foreach ($storedChannels as $stored) { - $maxNumber = max($maxNumber, (int)($stored['number'] ?? 0)); + if ($xmlMtime > $cacheMtime) { + return $this->rebuildCache(); } - foreach ($xml->channel as $channel) { - $id = (string)$channel['id']; - $name = (string)$channel->{'display-name'}; - $icon = isset($channel->icon) ? (string)$channel->icon['src'] : ''; + $data = json_decode(file_get_contents($this->cacheFile), true); - if (!isset($storedChannels[$id])) { - $maxNumber++; - - $storedChannels[$id] = [ - 'uuid' => bin2hex(random_bytes(16)), - 'name' => $name, - 'icon' => $icon, - 'country' => '', - 'language' => '', - 'enabled' => true, - 'favorite' => false, - 'number' => $maxNumber, - 'group' => 'Unknown', - ]; - } else { - $storedChannels[$id]['name'] = $name; - $storedChannels[$id]['icon'] = $icon; - } + if (!is_array($data)) { + return $this->rebuildCache(); } - uasort($storedChannels, function ($a, $b) { - return ((int)($a['number'] ?? 999999)) <=> ((int)($b['number'] ?? 999999)); - }); + return $data; + } - $config->saveChannels($storedChannels); + private function writeCache(array $data): void + { + file_put_contents( + $this->cacheFile, + json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) + ); } private function parseTime(string $value): ?DateTime