Add configurable update source and update-only version display

This commit is contained in:
2026-02-05 13:15:58 +01:00
parent 4c3f90233d
commit 9aedbee083
8 changed files with 144 additions and 2 deletions

View File

@@ -16,6 +16,7 @@ import json
import os import os
import re import re
import sys import sys
import xml.etree.ElementTree as ET
from pathlib import Path from pathlib import Path
from types import ModuleType from types import ModuleType
from urllib.parse import parse_qs, urlencode from urllib.parse import parse_qs, urlencode
@@ -401,6 +402,27 @@ def _get_setting_bool(setting_id: str, *, default: bool = False) -> bool:
return default 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: 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.""" """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) 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: def _show_root_menu() -> None:
handle = _get_handle() handle = _get_handle()
_log("Root-Menue wird angezeigt.") _log("Root-Menue wird angezeigt.")
@@ -890,8 +990,7 @@ def _show_root_menu() -> None:
plugins = _discover_plugins() plugins = _discover_plugins()
for plugin_name in sorted(plugins.keys(), key=lambda value: value.casefold()): for plugin_name in sorted(plugins.keys(), key=lambda value: value.casefold()):
display = f"{plugin_name}" _add_directory_item(handle, plugin_name, "plugin_menu", {"plugin": plugin_name}, is_folder=True)
_add_directory_item(handle, display, "plugin_menu", {"plugin": plugin_name}, is_folder=True)
_add_directory_item(handle, "Einstellungen", "settings") _add_directory_item(handle, "Einstellungen", "settings")
xbmcplugin.endOfDirectory(handle) xbmcplugin.endOfDirectory(handle)
@@ -2245,10 +2344,34 @@ def _open_settings() -> None:
"""Oeffnet das Kodi-Addon-Settings-Dialog.""" """Oeffnet das Kodi-Addon-Settings-Dialog."""
if xbmcaddon is None: # pragma: no cover - outside Kodi if xbmcaddon is None: # pragma: no cover - outside Kodi
raise RuntimeError("xbmcaddon ist nicht verfuegbar (KodiStub).") raise RuntimeError("xbmcaddon ist nicht verfuegbar (KodiStub).")
_sync_update_version_settings()
addon = xbmcaddon.Addon() addon = xbmcaddon.Addon()
addon.openSettings() 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: def _extract_first_int(value: str) -> int | None:
match = re.search(r"(\d+)", value or "") match = re.search(r"(\d+)", value or "")
if not match: if not match:
@@ -2599,6 +2722,8 @@ def run() -> None:
) )
elif action == "settings": elif action == "settings":
_open_settings() _open_settings()
elif action == "check_updates":
_run_update_check()
elif action == "seasons": elif action == "seasons":
_show_seasons(params.get("plugin", ""), params.get("title", ""), params.get("series_url", "")) _show_seasons(params.get("plugin", ""), params.get("title", ""), params.get("series_url", ""))
elif action == "episodes": elif action == "episodes":

View File

@@ -11,6 +11,7 @@ class BasisPlugin(ABC):
"""Abstrakte Basisklasse fuer alle Integrationen.""" """Abstrakte Basisklasse fuer alle Integrationen."""
name: str name: str
version: str = "0.0.0"
@abstractmethod @abstractmethod
async def search_titles(self, query: str) -> List[str]: async def search_titles(self, query: str) -> List[str]:

View File

@@ -691,6 +691,7 @@ def search_animes(query: str) -> List[SeriesResult]:
class AniworldPlugin(BasisPlugin): class AniworldPlugin(BasisPlugin):
name = "Aniworld" name = "Aniworld"
version = "1.0.0"
def __init__(self) -> None: def __init__(self) -> None:
self._anime_results: Dict[str, SeriesResult] = {} self._anime_results: Dict[str, SeriesResult] = {}

View File

@@ -507,6 +507,7 @@ class EinschaltenPlugin(BasisPlugin):
"""Metadata-Plugin für eine autorisierte Quelle.""" """Metadata-Plugin für eine autorisierte Quelle."""
name = "Einschalten" name = "Einschalten"
version = "1.0.0"
def __init__(self) -> None: def __init__(self) -> None:
self.is_available = REQUESTS_AVAILABLE self.is_available = REQUESTS_AVAILABLE

View File

@@ -219,6 +219,7 @@ def _get_soup(url: str, *, session: Optional[RequestsSession] = None) -> Beautif
class FilmpalastPlugin(BasisPlugin): class FilmpalastPlugin(BasisPlugin):
name = "Filmpalast" name = "Filmpalast"
version = "1.0.0"
def __init__(self) -> None: def __init__(self) -> None:
self._title_to_url: Dict[str, str] = {} self._title_to_url: Dict[str, str] = {}

View File

@@ -784,6 +784,7 @@ class SerienstreamPlugin(BasisPlugin):
"""Downloader-Plugin, das Serien von s.to ueber requests/bs4 bereitstellt.""" """Downloader-Plugin, das Serien von s.to ueber requests/bs4 bereitstellt."""
name = "Serienstream" name = "Serienstream"
version = "1.0.0"
POPULAR_GENRE_LABEL = "⭐ Beliebte Serien" POPULAR_GENRE_LABEL = "⭐ Beliebte Serien"
def __init__(self) -> None: def __init__(self) -> None:

View File

@@ -123,6 +123,7 @@ class TopstreamfilmPlugin(BasisPlugin):
"""Integration fuer eine HTML-basierte Suchseite.""" """Integration fuer eine HTML-basierte Suchseite."""
name = "Topstreamfilm" name = "Topstreamfilm"
version = "1.0.0"
def __init__(self) -> None: def __init__(self) -> None:
self._session: RequestsSession | None = None self._session: RequestsSession | None = None

View File

@@ -61,4 +61,15 @@
<setting id="tmdb_log_requests" type="bool" label="TMDB API Requests loggen" default="false" /> <setting id="tmdb_log_requests" type="bool" label="TMDB API Requests loggen" default="false" />
<setting id="tmdb_log_responses" type="bool" label="TMDB API Antworten loggen" default="false" /> <setting id="tmdb_log_responses" type="bool" label="TMDB API Antworten loggen" default="false" />
</category> </category>
<category label="Update">
<setting id="update_repo_url" type="text" label="Update-URL (addons.xml)" default="http://127.0.0.1:8080/repo/addons.xml" />
<setting id="run_update_check" type="action" label="Jetzt auf Updates pruefen" action="RunPlugin(plugin://plugin.video.viewit/?action=check_updates)" option="close" />
<setting id="update_info" type="text" label="Kodi-Repository-Updates werden ueber den Kodi-Update-Mechanismus verarbeitet." default="" enable="false" />
<setting id="update_version_addon" type="text" label="ViewIT Addon Version" default="-" enable="false" />
<setting id="update_version_serienstream" type="text" label="Serienstream Plugin Version" default="-" enable="false" />
<setting id="update_version_aniworld" type="text" label="Aniworld Plugin Version" default="-" enable="false" />
<setting id="update_version_einschalten" type="text" label="Einschalten Plugin Version" default="-" enable="false" />
<setting id="update_version_topstreamfilm" type="text" label="Topstreamfilm Plugin Version" default="-" enable="false" />
<setting id="update_version_filmpalast" type="text" label="Filmpalast Plugin Version" default="-" enable="false" />
</category>
</settings> </settings>