diff --git a/export.php b/export.php index dabfc60..8a8fe41 100644 --- a/export.php +++ b/export.php @@ -9,6 +9,7 @@ function export_parse_xmltv_programmes(string $xml, string $target_day): array { if (!class_exists('DOMDocument')) { return ['channels' => [], 'programmes' => [], 'error' => 'PHP DOM/XML extension is not installed']; } + libxml_use_internal_errors(true); $dom = new DOMDocument(); if (!$dom->loadXML($xml, LIBXML_NOERROR | LIBXML_NOWARNING)) { @@ -72,20 +73,79 @@ function export_parse_xmltv_programmes(string $xml, string $target_day): array { return ['channels' => $channels, 'programmes' => $rows, 'error' => '']; } +function export_window_definitions(string $target_day): array { + return [ + 'full' => [ + 'label' => '00h-24h', + 'start' => new DateTimeImmutable($target_day . ' 00:00:00'), + 'end' => new DateTimeImmutable($target_day . ' 23:59:59'), + ], + 'morning' => [ + 'label' => 'Morning', + 'start' => new DateTimeImmutable($target_day . ' 06:00:00'), + 'end' => new DateTimeImmutable($target_day . ' 11:59:59'), + ], + 'afternoon' => [ + 'label' => 'Afternoon', + 'start' => new DateTimeImmutable($target_day . ' 12:00:00'), + 'end' => new DateTimeImmutable($target_day . ' 17:59:59'), + ], + 'evening' => [ + 'label' => 'Evening', + 'start' => new DateTimeImmutable($target_day . ' 18:00:00'), + 'end' => new DateTimeImmutable($target_day . ' 23:59:59'), + ], + 'prime' => [ + 'label' => 'Prime time', + 'start' => new DateTimeImmutable($target_day . ' 20:00:00'), + 'end' => new DateTimeImmutable($target_day . ' 23:59:59'), + ], + ]; +} + +function export_filter_programmes(array $programmes, DateTimeImmutable $start, DateTimeImmutable $end): array { + return array_values(array_filter($programmes, static fn($p) => $p['stop'] > $start && $p['start'] < $end)); +} + +function export_hour_slots(DateTimeImmutable $start, DateTimeImmutable $end): array { + $slots = []; + $cursor = $start; + while ($cursor < $end) { + $slot_end = $cursor->modify('+1 hour'); + if ($slot_end > $end) $slot_end = $end; + $slots[] = ['start' => $cursor, 'end' => $slot_end]; + $cursor = $slot_end; + } + return $slots; +} + $sources = array_values($config['epg_sources'] ?? []); $source_index = max(0, min((int) ($_GET['source'] ?? 0), max(count($sources) - 1, 0))); $source = $sources[$source_index] ?? null; $target_day = preg_match('/^\d{4}-\d{2}-\d{2}$/', (string) ($_GET['day'] ?? '')) ? (string) $_GET['day'] : (new DateTimeImmutable('today'))->format('Y-m-d'); +$layout = in_array((string) ($_GET['layout'] ?? 'cards'), ['cards', 'timeline'], true) ? (string) $_GET['layout'] : 'cards'; +$limit = in_array((string) ($_GET['limit'] ?? '8'), ['4', '8', 'all'], true) ? (string) $_GET['limit'] : '8'; +$windows = export_window_definitions($target_day); +$window_key = array_key_exists((string) ($_GET['window'] ?? 'full'), $windows) ? (string) $_GET['window'] : 'full'; +$window = $windows[$window_key]; +$slots = export_hour_slots($window['start'], $window['end']->modify('+1 second')); $fetch = $source ? gridtv_fetch_url((string) $source['epg_url'], 25) : ['ok' => false, 'error' => 'No source configured', 'body' => '']; $parsed = $fetch['ok'] ? export_parse_xmltv_programmes($fetch['body'], $target_day) : ['channels' => [], 'programmes' => [], 'error' => $fetch['error'] ?: 'Unable to fetch XMLTV']; -$print_channels = []; + +$visible_channels = []; foreach ($parsed['channels'] as $channel_id => $channel) { - if (!empty($parsed['programmes'][$channel_id])) { - $print_channels[$channel_id] = $channel; - } + $filtered = export_filter_programmes($parsed['programmes'][$channel_id] ?? [], $window['start'], $window['end']->modify('+1 second')); + if (!$filtered) continue; + $visible_channels[$channel_id] = $channel + ['programmes' => $filtered]; } + +if ($limit !== 'all') { + $visible_channels = array_slice($visible_channels, 0, (int) $limit, true); +} + $day_label = (new DateTimeImmutable($target_day))->format('l d F Y'); +$layout_label = $layout === 'cards' ? 'Readable cards' : 'Dense timeline'; ?> @@ -95,16 +155,16 @@ $day_label = (new DateTimeImmutable($target_day))->format('l d F Y'); @@ -140,16 +208,30 @@ $day_label = (new DateTimeImmutable($target_day))->format('l d F Y');

Export 24h

-

Version “programme télé” pensée pour être imprimée proprement en PDF, ou exportée en image pour l’envoyer facilement à quelqu’un.

+

Version imprimable pensée pour être vraiment lisible. Le mode “cartes” sert au PDF ou à l’envoi à quelqu’un, la timeline dense reste disponible pour une vue plus technique.

-
+ + + +
@@ -163,7 +245,7 @@ $day_label = (new DateTimeImmutable($target_day))->format('l d F Y');
-
TV Guide · 24 hours · Printable edition
+
TV Guide · Printable edition ·
Le programme télé de la journée
@@ -173,63 +255,87 @@ $day_label = (new DateTimeImmutable($target_day))->format('l d F Y');
- 24h view - channel(s) + + channel(s) + Generated from XMLTV
Impossible de générer la feuille:
- -
Aucun programme trouvé pour cette journée.
- -
- $channel): ?> -
-
-
+ +
Aucun programme trouvé avec ce filtre.
+ +
+ +
+
+
+
programme(s)
-
+
+ modify('+1 hour'); - $items = []; - foreach ($parsed['programmes'][$channel_id] as $programme) { - if ($programme['stop'] <= $hour_start || $programme['start'] >= $hour_end) continue; - $items[] = $programme; - } +$meta = $programme['rating']; +$cats = $programme['categories'] ? implode(' · ', $programme['categories']) : ''; ?> -
-
+
+
format('H:i')) ?>
format('H:i')) ?>
+
+
+
+
+
+
+
+
+ +
+
+ +
+ +
+ +
+
+
+ +
+
+
programme(s)
+
+
+
+
+ + $p['stop'] > $slot['start'] && $p['start'] < $slot['end'])); +?> +
+
format('H:i')) ?>
-format('H:i') . ' - ' . $programme['stop']->format('H:i'); -if ($programme['rating']) $meta .= ' · ' . $programme['rating']; -$cats = $programme['categories'] ? implode(' · ', $programme['categories']) : ''; -?> -
-
-
-
-
+
+
+
format('H:i') . ' - ' . $programme['stop']->format('H:i')) ?>
- +
-
Astuce: “PDF / Print” donne un export A4/PDF propre. “Image” fabrique un PNG de la feuille affichée.
+ +
Astuce: pour un rendu vraiment propre, choisis “Readable cards” avec 4 ou 8 chaînes, puis exporte en PDF.
@@ -241,7 +347,7 @@ async function downloadImage() { const canvas = await html2canvas(sheet, {backgroundColor: '#f4ecda', scale: 2, useCORS: true}); const link = document.createElement('a'); link.href = canvas.toDataURL('image/png'); - link.download = 'gridtv-24h-.png'; + link.download = 'gridtv--.png'; link.click(); }