diff --git a/DOKUMENTATION.md b/DOKUMENTATION.md new file mode 100644 index 0000000..1fa6393 --- /dev/null +++ b/DOKUMENTATION.md @@ -0,0 +1,431 @@ +# xStream – Technische Dokumentation + +Version: `plugin.video.xstream 2026.02.11` / `script.module.xstreamscraper 2025.08.14` +Erstellt: 2026-08-17 + +--- + +## 1. Übersicht + +Das Projekt besteht aus zwei voneinander abhängigen Kodi-Addons: + +| Addon | Zweck | +|---|---| +| `plugin.video.xstream` | Vollständiges Video-Plugin: Browser-GUI, Site-Plugins, Hoster-Auflösung, Download | +| `script.module.xstreamscraper` | Companion-Modul: automatische Suche und Stream-Auflösung via TMDB-ID, für externe Aufrufer | + +**Zusammenfassung der Funktion:** xStream ist eine Kodi-Suchmaschine für Filme und Serien. Es lädt Inhalte von verschiedenen Streaming-Webseiten (sogenannte „Site-Plugins"), löst die gefundenen Hoster-Links über das `resolveurl`-Addon auf und spielt den Stream in Kodi ab. Das Scraper-Modul ergänzt dies um eine vollautomatische Suche, die auf TMDB-IDs basiert. + +--- + +## 2. Komponentenstruktur + +``` +Xstream-Standalone/ +├── plugin.video.xstream/ +│ ├── addon.xml # Addon-Metadaten und Abhängigkeiten +│ ├── default.py # Einstiegspunkt (Kodi ruft dies auf) +│ ├── service.py # Kodi-Service (läuft beim Kodi-Start) +│ ├── xstream.py # URL-Router und Hauptlogik +│ ├── sites/ # Site-Plugins (eine Datei pro Webseite) +│ │ ├── kinox.py +│ │ ├── aniworld.py +│ │ ├── burningseries.py +│ │ └── ... +│ └── resources/ +│ └── lib/ +│ ├── config.py # Addon-Einstellungen (Wrapper um xbmcaddon) +│ ├── tools.py # Logger, Parser, Cache-Hilfsfunktionen +│ ├── gui/ +│ │ ├── gui.py # Kodi-GUI-Abstraktion (Listeneinträge, Ansichten) +│ │ ├── guiElement.py # Einzelner Listeneintrag (Titel, Thumbnail, Metadaten) +│ │ ├── hoster.py # Hoster-GUI: Abspielen, Download, Weiterleiten +│ │ └── contextElement.py# Kontextmenü-Einträge +│ └── handler/ +│ ├── pluginHandler.py # Erkennung und Verwaltung der Site-Plugins +│ ├── requestHandler.py# HTTP-Client mit Cache, Cookies, Anti-Bot +│ ├── ParameterHandler.py # URL-Parameter lesen/schreiben +│ ├── jdownloaderHandler.py +│ ├── jdownloader2Handler.py +│ ├── myjdownloaderHandler.py +│ └── pyLoadHandler.py +└── script.module.xstreamscraper/ + ├── addon.xml + ├── main.py # Einstiegspunkt des Scraper-Moduls + ├── service.py # Service des Scraper-Moduls + └── resources/lib/ + ├── scraper.py # Kernlogik: Suche, Hoster-Auflösung, Wiedergabe + ├── settings.py # Globaler Zustand (collectMode, aDirectory) + ├── tools.py + ├── gui/ # GUI-Klassen (Subset von plugin.video.xstream) + └── handler/ # Request- und Parameter-Handler (Subset) +``` + +--- + +## 3. Ablauf: plugin.video.xstream + +### 3.1 Startup-Sequenz (Service) + +`service.py` wird von Kodi beim Start als separater Service-Prozess ausgeführt. + +``` +Kodi-Start + └─► service.py: main() + 1. cCache: setze '{addon_id}_main' = 'running' + 2. Resolver-Update von GitHub prüfen/ausführen (updateManager.resolverUpdate) + 3. Abhängigkeiten prüfen (checkDependence) – fehlende Addons installieren + 4. Domain-Prüfung aller Site-Plugins parallel (cPluginHandler.checkDomain) + → HTTP-Request an domain des jeweiligen Site-Plugins + → Status 200/3xx: Domain aktiv, globale Suche aktiviert + → Status 403–503: globale Suche deaktiviert, Link gesperrt + 5. PluginDB aktualisieren (falls neue Einstellungen vorhanden) + 6. cCache: setze '{addon_id}_main' = 'finished' + 7. Changelog-Popup anzeigen (falls konfiguriert) + 8. Alten HTML-Cache löschen (alle N Tage, konfigurierbar) +``` + +`showMainMenu()` in `xstream.py` wartet aktiv (max. 60 Sekunden / 0,5-Sekunden-Schleifen) auf das Signal `'finished'`, bevor das Hauptmenü aufgebaut wird. + +--- + +### 3.2 Browsing-Flow (Plugin-Aufruf) + +Kodi ruft `default.py` mit einer Plugin-URL der Form +`plugin://plugin.video.xstream/?site=kinox&function=showMovieMenu&...` auf. + +``` +default.py: main() + └─► Systempfade einrichten (sites/, lib/, art/sites/) + └─► xstream.py: parseUrl() + ├── ParameterHandler: URL-Parameter lesen + ├── Sonderfunktionen: clearCache, viewInfo, searchAlter, searchTMDB, ... + ├── Kein 'site'-Parameter → showMainMenu() + │ └── alle aktivierten Site-Plugins als Listeneinträge hinzufügen + ├── site = 'cHosterGui' → showHosterGui() + ├── site = 'globalSearch' → searchGlobal() + ├── site = 'xStream' → xStream-Einstellungen öffnen + ├── site = 'resolver' → Resolver-Einstellungen öffnen + └── site = '' + └── plugin = __import__('') + └── function = getattr(plugin, sFunction) + └── function() ← Site-Plugin übernimmt +``` + +--- + +### 3.3 Site-Plugin-Konvention + +Jede Datei in `sites/` ist ein eigenständiges Python-Modul. Es muss folgende Modul-Variablen und Funktionen bereitstellen: + +| Name | Pflicht | Beschreibung | +|---|---|---| +| `SITE_IDENTIFIER` | Ja | Eindeutiger Bezeichner (= Dateiname ohne `.py`) | +| `SITE_NAME` | Ja | Anzeigename im Menü | +| `SITE_ICON` | Nein | Dateiname des Icons in `resources/art/sites/` | +| `DOMAIN` | Nein | Standarddomain; kann per Einstellung überschrieben werden | +| `SITE_GLOBAL_SEARCH` | Nein | `True`/`False`; steuert globale Suche | +| `load()` | Ja | Einstiegspunkt – baut Hauptmenü des Sites auf | +| `_search(oGui, sSearchText)` | Nein | Globale Suche; befüllt `oGui` mit Ergebnissen | +| `showHosters()` | Typisch | Gibt Hoster-Liste zurück (list of dicts + Funktionsname am Ende) | +| `getHosterUrl(sUrl)` | Typisch | Löst einen Hoster-Link zur Stream-URL auf | + +**Beispiel (kinox.py, vereinfacht):** +```python +SITE_IDENTIFIER = 'kinox' +SITE_NAME = 'KinoX' +DOMAIN = 'ww22.kinoz.to' + +def load(): # Hauptmenü: News, Filme, Serien, Dokumentationen, Suche +def showMovieMenu(): # Untermenü: Kino, A-Z, Genre, Beliebt, Neu +def showHosters(): # Gibt Liste der Hoster-Dicts + 'getHosterUrl' zurück +def getHosterUrl(): # Löst Hoster-URL auf, gibt {'streamUrl':..., 'resolved':...} zurück +def _search(oGui, sSearchText): # Globale Suche, befüllt oGui +``` + +--- + +### 3.4 Hoster-Auflösungsflow + +``` +User klickt auf Film/Episode + └─► xstream.py: parseUrl() erkennt 'playMode' + ├── hosterSelect = 'Auto' → cHosterGui.streamAuto() + └── sonst → cHosterGui.stream() + +cHosterGui.stream(playMode, siteName, function): + 1. Site-Plugin laden, Hoster-Funktion aufrufen → Hoster-Liste + 2. Hoster nach Priorität sortieren (__getPriorities) + → resolveurl.HostedMediaFile(url=...).get_resolvers() + → Priorität nach Resolver-Ranking + Qualität + Sprache + 3. Auswahl: + ├── hosterSelect = 'List' → Hoster als Ordner anzeigen (showHosterFolder) + ├── Mehrere Hoster → Dialog zur Auswahl (_chooseHoster) + └── Genau ein Hoster → direkt weiter + 4. getHosterUrl(hoster['link']) aufrufen → {'streamUrl': ..., 'resolved': bool} + 5. cHosterGui.play / download / addToPlaylist / sendToJD / ... + +cHosterGui.play(siteResult): + 1. resolveurl.resolve(streamUrl) → direkter Stream-Link + 2. xbmcgui.ListItem mit Link erstellen + 3. m3u8/mpd → inputstream.adaptive aktivieren + 4. xbmcplugin.setResolvedUrl(handle, True, listItem) +``` + +--- + +### 3.5 Globale Suche + +``` +searchGlobal(sSearchText): + 1. cGui im collectMode = True (Ergebnisse werden im Speicher gesammelt) + 2. ThreadPoolExecutor (max. 10 Workers): + → alle aktivierten Plugins mit globalsearch != 'false' + → plugin._search(oGui, sSearchText) parallel aufrufen + 3. Fortschrittsdialog aktualisieren (0–50 %) + 4. collectMode = False + 5. Ergebnisse nach Site-Name sortiert als Listeneinträge hinzufügen (50–100 %) + +searchAlter (Kontextmenü „Weitere Quellen"): + - Wie searchGlobal, aber mit Titel-Normalisierung (Jahr, Staffelkennung entfernen) + - Ergebnisse werden nach Titel, Jahr und IMDB-ID gefiltert + +searchTMDB (aus TMDB-Kontext aufgerufen): + - Wie searchGlobal, ohne zusätzliche Filterung +``` + +--- + +## 4. HTTP-Request-Handler (`requestHandler.py`) + +`cRequestHandler` ist der zentrale HTTP-Client des Plugins. + +### Initialisierung (wichtige Parameter) + +| Parameter | Standard | Bedeutung | +|---|---|---| +| `caching` | `True` | HTML-Cache aktivieren | +| `ignoreErrors` | `False` | HTTP-Fehler still ignorieren | +| `method` | `'GET'` | HTTP-Methode | +| `compression` | `True` | gzip/deflate anfordern | +| `bypass_dns` | `False` | DNS-over-HTTPS verwenden | + +### Caching + +Zwei Cache-Ebenen, konfigurierbar per Einstellung: + +| Ebene | Speicherort | Aktivierung | +|---|---|---| +| Persistent | `{profile}/htmlcache/{md5(url)}` | Standard | +| Volatil (In-Memory) | `cCache` (Wörterbuch) | Einstellung `volatileHtmlCache = true` | + +Cache-Lebensdauer: konfigurierbar, Standard 6 Stunden (360 min × 60 s). + +### Anti-Bot-Mechanismen + +| Schutzmechanismus | Erkennung | Behandlung | +|---|---|---| +| Cloudflare | `cloudflare` im Response-Header | Fehlermeldung an Nutzer, kein Retry | +| DDOS-Guard | `DDOS-GUARD` im Response-Body | Automatischer Cookie-Challenge via `check.ddos-guard.net` | +| Blazingfast | `lazingfast` im Response-Body | AES-Entschlüsselung via pyaes, Cookie-Setzung | +| DNS-Sperre (CUII) | Redirect zu `notice.cuii.info` | Fehlerdialog; Bypass via DoH (Cloudflare) wenn aktiviert | + +### DNS-over-HTTPS (DoH) + +Wenn `bypass_dns = True` und Einstellung `bypassDNSlock = true`: +→ DNS-Auflösung via `https://cloudflare-dns.com/dns-query` statt System-DNS +→ IP-Adresse wird direkt für TCP-Verbindung genutzt; SNI-Header enthält weiterhin den Hostnamen +→ IP-Ergebnis wird im volatilen Cache gespeichert + +### Cookie-Verwaltung + +Pro Domain wird eine eigene Cookie-Datei im LWP-Format gespeichert: +`{profile}/cookies/{domain_mit_unterstrichen}.txt` + +--- + +## 5. Plugin-Handler (`pluginHandler.py`) + +Verwaltet die Erkennung und den Zustand der Site-Plugins. + +### PluginDB + +Eine JSON-Datei unter `{profile}/pluginDB`. Sie enthält für jedes Site-Plugin: + +```json +{ + "kinox": { + "name": "KinoX", + "identifier": "kinox", + "icon": "kinox.png", + "domain": "ww22.kinoz.to", + "globalsearch": "true", + "modified": 1700000000.0 + } +} +``` + +Die DB wird aktualisiert, wenn: +- Eine neue `.py`-Datei im `sites/`-Ordner gefunden wird +- Eine vorhandene Datei seit dem letzten Einlesen geändert wurde +- Die globale Sucheinstellung für ein Plugin geändert wurde + +### Domain-Prüfung (`checkDomain`) + +Wird beim Kodi-Start vom Service aufgerufen. Für jedes aktivierte Plugin: +1. HTTP-Request an `http://{domain}/` +2. Status-Code wird in der Einstellung `plugin_{id}_status` gespeichert +3. Bei Redirect (3xx): neue Domain aus dem `Location`-Header übernehmen +4. Bei 403–503: globale Suche für das Plugin deaktivieren + +--- + +## 6. GUI-Schicht (`gui.py`, `guiElement.py`) + +### `cGui` + +Abstraktion über Kodis `xbmcplugin`-API. Wesentliche Konzepte: + +**collectMode**: Wenn `True`, werden Listeneinträge nicht an Kodi gesendet, sondern in `self.searchResults` gesammelt. Wird für die globale Suche verwendet, damit alle Plugins ihre Ergebnisse in eine gemeinsame Liste schreiben können. + +**TMDB-Metadaten**: Wenn die Einstellung `TMDBMETA = true`, wird für jeden Eintrag automatisch `guiElement.getMeta()` aufgerufen (via TMDB-API), bevor er angezeigt wird. + +**Kontext-Menü-Einträge** (automatisch für alle Einträge): +- Trailer (wenn Addon `script.module.xstream.trailer` installiert) +- Erweiterte Info (TMDB-Infobox) +- Weitere Quellen (searchAlter) +- Playlist hinzufügen +- Download +- An JDownloader / JDownloader2 / My.JDownloader / pyLoad senden +- Manueller Hoster-Auswahldialog (wenn Auto-Modus aktiv) + +### `cGuiElement` + +Repräsentiert einen einzelnen Listeneintrag. Wesentliche Felder: + +| Feld | Methode | Beschreibung | +|---|---|---| +| Titel | `setTitle` | Anzeigename | +| Site-Name | `setSiteName` | Zugehöriges Site-Plugin | +| Funktion | `setFunction` | Aufzurufende Funktion beim Klick | +| Thumbnail | `setThumbnail` | Vorschaubild-URL | +| Fanart | `setFanart` | Hintergrundbild-URL | +| Medientyp | `setMediaType` | `movie`, `tvshow`, `season`, `episode` | +| Sprache | `setLanguage` | Sprach-Code (z. B. `DE`, `EN`) | +| Qualität | `setQuality` | z. B. `1080p`, `720p` | +| Jahr | `setYear` | Erscheinungsjahr | +| Staffel/Episode | `setSeason`/`setEpisode` | Serieninformationen | + +--- + +## 7. script.module.xstreamscraper + +Das Scraper-Modul ist für den Fall gedacht, dass externe Tools (z. B. über Kodi-Addons, die TMDB-IDs kennen) einen Film oder eine Serie automatisch abspielen wollen, ohne durch das xStream-Menü navigieren zu müssen. + +### Haupt-API: `scraper.play(_type, _id, season, episode)` + +``` +play("movie", "123456", None, None) +play("tv", "78910", "2", "5") # Staffel 2, Episode 5 +``` + +**Ablauf:** + +``` +play(_type, _id, season, episode): + 1. TMDB-API: Film-/Seriendaten laden (Name, Erscheinungsdatum, alternative Titel) + 2. Optional: vavoo-Integration prüfen (direkter Stream ohne Scraping) + 3. searchGlobal(name, searchtitles, isSerie, ...) aufrufen + → alle aktivierten Site-Plugins parallel durchsuchen (ThreadPoolExecutor) + → Ergebnisse filtern: + - Sprache muss Deutsch sein + - Medientyp muss passen (Film ≠ Serie) + - Bei Serien: Staffelnummer prüfen + - Jahresabgleich (±0 bei Filmen) + - Titelabgleich (normalisiert: Sonderzeichen entfernt) + 4. get_hosters(sources, isSerie, season, episode) aufrufen + → Bei Serien: get_episodes() navigiert zu korrekter Episode + → site-plugin.getHosterUrl() für jeden Treffer + 5. Jeden Hoster der Reihe nach testen: + → resolveurl.resolve(streamUrl) aufrufen + → HTTP-HEAD-Request prüfen (Content-Type muss Video sein) + → Erster gültiger Link → _play(url) + 6. _play(url): + → xbmcgui.ListItem mit URL + → m3u8 → inputstream.adaptive + → xbmcplugin.setResolvedUrl() +``` + +### Unterschied zur manuellen Nutzung via plugin.video.xstream + +| Aspekt | plugin.video.xstream | script.module.xstreamscraper | +|---|---|---| +| Einstieg | Nutzer-Navigation | TMDB-ID als Parameter | +| Hoster-Auswahl | Manuell oder nach Priorität | Vollautomatisch, erster funktionierender | +| Stream-Validierung | Keiner | HTTP-HEAD-Request vor Wiedergabe | +| Sprach-/Jahresfilter | Keine automatische Filterung | Automatisch (nur Deutsch, korrektes Jahr) | +| Zielgruppe | Endnutzer in Kodi-GUI | Andere Addons / Automatisierung | + +--- + +## 8. Einstellungen (Auszug) + +Alle Einstellungen befinden sich in `resources/settings.xml` und werden über `cConfig` gelesen. + +| Einstellung | Bedeutung | +|---|---| +| `plugin_{id}` | Site-Plugin aktiviert (`true`/`false`) | +| `plugin_{id}.domain` | Überschreibt Standard-Domain des Plugins | +| `plugin_{id}_status` | Letzter HTTP-Status-Code der Domain | +| `plugin_{id}_checkdomain` | Domain-Prüfung für dieses Plugin aktiviert | +| `global_search_{id}` | Globale Suche für dieses Plugin aktiviert | +| `hosterSelect` | `Auto` (automatisch) oder `List` (manuelle Auswahl) | +| `cacheTime` | HTML-Cache-Lebensdauer in Minuten (Standard: 360) | +| `requestTimeout` | HTTP-Timeout in Sekunden (Standard: 10) | +| `volatileHtmlCache` | In-Memory-Cache aktivieren | +| `bypassDNSlock` | DNS-over-HTTPS für gesperrte Domains | +| `TMDBMETA` | TMDB-Metadaten für Listeneinträge laden | +| `metaOverwrite` | TMDB-Daten ersetzen statt ergänzen | +| `prefLanguage` | Bevorzugte Sprache (`1` = Deutsch, `2` = Englisch) | +| `preferedQuality` | Bevorzugte Videoqualität für Hoster-Sortierung | +| `presortHoster` | Hoster nach Resolver-Priorität vorsortieren | +| `maxHoster` | Maximale Anzahl Hoster in der Liste | +| `jd_enabled` / `jd2_enabled` / `myjd_enabled` / `pyload_enabled` | Download-Manager aktivieren | +| `githubUpdateResolver` | Resolver bei Kodi-Start aktualisieren | +| `cacheDeltaDay` | HTML-Cache alle N Tage beim Start löschen | +| `popup.update.notification` | Changelog-Popup beim Start anzeigen | + +--- + +## 9. Abhängigkeiten + +### plugin.video.xstream + +| Addon | Pflicht | Zweck | +|---|---|---| +| `script.module.requests` | Ja | HTTP (via scraper-Modul) | +| `script.module.resolveurl` ≥ 5.1.173 | Ja | Hoster-Auflösung | +| `script.module.six` ≥ 1.11.0 | Ja | Python-2/3-Kompatibilität | +| `script.module.pyaes` | Ja | AES-Entschlüsselung (Blazingfast-Bypass) | +| `script.module.inputstreamhelper` ≥ 0.3.3 | Optional | HLS/DASH über inputstream.adaptive | +| `repository.resolveurl` | Optional | Resolver-Repository | +| `script.module.pydevd` / `script.module.web-pdb` | Optional | Debugging | + +### script.module.xstreamscraper + +Zusätzlich zu den obigen: + +| Addon | Pflicht | Zweck | +|---|---|---| +| `plugin.video.xstream` | Ja | Nutzt dessen Site-Plugins direkt | +| `plugin.video.vavooto` | Ja | Vavoo-Stream-Integration | + +--- + +## 10. Bekannte Einschränkungen + +- **Cloudflare**: Seiten mit aktivem Cloudflare-Schutz werden nicht automatisch umgangen. Der Nutzer erhält eine Fehlermeldung. +- **Domain-Wechsel**: Wenn eine Streaming-Seite ihre Domain ändert, muss entweder der Kodi-Start abgewartet werden (Domain-Prüfung aktualisiert die Einstellung automatisch via Redirect-Erkennung) oder die Domain muss manuell in den xStream-Einstellungen angepasst werden. +- **Thread-Safety in Global Search**: Die Site-Plugins werden parallel in Threads ausgeführt. Plugins, die globalen Zustand modifizieren, können Race-Conditions verursachen. +- **scraper.py `plugin_` Funktion**: Der Scraper importiert Site-Plugins dynamisch und setzt `settings.urlparams` als globalen Zustand – dies ist nicht thread-sicher und kann bei paralleler Nutzung zu Problemen führen. +- **Kodi-Version**: Der Code enthält explizite Versionsprüfungen (`kodi_version[:2] < '20'` usw.). Unter Kodi 19 wird `setInfo` statt `getVideoInfoTag()` verwendet.