Neu: KiKA Serien Bridge
This commit is contained in:
219
KiKASeriesBridge.php
Normal file
219
KiKASeriesBridge.php
Normal file
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
class KiKASeriesBridge extends BridgeAbstract
|
||||
{
|
||||
const NAME = 'KiKA Serien Bridge';
|
||||
const URI = 'https://www.kika.de';
|
||||
const API_URI = 'https://www.kika.de/_next-api/proxy/v1/brands/';
|
||||
const DESCRIPTION = 'Gibt die neuesten Episoden einer Serie von KiKA zurück.';
|
||||
const MAINTAINER = 'Akamaru';
|
||||
const CACHE_TIMEOUT = 3600; // 1 Stunde
|
||||
|
||||
const PARAMETERS = [
|
||||
'Serie' => [
|
||||
'series_id' => [
|
||||
'name' => 'Serien-ID',
|
||||
'type' => 'text',
|
||||
'required' => true,
|
||||
'exampleValue' => 'tanoshii-100',
|
||||
'title' => 'Die Serien-ID findest du in der URL: kika.de/serien-name/serien-name-100'
|
||||
],
|
||||
'limit' => [
|
||||
'name' => 'Maximale Anzahl an Episoden',
|
||||
'type' => 'number',
|
||||
'required' => false,
|
||||
'defaultValue' => 50
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
private $seriesTitle = '';
|
||||
private $seriesUrl = '';
|
||||
|
||||
public function getIcon()
|
||||
{
|
||||
return 'https://www.google.com/s2/favicons?domain=www.kika.de&sz=32';
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
if (!empty($this->seriesTitle)) {
|
||||
return $this->seriesTitle . ' - KiKA';
|
||||
}
|
||||
return parent::getName();
|
||||
}
|
||||
|
||||
public function getURI()
|
||||
{
|
||||
if (!empty($this->seriesUrl)) {
|
||||
return self::URI . $this->seriesUrl;
|
||||
}
|
||||
|
||||
$seriesId = $this->getInput('series_id');
|
||||
if (!empty($seriesId)) {
|
||||
return self::URI . '/' . $seriesId;
|
||||
}
|
||||
|
||||
return self::URI;
|
||||
}
|
||||
|
||||
public function collectData()
|
||||
{
|
||||
$seriesId = $this->getInput('series_id');
|
||||
$limit = $this->getInput('limit') ?? 50;
|
||||
|
||||
if (empty($seriesId)) {
|
||||
returnClientError('Serien-ID ist erforderlich.');
|
||||
}
|
||||
|
||||
// Serien-Informationen abrufen
|
||||
$brandsUrl = self::API_URI . urlencode($seriesId);
|
||||
$brandsJson = getContents($brandsUrl);
|
||||
$brandsData = json_decode($brandsJson, true);
|
||||
|
||||
if (!$brandsData) {
|
||||
returnServerError('Serie nicht gefunden. Überprüfe die Serien-ID.');
|
||||
}
|
||||
|
||||
$this->seriesTitle = $brandsData['title'] ?? 'Unbekannte Serie';
|
||||
$this->seriesUrl = $brandsData['urlPath'] ?? '';
|
||||
|
||||
// Videos-API-URL extrahieren
|
||||
$videosPageUrl = $brandsData['videoSubchannel']['videosPageUrl'] ?? null;
|
||||
if (!$videosPageUrl) {
|
||||
returnServerError('Keine Videos für diese Serie gefunden.');
|
||||
}
|
||||
|
||||
// Alle Seiten der API abrufen (Videos sind nicht chronologisch sortiert)
|
||||
$videos = [];
|
||||
$page = 0;
|
||||
$maxPages = 10; // Sicherheitslimit
|
||||
|
||||
while ($page < $maxPages) {
|
||||
$pageUrl = preg_replace('/page=\d+/', 'page=' . $page, $videosPageUrl);
|
||||
|
||||
try {
|
||||
$videosJson = getContents($pageUrl);
|
||||
} catch (Exception $e) {
|
||||
// 404 = keine weiteren Seiten
|
||||
break;
|
||||
}
|
||||
|
||||
$videosData = json_decode($videosJson, true);
|
||||
|
||||
if (!$videosData || !isset($videosData['content']) || !is_array($videosData['content'])) {
|
||||
break;
|
||||
}
|
||||
|
||||
$pageVideos = $videosData['content'];
|
||||
if (empty($pageVideos)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$videos = array_merge($videos, $pageVideos);
|
||||
$page++;
|
||||
}
|
||||
|
||||
if (empty($videos)) {
|
||||
returnServerError('Keine Episoden gefunden.');
|
||||
}
|
||||
|
||||
// Sortiere nach Datum (neueste zuerst)
|
||||
usort($videos, function ($a, $b) {
|
||||
$timeA = strtotime($a['date'] ?? '1970-01-01');
|
||||
$timeB = strtotime($b['date'] ?? '1970-01-01');
|
||||
return $timeB - $timeA;
|
||||
});
|
||||
|
||||
// Limitiere die Anzahl
|
||||
$videos = array_slice($videos, 0, (int)$limit);
|
||||
|
||||
foreach ($videos as $video) {
|
||||
$item = [];
|
||||
|
||||
// Titel zusammenbauen
|
||||
$episodeTitle = $video['title'] ?? 'Unbekannte Episode';
|
||||
$season = $video['videoDetails']['season'] ?? null;
|
||||
$episodeNum = $video['videoDetails']['episodeNumber'] ?? null;
|
||||
|
||||
if ($season && $episodeNum) {
|
||||
$item['title'] = sprintf(
|
||||
'%s S%02dE%02d: %s',
|
||||
$this->seriesTitle,
|
||||
(int)$season,
|
||||
(int)$episodeNum,
|
||||
$episodeTitle
|
||||
);
|
||||
} else {
|
||||
$item['title'] = $this->seriesTitle . ': ' . $episodeTitle;
|
||||
}
|
||||
|
||||
// URL zur Episode
|
||||
$videoId = $video['id'] ?? '';
|
||||
$urlPath = $video['urlPath'] ?? '';
|
||||
if (!empty($urlPath) && !empty($videoId)) {
|
||||
$item['uri'] = self::URI . $urlPath . '/' . $videoId;
|
||||
} else {
|
||||
$item['uri'] = $this->getURI();
|
||||
}
|
||||
|
||||
// Eindeutige ID
|
||||
$item['uid'] = 'kika-' . $videoId;
|
||||
|
||||
// Zeitstempel
|
||||
if (!empty($video['date'])) {
|
||||
$timestamp = strtotime($video['date']);
|
||||
if ($timestamp !== false) {
|
||||
$item['timestamp'] = $timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
// Autor
|
||||
$item['author'] = 'KiKA';
|
||||
|
||||
// Content: Bild + Beschreibung + Details
|
||||
$content = '';
|
||||
|
||||
// Vorschaubild
|
||||
$imageUrlScheme = $video['teaserImage']['urlScheme'] ?? null;
|
||||
if ($imageUrlScheme) {
|
||||
$imageUrl = self::URI . str_replace(['**imageVariant**', '**width**'], ['tlarge169', '960'], $imageUrlScheme);
|
||||
$imageAlt = $video['teaserImage']['alt'] ?? $episodeTitle;
|
||||
$content .= '<img src="' . htmlspecialchars($imageUrl) . '" alt="' . htmlspecialchars($imageAlt) . '" /><br>';
|
||||
$item['enclosures'] = [$imageUrl];
|
||||
}
|
||||
|
||||
// Beschreibung
|
||||
$description = $video['videoDetails']['description'] ?? $video['teaserText'] ?? '';
|
||||
if (!empty($description)) {
|
||||
$content .= '<p>' . htmlspecialchars($description) . '</p>';
|
||||
}
|
||||
|
||||
$item['content'] = $content;
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
public function detectParameters($url)
|
||||
{
|
||||
// URL-Format: https://www.kika.de/serien-name/serien-name-100
|
||||
// oder: https://www.kika.de/serien-name/videos/video-id
|
||||
if (preg_match('#kika\.de/([^/]+)/([^/]+-\d+)(?:/|$)#i', $url, $matches)) {
|
||||
return [
|
||||
'series_id' => $matches[2]
|
||||
];
|
||||
}
|
||||
|
||||
// Fallback: Versuche nur den Slug zu extrahieren
|
||||
if (preg_match('#kika\.de/([a-z0-9-]+-\d+)#i', $url, $matches)) {
|
||||
return [
|
||||
'series_id' => $matches[1]
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user