From 4f03da944708ff1d3070d4801f3118234b3b8db9 Mon Sep 17 00:00:00 2001 From: Akamaru Date: Sat, 27 Dec 2025 17:53:58 +0100 Subject: [PATCH] Neu: FilmFans Bridge --- FilmFansBridge.php | 209 +++++++++++++++++++++++++++++++++++++++++++++ README.md | 10 +++ 2 files changed, 219 insertions(+) create mode 100644 FilmFansBridge.php diff --git a/FilmFansBridge.php b/FilmFansBridge.php new file mode 100644 index 0000000..b701123 --- /dev/null +++ b/FilmFansBridge.php @@ -0,0 +1,209 @@ + [ + 'name' => 'Maximale Anzahl', + 'type' => 'number', + 'required' => false, + 'defaultValue' => 20, + 'title' => 'Maximale Anzahl an Uploads (max. 50)' + ] + ] + ]; + + public function collectData() + { + // HTML von der Startseite laden + $html = getSimpleHTMLDOM(self::URI); + if (!$html) { + returnServerError('Konnte FilmFans.org nicht laden'); + } + + // Upload-Einträge finden + $entries = $html->find('div.sra'); + if (empty($entries)) { + returnServerError('Keine Uploads gefunden'); + } + + // Jeden Eintrag parsen + foreach ($entries as $entry) { + // Upload-Zeit aus lsf-icon timed extrahieren + $timeTag = $entry->find('span.lsf-icon.timed', 0); + $uploadTime = $timeTag ? trim($timeTag->plaintext) : ''; + + // Timestamp berechnen (HH:MM Format zu Unix timestamp) + $timestamp = $this->parseUploadTime($uploadTime); + + // h2 Tag finden + $h2 = $entry->find('h2', 0); + if (!$h2) { + continue; + } + + // Titel extrahieren (nur der Text vor dem Tag) + $titleText = ''; + foreach ($h2->nodes as $node) { + if ($node->tag === 'text') { + $titleText .= $node->outertext; + } elseif ($node->tag === 'i') { + break; // Stop bei Jahr + } + } + $title = trim($titleText); + + // Jahr aus Tag extrahieren + $yearTag = $h2->find('i', 0); + $year = $yearTag ? trim($yearTag->plaintext, '()') : ''; + + // Movie URL extrahieren (erstes Tag im Entry) + $linkTag = $entry->find('a', 0); + $movieUrl = $linkTag ? $linkTag->href : ''; + if ($movieUrl && strpos($movieUrl, '/') === 0) { + $movieUrl = 'https://filmfans.org' . $movieUrl; + } + + // Cover-Bild extrahieren (aus i.cover) + $coverTag = $entry->find('i.cover img', 0); + $coverUrl = ''; + if ($coverTag && $coverTag->src) { + $coverUrl = $coverTag->src; + if (strpos($coverUrl, '/') === 0) { + $coverUrl = 'https://filmfans.org' . $coverUrl; + } + } + + // Beschreibung extrahieren + $descTag = $entry->find('p.description', 0); + $description = $descTag ? trim($descTag->plaintext) : ''; + + // Release-Namen sammeln (alle Tags innerhalb h2 > span) + $releases = []; + $releaseSpan = $h2->find('span', 0); + if ($releaseSpan) { + foreach ($releaseSpan->find('a') as $releaseLink) { + $releaseName = trim($releaseLink->plaintext); + if ($releaseName) { + $releases[] = $releaseName; + } + } + } + + // Sprachen extrahieren + $languages = []; + foreach ($entry->find('span.audiotag img') as $langImg) { + if ($langImg->src) { + // /images/DE.svg → DE + if (preg_match('#/images/([A-Z]{2})\.svg#', $langImg->src, $match)) { + $languages[] = $match[1]; + } + } + } + + // Genres extrahieren + $genres = []; + foreach ($entry->find('a.genre') as $genreLink) { + $genre = trim($genreLink->plaintext); + if ($genre) { + $genres[] = $genre; + } + } + + // Item erstellen + $item = [ + 'title' => $title . ($year ? " ($year)" : ''), + 'uri' => $movieUrl ?: self::URI, + 'timestamp' => $timestamp, + 'content' => $this->buildContent($coverUrl, $description, $releases, $languages, $genres), + 'author' => 'FilmFans', + 'categories' => $genres, + 'enclosures' => $coverUrl ? [$coverUrl] : [], + 'uid' => md5($movieUrl) + ]; + + $this->items[] = $item; + } + + // Nach Timestamp sortieren (neueste zuerst) + usort($this->items, function ($a, $b) { + return $b['timestamp'] <=> $a['timestamp']; + }); + + // Limit anwenden + $limit = min((int)$this->getInput('limit'), 50); + $this->items = array_slice($this->items, 0, $limit); + } + + private function parseUploadTime($timeString) + { + // Format: "HH:MM" z.B. "13:57" + if (preg_match('/^(\d{1,2}):(\d{2})$/', $timeString, $match)) { + $hours = (int)$match[1]; + $minutes = (int)$match[2]; + + $today = date('Y-m-d'); + $timestamp = strtotime("$today $hours:$minutes:00"); + + // Wenn Timestamp in der Zukunft liegt (mehr als 1 Stunde), ist es von gestern + if ($timestamp > time() + 3600) { + $timestamp = strtotime("yesterday $hours:$minutes:00"); + } + + return $timestamp; + } + + // Fallback: aktueller Zeitstempel + return time(); + } + + private function buildContent($coverUrl, $description, $releases, $languages, $genres) + { + $content = ''; + + // Cover-Bild + if ($coverUrl) { + $content .= 'Cover
'; + } + + // Beschreibung + if ($description) { + $content .= '

Beschreibung: ' . htmlspecialchars($description) . '

'; + } + + // Sprachen + if (!empty($languages)) { + $content .= '

Sprachen: ' . implode(', ', $languages) . '

'; + } + + // Genres + if (!empty($genres)) { + $content .= '

Genres: ' . implode(', ', $genres) . '

'; + } + + // Releases + if (!empty($releases)) { + $content .= '

Releases:

    '; + foreach ($releases as $release) { + $content .= '
  • ' . htmlspecialchars($release) . '
  • '; + } + $content .= '
'; + } + + return $content; + } + + public function getIcon() + { + return 'https://www.google.com/s2/favicons?domain=filmfans.org&sz=32'; + } +} diff --git a/README.md b/README.md index 17fd1eb..86646ca 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,16 @@ Diese Sammlung enthält verschiedene Bridge-Implementierungen für RSS-Bridge, u ### [EverSD News Bridge](https://bridge.ponywave.de/#bridge-EverSDBridge) (Von Akamaru) - **Beschreibung**: EverSD News und Changelog. +### [FilmFans Bridge](https://bridge.ponywave.de/#bridge-FilmFansBridge) (Von Akamaru) +- **Beschreibung**: Gibt die neuesten Film-Uploads von FilmFans zurück +- **Parameter**: + - **Limit** (optional): Maximale Anzahl an Uploads (Standard: 20, max: 50) +- **Hinweise**: + - Zeigt Filmtitel, Cover-Bilder, Beschreibungen und Release-Namen + - Alle Releases eines Films werden im Content aufgelistet + - Extrahiert Sprachen und Genres + - Sortiert nach Upload-Zeit (neueste zuerst) + ### [Florida TV Bridge](https://bridge.ponywave.de/#bridge-FloridaTVBridge) (Von Akamaru) - **Beschreibung**: Neueste News von FloridaTV Entertainment.