dev: bump to 0.1.66 and harden resolveurl + serienstream

This commit is contained in:
2026-02-25 16:35:16 +01:00
parent 74d15cb25e
commit 73f07d20b4
20 changed files with 522 additions and 232 deletions

View File

@@ -109,7 +109,7 @@ except ImportError: # pragma: no cover - allow importing outside Kodi (e.g. lin
from plugin_interface import BasisPlugin
from http_session_pool import close_all_sessions
from plugin_helpers import normalize_resolved_stream_url
from plugin_helpers import normalize_resolved_stream_url, show_error, show_notification
from metadata_utils import (
collect_plugin_metadata as _collect_plugin_metadata,
merge_metadata as _merge_metadata,
@@ -3510,39 +3510,31 @@ def _apply_update_channel(*, silent: bool = False) -> bool:
if not silent:
if not applied:
warning_icon = getattr(xbmcgui, "NOTIFICATION_WARNING", xbmcgui.NOTIFICATION_INFO)
xbmcgui.Dialog().notification(
show_notification(
"Updates",
"Kanal gespeichert, aber repository.viewit nicht gefunden.",
warning_icon,
5000,
icon=warning_icon,
milliseconds=5000,
)
elif target_version == "-":
xbmcgui.Dialog().notification(
"Updates",
"Kanal angewendet, aber keine Version im Kanal gefunden.",
xbmcgui.NOTIFICATION_ERROR,
5000,
)
show_error("Updates", "Kanal angewendet, aber keine Version im Kanal gefunden.", milliseconds=5000)
elif not install_result:
xbmcgui.Dialog().notification(
show_error(
"Updates",
f"Kanal angewendet, Installation von {target_version} fehlgeschlagen.",
xbmcgui.NOTIFICATION_ERROR,
5000,
milliseconds=5000,
)
elif target_version == installed_version:
xbmcgui.Dialog().notification(
show_notification(
"Updates",
f"Kanal angewendet: {_channel_label(_selected_update_channel())} ({target_version} bereits installiert)",
xbmcgui.NOTIFICATION_INFO,
4500,
milliseconds=4500,
)
else:
xbmcgui.Dialog().notification(
show_notification(
"Updates",
f"Kanal angewendet: {_channel_label(_selected_update_channel())} -> {target_version} installiert",
xbmcgui.NOTIFICATION_INFO,
5000,
milliseconds=5000,
)
_sync_update_version_settings()
return applied and install_result
@@ -3559,14 +3551,11 @@ def _run_update_check(*, silent: bool = False) -> None:
if callable(builtin):
builtin("ActivateWindow(addonbrowser,addons://updates/)")
if not silent:
xbmcgui.Dialog().notification("Updates", "Update-Check gestartet.", xbmcgui.NOTIFICATION_INFO, 4000)
show_notification("Updates", "Update-Check gestartet.", milliseconds=4000)
except Exception as exc:
_log(f"Update-Pruefung fehlgeschlagen: {exc}", xbmc.LOGWARNING)
if not silent:
try:
xbmcgui.Dialog().notification("Updates", "Update-Check fehlgeschlagen.", xbmcgui.NOTIFICATION_ERROR, 4000)
except Exception:
pass
show_error("Updates", "Update-Check fehlgeschlagen.", milliseconds=4000)
def _show_version_selector() -> None:
@@ -3579,7 +3568,7 @@ def _show_version_selector() -> None:
versions = _filter_versions_for_channel(channel, _fetch_repo_versions(info_url))
if not versions:
xbmcgui.Dialog().notification("Updates", "Keine Versionen im Repo gefunden.", xbmcgui.NOTIFICATION_ERROR, 4000)
show_error("Updates", "Keine Versionen im Repo gefunden.", milliseconds=4000)
return
installed = _get_setting_string("update_installed_version").strip() or "-"
@@ -3617,13 +3606,13 @@ def _show_version_selector() -> None:
if not confirmed:
return
xbmcgui.Dialog().notification("Updates", f"Installation gestartet: {version}", xbmcgui.NOTIFICATION_INFO, 2500)
show_notification("Updates", f"Installation gestartet: {version}", milliseconds=2500)
ok = _install_addon_version(info_url, version)
if ok:
_sync_update_version_settings()
xbmcgui.Dialog().notification("Updates", f"Version {version} installiert.", xbmcgui.NOTIFICATION_INFO, 4000)
show_notification("Updates", f"Version {version} installiert.", milliseconds=4000)
else:
xbmcgui.Dialog().notification("Updates", f"Installation von {version} fehlgeschlagen.", xbmcgui.NOTIFICATION_ERROR, 4500)
show_error("Updates", f"Installation von {version} fehlgeschlagen.", milliseconds=4500)
def _maybe_run_auto_update_check(action: str | None) -> None:
@@ -3677,6 +3666,58 @@ def _is_resolveurl_missing_error(message: str) -> bool:
return str(message or "").strip().casefold() == "resolveurl missing"
def _looks_like_unresolved_hoster_link(url: str) -> bool:
raw = (url or "").strip()
if not raw:
return False
media_url = raw.split("|", 1)[0].strip()
try:
parsed = urlparse(media_url)
except Exception:
return False
host = (parsed.netloc or "").casefold()
path = (parsed.path or "").casefold()
if parsed.scheme not in {"http", "https"} or not host:
return False
known_hoster_domains = (
"voe.sx",
"supervideo.",
"doodstream.",
"vidnest.",
"vidara.",
"filemoon.",
"streamtape.",
"vidmoly.",
"veev.",
"strmup.",
)
if not any(domain in host for domain in known_hoster_domains):
return False
return path.startswith(("/e/", "/v/", "/d/", "/embed"))
def _resolve_unresolved_hoster_link(url: str, *, source_url: str) -> tuple[str, str]:
candidate = (url or "").strip()
if not _looks_like_unresolved_hoster_link(candidate):
return candidate, ""
_log(f"ResolveURL dispatch: {candidate}", xbmc.LOGDEBUG)
try:
from resolveurl_backend import resolve as resolve_with_resolveurl # type: ignore
except Exception:
resolve_with_resolveurl = None
if callable(resolve_with_resolveurl):
try:
resolved = resolve_with_resolveurl(candidate)
except Exception:
resolved = None
if resolved:
_log(f"ResolveURL output: {resolved}", xbmc.LOGDEBUG)
return normalize_resolved_stream_url(resolved, source_url=source_url or candidate), ""
err = _resolveurl_last_error()
_log(f"ResolveURL output: <none> ({err})", xbmc.LOGDEBUG)
return candidate, err
def _play_final_link(
link: str,
*,
@@ -3818,23 +3859,30 @@ def _play_episode(
err = _resolveurl_last_error()
if _is_cloudflare_challenge_error(err):
_log(f"ResolveURL Cloudflare-Challenge: {err}", xbmc.LOGWARNING)
xbmcgui.Dialog().notification(
show_notification(
"Wiedergabe",
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
xbmcgui.NOTIFICATION_INFO,
4500,
milliseconds=4500,
)
return
final_link = resolved_link or link
final_link = normalize_resolved_stream_url(final_link, source_url=link)
final_link, resolve_err = _resolve_unresolved_hoster_link(final_link, source_url=link)
if _looks_like_unresolved_hoster_link(final_link):
err = (resolve_err or _resolveurl_last_error()).strip()
if _is_resolveurl_missing_error(err):
show_error("Wiedergabe", "ResolveURL fehlt oder ist nicht geladen.", milliseconds=4500)
else:
show_error("Wiedergabe", "Hoster-Link konnte nicht aufgeloest werden.", milliseconds=4500)
_log(f"Hoster-Link blieb unaufgeloest: {final_link} (error={err})", xbmc.LOGWARNING)
return
err = _resolveurl_last_error()
if _is_cloudflare_challenge_error(err) and final_link.strip() == link.strip():
_log(f"ResolveURL Cloudflare-Challenge (unresolved): {err}", xbmc.LOGWARNING)
xbmcgui.Dialog().notification(
show_notification(
"Wiedergabe",
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
xbmcgui.NOTIFICATION_INFO,
4500,
milliseconds=4500,
)
return
finally:
@@ -3933,23 +3981,30 @@ def _play_episode_url(
err = _resolveurl_last_error()
if _is_cloudflare_challenge_error(err):
_log(f"ResolveURL Cloudflare-Challenge: {err}", xbmc.LOGWARNING)
xbmcgui.Dialog().notification(
show_notification(
"Wiedergabe",
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
xbmcgui.NOTIFICATION_INFO,
4500,
milliseconds=4500,
)
return
final_link = resolved_link or link
final_link = normalize_resolved_stream_url(final_link, source_url=link)
final_link, resolve_err = _resolve_unresolved_hoster_link(final_link, source_url=link)
if _looks_like_unresolved_hoster_link(final_link):
err = (resolve_err or _resolveurl_last_error()).strip()
if _is_resolveurl_missing_error(err):
show_error("Wiedergabe", "ResolveURL fehlt oder ist nicht geladen.", milliseconds=4500)
else:
show_error("Wiedergabe", "Hoster-Link konnte nicht aufgeloest werden.", milliseconds=4500)
_log(f"Hoster-Link blieb unaufgeloest: {final_link} (error={err})", xbmc.LOGWARNING)
return
err = _resolveurl_last_error()
if _is_cloudflare_challenge_error(err) and final_link.strip() == link.strip():
_log(f"ResolveURL Cloudflare-Challenge (unresolved): {err}", xbmc.LOGWARNING)
xbmcgui.Dialog().notification(
show_notification(
"Wiedergabe",
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
xbmcgui.NOTIFICATION_INFO,
4500,
milliseconds=4500,
)
return
finally: