Add configurable update source and update-only version display
This commit is contained in:
129
addon/default.py
129
addon/default.py
@@ -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":
|
||||||
|
|||||||
@@ -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]:
|
||||||
|
|||||||
@@ -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] = {}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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] = {}
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user