diff --git a/README.md b/README.md index e46090c..7acafb8 100644 --- a/README.md +++ b/README.md @@ -2,41 +2,37 @@ ViewIT Logo -ViewIT ist ein Kodi‑Addon zum Durchsuchen und Abspielen von Inhalten der unterstützten Anbieter. +ViewIT ist ein Kodi Addon. +Es durchsucht Provider und startet Streams. ## Projektstruktur -- `addon/` Kodi‑Addon Quellcode -- `scripts/` Build‑Scripts (arbeiten mit `addon/` + `dist/`) -- `dist/` Build‑Ausgaben (ZIPs) -- `docs/`, `tests/` +- `addon/` Kodi Addon Quellcode +- `scripts/` Build Scripts +- `dist/` Build Ausgaben +- `docs/` Doku +- `tests/` Tests -## Build & Release -- Addon‑Ordner bauen: `./scripts/build_install_addon.sh` → `dist//` -- Kodi‑ZIP bauen: `./scripts/build_kodi_zip.sh` → `dist/-.zip` -- Addon‑Version in `addon/addon.xml` -- Reproduzierbare ZIPs: optional `SOURCE_DATE_EPOCH` setzen +## Build und Release +- Addon Ordner bauen: `./scripts/build_install_addon.sh` +- Kodi ZIP bauen: `./scripts/build_kodi_zip.sh` +- Version pflegen: `addon/addon.xml` +- Reproduzierbares ZIP: `SOURCE_DATE_EPOCH` optional setzen -## Lokales Kodi-Repository -- Repository bauen (inkl. ZIPs + `addons.xml` + `addons.xml.md5`): `./scripts/build_local_kodi_repo.sh` -- Lokal bereitstellen: `./scripts/serve_local_kodi_repo.sh` -- Standard-URL: `http://127.0.0.1:8080/repo/addons.xml` -- Optional eigene URL beim Build setzen: `REPO_BASE_URL=http://:/repo ./scripts/build_local_kodi_repo.sh` +## Lokales Kodi Repository +- Repository bauen: `./scripts/build_local_kodi_repo.sh` +- Repository starten: `./scripts/serve_local_kodi_repo.sh` +- Standard URL: `http://127.0.0.1:8080/repo/addons.xml` +- Eigene URL beim Build: `REPO_BASE_URL=http://:/repo ./scripts/build_local_kodi_repo.sh` -## Gitea Release-Asset Upload -- ZIP bauen: `./scripts/build_kodi_zip.sh` -- Token setzen: `export GITEA_TOKEN=` -- Asset an Tag hochladen (erstellt Release bei Bedarf): `./scripts/publish_gitea_release.sh` -- Optional: `--tag v0.1.50 --asset dist/plugin.video.viewit-0.1.50.zip` - -## Entwicklung (kurz) -- Hauptlogik: `addon/default.py` +## Entwicklung +- Router: `addon/default.py` - Plugins: `addon/plugins/*_plugin.py` -- Einstellungen: `addon/resources/settings.xml` +- Settings: `addon/resources/settings.xml` -## Tests mit Abdeckung -- Dev-Abhängigkeiten installieren: `./.venv/bin/pip install -r requirements-dev.txt` -- Tests + Coverage starten: `./.venv/bin/pytest` -- Optional (XML-Report): `./.venv/bin/pytest --cov-report=xml` +## Tests +- Dev Pakete installieren: `./.venv/bin/pip install -r requirements-dev.txt` +- Tests starten: `./.venv/bin/pytest` +- XML Report: `./.venv/bin/pytest --cov-report=xml` ## Dokumentation Siehe `docs/`. diff --git a/docs/DEFAULT_ROUTER.md b/docs/DEFAULT_ROUTER.md index 06835c7..4503d58 100644 --- a/docs/DEFAULT_ROUTER.md +++ b/docs/DEFAULT_ROUTER.md @@ -1,55 +1,49 @@ -# ViewIT – Hauptlogik (`addon/default.py`) +# ViewIT Hauptlogik (`addon/default.py`) -Dieses Dokument beschreibt den Einstiegspunkt des Addons und die zentrale Steuerlogik. +Diese Datei ist der Router des Addons. +Sie verbindet Kodi UI, Plugin Calls und Playback. -## Aufgabe der Datei -`addon/default.py` ist der Router des Addons. Er: -- lädt die Plugin‑Module dynamisch, -- stellt die Kodi‑Navigation bereit, -- übersetzt UI‑Aktionen in Plugin‑Aufrufe, -- startet die Wiedergabe und verwaltet Playstate/Resume. +## Kernaufgabe +- Plugins laden +- Menues bauen +- Aktionen auf Plugin Methoden mappen +- Playback starten +- Playstate speichern -## Ablauf (high level) -1. **Plugin‑Discovery**: Lädt alle `addon/plugins/*.py` (ohne `_`‑Prefix). Bevorzugt `Plugin = `, sonst werden `BasisPlugin`‑Subklassen deterministisch instanziiert. -2. **Navigation**: Baut Kodi‑Listen (Serien/Staffeln/Episoden) auf Basis der Plugin‑Antworten. -3. **Playback**: Holt Stream‑Links aus dem Plugin und startet die Wiedergabe. -4. **Playstate**: Speichert Resume‑Daten lokal (`playstate.json`) und setzt `playcount`/Resume‑Infos. +## Ablauf +1. Plugin Discovery fuer `addon/plugins/*.py` ohne `_` Prefix. +2. Navigation fuer Titel, Staffeln und Episoden. +3. Playback: Link holen, optional aufloesen, abspielen. +4. Playstate: watched und resume in `playstate.json` schreiben. -## Routing & Aktionen -Die Datei arbeitet mit URL‑Parametern (Kodi‑Plugin‑Standard). Typische Aktionen: -- `search` → Suche über ein Plugin -- `seasons` → Staffeln für einen Titel -- `episodes` → Episoden für eine Staffel -- `play` → Stream‑Link auflösen und abspielen +## Routing +Der Router liest Query Parameter aus `sys.argv[2]`. +Typische Aktionen: +- `search` +- `seasons` +- `episodes` +- `play_episode` +- `play_movie` +- `play_episode_url` -Die genaue Aktion wird aus den Query‑Parametern gelesen und an das entsprechende Plugin delegiert. +## Playstate +- Speicherort: Addon Profilordner, Datei `playstate.json` +- Key: Plugin + Titel + Staffel + Episode +- Werte: watched, playcount, resume_position, resume_total -## Playstate (Resume/Watched) -- **Speicherort**: `playstate.json` im Addon‑Profilordner. -- **Key**: Kombination aus Plugin‑Name, Titel, Staffel, Episode. -- **Verwendung**: - - `playcount` wird gesetzt, wenn „gesehen“ markiert ist. - - `resume_position`/`resume_total` werden gesetzt, wenn vorhanden. +## Wichtige Helper +- Plugin Loader und Discovery +- UI Builder fuer ListItems +- Playstate Load/Save/Merge +- TMDB Merge mit Source Fallback -## Wichtige Hilfsfunktionen -- **Plugin‑Loader**: findet & instanziiert Plugins. -- **UI‑Helper**: setzt Content‑Type, baut Verzeichnisseinträge. -- **Playstate‑Helper**: `_load_playstate`, `_save_playstate`, `_apply_playstate_to_info`. -- **Metadata‑Merge**: Plugin‑Metadaten können TMDB übersteuern, TMDB dient als Fallback. +## Fehlerverhalten +- Importfehler pro Plugin werden isoliert behandelt. +- Fehler in einem Plugin sollen das Addon nicht stoppen. +- User bekommt kurze Fehlermeldungen in Kodi. -## Fehlerbehandlung -- Plugin‑Importfehler werden isoliert behandelt, damit das Addon nicht komplett ausfällt. -- Netzwerk‑Fehler werden in Plugins abgefangen, `default.py` sollte nur saubere Fehlermeldungen weitergeben. - -## Debugging -- Globale Debug‑Settings werden über `addon/resources/settings.xml` gesteuert. -- Plugins loggen URLs/HTML optional (siehe jeweilige Plugin‑Doku). - -## Änderungen & Erweiterungen -Für neue Aktionen: -1. Neue Aktion im Router registrieren. -2. UI‑Einträge passend anlegen. -3. Entsprechende Plugin‑Methode definieren oder erweitern. - -## Hinweis zur Erstellung -Teile dieser Dokumentation wurden KI‑gestützt erstellt und bei Bedarf manuell angepasst. +## Erweiterung +Fuer neue Aktion im Router: +1. Action im `run()` Handler registrieren. +2. ListItem mit passenden Parametern bauen. +3. Zielmethode im Plugin bereitstellen. diff --git a/docs/PLUGIN_DEVELOPMENT.md b/docs/PLUGIN_DEVELOPMENT.md index b2610ce..5133117 100644 --- a/docs/PLUGIN_DEVELOPMENT.md +++ b/docs/PLUGIN_DEVELOPMENT.md @@ -1,118 +1,85 @@ -# ViewIT – Entwicklerdoku Plugins (`addon/plugins/*_plugin.py`) +# ViewIT Plugin Entwicklung (`addon/plugins/*_plugin.py`) -Diese Doku beschreibt, wie Plugins im ViewIT‑Addon aufgebaut sind und wie neue Provider‑Integrationen entwickelt werden. +Diese Datei beschreibt den Vertrag zwischen Router und Plugins. ## Grundlagen -- Jedes Plugin ist eine einzelne Datei unter `addon/plugins/`. -- Dateinamen **ohne** `_`‑Prefix werden automatisch geladen. -- Jede Datei enthält eine Klasse, die von `BasisPlugin` erbt. -- Optional: `Plugin = ` als expliziter Einstiegspunkt (bevorzugt vom Loader). +- Ein Plugin ist eine Python Datei in `addon/plugins/`. +- Dateien mit `_` Prefix werden nicht geladen. +- Plugin Klasse erbt von `BasisPlugin`. +- Optional: `Plugin = ` als klarer Einstiegspunkt. -## Pflicht‑Methoden (BasisPlugin) -Jedes Plugin muss diese Methoden implementieren: +## Pflichtmethoden +Jedes Plugin implementiert: - `async search_titles(query: str) -> list[str]` - `seasons_for(title: str) -> list[str]` - `episodes_for(title: str, season: str) -> list[str]` -## Vertrag Plugin ↔ Hauptlogik (`default.py`) -Die Hauptlogik ruft Plugin-Methoden auf und verarbeitet ausschließlich deren Rückgaben. +## Wichtige optionale Methoden +- `stream_link_for(...)` +- `resolve_stream_link(...)` +- `metadata_for(...)` +- `available_hosters_for(...)` +- `series_url_for_title(...)` +- `remember_series_url(...)` +- `episode_url_for(...)` +- `available_hosters_for_url(...)` +- `stream_link_for_url(...)` -Wesentliche Rückgaben an die Hauptlogik: -- `search_titles(...)` → Liste von Titel-Strings für die Trefferliste -- `seasons_for(...)` → Liste von Staffel-Labels -- `episodes_for(...)` → Liste von Episoden-Labels -- `stream_link_for(...)` → Hoster-/Player-Link (nicht zwingend finale Media-URL) -- `resolve_stream_link(...)` → finale/spielbare URL nach Redirect/Resolver -- `metadata_for(...)` → Info-Labels/Art (Plot/Poster) aus der Quelle -- Optional `available_hosters_for(...)` → auswählbare Hoster-Namen im Dialog -- Optional `series_url_for_title(...)` → stabile Detail-URL pro Titel für Folgeaufrufe -- Optional `remember_series_url(...)` → Übernahme einer bereits bekannten Detail-URL +## Film Provider Standard +Wenn keine echten Staffeln existieren: +- `seasons_for(title)` gibt `['Film']` +- `episodes_for(title, 'Film')` gibt `['Stream']` -Standard für Film-Provider (ohne echte Staffeln): -- `seasons_for(title)` gibt `["Film"]` zurück -- `episodes_for(title, "Film")` gibt `["Stream"]` zurück +## Capabilities +Ein Plugin kann Features melden ueber `capabilities()`. +Bekannte Werte: +- `popular_series` +- `genres` +- `latest_episodes` +- `new_titles` +- `alpha` +- `series_catalog` -## Optionale Features (Capabilities) -Über `capabilities()` kann das Plugin zusätzliche Funktionen anbieten: -- `popular_series` → `popular_series()` -- `genres` → `genres()` + `titles_for_genre(genre)` -- `latest_episodes` → `latest_episodes(page=1)` -- `new_titles` → `new_titles_page(page=1)` -- `alpha` → `alpha_index()` + `titles_for_alpha_page(letter, page)` -- `series_catalog` → `series_catalog_page(page=1)` +## Suche +Aktuelle Regeln fuer Suchtreffer: +- Match auf Titel +- Wortbasiert +- Keine Teilwort Treffer im selben Wort +- Beschreibungen nicht fuer Match nutzen -## Empfohlene Struktur -- Konstanten für URLs/Endpoints (BASE_URL, Pfade, Templates) -- `requests` + `bs4` optional (fehlt beides, Plugin sollte sauber deaktivieren) -- Helper‑Funktionen für Parsing und Normalisierung -- Caches für Such‑, Staffel‑ und Episoden‑Daten +## Settings +Pro Plugin meist `*_base_url`. +Beispiele: +- `serienstream_base_url` +- `aniworld_base_url` +- `einschalten_base_url` +- `topstream_base_url` +- `filmpalast_base_url` +- `doku_streams_base_url` -## Suche (aktuelle Policy) -- **Nur Titel‑Matches** -- **Wortbasierter Match** nach Normalisierung (Lowercase + Nicht‑Alnum → Leerzeichen) -- Keine Teilwort-Treffer innerhalb eines Wortes (Beispiel: `hund` matcht nicht `thunder`) -- Keine Beschreibung/Plot/Meta für Matches +## Playback Flow +1. Episode oder Film auswaehlen. +2. Optional Hosterliste anzeigen. +3. `stream_link_for` oder `stream_link_for_url` aufrufen. +4. `resolve_stream_link` aufrufen. +5. Finale URL an Kodi geben. -## Namensgebung -- Plugin‑Klassenname: `XxxPlugin` -- Anzeigename (Property `name`): **mit Großbuchstaben beginnen** (z. B. `Serienstream`, `Einschalten`) - -## Settings pro Plugin -Standard: `*_base_url` (Domain / BASE_URL) -- Beispiele: - - `serienstream_base_url` - - `aniworld_base_url` - - `einschalten_base_url` - - `topstream_base_url` - - `filmpalast_base_url` - - `doku_streams_base_url` - -## Playback -- `stream_link_for(...)` implementieren (liefert bevorzugten Hoster-Link). -- `available_hosters_for(...)` bereitstellen, wenn die Seite mehrere Hoster anbietet. -- `resolve_stream_link(...)` nach einheitlichem Flow umsetzen: - 1. Redirects auflösen (falls vorhanden) - 2. ResolveURL (`resolveurl_backend.resolve`) versuchen - 3. Bei Fehlschlag auf den besten verfügbaren Link zurückfallen -- Optional `set_preferred_hosters(...)` unterstützen, damit die Hoster-Auswahl aus der Hauptlogik direkt greift. - -## Standard‑Flow (empfohlen) -1. **Suche**: nur Titel liefern und Titel→Detail-URL mappen. -2. **Navigation**: `series_url_for_title`/`remember_series_url` unterstützen, damit URLs zwischen Aufrufen stabil bleiben. -3. **Auswahl Hoster**: Hoster-Namen aus der Detailseite extrahieren und anbieten. -4. **Playback**: Hoster-Link liefern, danach konsistent über `resolve_stream_link` finalisieren. -5. **Metadaten**: `metadata_for` nutzen, Plot/Poster aus der Quelle zurückgeben. -6. **Fallbacks**: bei Layout-Unterschieden defensiv parsen und Logging aktivierbar halten. - -## Debugging -Global gesteuert über Settings: -- `debug_log_urls` -- `debug_dump_html` -- `debug_show_url_info` - -Plugins sollten die Helper aus `addon/plugin_helpers.py` nutzen: +## Logging +Nutze Helper aus `addon/plugin_helpers.py`: - `log_url(...)` - `dump_response_html(...)` - `notify_url(...)` -## Template -`addon/plugins/_template_plugin.py` dient als Startpunkt für neue Provider. +## Build und Checks +- ZIP: `./scripts/build_kodi_zip.sh` +- Addon Ordner: `./scripts/build_install_addon.sh` +- Manifest: `python3 scripts/generate_plugin_manifest.py` +- Snapshot Checks: `python3 qa/run_plugin_snapshots.py` -## Build & Test -- ZIP bauen: `./scripts/build_kodi_zip.sh` -- Addon‑Ordner: `./scripts/build_install_addon.sh` -- Plugin‑Manifest aktualisieren: `python3 scripts/generate_plugin_manifest.py` -- Live-Snapshot-Checks: `python3 qa/run_plugin_snapshots.py` (aktualisieren mit `--update`) - -## Beispiel‑Checkliste -- [ ] `name` korrekt gesetzt -- [ ] `*_base_url` in Settings vorhanden -- [ ] Suche matcht nur Titel und wortbasiert -- [ ] `stream_link_for` + `resolve_stream_link` folgen dem Standard-Flow -- [ ] Optional: `available_hosters_for` + `set_preferred_hosters` vorhanden -- [ ] Optional: `series_url_for_title` + `remember_series_url` vorhanden -- [ ] Fehlerbehandlung und Timeouts vorhanden -- [ ] Optional: Caches für Performance - -## Hinweis zur Erstellung -Teile dieser Dokumentation wurden KI‑gestützt erstellt und bei Bedarf manuell angepasst. +## Kurze Checkliste +- `name` gesetzt und korrekt +- `*_base_url` in Settings vorhanden +- Suche liefert nur passende Titel +- Playback Methoden vorhanden +- Fehler und Timeouts behandelt +- Cache nur da, wo er Zeit spart diff --git a/docs/PLUGIN_SYSTEM.md b/docs/PLUGIN_SYSTEM.md index bbfa99b..37394ac 100644 --- a/docs/PLUGIN_SYSTEM.md +++ b/docs/PLUGIN_SYSTEM.md @@ -1,115 +1,71 @@ -## ViewIt Plugin-System +# ViewIT Plugin System -Dieses Dokument beschreibt, wie das Plugin-System von **ViewIt** funktioniert und wie die Community neue Integrationen hinzufügen kann. +Dieses Dokument beschreibt Laden, Vertrag und Betrieb der Plugins. -### Überblick +## Ueberblick +Der Router laedt Provider Integrationen aus `addon/plugins/*.py`. +Aktive Plugins werden instanziiert und im UI genutzt. -ViewIt lädt Provider-Integrationen dynamisch aus `addon/plugins/*.py`. Jede Datei enthält eine Klasse, die von `BasisPlugin` erbt. Beim Start werden alle Plugins instanziiert und nur aktiv genutzt, wenn sie verfügbar sind. +Relevante Dateien: +- `addon/default.py` +- `addon/plugin_interface.py` +- `docs/DEFAULT_ROUTER.md` +- `docs/PLUGIN_DEVELOPMENT.md` -Weitere Details: -- `docs/DEFAULT_ROUTER.md` (Hauptlogik in `addon/default.py`) -- `docs/PLUGIN_DEVELOPMENT.md` (Entwicklerdoku für Plugins) -- `docs/PLUGIN_MANIFEST.json` (zentraler Überblick über Plugins, Versionen, Capabilities) +## Aktuelle Plugins +- `serienstream_plugin.py` +- `topstreamfilm_plugin.py` +- `einschalten_plugin.py` +- `aniworld_plugin.py` +- `filmpalast_plugin.py` +- `dokustreams_plugin.py` +- `_template_plugin.py` (Vorlage) -### Aktuelle Plugins +## Discovery Ablauf +In `addon/default.py`: +1. Finde `*.py` in `addon/plugins/` +2. Ueberspringe Dateien mit `_` Prefix +3. Importiere Modul +4. Nutze `Plugin = `, falls vorhanden +5. Sonst instanziiere `BasisPlugin` Subklassen deterministisch +6. Ueberspringe Plugins mit `is_available = False` -- `serienstream_plugin.py` – Serienstream (s.to) -- `topstreamfilm_plugin.py` – Topstreamfilm -- `einschalten_plugin.py` – Einschalten -- `aniworld_plugin.py` – Aniworld -- `filmpalast_plugin.py` – Filmpalast -- `dokustreams_plugin.py` – Doku-Streams -- `_template_plugin.py` – Vorlage für neue Plugins +## Basis Interface +`BasisPlugin` definiert den Kern: +- `search_titles` +- `seasons_for` +- `episodes_for` -### Plugin-Discovery (Ladeprozess) +Weitere Methoden sind optional und werden nur genutzt, wenn vorhanden. -Der Loader in `addon/default.py`: +## Capabilities +Plugins koennen Features aktiv melden. +Typische Werte: +- `popular_series` +- `genres` +- `latest_episodes` +- `new_titles` +- `alpha` +- `series_catalog` -1. Sucht alle `*.py` in `addon/plugins/` -2. Überspringt Dateien, die mit `_` beginnen -3. Lädt Module dynamisch -4. Nutzt `Plugin = ` als bevorzugten Einstiegspunkt (falls vorhanden) -5. Fallback: instanziert Klassen, die von `BasisPlugin` erben (deterministisch sortiert) -6. Ignoriert Plugins mit `is_available = False` +Das UI zeigt nur Menues fuer aktiv gemeldete Features. -Damit bleiben fehlerhafte Plugins isoliert und blockieren nicht das gesamte Add-on. +## Metadaten Quelle +`prefer_source_metadata = True` bedeutet: +- Quelle zuerst +- TMDB nur Fallback -### Plugin-Manifest (Audit & Repro) -`docs/PLUGIN_MANIFEST.json` listet alle Plugins mit Version, Capabilities und Basis-Settings. -Erzeugung: `python3 scripts/generate_plugin_manifest.py` +## Stabilitaet +- Keine Netz Calls im Import Block. +- Fehler im Plugin muessen lokal behandelt werden. +- Ein defektes Plugin darf andere Plugins nicht blockieren. -### BasisPlugin – verpflichtende Methoden +## Build +Kodi ZIP bauen: -Definiert in `addon/plugin_interface.py`: - -- `async search_titles(query: str) -> list[str]` -- `seasons_for(title: str) -> list[str]` -- `episodes_for(title: str, season: str) -> list[str]` -- optional `metadata_for(title: str) -> (info_labels, art, cast)` - -### Optionale Features (Capabilities) - -Plugins können zusätzliche Features anbieten: - -- `capabilities() -> set[str]` -- `popular_series`: liefert beliebte Serien -- `genres`: Genre-Liste verfügbar -- `latest_episodes`: neue Episoden verfügbar -- `new_titles`: neue Titel verfügbar -- `alpha`: A-Z Index verfügbar -- `series_catalog`: Serienkatalog verfügbar -- `popular_series() -> list[str]` -- `genres() -> list[str]` -- `titles_for_genre(genre: str) -> list[str]` -- `latest_episodes(page: int = 1) -> list[LatestEpisode]` (wenn angeboten) -- `new_titles_page(page: int = 1) -> list[str]` (wenn angeboten) -- `alpha_index() -> list[str]` (wenn angeboten) -- `series_catalog_page(page: int = 1) -> list[str]` (wenn angeboten) - -Metadaten: -- `prefer_source_metadata = True` bedeutet: Plugin-Metadaten gehen vor TMDB, TMDB dient nur als Fallback. - -ViewIt zeigt im UI nur die Features an, die ein Plugin tatsächlich liefert. - -### Plugin-Struktur (empfohlen) - -Eine Integration sollte typischerweise bieten: - -- Konstante `BASE_URL` -- `search_titles()` mit Provider-Suche -- `seasons_for()` und `episodes_for()` mit HTML-Parsing -- `stream_link_for()` optional für direkte Playback-Links -- `metadata_for()` optional für Plot/Poster aus der Quelle -- Optional: `available_hosters_for()` oder Provider-spezifische Helfer - -Als Startpunkt dient `addon/plugins/_template_plugin.py`. - -### Community-Erweiterungen (Workflow) - -1. Fork/Branch erstellen -2. Neue Datei unter `addon/plugins/` hinzufügen (z. B. `meinprovider_plugin.py`) -3. Klasse erstellen, die `BasisPlugin` implementiert -4. In Kodi testen (ZIP bauen, installieren) -5. PR öffnen - -### Qualitätsrichtlinien - -- Keine Netzwerkzugriffe im Import-Top-Level -- Netzwerkzugriffe nur in Methoden (z. B. `search_titles`) -- Fehler sauber abfangen und verständliche Fehlermeldungen liefern -- Kein globaler Zustand, der über Instanzen hinweg überrascht -- Provider-spezifische Parser in Helper-Funktionen kapseln -- Reproduzierbare Reihenfolge: `Plugin`-Alias nutzen oder Klassenname eindeutig halten - -### Debugging & Logs - -Hilfreiche Logs werden nach `userdata/addon_data/plugin.video.viewit/logs/` geschrieben. -Provider sollten URL-Logging optional halten (Settings). - -### ZIP-Build - -``` +```bash ./scripts/build_kodi_zip.sh ``` -Das ZIP liegt anschließend unter `dist/plugin.video.viewit-.zip`. +Ergebnis: +`dist/plugin.video.viewit-.zip`