diff --git a/addon/default.py b/addon/default.py index de87cb8..0584aac 100644 --- a/addon/default.py +++ b/addon/default.py @@ -16,6 +16,7 @@ import json import os import re import sys +import xml.etree.ElementTree as ET from pathlib import Path from types import ModuleType from urllib.parse import parse_qs, urlencode @@ -401,6 +402,27 @@ def _get_setting_bool(setting_id: str, *, default: bool = False) -> bool: return default +def _set_setting_string(setting_id: str, value: str) -> None: + if xbmcaddon is None: + return + addon = _get_addon() + if addon is None: + return + setter = getattr(addon, "setSettingString", None) + if callable(setter): + try: + setter(setting_id, str(value)) + return + except TypeError: + return + setter = getattr(addon, "setSetting", None) + if callable(setter): + try: + setter(setting_id, str(value)) + except TypeError: + return + + def _apply_video_info(item, info_labels: dict[str, object] | None, cast: list[TmdbCastMember] | None) -> None: """Setzt Metadaten bevorzugt via InfoTagVideo (Kodi v20+), mit Fallback auf deprecated APIs.""" @@ -883,6 +905,84 @@ def _add_directory_item( xbmcplugin.addDirectoryItem(handle=handle, url=url, listitem=item, isFolder=is_folder) +def _plugin_version(plugin: BasisPlugin) -> str: + raw = getattr(plugin, "version", "0.0.0") + text = str(raw or "").strip() + return text or "0.0.0" + + +def _normalize_update_info_url(raw: str) -> str: + value = str(raw or "").strip() + default = "http://127.0.0.1:8080/repo/addons.xml" + if not value: + return default + if value.endswith("/addons.xml"): + return value + return value.rstrip("/") + "/addons.xml" + + +def _repo_addon_xml_path() -> str: + if xbmcvfs is None: + return "" + try: + return xbmcvfs.translatePath("special://home/addons/repository.viewit/addon.xml") + except Exception: + return "" + + +def _update_repository_source(info_url: str) -> bool: + path = _repo_addon_xml_path() + if not path: + return False + if not os.path.exists(path): + return False + try: + tree = ET.parse(path) + root = tree.getroot() + dir_node = root.find(".//dir") + if dir_node is None: + return False + info = dir_node.find("info") + checksum = dir_node.find("checksum") + datadir = dir_node.find("datadir") + if info is None or checksum is None or datadir is None: + return False + base = info_url[: -len("/addons.xml")] if info_url.endswith("/addons.xml") else info_url.rstrip("/") + info.text = info_url + checksum.text = f"{base}/addons.xml.md5" + datadir.text = f"{base}/" + tree.write(path, encoding="utf-8", xml_declaration=True) + return True + except Exception as exc: + _log(f"Repository-URL konnte nicht gesetzt werden: {exc}", xbmc.LOGWARNING) + return False + + +def _sync_update_version_settings() -> None: + addon = _get_addon() + addon_version = "0.0.0" + if addon is not None: + try: + addon_version = str(addon.getAddonInfo("version") or "0.0.0") + except Exception: + addon_version = "0.0.0" + _set_setting_string("update_version_addon", addon_version) + + versions = { + "update_version_serienstream": "-", + "update_version_aniworld": "-", + "update_version_einschalten": "-", + "update_version_topstreamfilm": "-", + "update_version_filmpalast": "-", + } + for plugin in _discover_plugins().values(): + key = f"update_version_{str(plugin.name).strip().lower()}" + if key in versions: + versions[key] = _plugin_version(plugin) + for key, value in versions.items(): + _set_setting_string(key, value) + + def _show_root_menu() -> None: handle = _get_handle() _log("Root-Menue wird angezeigt.") @@ -890,8 +990,7 @@ def _show_root_menu() -> None: plugins = _discover_plugins() for plugin_name in sorted(plugins.keys(), key=lambda value: value.casefold()): - display = f"{plugin_name}" - _add_directory_item(handle, display, "plugin_menu", {"plugin": plugin_name}, is_folder=True) + _add_directory_item(handle, plugin_name, "plugin_menu", {"plugin": plugin_name}, is_folder=True) _add_directory_item(handle, "Einstellungen", "settings") xbmcplugin.endOfDirectory(handle) @@ -2245,10 +2344,34 @@ def _open_settings() -> None: """Oeffnet das Kodi-Addon-Settings-Dialog.""" if xbmcaddon is None: # pragma: no cover - outside Kodi raise RuntimeError("xbmcaddon ist nicht verfuegbar (KodiStub).") + _sync_update_version_settings() addon = xbmcaddon.Addon() addon.openSettings() +def _run_update_check() -> None: + """Stoesst Kodi-Repo- und Addon-Updates an und informiert den Benutzer.""" + if xbmc is None: # pragma: no cover - outside Kodi + return + try: + info_url = _normalize_update_info_url(_get_setting_string("update_repo_url")) + _set_setting_string("update_repo_url", info_url) + _sync_update_version_settings() + _update_repository_source(info_url) + builtin = getattr(xbmc, "executebuiltin", None) + if callable(builtin): + builtin("UpdateAddonRepos") + builtin("UpdateLocalAddons") + builtin("ActivateWindow(addonbrowser,addons://updates/)") + xbmcgui.Dialog().notification("ViewIT Update", "Update-Pruefung gestartet.", xbmcgui.NOTIFICATION_INFO, 4000) + except Exception as exc: + _log(f"Update-Pruefung fehlgeschlagen: {exc}", xbmc.LOGWARNING) + try: + xbmcgui.Dialog().notification("ViewIT Update", "Update-Pruefung fehlgeschlagen.", xbmcgui.NOTIFICATION_ERROR, 4000) + except Exception: + pass + + def _extract_first_int(value: str) -> int | None: match = re.search(r"(\d+)", value or "") if not match: @@ -2599,6 +2722,8 @@ def run() -> None: ) elif action == "settings": _open_settings() + elif action == "check_updates": + _run_update_check() elif action == "seasons": _show_seasons(params.get("plugin", ""), params.get("title", ""), params.get("series_url", "")) elif action == "episodes": diff --git a/addon/plugin_interface.py b/addon/plugin_interface.py index a8b5b37..f8c266d 100644 --- a/addon/plugin_interface.py +++ b/addon/plugin_interface.py @@ -11,6 +11,7 @@ class BasisPlugin(ABC): """Abstrakte Basisklasse fuer alle Integrationen.""" name: str + version: str = "0.0.0" @abstractmethod async def search_titles(self, query: str) -> List[str]: diff --git a/addon/plugins/aniworld_plugin.py b/addon/plugins/aniworld_plugin.py index 9749478..7271a7a 100644 --- a/addon/plugins/aniworld_plugin.py +++ b/addon/plugins/aniworld_plugin.py @@ -691,6 +691,7 @@ def search_animes(query: str) -> List[SeriesResult]: class AniworldPlugin(BasisPlugin): name = "Aniworld" + version = "1.0.0" def __init__(self) -> None: self._anime_results: Dict[str, SeriesResult] = {} diff --git a/addon/plugins/einschalten_plugin.py b/addon/plugins/einschalten_plugin.py index 32f1562..62f35b5 100644 --- a/addon/plugins/einschalten_plugin.py +++ b/addon/plugins/einschalten_plugin.py @@ -507,6 +507,7 @@ class EinschaltenPlugin(BasisPlugin): """Metadata-Plugin für eine autorisierte Quelle.""" name = "Einschalten" + version = "1.0.0" def __init__(self) -> None: self.is_available = REQUESTS_AVAILABLE diff --git a/addon/plugins/filmpalast_plugin.py b/addon/plugins/filmpalast_plugin.py index 958907f..19097ee 100644 --- a/addon/plugins/filmpalast_plugin.py +++ b/addon/plugins/filmpalast_plugin.py @@ -219,6 +219,7 @@ def _get_soup(url: str, *, session: Optional[RequestsSession] = None) -> Beautif class FilmpalastPlugin(BasisPlugin): name = "Filmpalast" + version = "1.0.0" def __init__(self) -> None: self._title_to_url: Dict[str, str] = {} diff --git a/addon/plugins/serienstream_plugin.py b/addon/plugins/serienstream_plugin.py index c8dfa13..d2f67f3 100644 --- a/addon/plugins/serienstream_plugin.py +++ b/addon/plugins/serienstream_plugin.py @@ -784,6 +784,7 @@ class SerienstreamPlugin(BasisPlugin): """Downloader-Plugin, das Serien von s.to ueber requests/bs4 bereitstellt.""" name = "Serienstream" + version = "1.0.0" POPULAR_GENRE_LABEL = "⭐ Beliebte Serien" def __init__(self) -> None: diff --git a/addon/plugins/topstreamfilm_plugin.py b/addon/plugins/topstreamfilm_plugin.py index 469dff3..97c9e4b 100644 --- a/addon/plugins/topstreamfilm_plugin.py +++ b/addon/plugins/topstreamfilm_plugin.py @@ -123,6 +123,7 @@ class TopstreamfilmPlugin(BasisPlugin): """Integration fuer eine HTML-basierte Suchseite.""" name = "Topstreamfilm" + version = "1.0.0" def __init__(self) -> None: self._session: RequestsSession | None = None diff --git a/addon/resources/settings.xml b/addon/resources/settings.xml index 1dc863a..90fa06b 100644 --- a/addon/resources/settings.xml +++ b/addon/resources/settings.xml @@ -61,4 +61,15 @@ + + + + + + + + + + +