From 86ef0c8f83cbd307908bc61c1c5658cbad8f6b06 Mon Sep 17 00:00:00 2001 From: Akamaru Date: Mon, 24 Nov 2025 19:15:33 +0100 Subject: [PATCH] Neu: TheTVDB Bridge --- TheTVDBBridge.php | 183 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 TheTVDBBridge.php diff --git a/TheTVDBBridge.php b/TheTVDBBridge.php new file mode 100644 index 0000000..fb675d1 --- /dev/null +++ b/TheTVDBBridge.php @@ -0,0 +1,183 @@ + [ + 'name' => 'Series Slug', + 'type' => 'text', + 'required' => true, + 'exampleValue' => 'hot-ones', + 'title' => 'Series slug from the URL (e.g., "hot-ones" from https://www.thetvdb.com/series/hot-ones)' + ], + 'limit' => [ + 'name' => 'Max Episodes', + 'type' => 'number', + 'required' => false, + 'defaultValue' => 20, + 'title' => 'Maximum number of episodes to return (default: 20)' + ] + ] + ]; + + public function collectData() + { + $series = $this->getInput('series'); + $limit = $this->getInput('limit') ?? 20; + $url = self::URI . 'series/' . $series . '/allseasons/official'; + + $html = getSimpleHTMLDOM($url); + + if (!$html) { + throw new \Exception('Unable to load page: ' . $url); + } + + // Extract series title for author field + // Try to get series title from the page title + $titleElement = $html->find('title', 0); + if ($titleElement) { + // Format: "Hot Ones - Aired Order - All Seasons - TheTVDB.com" + $pageTitle = trim($titleElement->plaintext); + // Take only the first part before " - " + $parts = explode(' - ', $pageTitle); + if (!empty($parts[0])) { + $this->seriesTitle = trim($parts[0]); + } + } + + // Find all episode list items + $episodes = $html->find('li.list-group-item'); + + if (empty($episodes)) { + throw new \Exception('No episodes found. Please check if the series slug is correct.'); + } + + // Reverse to get newest episodes first + $episodes = array_reverse($episodes); + + $count = 0; + + foreach ($episodes as $episode) { + if ($count >= $limit) { + break; + } + + // Skip if this is not an episode (some list items might be headers) + $heading = $episode->find('h4.list-group-item-heading', 0); + if (!$heading) { + continue; + } + + // Check if this is a special episode + $isSpecial = strpos($episode->class, 'list-group-item-special') !== false; + + // Extract episode label (S28E08 or SPECIAL 0x202) + $labelElement = $isSpecial + ? $episode->find('small.episode-label', 0) + : $episode->find('span.episode-label', 0); + + $episodeLabel = $labelElement ? trim($labelElement->plaintext) : ''; + + // Convert SPECIAL format to S00E## format + if ($isSpecial && preg_match('/SPECIAL\s+0x(\d+)/i', $episodeLabel, $matches)) { + $episodeLabel = 'S00E' . str_pad($matches[1], 2, '0', STR_PAD_LEFT); + } + + // Extract title and URL + $titleLink = $episode->find('h4 a', 0); + if (!$titleLink) { + continue; // Skip if no title link found + } + + $title = trim($titleLink->plaintext); + $episodeUrl = self::URI . ltrim($titleLink->href, '/'); + + // Extract air date + $dateElement = $episode->find('ul.list-inline li', 0); + $airDate = $dateElement ? trim($dateElement->plaintext) : ''; + + // Extract network/platform + $networkElement = $episode->find('ul.list-inline li', 1); + $network = $networkElement ? trim($networkElement->plaintext) : ''; + + // Extract description + $descElement = $episode->find('.list-group-item-text p', 0); + $description = $descElement ? trim($descElement->innertext) : ''; + + // Extract thumbnail image + $imgElement = $episode->find('.list-group-item-text img', 0); + $thumbnail = ''; + if ($imgElement) { + // Check for lazy-loaded images (data-src) first, then fallback to src + $thumbnail = $imgElement->getAttribute('data-src') ?: $imgElement->src; + + // Make sure URL is absolute + if ($thumbnail && !str_starts_with($thumbnail, 'http')) { + $thumbnail = 'https:' . $thumbnail; + } + } + + // Build content HTML + $content = ''; + + // Add thumbnail if available + if ($thumbnail) { + $content .= '

'; + } + + // Add description + if ($description) { + $content .= '

' . $description . '

'; + } + + // Parse timestamp from air date + $timestamp = null; + if ($airDate) { + $parsedTime = strtotime($airDate); + if ($parsedTime !== false) { + $timestamp = $parsedTime; + } + } + + // Create RSS item + $item = [ + 'uri' => $episodeUrl, + 'title' => $this->seriesTitle ? ($this->seriesTitle . ' ' . $episodeLabel . ' - ' . $title) : ($episodeLabel . ' - ' . $title), + 'author' => $this->seriesTitle, + 'content' => $content, + 'timestamp' => $timestamp, + 'uid' => $episodeUrl, + ]; + + $this->items[] = $item; + $count++; + } + } + + public function getName() + { + if ($this->seriesTitle) { + return $this->seriesTitle . ' - TheTVDB'; + } + return parent::getName(); + } + + public function getIcon() + { + return 'https://www.google.com/s2/favicons?domain=thetvdb.com&sz=32'; + } +}