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 @@
+
+
+
+
+
+
+
+
+
+
+