Neu: FilmFans Bridge
This commit is contained in:
209
FilmFansBridge.php
Normal file
209
FilmFansBridge.php
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
class FilmFansBridge extends BridgeAbstract
|
||||||
|
{
|
||||||
|
const NAME = 'FilmFans';
|
||||||
|
const URI = 'https://filmfans.org/';
|
||||||
|
const DESCRIPTION = 'Gibt die neuesten Uploads von FilmFans zurück';
|
||||||
|
const MAINTAINER = 'Akamaru';
|
||||||
|
const CACHE_TIMEOUT = 1800; // 30 Minuten
|
||||||
|
|
||||||
|
const PARAMETERS = [
|
||||||
|
[
|
||||||
|
'limit' => [
|
||||||
|
'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 <i> 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 <i> Tag extrahieren
|
||||||
|
$yearTag = $h2->find('i', 0);
|
||||||
|
$year = $yearTag ? trim($yearTag->plaintext, '()') : '';
|
||||||
|
|
||||||
|
// Movie URL extrahieren (erstes <a> 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 <a> 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 .= '<img src="' . htmlspecialchars($coverUrl) . '" alt="Cover" /><br>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Beschreibung
|
||||||
|
if ($description) {
|
||||||
|
$content .= '<p><strong>Beschreibung:</strong> ' . htmlspecialchars($description) . '</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprachen
|
||||||
|
if (!empty($languages)) {
|
||||||
|
$content .= '<p><strong>Sprachen:</strong> ' . implode(', ', $languages) . '</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Genres
|
||||||
|
if (!empty($genres)) {
|
||||||
|
$content .= '<p><strong>Genres:</strong> ' . implode(', ', $genres) . '</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Releases
|
||||||
|
if (!empty($releases)) {
|
||||||
|
$content .= '<p><strong>Releases:</strong></p><ul>';
|
||||||
|
foreach ($releases as $release) {
|
||||||
|
$content .= '<li>' . htmlspecialchars($release) . '</li>';
|
||||||
|
}
|
||||||
|
$content .= '</ul>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIcon()
|
||||||
|
{
|
||||||
|
return 'https://www.google.com/s2/favicons?domain=filmfans.org&sz=32';
|
||||||
|
}
|
||||||
|
}
|
||||||
10
README.md
10
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)
|
### [EverSD News Bridge](https://bridge.ponywave.de/#bridge-EverSDBridge) (Von Akamaru)
|
||||||
- **Beschreibung**: EverSD News und Changelog.
|
- **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)
|
### [Florida TV Bridge](https://bridge.ponywave.de/#bridge-FloridaTVBridge) (Von Akamaru)
|
||||||
- **Beschreibung**: Neueste News von FloridaTV Entertainment.
|
- **Beschreibung**: Neueste News von FloridaTV Entertainment.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user