diff --git a/CHANGELOG-DEV.md b/CHANGELOG-DEV.md index cd7e51f..6362991 100644 --- a/CHANGELOG-DEV.md +++ b/CHANGELOG-DEV.md @@ -1,3 +1,7 @@ +## 0.1.91.0-dev - 2026-04-10 + +- dev: bump to 0.1.90.5 pre-commit Hook synchronisiert Changelog-Version mit ZIP-Version + ## 0.1.90.5-dev - 2026-04-10 - dev: bump to 0.1.90.0 Changelog und Release-Prozess fix diff --git a/addon/CHANGELOG-USER.md b/addon/CHANGELOG-USER.md index 2345758..a6b6e53 100644 --- a/addon/CHANGELOG-USER.md +++ b/addon/CHANGELOG-USER.md @@ -1,8 +1,15 @@ -## 0.1.91.0 +## 0.1.91.5 + +**TMDb Helper Integration** +- SerienStream-Player: Staffel- und Episodennummer werden jetzt korrekt an ViewIT übergeben +- Direkte Episodenwiedergabe über TMDb Helper funktioniert jetzt zuverlässig + +**Autoplay / Hoster** +- Bevorzugter Hoster wird jetzt korrekt ausgelesen und angewendet +- Gesperrte Hoster: Einstellungen → Tools → „Gesperrte Hoster konfigurieren" – ausgewählte Hoster werden bei der Wiedergabe übersprungen **Allgemein** - Changelog und Release-Prozess fix. -- Changelog aktualisiert. ## 0.1.90.0 diff --git a/addon/addon.xml b/addon/addon.xml index 9be76b0..ff9661b 100644 --- a/addon/addon.xml +++ b/addon/addon.xml @@ -1,5 +1,5 @@ - + diff --git a/addon/default.py b/addon/default.py index 2a28b71..034db56 100644 --- a/addon/default.py +++ b/addon/default.py @@ -119,6 +119,7 @@ from metadata_utils import ( from tmdb import TmdbCastMember, TmdbExternalIds, fetch_external_ids, fetch_tv_episode_credits, lookup_movie, lookup_tv_season, lookup_tv_season_summary, lookup_tv_show from core.metadata import _resolve_tmdb_api_key from core.router import Router +from hosters import ALL_HOSTERS, normalize_hoster_name _router = Router() @@ -618,18 +619,69 @@ def _get_setting_int(setting_id: str, *, default: int = 0) -> int: return default -_PREFERRED_HOSTERS_LIST = [ - "voe", "streamtape", "doodstream", "vidoza", "mixdrop", "supervideo", "dropload", -] +_PREFERRED_HOSTERS_LIST = [h["id"] for h in ALL_HOSTERS] def _get_preferred_hoster() -> str: """Liest preferred_hoster (enum-Index) und gibt den Hosternamen zurueck.""" raw = _get_setting_string("preferred_hoster").strip() + raw_int = _get_setting_int("preferred_hoster", default=-1) + _log(f"preferred_hoster raw_string='{raw}' raw_int={raw_int}", xbmc.LOGINFO) + # getSettingString liefert bei enum den Display-Wert (z.B. "voe"), nicht den Index. + # Prüfe ob raw bereits ein bekannter Hoster-Name ist. + if raw.casefold() in [h.casefold() for h in _PREFERRED_HOSTERS_LIST]: + return raw + # Fallback: Index-basiert über getSettingInt + if raw_int >= 0: + try: + return _PREFERRED_HOSTERS_LIST[raw_int] + except IndexError: + pass + # Fallback: Index aus String try: return _PREFERRED_HOSTERS_LIST[int(raw)] except (ValueError, IndexError): - return raw # Fallback: Textwert direkt verwenden + return raw + + +def _get_blocked_hosters() -> set[str]: + """Gibt die Menge der gesperrten Hoster-IDs zurück.""" + raw = _get_setting_string("blocked_hosters").strip() + if not raw: + return set() + return {h.strip().casefold() for h in raw.split(",") if h.strip()} + + +def _filter_hosters(hosters: list[str], blocked: set[str]) -> list[str]: + """Entfernt gesperrte Hoster aus der Liste. Unbekannte Hoster bleiben erhalten.""" + if not blocked: + return hosters + result = [] + for h in hosters: + hid = normalize_hoster_name(h) + if hid and hid.casefold() in blocked: + _log(f"Hoster '{h}' ist gesperrt und wird entfernt.", xbmc.LOGDEBUG) + continue + result.append(h) + return result + + +def _configure_blocked_hosters_dialog() -> None: + """Multi-Select-Dialog zum Sperren/Entsperren von Hostern.""" + blocked = _get_blocked_hosters() + labels = [h["display"] for h in ALL_HOSTERS] + preselected = [i for i, h in enumerate(ALL_HOSTERS) if h["id"].casefold() in blocked] + + result = xbmcgui.Dialog().multiselect("Gesperrte Hoster auswaehlen", labels, preselect=preselected) + if result is None: + return # Abgebrochen + + blocked_ids = sorted(ALL_HOSTERS[i]["id"] for i in result) + _set_setting_string("blocked_hosters", ",".join(blocked_ids)) + count = len(blocked_ids) + msg = f"{count} Hoster gesperrt" if count else "Keine Hoster gesperrt" + xbmcgui.Dialog().notification("Hoster-Sperrung", msg, xbmcgui.NOTIFICATION_INFO, 3000) + _log(f"Gesperrte Hoster gespeichert: {blocked_ids}", xbmc.LOGINFO) def _metadata_policy( @@ -1258,7 +1310,28 @@ RESOLVEURL_ADDON_ID = "script.module.resolveurl" RESOLVEURL_AUTO_INSTALL_INTERVAL_SEC = 6 * 60 * 60 YTDLP_ADDON_ID = "script.module.yt-dlp" TMDB_HELPER_ADDON_ID = "plugin.video.themoviedb.helper" -TMDB_HELPER_PLAYERS_DIR = Path.home() / ".kodi" / "addons" / TMDB_HELPER_ADDON_ID / "resources" / "players" + + +def _kodi_userdata_dir() -> Path: + """Ermittelt das Kodi-Userdata-Verzeichnis plattformübergreifend.""" + import platform + system = platform.system() + if system == "Windows": + appdata = os.environ.get("APPDATA") or os.environ.get("LOCALAPPDATA") or "" + if appdata: + return Path(appdata) / "Kodi" / "userdata" + if system == "Darwin": + return Path.home() / "Library" / "Application Support" / "Kodi" / "userdata" + # Linux, LibreELEC, OSMC, Flatpak-Fallback → ~/.kodi/userdata + flatpak = Path.home() / ".var" / "app" / "tv.kodi.Kodi" / "data" / "userdata" + if flatpak.exists(): + return flatpak + return Path.home() / ".kodi" / "userdata" + + +def _tmdb_helper_players_dir() -> Path: + """Gibt das players/-Verzeichnis im TMDb-Helper-Addon-Data-Ordner zurück.""" + return _kodi_userdata_dir() / "addon_data" / TMDB_HELPER_ADDON_ID / "players" def _selected_update_channel() -> int: @@ -1768,10 +1841,11 @@ def _install_tmdb_helper_players() -> None: if not source_files: xbmcgui.Dialog().notification("TMDb Helper", "Keine Player-Dateien gefunden.", xbmcgui.NOTIFICATION_WARNING, 4000) return - TMDB_HELPER_PLAYERS_DIR.mkdir(parents=True, exist_ok=True) + dest_dir = _tmdb_helper_players_dir() + dest_dir.mkdir(parents=True, exist_ok=True) import shutil for src in source_files: - shutil.copy2(src, TMDB_HELPER_PLAYERS_DIR / src.name) + shutil.copy2(src, dest_dir / src.name) _log(f"TMDb Helper Player installiert: {[f.name for f in source_files]}") xbmcgui.Dialog().notification("TMDb Helper", f"{len(source_files)} Player installiert.", xbmcgui.NOTIFICATION_INFO, 3000) except Exception as exc: @@ -4543,6 +4617,9 @@ def _play_episode( forced_hoster = (forced_hoster or "").strip() autoplay = _get_setting_bool("autoplay_enabled", default=False) preferred = _get_preferred_hoster() + blocked = _get_blocked_hosters() + available_hosters = _filter_hosters(available_hosters, blocked) + _log(f"Autoplay={autoplay}, preferred='{preferred}', blocked={blocked}, hosters={available_hosters}", xbmc.LOGINFO) if available_hosters: if forced_hoster: for hoster in available_hosters: @@ -4671,6 +4748,9 @@ def _play_episode_url( selected_hoster: str | None = None autoplay = _get_setting_bool("autoplay_enabled", default=False) preferred = _get_preferred_hoster() + blocked = _get_blocked_hosters() + available_hosters = _filter_hosters(available_hosters, blocked) + _log(f"Autoplay={autoplay}, preferred='{preferred}', blocked={blocked}, hosters={available_hosters}", xbmc.LOGINFO) if available_hosters: if autoplay and preferred: pref_lower = preferred.casefold() @@ -5518,6 +5598,11 @@ def _route_install_tmdb_helper_players(params: dict[str, str]) -> None: _install_tmdb_helper_players() +@_router.route("configure_blocked_hosters") +def _route_configure_blocked_hosters(params: dict[str, str]) -> None: + _configure_blocked_hosters_dialog() + + @_router.route("choose_source") def _route_choose_source(params: dict[str, str]) -> None: _show_choose_source(params.get("title", ""), params.get("plugins", "")) diff --git a/addon/hosters.py b/addon/hosters.py new file mode 100644 index 0000000..813b358 --- /dev/null +++ b/addon/hosters.py @@ -0,0 +1,33 @@ +"""Zentrale Hoster-Definitionen für ViewIT.""" + +ALL_HOSTERS: list[dict] = [ + {"id": "voe", "display": "VOE", "aliases": ["voe", "voe.sx"]}, + {"id": "streamtape", "display": "Streamtape", "aliases": ["streamtape", "streamtape.com"]}, + {"id": "doodstream", "display": "Doodstream", "aliases": ["doodstream", "dood", "ds2play", "playmogo"]}, + {"id": "vidoza", "display": "Vidoza", "aliases": ["vidoza", "vidoza.net"]}, + {"id": "mixdrop", "display": "Mixdrop", "aliases": ["mixdrop", "mixdrop.co"]}, + {"id": "supervideo", "display": "Supervideo", "aliases": ["supervideo", "supervideo.tv"]}, + {"id": "dropload", "display": "Dropload", "aliases": ["dropload", "dropload.io"]}, +] + + +def get_hoster_ids() -> list[str]: + return [h["id"] for h in ALL_HOSTERS] + + +def get_display_names() -> list[str]: + return [h["display"] for h in ALL_HOSTERS] + + +def normalize_hoster_name(name: str) -> str | None: + """Wandelt Display-Name oder Alias in Hoster-ID um. Gibt None zurück wenn unbekannt.""" + key = (name or "").casefold().strip() + for h in ALL_HOSTERS: + if key == h["id"].casefold(): + return h["id"] + if any(key == a.casefold() for a in h["aliases"]): + return h["id"] + # Substring-Fallback: "Voe Stream" → "voe" + if key and h["id"].casefold() in key: + return h["id"] + return None diff --git a/addon/icon.png b/addon/icon.png index 9e65f73..64e1550 100644 Binary files a/addon/icon.png and b/addon/icon.png differ diff --git a/addon/resources/players/viewit_serienstream.json b/addon/resources/players/viewit_serienstream.json index dfc1dcc..d189f5f 100644 --- a/addon/resources/players/viewit_serienstream.json +++ b/addon/resources/players/viewit_serienstream.json @@ -7,15 +7,9 @@ "assert" : { "play_movie": ["title"], "play_episode": ["showname", "season", "episode"], - "search_movie": ["title"], - "search_episode": ["showname"] + "search_movie": ["title"] }, - "play_movie" : "plugin://plugin.video.viewit/?action=play_direct&plugin=Serienstream&type=movie&query={title_url}", - "play_episode" : "plugin://plugin.video.viewit/?action=play_direct&plugin=Serienstream&type=episode&query={showname_url}&season={season}&episode={episode}", - "search_movie" : [ - "plugin://plugin.video.viewit/?action=plugin_search&plugin=Serienstream&query={title_url}" - ], - "search_episode" : [ - "plugin://plugin.video.viewit/?action=plugin_search&plugin=Serienstream&query={showname_url}" - ] + "play_movie" : "plugin://plugin.video.viewit/?action=play_direct&plugin=Serienstream&type=movie&query={title}", + "play_episode" : "plugin://plugin.video.viewit/?action=play_direct&plugin=Serienstream&type=episode&query={showname}&season={season}&episode={episode}", + "search_movie" : "plugin://plugin.video.viewit/?action=plugin_search&plugin=Serienstream&query={title}" } diff --git a/addon/resources/settings.xml b/addon/resources/settings.xml index 3ccb24b..bb9ceae 100644 --- a/addon/resources/settings.xml +++ b/addon/resources/settings.xml @@ -142,6 +142,9 @@ + + +