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)
|
||||
- **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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user