From d242ee0d4b624d505f592ecff9fd6053564af595 Mon Sep 17 00:00:00 2001 From: Akamaru Date: Wed, 19 Nov 2025 14:30:50 +0100 Subject: [PATCH] Neu: ARD Mediathek Serien Bridge --- ARDMediathekSeriesBridge.php | 174 +++++++++++++++++++++++++++++++++++ README.md | 6 ++ 2 files changed, 180 insertions(+) create mode 100644 ARDMediathekSeriesBridge.php diff --git a/ARDMediathekSeriesBridge.php b/ARDMediathekSeriesBridge.php new file mode 100644 index 0000000..9901e31 --- /dev/null +++ b/ARDMediathekSeriesBridge.php @@ -0,0 +1,174 @@ + [ + 'series_id' => [ + 'name' => 'Serien-ID (z.B. "Y3JpZDovL2hyLW9ubGluZS8zODIyMDAzMw")', + 'type' => 'text', + 'required' => true, + 'exampleValue' => 'Y3JpZDovL2hyLW9ubGluZS8zODIyMDAzMw' + ], + 'limit' => [ + 'name' => 'Maximale Anzahl an Episoden', + 'type' => 'number', + 'required' => false, + 'defaultValue' => 20 + ] + ] + ]; + + public function getIcon() + { + return 'https://www.google.com/s2/favicons?domain=www.ardmediathek.de&sz=32'; + } + private $seriesTitle = ''; + private $seriesDescription = ''; + + public function getName() + { + if (!empty($this->seriesTitle)) { + return $this->seriesTitle . ' - ARD Mediathek'; + } + return parent::getName(); + } + + public function collectData() + { + $seriesId = $this->getInput('series_id'); + $limit = $this->getInput('limit') ?? 20; + + if (empty($seriesId)) { + returnClientError('Ungültige Serien-ID.'); + } + + $apiUrl = self::API_URI . urlencode($seriesId); + $jsonData = getContents($apiUrl); + $data = json_decode($jsonData, true); + + if (!$data) { + returnServerError('Keine Daten von der ARD Mediathek API erhalten.'); + } + + // Extrahiere Serien-Informationen + $this->seriesTitle = $data['title'] ?? 'Unbekannte Serie'; + $this->seriesDescription = $data['synopsis'] ?? ''; + + // Die Episoden befinden sich im ersten Widget unter 'teasers' + if (!isset($data['widgets'][0]['teasers']) || !is_array($data['widgets'][0]['teasers'])) { + returnServerError('Keine Episoden gefunden.'); + } + + $teasers = $data['widgets'][0]['teasers']; + + // Sortiere nach Sendedatum (neueste zuerst) + usort($teasers, function ($a, $b) { + $timeA = strtotime($a['broadcastedOn'] ?? 0); + $timeB = strtotime($b['broadcastedOn'] ?? 0); + return $timeB - $timeA; + }); + + // Limitiere die Anzahl der Episoden + $teasers = array_slice($teasers, 0, $limit); + + foreach ($teasers as $teaser) { + $item = []; + + // Titel der Episode + $item['title'] = $teaser['longTitle'] ?? $teaser['mediumTitle'] ?? $teaser['shortTitle'] ?? 'Unbekannter Titel'; + + // URL zur Episode + $episodeId = $teaser['id'] ?? ''; + if (!empty($episodeId)) { + $item['uri'] = self::URI . '/video/' . $episodeId; + } else { + $item['uri'] = self::URI; + } + + // Zeitstempel + if (isset($teaser['broadcastedOn'])) { + $item['timestamp'] = strtotime($teaser['broadcastedOn']); + } + + // Eindeutige ID + $item['uid'] = $episodeId; + + // Sender/Autor + if (isset($teaser['publicationService']['name'])) { + $item['author'] = $teaser['publicationService']['name']; + } + + // Content: Bild + Beschreibung + Verfügbarkeit + $content = ''; + + // Vorschaubild + if (isset($teaser['images']['aspect16x9']['src'])) { + $imageUrl = $teaser['images']['aspect16x9']['src']; + // Setze eine vernünftige Bildbreite + $imageUrl = str_replace('{width}', '960', $imageUrl); + $imageAlt = $teaser['images']['aspect16x9']['alt'] ?? ''; + $content .= '' . htmlspecialchars($imageAlt) . '
'; + } + + // Dauer + if (isset($teaser['duration'])) { + $duration = gmdate('H:i:s', $teaser['duration']); + $content .= '

Dauer: ' . $duration . '

'; + } + + // Verfügbar bis + if (isset($teaser['availableTo'])) { + $availableTo = date('d.m.Y H:i', strtotime($teaser['availableTo'])); + $content .= '

Verfügbar bis: ' . $availableTo . ' Uhr

'; + } + + // Untertitel verfügbar + if (isset($teaser['subtitled']) && $teaser['subtitled']) { + $content .= '

Untertitel verfügbar

'; + } + + $item['content'] = $content; + + // Enclosure für das Vorschaubild + if (isset($teaser['images']['aspect16x9']['src'])) { + $imageUrl = str_replace('{width}', '960', $teaser['images']['aspect16x9']['src']); + $item['enclosures'] = [$imageUrl]; + } + + $this->items[] = $item; + } + } + + public function getURI() + { + $seriesId = $this->getInput('series_id'); + + if (empty($seriesId)) { + return self::URI; + } + + return self::URI . '/serie/' . $seriesId; + } + + public function detectParameters($url) + { + // Versuche die Serien-ID aus verschiedenen ARD Mediathek URL-Formaten zu extrahieren + // Format 1: https://www.ardmediathek.de/serie/Y3JpZDovL2hyLW9ubGluZS8zODIyMDAzMw + // Format 2: https://www.ardmediathek.de/serie/name/staffel-X/Y3JpZDovL2hyLW9ubGluZS8zODIyMDAzMw/X + if (preg_match('#ardmediathek\.de/serie/(?:[^/]+/(?:staffel-\d+/)?)?([A-Za-z0-9_-]+)(?:/\d+)?#', $url, $matches)) { + return [ + 'series_id' => $matches[1] + ]; + } + + return null; + } +} diff --git a/README.md b/README.md index 8530ec3..526af0f 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,12 @@ Diese Sammlung enthält verschiedene Bridge-Implementierungen für RSS-Bridge, u - **Parameter**: - **Show-ID**: Die ID der Serie (z.B. 1333 aus der URL /video/1333-serienname/) +### [ARD Mediathek Serien Bridge](https://bridge.ponywave.de/#bridge-ARDMediathekSeriesBridge) (Von Akamaru) +- **Beschreibung**: Gibt die neuesten Episoden einer Serie aus der ARD Mediathek zurück +- **Parameter**: + - **Serien-ID**: Die ID der Serie (z.B. "Y3JpZDovL2hyLW9ubGluZS8zODIyMDAzMw" aus der URL /serie/Y3JpZDovL2hyLW9ubGluZS8zODIyMDAzMw) + - **Limit** (optional): Maximale Anzahl an Episoden (Standard: 20) + ### [Ananta Game News Bridge](https://bridge.ponywave.de/#bridge-AnantaBridge) (Von Akamaru) - **Beschreibung**: Zeigt die neuesten Nachrichten von Ananta Game