409 lines
14 KiB
PHP
409 lines
14 KiB
PHP
<?php
|
|
|
|
class ProgrammeService
|
|
{
|
|
private string $timezone;
|
|
|
|
public function __construct(string $timezone = 'Europe/Vienna')
|
|
{
|
|
$this->timezone = $timezone;
|
|
}
|
|
|
|
public function load(array $data, ?Config $config = null): array
|
|
{
|
|
$channelConfig = $config ? $config->getChannels() : [];
|
|
$channels = [];
|
|
$programmes = [];
|
|
|
|
foreach ($data['channels'] ?? [] as $id => $channel) {
|
|
$cfg = $channelConfig[$id] ?? [];
|
|
|
|
if (($cfg['enabled'] ?? true) !== true) {
|
|
continue;
|
|
}
|
|
|
|
$channels[$id] = [
|
|
'id' => $id,
|
|
'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'] ?? '',
|
|
'metadata' => $p['metadata'] ?? [],
|
|
'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 getCurrentProgrammes(array $data, ?Config $config = null): array
|
|
{
|
|
$channelConfig = $config ? $config->getChannels() : [];
|
|
$now = time();
|
|
$items = [];
|
|
|
|
foreach ($data['channels'] ?? [] as $id => $channel) {
|
|
$cfg = $channelConfig[$id] ?? [];
|
|
|
|
if (($cfg['enabled'] ?? true) !== true) {
|
|
continue;
|
|
}
|
|
|
|
$programmes = $channel['programmes'] ?? [];
|
|
|
|
usort($programmes, function ($a, $b) {
|
|
return ((int)($a['start'] ?? 0)) <=> ((int)($b['start'] ?? 0));
|
|
});
|
|
|
|
foreach ($programmes as $index => $p) {
|
|
$start = (int)($p['start'] ?? 0);
|
|
$stop = (int)($p['stop'] ?? 0);
|
|
|
|
if ($now >= $start && $now < $stop) {
|
|
$duration = max(1, $stop - $start);
|
|
$elapsed = max(0, $now - $start);
|
|
$percent = min(100, round(($elapsed / $duration) * 100));
|
|
|
|
$next = $programmes[$index + 1] ?? null;
|
|
|
|
$items[] = [
|
|
'channel_id' => $id,
|
|
'channel_name' => $cfg['name'] ?? $channel['name'] ?? $id,
|
|
'channel_icon' => $cfg['icon'] ?? $channel['icon'] ?? '',
|
|
'channel_number' => (int)($cfg['number'] ?? 999999),
|
|
'favorite' => (bool)($cfg['favorite'] ?? false),
|
|
|
|
'title' => $p['title'] ?? '',
|
|
'subtitle' => $p['subtitle'] ?? '',
|
|
'description' => $p['description'] ?? '',
|
|
'category' => $p['category'] ?? '',
|
|
'genre_icon' => $this->getGenreIcon($p['category'] ?? ''),
|
|
'metadata' => $p['metadata'] ?? [],
|
|
'year' => $p['year'] ?? '',
|
|
'rating' => $p['rating'] ?? '',
|
|
'episode' => $p['episode'] ?? '',
|
|
'quality' => $this->detectQuality($p),
|
|
|
|
'start' => $start,
|
|
'stop' => $stop,
|
|
'percent' => $percent,
|
|
'remaining_minutes' => max(0, (int)ceil(($stop - $now) / 60)),
|
|
|
|
'next_title' => $next['title'] ?? '',
|
|
'next_subtitle' => $next['subtitle'] ?? '',
|
|
'next_start' => isset($next['start']) ? (int)$next['start'] : null,
|
|
'next_stop' => isset($next['stop']) ? (int)$next['stop'] : null,
|
|
'next_category' => $next['category'] ?? '',
|
|
];
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
usort($items, function ($a, $b) {
|
|
return $a['channel_number'] <=> $b['channel_number'];
|
|
});
|
|
|
|
return $items;
|
|
}
|
|
|
|
public function getDayProgrammes(Config $config, array $data, string $date, ?string $channelId = null): array
|
|
{
|
|
$channelConfig = $config->getChannels();
|
|
|
|
$tz = new DateTimeZone($this->timezone);
|
|
$dayStart = new DateTime($date . ' 00:00:00', $tz);
|
|
$dayEnd = clone $dayStart;
|
|
$dayEnd->modify('+1 day');
|
|
|
|
$startTs = $dayStart->getTimestamp();
|
|
$endTs = $dayEnd->getTimestamp();
|
|
|
|
$items = [];
|
|
|
|
foreach ($data['channels'] ?? [] as $id => $channel) {
|
|
if ($channelId !== null && $id !== $channelId) {
|
|
continue;
|
|
}
|
|
|
|
$cfg = $channelConfig[$id] ?? [];
|
|
|
|
if (($cfg['enabled'] ?? true) !== true) {
|
|
continue;
|
|
}
|
|
|
|
$programmes = [];
|
|
|
|
foreach ($channel['programmes'] ?? [] as $p) {
|
|
$start = (int)($p['start'] ?? 0);
|
|
$stop = (int)($p['stop'] ?? 0);
|
|
|
|
if ($start < $endTs && $stop > $startTs) {
|
|
$visibleStart = max($start, $startTs);
|
|
$visibleStop = min($stop, $endTs);
|
|
|
|
$startDt = (new DateTime())->setTimestamp($visibleStart)->setTimezone($tz);
|
|
$stopDt = (new DateTime())->setTimestamp($visibleStop)->setTimezone($tz);
|
|
|
|
$startMinute = ((int)$startDt->format('H') * 60) + (int)$startDt->format('i');
|
|
$stopMinute = ((int)$stopDt->format('H') * 60) + (int)$stopDt->format('i');
|
|
|
|
if ($visibleStop >= $endTs) {
|
|
$stopMinute = 1440;
|
|
}
|
|
|
|
$durationMinutes = max(1, (int)(($visibleStop - $visibleStart) / 60));
|
|
|
|
$programmes[] = [
|
|
'id' => md5($id . '|' . $start . '|' . $stop . '|' . ($p['title'] ?? '')),
|
|
'title' => $p['title'] ?? '',
|
|
'subtitle' => $p['subtitle'] ?? '',
|
|
'description' => $p['description'] ?? '',
|
|
'category' => $p['category'] ?? '',
|
|
'metadata' => $p['metadata'] ?? [],
|
|
'start' => $start,
|
|
'stop' => $stop,
|
|
'visible_start' => $visibleStart,
|
|
'visible_stop' => $visibleStop,
|
|
'start_minute' => $startMinute,
|
|
'stop_minute' => $stopMinute,
|
|
'duration_minutes' => $durationMinutes,
|
|
'is_current' => time() >= $start && time() < $stop,
|
|
'is_past' => $stop < time(),
|
|
];
|
|
}
|
|
}
|
|
|
|
if (!$programmes) {
|
|
continue;
|
|
}
|
|
|
|
usort($programmes, function ($a, $b) {
|
|
return $a['start'] <=> $b['start'];
|
|
});
|
|
|
|
$items[] = [
|
|
'channel_id' => $id,
|
|
'channel_name' => $cfg['name'] ?? $channel['name'] ?? $id,
|
|
'channel_icon' => $cfg['icon'] ?? $channel['icon'] ?? '',
|
|
'channel_number' => (int)($cfg['number'] ?? 999999),
|
|
'favorite' => (bool)($cfg['favorite'] ?? false),
|
|
'programmes' => $programmes,
|
|
];
|
|
}
|
|
|
|
usort($items, function ($a, $b) {
|
|
return $a['channel_number'] <=> $b['channel_number'];
|
|
});
|
|
|
|
return $items;
|
|
}
|
|
|
|
public function search(
|
|
Config $config,
|
|
array $data,
|
|
string $query,
|
|
int $limit = 20
|
|
): array {
|
|
$query = trim($query);
|
|
$limit = max(1, min(50, $limit));
|
|
|
|
if (mb_strlen($query) < 2) {
|
|
return [];
|
|
}
|
|
|
|
$channelConfig = $config->getChannels();
|
|
$tz = new DateTimeZone($this->timezone);
|
|
|
|
$dayStart = new DateTime('today', $tz);
|
|
$dayEnd = clone $dayStart;
|
|
$dayEnd->modify('+1 day');
|
|
|
|
$startTs = $dayStart->getTimestamp();
|
|
$endTs = $dayEnd->getTimestamp();
|
|
|
|
$channelResults = [];
|
|
$programmeResults = [];
|
|
|
|
foreach ($data['channels'] ?? [] as $channelId => $channel) {
|
|
$cfg = $channelConfig[$channelId] ?? [];
|
|
|
|
if (($cfg['enabled'] ?? true) !== true) {
|
|
continue;
|
|
}
|
|
|
|
$channelName = $cfg['name'] ?? $channel['name'] ?? $channelId;
|
|
$channelNumber = (int)($cfg['number'] ?? 999999);
|
|
$channelIcon = $cfg['icon'] ?? $channel['icon'] ?? '';
|
|
$favorite = (bool)($cfg['favorite'] ?? false);
|
|
|
|
if (mb_stripos($channelName, $query) !== false) {
|
|
$channelResults[] = [
|
|
'type' => 'channel',
|
|
'channel_id' => $channelId,
|
|
'channel_name' => $channelName,
|
|
'channel_number' => $channelNumber,
|
|
'channel_icon' => $channelIcon,
|
|
'favorite' => $favorite,
|
|
];
|
|
}
|
|
|
|
foreach ($channel['programmes'] ?? [] as $programme) {
|
|
$start = (int)($programme['start'] ?? 0);
|
|
$stop = (int)($programme['stop'] ?? 0);
|
|
|
|
// Nur Sendungen, die den heutigen Tag berühren
|
|
if ($start >= $endTs || $stop <= $startTs) {
|
|
continue;
|
|
}
|
|
|
|
$title = trim((string)($programme['title'] ?? ''));
|
|
$subtitle = trim((string)($programme['subtitle'] ?? ''));
|
|
|
|
$matches =
|
|
mb_stripos($title, $query) !== false ||
|
|
mb_stripos($subtitle, $query) !== false;
|
|
|
|
if (!$matches) {
|
|
continue;
|
|
}
|
|
|
|
$startDateTime = (new DateTime())
|
|
->setTimestamp($start)
|
|
->setTimezone($tz);
|
|
|
|
$stopDateTime = (new DateTime())
|
|
->setTimestamp($stop)
|
|
->setTimezone($tz);
|
|
|
|
$programmeResults[] = [
|
|
'type' => 'programme',
|
|
'id' => md5(
|
|
$channelId . '|' .
|
|
$start . '|' .
|
|
$stop . '|' .
|
|
$title
|
|
),
|
|
|
|
'channel_id' => $channelId,
|
|
'channel_name' => $channelName,
|
|
'channel_number' => $channelNumber,
|
|
'channel_icon' => $channelIcon,
|
|
'favorite' => $favorite,
|
|
|
|
'title' => $title,
|
|
'subtitle' => $subtitle,
|
|
'description' => $programme['description'] ?? '',
|
|
'category' => $programme['category'] ?? '',
|
|
'metadata' => $programme['metadata'] ?? [],
|
|
'year' => $programme['year'] ?? '',
|
|
'rating' => $programme['rating'] ?? '',
|
|
'episode' => $programme['episode'] ?? '',
|
|
'icon' => $programme['icon'] ?? '',
|
|
|
|
'start' => $start,
|
|
'stop' => $stop,
|
|
'start_time' => $startDateTime->format('H:i'),
|
|
'stop_time' => $stopDateTime->format('H:i'),
|
|
'date' => $startDateTime->format('Y-m-d'),
|
|
'is_current' => time() >= $start && time() < $stop,
|
|
'is_past' => $stop <= time(),
|
|
];
|
|
}
|
|
}
|
|
|
|
usort($channelResults, function (array $a, array $b): int {
|
|
return [
|
|
$a['channel_number'],
|
|
mb_strtolower($a['channel_name']),
|
|
] <=> [
|
|
$b['channel_number'],
|
|
mb_strtolower($b['channel_name']),
|
|
];
|
|
});
|
|
|
|
usort($programmeResults, function (array $a, array $b): int {
|
|
return [
|
|
$a['start'],
|
|
$a['channel_number'],
|
|
] <=> [
|
|
$b['start'],
|
|
$b['channel_number'],
|
|
];
|
|
});
|
|
|
|
return array_slice(
|
|
array_merge($channelResults, $programmeResults),
|
|
0,
|
|
$limit
|
|
);
|
|
}
|
|
|
|
private function getGenreIcon(string $category): string
|
|
{
|
|
$category = mb_strtolower($category);
|
|
|
|
return match (true) {
|
|
str_contains($category, 'film'),
|
|
str_contains($category, 'movie'),
|
|
str_contains($category, 'kino') => '🎬',
|
|
|
|
str_contains($category, 'serie'),
|
|
str_contains($category, 'series') => '📺',
|
|
|
|
str_contains($category, 'sport'),
|
|
str_contains($category, 'football'),
|
|
str_contains($category, 'fußball'),
|
|
str_contains($category, 'fussball') => '⚽',
|
|
|
|
str_contains($category, 'news'),
|
|
str_contains($category, 'nachrichten'),
|
|
str_contains($category, 'journal') => '📰',
|
|
|
|
str_contains($category, 'doku'),
|
|
str_contains($category, 'documentary') => '🌍',
|
|
|
|
str_contains($category, 'kinder'),
|
|
str_contains($category, 'kids'),
|
|
str_contains($category, 'children') => '🧸',
|
|
|
|
str_contains($category, 'musik'),
|
|
str_contains($category, 'music') => '🎵',
|
|
|
|
default => '▶',
|
|
};
|
|
}
|
|
|
|
private function detectQuality(array $programme): string
|
|
{
|
|
$text = mb_strtolower(
|
|
($programme['title'] ?? '') . ' ' .
|
|
($programme['subtitle'] ?? '') . ' ' .
|
|
($programme['description'] ?? '')
|
|
);
|
|
|
|
return match (true) {
|
|
str_contains($text, 'uhd'),
|
|
str_contains($text, '4k') => 'UHD',
|
|
|
|
str_contains($text, 'hd') => 'HD',
|
|
|
|
default => '',
|
|
};
|
|
}
|
|
}
|