This commit is contained in:
Gogs
2026-07-07 15:54:11 +02:00
parent c60bfc874a
commit 6d4189730f
17 changed files with 1293 additions and 16 deletions
+13
View File
@@ -0,0 +1,13 @@
<?php
require_once '/var/www/src/bootstrap.php';
$subtitle = $argv[1] ?? 'S1E1 ║ Bikini Busch ║ My Neighbor\'s Bush ║ Erotikserie ║ USA ║ 2025';
$parser = new MetadataParser();
$result = $parser->parse([
'subtitle' => $subtitle,
]);
print_r($result->toArray());
+49
View File
@@ -0,0 +1,49 @@
<?php
$file = $argv[1] ?? '/var/www/cache/guide.json';
if (!file_exists($file)) {
fwrite(STDERR, "Datei nicht gefunden: {$file}\n");
exit(1);
}
$data = json_decode(file_get_contents($file), true);
if (!is_array($data)) {
fwrite(STDERR, "JSON ungültig.\n");
exit(1);
}
$patterns = [];
$samples = [];
foreach ($data['channels'] ?? [] as $channel) {
foreach ($channel['programmes'] ?? [] as $programme) {
$subtitle = trim($programme['subtitle'] ?? '');
if ($subtitle === '') {
continue;
}
$parts = array_map('trim', explode('║', $subtitle));
$count = count($parts);
$key = $count . ' Teile';
$patterns[$key] = ($patterns[$key] ?? 0) + 1;
if (!isset($samples[$key])) {
$samples[$key] = $subtitle;
}
}
}
arsort($patterns);
echo "Rytec Subtitle Analyse\n";
echo "======================\n\n";
foreach ($patterns as $pattern => $count) {
echo str_pad($pattern, 12) . str_pad((string)$count, 8, ' ', STR_PAD_LEFT) . "\n";
echo " Beispiel: " . $samples[$pattern] . "\n\n";
}
+31
View File
@@ -0,0 +1,31 @@
<?php
$url = 'https://raw.githubusercontent.com/doglover3920/EPGimport-Sources/main/rytec.sources.xml';
$xmlText = file_get_contents($url);
if ($xmlText === false) {
fwrite(STDERR, "Konnte rytec.sources.xml nicht laden.\n");
exit(1);
}
$xml = simplexml_load_string($xmlText);
if (!$xml) {
fwrite(STDERR, "Konnte rytec.sources.xml nicht lesen.\n");
exit(1);
}
foreach ($xml->xpath('//sourcecat') as $cat) {
$catName = (string)$cat['sourcecatname'];
echo "\n=== {$catName} ===\n";
foreach ($cat->source as $source) {
$description = trim((string)$source->description);
echo "- {$description}\n";
foreach ($source->url as $sourceUrl) {
echo " " . trim((string)$sourceUrl) . "\n";
}
}
}
+63
View File
@@ -0,0 +1,63 @@
<?php
require_once '/var/www/src/bootstrap.php';
$config = new Config(
'/var/www/config/config.json',
'/var/www/config/channels.json'
);
$epg = $config->get('epg', []);
$url = $epg['url'] ?? null;
if (!$url) {
fwrite(STDERR, "Keine epg.url in config.json gesetzt.\n");
exit(1);
}
$target = '/var/www/epg/guide.xml';
$xzFile = $target . '.xz';
$tmpXml = $target . '.tmp';
echo "Lade EPG: {$url}\n";
$data = file_get_contents($url);
if ($data === false || trim($data) === '') {
fwrite(STDERR, "Download fehlgeschlagen oder leer.\n");
exit(1);
}
file_put_contents($xzFile, $data);
echo "Entpacke XZ...\n";
$command = 'xz -dc ' . escapeshellarg($xzFile) . ' > ' . escapeshellarg($tmpXml);
exec($command, $output, $code);
if ($code !== 0 || !file_exists($tmpXml) || filesize($tmpXml) === 0) {
fwrite(STDERR, "XZ konnte nicht entpackt werden.\n");
exit(1);
}
$xml = @simplexml_load_file($tmpXml);
if (!$xml) {
unlink($tmpXml);
fwrite(STDERR, "XML ungültig.\n");
exit(1);
}
rename($tmpXml, $target);
echo "guide.xml aktualisiert.\n";
$cache = new XmlTvCacheService(
$target,
$config->get('timezone', 'Europe/Vienna'),
'/var/www/cache/guide.json'
);
$cache->rebuildCache();
echo "guide.json und guide.version aktualisiert.\n";
+936 -1
View File
@@ -6,7 +6,7 @@
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"favorite": true,
"number": 1,
"group": "Unknown"
},
@@ -20,5 +20,940 @@
"favorite": true,
"number": 2,
"group": "Unknown"
},
"Waidwerk.de": {
"uuid": "7c39fb8df69e25922a78508df7688dd7",
"name": "Waidwerk",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 3,
"group": "Unknown"
},
"SonnenKlar.de": {
"uuid": "b45be206f058275fec3e81acf235ce0e",
"name": "sonnenklar.TV",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 4,
"group": "Unknown"
},
"LustPur.de": {
"uuid": "9e711dfc39bb6024e6e19ba1d5cc0856",
"name": "Lust Pur",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 5,
"group": "Unknown"
},
"NickJr.de": {
"uuid": "c0858303a1917b7fc83f82bc9176e27e",
"name": "Nick Jr.",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 6,
"group": "Unknown"
},
"Jukebox.de": {
"uuid": "d63e9168e7c4e8b5bd6dc247793d84ba",
"name": "Jukebox",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 7,
"group": "Unknown"
},
"Kinowelt.de": {
"uuid": "149398af7ab7876a749e79dd0ffce374",
"name": "KinoweltTV",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 8,
"group": "Unknown"
},
"Heimatkanal.de": {
"uuid": "9b508da77f8fb3c9f50b3f943a9eac58",
"name": "Heimatkanal",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 9,
"group": "Unknown"
},
"GoldstarTV.de": {
"uuid": "4c50c28383c4b4247d12d2aef810d9ce",
"name": "Goldstar TV",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 10,
"group": "Unknown"
},
"MotorVision.de": {
"uuid": "509dc630b672a5046d29a2fe63941c86",
"name": "Motorvision",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 11,
"group": "Unknown"
},
"MorethanSportsTV.de": {
"uuid": "6edfbb95f08e5929884c618d1598db89",
"name": "More than Sports TV",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 12,
"group": "Unknown"
},
"Silverline.de": {
"uuid": "3b166f29d9750f3cb5f92a85092dc9d2",
"name": "SILVERLINE",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 13,
"group": "Unknown"
},
"Sat1Comedy.de": {
"uuid": "cd70d139bd14027bd6e825ac65fdf2b8",
"name": "Sat1 Emotions",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 14,
"group": "Unknown"
},
"ProSiebenFun.de": {
"uuid": "afbc420b3fb37b54e9984c56a0eb2b54",
"name": "ProSieben Fun",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 15,
"group": "Unknown"
},
"GuteLauneTV.de": {
"uuid": "dd55594d98eae9de065f6c1569d9df92",
"name": "Gute Laune TV",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 16,
"group": "Unknown"
},
"QVC.de": {
"uuid": "79b3789d98b63e98c1627cddef7658e2",
"name": "QVC",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 17,
"group": "Unknown"
},
"CartoonNetwork.de": {
"uuid": "2893599f10648e2bdc86388aa0de6011",
"name": "Cartoon Network",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 18,
"group": "Unknown"
},
"WeltDerWunder.de": {
"uuid": "f13b77009a3024471431b5ed33918547",
"name": "Welt der wunder",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 19,
"group": "Unknown"
},
"Cartoonito.de": {
"uuid": "7dea097d5a482fd3800f35119b80013c",
"name": "Cartoonito",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 20,
"group": "Unknown"
},
"SciFi.de": {
"uuid": "a8e96f3bc0ee92ac123bfa5fc33e53be",
"name": "Syfy HD",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 21,
"group": "Unknown"
},
"RTLLiving.de": {
"uuid": "9d71db690ea9f4fbb74b8f1a269acfb8",
"name": "RTL Living",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 22,
"group": "Unknown"
},
"RTLCrime.de": {
"uuid": "2984dcc3df9469a1e5bd048c1117dd6a",
"name": "RTL Crime",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 23,
"group": "Unknown"
},
"Romance.de": {
"uuid": "0a18f79a9e692529d668ef408ec7af57",
"name": "Romance TV",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 24,
"group": "Unknown"
},
"RiC.de": {
"uuid": "ad6424c7586e2e6b5a8e17d217d22b7b",
"name": "RiC",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 25,
"group": "Unknown"
},
"HopeChannel.de": {
"uuid": "9259ed1824685eefcc1fc53dc4b7ad75",
"name": "HOPE Channel (Deutschland)",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 26,
"group": "Unknown"
},
"13thStreet.de": {
"uuid": "06c4cc8de2b115181c4167e2dd2d85d4",
"name": "13th Street",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 27,
"group": "Unknown"
},
"MarcoPoloTV.de": {
"uuid": "2490efe228d2a3f775330490d306532b",
"name": "Marco Polo TV",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 28,
"group": "Unknown"
},
"FolxTV.de": {
"uuid": "abadddc0d1187015d8a5fcc81347ff73",
"name": "Folx.TV",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 29,
"group": "Unknown"
},
"MelodieTV.at": {
"uuid": "0cf3bc1da3bc797da85b76d89f77ad36",
"name": "Melodie TV",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 30,
"group": "Unknown"
},
"GEO.de": {
"uuid": "e8bb524597bb77e601ab62181e56b585",
"name": "GEO",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 31,
"group": "Unknown"
},
"TeleclubZoom.de": {
"uuid": "9a39880a3ae50b8a7c61ffc1151726b8",
"name": "Teleclub Zoom D",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 32,
"group": "Unknown"
},
"Universal.de": {
"uuid": "67652bba6c03b1cb8070efba8cc4d636",
"name": "Universal Channel",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 33,
"group": "Unknown"
},
"StingrayDJAZZ.de": {
"uuid": "14632348cb1a6d0634edf3e3b2b1e15a",
"name": "Stingray DJAZZ",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 34,
"group": "Unknown"
},
"Doxx.de": {
"uuid": "1c007cf898c6925962004f8cb7450ad7",
"name": "Doxx",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 35,
"group": "Unknown"
},
"CuriosityChannel.de": {
"uuid": "8b6182ad1b00e2ef9442336f373eae02",
"name": "Curiosity Channel",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 36,
"group": "Unknown"
},
"SpiegelGeschichte.de": {
"uuid": "c077c96c5fe8680d1cf95e1e417904ac",
"name": "Spiegel Geschichte",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 37,
"group": "Unknown"
},
"AXNBlack.de": {
"uuid": "d0073a7cd57d51497f7573cdeed72efb",
"name": "AXN Black",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 38,
"group": "Unknown"
},
"RTLPassion.de": {
"uuid": "b443978f10a1fb09ee536cef80879281",
"name": "Passion",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 39,
"group": "Unknown"
},
"NatGeoWild.de": {
"uuid": "6d22aadc8379c796624b0c8938be63e2",
"name": "Nat Geo Wild",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 40,
"group": "Unknown"
},
"NatGeoHD.de": {
"uuid": "ec42504124fd80a2cd4dc123771c114a",
"name": "National Geographic",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 41,
"group": "Unknown"
},
"KTV.de": {
"uuid": "55130afe1583a4fcb9840725f9f953a1",
"name": "K-TV",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 42,
"group": "Unknown"
},
"Dokusat.de": {
"uuid": "3043fa847b0bc74afcfb1068cf6811e3",
"name": "DOKUSAT",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 43,
"group": "Unknown"
},
"HSE24.de": {
"uuid": "1848bf7daf38a9cc847ecaad5dd58c70",
"name": "HSE24",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 44,
"group": "Unknown"
},
"StimmungsgartenTV.at": {
"uuid": "8f93e6f411fa3e856ea55b07a791636d",
"name": "Stimmungsgarten TV",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 45,
"group": "Unknown"
},
"OneTerra.de": {
"uuid": "fb876f337af62d6054ec3d0c31aef0eb",
"name": "One Terra",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 46,
"group": "Unknown"
},
"JustCooking.de": {
"uuid": "0865f4e098485fb552b85a8917f8ed0e",
"name": "Just Cooking",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 47,
"group": "Unknown"
},
"CrimeTime.de": {
"uuid": "6b2268cb1fa86039d8333ffd62ff3164",
"name": "Crime Time",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 48,
"group": "Unknown"
},
"XPLORE.de": {
"uuid": "cb92b8860cc95170eba1c49d180a0032",
"name": "XPLORE",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 49,
"group": "Unknown"
},
"HistoryChannel.de": {
"uuid": "d1ea08829889f004d3c8c7635d050b8b",
"name": "History HD",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 50,
"group": "Unknown"
},
"AnimalPlanet.de": {
"uuid": "9047c69ea877e2a36bedaed4c4bec1d6",
"name": "Animal Planet",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 51,
"group": "Unknown"
},
"SchlagerparadiesTV.de": {
"uuid": "9b101e6c0e7a3b60e8d2bb7f140720d6",
"name": "Schlagerparadies.TV",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 52,
"group": "Unknown"
},
"SkyNature.de": {
"uuid": "07530a17e31267a0d46886d813ce57c6",
"name": "Sky Nature",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 53,
"group": "Unknown"
},
"SkyDocumentaries.de": {
"uuid": "c3d64ebf6ac0e1fad0b640b219bf749b",
"name": "Sky Documentaries",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 54,
"group": "Unknown"
},
"Sky1.de": {
"uuid": "60accaa7994c650e83d596c47598ebf2",
"name": "Sky 1",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 55,
"group": "Unknown"
},
"FixFoxi.de": {
"uuid": "097b79ba00395357b152b438b881a496",
"name": "Fix & Foxi",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 56,
"group": "Unknown"
},
"ZweiMusicTelevision.de": {
"uuid": "4e1b5e6d6b8c96108dcb1d1fff880e58",
"name": "Zwei Music Television",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 57,
"group": "Unknown"
},
"OneMusicTelevision.de": {
"uuid": "30d07ad39d093e6e7f1616a3945c2e32",
"name": "One Music Television",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 58,
"group": "Unknown"
},
"Volksmusik.TV.de": {
"uuid": "9bc0cf4731e8af6dd0881d9cbec176c3",
"name": "Volksmusik.TV",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 59,
"group": "Unknown"
},
"AutoMotorSport.de": {
"uuid": "8d1e71830f0220cf123bdbf0aa36511e",
"name": "Auto Motor Sport Ch.",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 60,
"group": "Unknown"
},
"Nicktoons.de": {
"uuid": "8540f542d08471a7fec72e161c79a8db",
"name": "NickToons",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 61,
"group": "Unknown"
},
"BonGusto.de": {
"uuid": "81b814ed603dd7b42994977832a33430",
"name": "Bon Gusto HDTV (Germany)",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 62,
"group": "Unknown"
},
"sportdigital.de": {
"uuid": "316a2675287a8de8392d588086d39e60",
"name": "sportdigital",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 63,
"group": "Unknown"
},
"eSports1.de": {
"uuid": "f5579fce130d62fecced97ed593ecf6f",
"name": "eSports1",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 64,
"group": "Unknown"
},
"DMAX.ch": {
"uuid": "e80354ae9085014416542ad27bdb1e6d",
"name": "DMAX",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 65,
"group": "Unknown"
},
"CrimeInvestigation.de": {
"uuid": "72f53fb9f7e907c5432b51cac2ab45b2",
"name": "Crime Investigation",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 66,
"group": "Unknown"
},
"DiscoveryChannel.de": {
"uuid": "ce6e2a690ef5d6bdeed95c2c2bd6e694",
"name": "Discovery Channel",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 67,
"group": "Unknown"
},
"AXNWhite.de": {
"uuid": "0172bb282868afdf299d8db189baa207",
"name": "AXN White",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 68,
"group": "Unknown"
},
"TraceSportStars.de": {
"uuid": "7d5e897274edb5de649f8057c394f55e",
"name": "Trace Sport",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 69,
"group": "Unknown"
},
"SPIEGEL_TV_Konflikt.de": {
"uuid": "55fc3f2e61d4d8999f200b2166a63150",
"name": "SPIEGEL TV Konflikt",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 70,
"group": "Unknown"
},
"TopTrueCrime.de": {
"uuid": "3bb27c9f5c3327f8fcd20156a8bed70e",
"name": "Top True Crime",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 71,
"group": "Unknown"
},
"StarkeFrauen.de": {
"uuid": "a22a0f06a47d6cc65a850f0f1f70cd22",
"name": "Starke Frauen",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 72,
"group": "Unknown"
},
"HSETrend.de": {
"uuid": "21322f41712e26cdc61a4d3e2d9b50c2",
"name": "HSE24 Trend",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 73,
"group": "Unknown"
},
"JuweloTV.de": {
"uuid": "5291c2ab746747b19d99b516720c9d03",
"name": "Juwelo",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 74,
"group": "Unknown"
},
"QwestTV.de": {
"uuid": "c05cd0e974aa19f276fab0d152190f52",
"name": "Qwest TV",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 75,
"group": "Unknown"
},
"Tierwelt.de": {
"uuid": "6f8c5e8ac916c268920bc1b2d1ea7d59",
"name": "Tierwelt",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 76,
"group": "Unknown"
},
"JustFishing.de": {
"uuid": "5a8bee394b422be897e5a909929197c4",
"name": "Just Fishing",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 77,
"group": "Unknown"
},
"Bronco.de": {
"uuid": "f3559edc34412c7b822aed677ad240b0",
"name": "Bronco",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 78,
"group": "Unknown"
},
"Hohenrausch.de": {
"uuid": "775cc4a03c27343cfae0bbf87b8266d5",
"name": "Höhenrausch",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 79,
"group": "Unknown"
},
"Rockland.de": {
"uuid": "ca59aa7e0f56361e7c86543081c1df33",
"name": "Rockland",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 80,
"group": "Unknown"
},
"DuckTV.day": {
"uuid": "a839cf26e3299413ebc6fbdb25ca4e46",
"name": "Duck TV",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 81,
"group": "Unknown"
},
"EWTN.de": {
"uuid": "d5af830b7b04d003a3755eeb645296ff",
"name": "EWTN",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 82,
"group": "Unknown"
},
"DeluxeLounge.de": {
"uuid": "6cbd39ed99d952cca1f3a1cdac3105d9",
"name": "Deluxe Lounge",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 83,
"group": "Unknown"
},
"DeluxeRock.de": {
"uuid": "04752424234e4e712ffd40f87a02baf2",
"name": "Deluxe Rock",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 84,
"group": "Unknown"
},
"DeluxeDance.de": {
"uuid": "7ee9e8a42840a750efa6b4dac490a1e7",
"name": "Deluxe Dance",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 85,
"group": "Unknown"
},
"DeluxeFlashback.de": {
"uuid": "51aebb6b4505d334954ed8ac9a070add",
"name": "Deluxe Flashback",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 86,
"group": "Unknown"
},
"DeluxeRap.de": {
"uuid": "10f3637dfebdc5fed88313e21f10945e",
"name": "Deluxe Rap",
"icon": "",
"country": "",
"language": "",
"enabled": true,
"favorite": false,
"number": 87,
"group": "Unknown"
}
}
+5 -1
View File
@@ -4,5 +4,9 @@
"language": "de",
"theme": "dark",
"days": 7,
"default_view": "today"
"default_view": "today",
"epg": {
"provider": "rytec",
"url": "http://www.xmltvepg.nl/rytecDE_Common.xz"
}
}
+7
View File
@@ -76,3 +76,10 @@
line-height: 1.55;
margin: 0;
}
.programme-modal-meta {
margin: .5rem 0 1rem;
color: var(--muted);
font-size: .92rem;
line-height: 1.4;
}
+40 -1
View File
@@ -10,6 +10,7 @@ export function initProgrammeModal() {
const description = document.getElementById("modalDescription");
const category = document.getElementById("modalCategory");
const time = document.getElementById("modalTime");
const meta = document.getElementById("modalMeta");
const openModal = (programmeElement) => {
const id = programmeElement.dataset.programmeId;
@@ -19,10 +20,47 @@ export function initProgrammeModal() {
return;
}
const metadata = programme.metadata || {};
title.textContent = programme.title || "";
subtitle.textContent = programme.subtitle || "";
subtitle.textContent =
metadata.episode_title ||
programme.subtitle ||
"";
description.textContent = programme.description || "Keine Beschreibung vorhanden.";
category.textContent = programme.category || "";
const metaItems = [];
if (metadata.season && metadata.episode) {
let text = `Staffel ${metadata.season} · Episode ${metadata.episode}`;
if (metadata.episode_total) {
text += `/${metadata.episode_total}`;
}
metaItems.push(text);
}
if (metadata.country) {
metaItems.push(metadata.country);
}
if (metadata.year) {
metaItems.push(metadata.year);
}
if (metadata.age_rating) {
metaItems.push(`FSK ${metadata.age_rating}`);
}
if (Array.isArray(metadata.genres) && metadata.genres.length) {
metaItems.push(metadata.genres.join(", "));
}
meta.textContent = metaItems.join(" • ");
meta.hidden = metaItems.length === 0;
time.textContent = `${programme.channel || ""} · ${programme.start || ""} ${programme.stop || ""}`;
modal.hidden = false;
@@ -31,6 +69,7 @@ export function initProgrammeModal() {
const closeModal = () => {
modal.hidden = true;
meta.textContent = "";
document.body.classList.remove("modal-open");
};
+24
View File
@@ -0,0 +1,24 @@
<?php
class MetadataParser
{
private SubtitleParser $subtitleParser;
public function __construct()
{
$this->subtitleParser = new SubtitleParser();
}
public function parse(array $programme): ParsedMetadata
{
$metadata = new ParsedMetadata();
$subtitle = trim($programme['subtitle'] ?? '');
if ($subtitle !== '') {
$this->subtitleParser->parse($subtitle, $metadata);
}
return $metadata;
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
class ParsedMetadata
{
public ?int $season = null;
public ?int $episode = null;
public ?int $episodeTotal = null;
public ?string $episodeTitle = null;
public ?string $originalTitle = null;
public array $genres = [];
public ?string $country = null;
public ?int $year = null;
public ?int $ageRating = null;
public function toArray(): array
{
return [
'season' => $this->season,
'episode' => $this->episode,
'episode_total' => $this->episodeTotal,
'episode_title' => $this->episodeTitle,
'original_title' => $this->originalTitle,
'genres' => $this->genres,
'country' => $this->country,
'year' => $this->year,
'age_rating' => $this->ageRating,
];
}
}
+66
View File
@@ -0,0 +1,66 @@
<?php
class SubtitleParser
{
public function parse(string $subtitle, ParsedMetadata $metadata): void
{
$parts = array_values(array_filter(
array_map('trim', explode('║', $subtitle)),
fn ($part) => $part !== ''
));
$count = count($parts);
if ($count >= 6) {
$this->parseEpisodeCode($parts[0], $metadata);
$metadata->episodeTitle = $parts[1] ?? null;
$metadata->originalTitle = $parts[2] ?? null;
$metadata->genres = $this->parseGenres($parts[3] ?? '');
$metadata->country = $this->normalizeCountry($parts[4] ?? '');
$metadata->year = $this->parseYear($parts[5] ?? '');
$metadata->ageRating = $this->parseAgeRating($parts[6] ?? '');
return;
}
}
private function parseEpisodeCode(string $value, ParsedMetadata $metadata): void
{
if (preg_match('/^S(\d+)E(\d+)(?:\/(\d+))?$/i', trim($value), $m)) {
$metadata->season = (int)$m[1];
$metadata->episode = (int)$m[2];
$metadata->episodeTotal = isset($m[3]) ? (int)$m[3] : null;
}
}
private function parseGenres(string $value): array
{
return array_values(array_filter(
array_map('trim', explode(',', $value)),
fn ($genre) => $genre !== ''
));
}
private function normalizeCountry(string $value): ?string
{
$value = trim($value);
return $value !== '' ? $value : null;
}
private function parseYear(string $value): ?int
{
if (preg_match('/\b(19|20)\d{2}\b/', $value, $m)) {
return (int)$m[0];
}
return null;
}
private function parseAgeRating(string $value): ?int
{
if (preg_match('/(\d+)\+?/', $value, $m)) {
return (int)$m[1];
}
return null;
}
}
+3
View File
@@ -35,6 +35,7 @@ class ProgrammeService
'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)),
];
@@ -80,6 +81,7 @@ class ProgrammeService
'subtitle' => $p['subtitle'] ?? '',
'description' => $p['description'] ?? '',
'category' => $p['category'] ?? '',
'metadata' => $p['metadata'] ?? [],
'start' => $start,
'stop' => $stop,
'percent' => $percent,
@@ -151,6 +153,7 @@ class ProgrammeService
'subtitle' => $p['subtitle'] ?? '',
'description' => $p['description'] ?? '',
'category' => $p['category'] ?? '',
'metadata' => $p['metadata'] ?? [],
'start' => $start,
'stop' => $stop,
'visible_start' => $visibleStart,
+20 -13
View File
@@ -52,6 +52,9 @@ class XmlTvCacheService
}
$xml = simplexml_load_file($this->file);
$metadataParser = new MetadataParser();
foreach ($xml->channel as $channel) {
$id = (string)$channel['id'];
@@ -78,20 +81,24 @@ class XmlTvCacheService
continue;
}
$data['channels'][$channelId]['programmes'][] = [
'title' => (string)$programme->title,
'subtitle' => (string)$programme->{'sub-title'},
'description' => (string)$programme->desc,
'category' => (string)$programme->category,
'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,
$item = [
'title' => (string)$programme->title,
'subtitle' => (string)$programme->{'sub-title'},
'description' => (string)$programme->desc,
'category' => (string)$programme->category,
'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,
];
$item['metadata'] = $metadataParser->parse($item)->toArray();
$data['channels'][$channelId]['programmes'][] = $item;
}
foreach ($data['channels'] as &$channel) {
+1
View File
@@ -6,6 +6,7 @@ spl_autoload_register(function (string $class): void {
'/var/www/src/Controllers/' . $class . '.php',
'/var/www/src/Services/' . $class . '.php',
'/var/www/src/Http/' . $class . '.php',
'/var/www/src/Metadata/' . $class . '.php',
];
foreach ($paths as $file) {
+2
View File
@@ -48,6 +48,7 @@ foreach ($dayProgrammes as $channel) {
'subtitle' => $programme['subtitle'],
'description' => $programme['description'],
'category' => $programme['category'],
'metadata' => $programme['metadata'] ?? [],
'start' => date('H:i', $programme['start']),
'stop' => date('H:i', $programme['stop']),
];
@@ -236,6 +237,7 @@ foreach ($dayProgrammes as $channel) {
<button class="programme-modal-close" type="button" data-modal-close>×</button>
<div class="programme-modal-time" id="modalTime"></div>
<div class="programme-modal-meta" id="modalMeta"></div>
<h2 id="modalTitle"></h2>
<div class="programme-modal-subtitle" id="modalSubtitle"></div>
<div class="programme-modal-category" id="modalCategory"></div>
+1
View File
@@ -11,6 +11,7 @@ services:
- ./app/templates:/var/www/templates
- ./app/config:/var/www/config
- ./app/cache:/var/www/cache
- ./app/bin:/var/www/bin
- ./vendor:/var/www/vendor
- ./epg:/var/www/epg
- ./logos:/var/www/html/logos