Files
Xstream-Standalone/DOKUMENTATION.md
Task-Orchestrator 28b9afb234 DAP-74: Technische Dokumentation der xStream Kodi-Addons erstellt
Analysiert und dokumentiert:
- plugin.video.xstream: Startup-Sequenz, URL-Routing, Site-Plugin-Konvention,
  Browsing- und Hoster-Auflösungsflow, globale Suche
- script.module.xstreamscraper: automatische TMDB-basierte Stream-Suche
- HTTP-Request-Handler: Caching, Cookie-Verwaltung, Anti-Bot-Mechanismen
- Plugin-Handler, GUI-Schicht, Einstellungsreferenz, Abhängigkeiten

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-17 21:30:12 +02:00

432 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 403503: 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-name>'
└── plugin = __import__('<plugin-name>')
└── 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 (050 %)
4. collectMode = False
5. Ergebnisse nach Site-Name sortiert als Listeneinträge hinzufügen (50100 %)
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 403503: 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.