dev: bump to 0.1.74-dev – BurningSeries entfernt, Paging-Fix Neuste Titel
This commit is contained in:
@@ -1,3 +1,7 @@
|
|||||||
|
## 0.1.73-dev - 2026-03-07
|
||||||
|
|
||||||
|
- dev: bump to 0.1.73-dev – Autoplay-Setting, Moflix Hoster-Dialog, Update-Hinweis im Hauptmenue
|
||||||
|
|
||||||
# Changelog (Dev)
|
# Changelog (Dev)
|
||||||
|
|
||||||
## 0.1.71-dev - 2026-03-01
|
## 0.1.71-dev - 2026-03-01
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<addon id="plugin.video.viewit" name="ViewIt" version="0.1.73-dev" provider-name="ViewIt">
|
<addon id="plugin.video.viewit" name="ViewIt" version="0.1.74-dev" provider-name="ViewIt">
|
||||||
<requires>
|
<requires>
|
||||||
<import addon="xbmc.python" version="3.0.0" />
|
<import addon="xbmc.python" version="3.0.0" />
|
||||||
<import addon="script.module.requests" />
|
<import addon="script.module.requests" />
|
||||||
|
|||||||
259
addon/default.py
259
addon/default.py
@@ -1203,6 +1203,7 @@ def _normalize_update_info_url(raw: str) -> str:
|
|||||||
UPDATE_CHANNEL_MAIN = 0
|
UPDATE_CHANNEL_MAIN = 0
|
||||||
UPDATE_CHANNEL_NIGHTLY = 1
|
UPDATE_CHANNEL_NIGHTLY = 1
|
||||||
UPDATE_CHANNEL_CUSTOM = 2
|
UPDATE_CHANNEL_CUSTOM = 2
|
||||||
|
UPDATE_CHANNEL_DEV = 3
|
||||||
_AUTO_UPDATE_INTERVALS = [1 * 60 * 60, 6 * 60 * 60, 24 * 60 * 60] # 1h, 6h, 24h
|
_AUTO_UPDATE_INTERVALS = [1 * 60 * 60, 6 * 60 * 60, 24 * 60 * 60] # 1h, 6h, 24h
|
||||||
UPDATE_HTTP_TIMEOUT_SEC = 8
|
UPDATE_HTTP_TIMEOUT_SEC = 8
|
||||||
UPDATE_ADDON_ID = "plugin.video.viewit"
|
UPDATE_ADDON_ID = "plugin.video.viewit"
|
||||||
@@ -1212,7 +1213,7 @@ RESOLVEURL_AUTO_INSTALL_INTERVAL_SEC = 6 * 60 * 60
|
|||||||
|
|
||||||
def _selected_update_channel() -> int:
|
def _selected_update_channel() -> int:
|
||||||
channel = _get_setting_int("update_channel", default=UPDATE_CHANNEL_MAIN)
|
channel = _get_setting_int("update_channel", default=UPDATE_CHANNEL_MAIN)
|
||||||
if channel not in {UPDATE_CHANNEL_MAIN, UPDATE_CHANNEL_NIGHTLY, UPDATE_CHANNEL_CUSTOM}:
|
if channel not in {UPDATE_CHANNEL_MAIN, UPDATE_CHANNEL_NIGHTLY, UPDATE_CHANNEL_CUSTOM, UPDATE_CHANNEL_DEV}:
|
||||||
return UPDATE_CHANNEL_MAIN
|
return UPDATE_CHANNEL_MAIN
|
||||||
return channel
|
return channel
|
||||||
|
|
||||||
@@ -1222,6 +1223,8 @@ def _channel_label(channel: int) -> str:
|
|||||||
return "Nightly"
|
return "Nightly"
|
||||||
if channel == UPDATE_CHANNEL_CUSTOM:
|
if channel == UPDATE_CHANNEL_CUSTOM:
|
||||||
return "Custom"
|
return "Custom"
|
||||||
|
if channel == UPDATE_CHANNEL_DEV:
|
||||||
|
return "Dev"
|
||||||
return "Main"
|
return "Main"
|
||||||
|
|
||||||
|
|
||||||
@@ -1246,11 +1249,17 @@ def _is_nightly_version(version: str) -> bool:
|
|||||||
return bool(re.match(r"^\d+\.\d+\.\d+-nightly$", str(version or "").strip()))
|
return bool(re.match(r"^\d+\.\d+\.\d+-nightly$", str(version or "").strip()))
|
||||||
|
|
||||||
|
|
||||||
|
def _is_dev_version(version: str) -> bool:
|
||||||
|
return bool(re.match(r"^\d+\.\d+\.\d+-dev$", str(version or "").strip()))
|
||||||
|
|
||||||
|
|
||||||
def _filter_versions_for_channel(channel: int, versions: list[str]) -> list[str]:
|
def _filter_versions_for_channel(channel: int, versions: list[str]) -> list[str]:
|
||||||
if channel == UPDATE_CHANNEL_MAIN:
|
if channel == UPDATE_CHANNEL_MAIN:
|
||||||
return [v for v in versions if _is_stable_version(v)]
|
return [v for v in versions if _is_stable_version(v)]
|
||||||
if channel == UPDATE_CHANNEL_NIGHTLY:
|
if channel == UPDATE_CHANNEL_NIGHTLY:
|
||||||
return [v for v in versions if _is_nightly_version(v)]
|
return [v for v in versions if _is_nightly_version(v)]
|
||||||
|
if channel == UPDATE_CHANNEL_DEV:
|
||||||
|
return [v for v in versions if _is_dev_version(v)]
|
||||||
return list(versions)
|
return list(versions)
|
||||||
|
|
||||||
|
|
||||||
@@ -1260,6 +1269,8 @@ def _resolve_update_info_url() -> str:
|
|||||||
raw = _get_setting_string("update_repo_url_nightly")
|
raw = _get_setting_string("update_repo_url_nightly")
|
||||||
elif channel == UPDATE_CHANNEL_CUSTOM:
|
elif channel == UPDATE_CHANNEL_CUSTOM:
|
||||||
raw = _get_setting_string("update_repo_url")
|
raw = _get_setting_string("update_repo_url")
|
||||||
|
elif channel == UPDATE_CHANNEL_DEV:
|
||||||
|
raw = _get_setting_string("update_repo_url_dev")
|
||||||
else:
|
else:
|
||||||
raw = _get_setting_string("update_repo_url_main")
|
raw = _get_setting_string("update_repo_url_main")
|
||||||
return _normalize_update_info_url(raw)
|
return _normalize_update_info_url(raw)
|
||||||
@@ -1661,24 +1672,6 @@ def _show_root_menu() -> None:
|
|||||||
for plugin_name in sorted(plugins.keys(), key=lambda value: value.casefold()):
|
for plugin_name in sorted(plugins.keys(), key=lambda value: value.casefold()):
|
||||||
_add_directory_item(handle, plugin_name, "plugin_menu", {"plugin": plugin_name}, is_folder=True)
|
_add_directory_item(handle, plugin_name, "plugin_menu", {"plugin": plugin_name}, is_folder=True)
|
||||||
|
|
||||||
# Katalog-Caches im Hintergrund vorwaermen (fire-and-forget)
|
|
||||||
for _pname, _plugin in plugins.items():
|
|
||||||
_warmer = getattr(_plugin, "warm_catalog_cache", None)
|
|
||||||
if callable(_warmer):
|
|
||||||
def _warm_and_notify(_fn=_warmer, _name=_pname):
|
|
||||||
try:
|
|
||||||
loaded = _fn()
|
|
||||||
except Exception:
|
|
||||||
loaded = False
|
|
||||||
if loaded:
|
|
||||||
# executebuiltin ist thread-sicher; xbmcgui.Dialog() darf nicht
|
|
||||||
# aus Daemon-Threads aufgerufen werden (Kodi-Absturzgefahr).
|
|
||||||
safe_name = _name.replace('"', "")
|
|
||||||
xbmc.executebuiltin(
|
|
||||||
f'Notification("{safe_name}", "Suchindex geladen", 3000, "")'
|
|
||||||
)
|
|
||||||
threading.Thread(target=_warm_and_notify, daemon=True, name=f"viewit-warmup-{_pname}").start()
|
|
||||||
|
|
||||||
# Trakt-Menue (nur wenn aktiviert)
|
# Trakt-Menue (nur wenn aktiviert)
|
||||||
if _get_setting_bool("trakt_enabled", default=False):
|
if _get_setting_bool("trakt_enabled", default=False):
|
||||||
if _trakt_load_token():
|
if _trakt_load_token():
|
||||||
@@ -1704,6 +1697,7 @@ def _show_plugin_menu(plugin_name: str) -> None:
|
|||||||
|
|
||||||
xbmcplugin.setPluginCategory(handle, plugin_name)
|
xbmcplugin.setPluginCategory(handle, plugin_name)
|
||||||
|
|
||||||
|
if callable(getattr(plugin, "search_titles", None)):
|
||||||
_add_directory_item(handle, "Suche", "plugin_search", {"plugin": plugin_name}, is_folder=True)
|
_add_directory_item(handle, "Suche", "plugin_search", {"plugin": plugin_name}, is_folder=True)
|
||||||
|
|
||||||
if _plugin_has_capability(plugin, "new_titles") or _plugin_has_capability(plugin, "latest_episodes"):
|
if _plugin_has_capability(plugin, "new_titles") or _plugin_has_capability(plugin, "latest_episodes"):
|
||||||
@@ -2527,34 +2521,6 @@ def _show_episodes(plugin_name: str, title: str, season: str, series_url: str =
|
|||||||
xbmcplugin.endOfDirectory(handle)
|
xbmcplugin.endOfDirectory(handle)
|
||||||
|
|
||||||
|
|
||||||
def _show_genre_sources() -> None:
|
|
||||||
handle = _get_handle()
|
|
||||||
_log("Genre-Quellen laden.")
|
|
||||||
plugins = _discover_plugins()
|
|
||||||
sources: list[tuple[str, BasisPlugin]] = []
|
|
||||||
for plugin_name, plugin in plugins.items():
|
|
||||||
if plugin.__class__.genres is BasisPlugin.genres:
|
|
||||||
continue
|
|
||||||
if plugin.__class__.titles_for_genre is BasisPlugin.titles_for_genre:
|
|
||||||
continue
|
|
||||||
sources.append((plugin_name, plugin))
|
|
||||||
|
|
||||||
if not sources:
|
|
||||||
xbmcgui.Dialog().notification("Genres", "Keine Quellen mit Genres gefunden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
|
||||||
xbmcplugin.endOfDirectory(handle)
|
|
||||||
return
|
|
||||||
|
|
||||||
for plugin_name, plugin in sources:
|
|
||||||
_add_directory_item(
|
|
||||||
handle,
|
|
||||||
f"Genres [{plugin_name}]",
|
|
||||||
"genres",
|
|
||||||
{"plugin": plugin_name},
|
|
||||||
is_folder=True,
|
|
||||||
)
|
|
||||||
xbmcplugin.endOfDirectory(handle)
|
|
||||||
|
|
||||||
|
|
||||||
def _show_genres(plugin_name: str) -> None:
|
def _show_genres(plugin_name: str) -> None:
|
||||||
handle = _get_handle()
|
handle = _get_handle()
|
||||||
_log(f"Genres laden: {plugin_name}")
|
_log(f"Genres laden: {plugin_name}")
|
||||||
@@ -2596,44 +2562,6 @@ def _show_genres(plugin_name: str) -> None:
|
|||||||
xbmcplugin.endOfDirectory(handle)
|
xbmcplugin.endOfDirectory(handle)
|
||||||
|
|
||||||
|
|
||||||
def _show_categories(plugin_name: str) -> None:
|
|
||||||
handle = _get_handle()
|
|
||||||
_log(f"Kategorien laden: {plugin_name}")
|
|
||||||
plugin = _discover_plugins().get(plugin_name)
|
|
||||||
if plugin is None:
|
|
||||||
xbmcgui.Dialog().notification("Kategorien", "Quelle nicht gefunden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
|
||||||
xbmcplugin.endOfDirectory(handle)
|
|
||||||
return
|
|
||||||
getter = getattr(plugin, "categories", None)
|
|
||||||
if not callable(getter):
|
|
||||||
xbmcgui.Dialog().notification("Kategorien", "Kategorien nicht verfuegbar.", xbmcgui.NOTIFICATION_INFO, 3000)
|
|
||||||
xbmcplugin.endOfDirectory(handle)
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
categories = _run_with_progress(
|
|
||||||
"Kategorien",
|
|
||||||
f"{plugin_name}: Kategorien werden geladen...",
|
|
||||||
lambda: list(getter() or []),
|
|
||||||
)
|
|
||||||
except Exception as exc:
|
|
||||||
_log(f"Kategorien konnten nicht geladen werden ({plugin_name}): {exc}", xbmc.LOGWARNING)
|
|
||||||
xbmcgui.Dialog().notification("Kategorien", "Kategorien konnten nicht geladen werden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
|
||||||
xbmcplugin.endOfDirectory(handle)
|
|
||||||
return
|
|
||||||
for category in categories:
|
|
||||||
category = str(category).strip()
|
|
||||||
if not category:
|
|
||||||
continue
|
|
||||||
_add_directory_item(
|
|
||||||
handle,
|
|
||||||
category,
|
|
||||||
"category_titles_page",
|
|
||||||
{"plugin": plugin_name, "category": category, "page": "1"},
|
|
||||||
is_folder=True,
|
|
||||||
)
|
|
||||||
xbmcplugin.endOfDirectory(handle)
|
|
||||||
|
|
||||||
|
|
||||||
def _show_paged_title_list(
|
def _show_paged_title_list(
|
||||||
plugin_name: str,
|
plugin_name: str,
|
||||||
filter_value: str,
|
filter_value: str,
|
||||||
@@ -2767,18 +2695,6 @@ def _show_paged_title_list(
|
|||||||
xbmcplugin.endOfDirectory(handle)
|
xbmcplugin.endOfDirectory(handle)
|
||||||
|
|
||||||
|
|
||||||
def _show_category_titles_page(plugin_name: str, category: str, page: int = 1) -> None:
|
|
||||||
_show_paged_title_list(
|
|
||||||
plugin_name, category, page,
|
|
||||||
dialog_label="Kategorien",
|
|
||||||
page_action="category_titles_page",
|
|
||||||
filter_param="category",
|
|
||||||
paging_method="titles_for_genre_page",
|
|
||||||
count_method="genre_page_count",
|
|
||||||
has_more_method="genre_has_more",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _show_genre_titles_page(plugin_name: str, genre: str, page: int = 1) -> None:
|
def _show_genre_titles_page(plugin_name: str, genre: str, page: int = 1) -> None:
|
||||||
_show_paged_title_list(
|
_show_paged_title_list(
|
||||||
plugin_name, genre, page,
|
plugin_name, genre, page,
|
||||||
@@ -3351,6 +3267,8 @@ def _show_new_titles(plugin_name: str, page: int = 1, *, action_name: str = "new
|
|||||||
show_next = bool(has_more_getter(page))
|
show_next = bool(has_more_getter(page))
|
||||||
except Exception:
|
except Exception:
|
||||||
show_next = False
|
show_next = False
|
||||||
|
elif callable(paging_getter) and page_items:
|
||||||
|
show_next = True
|
||||||
elif "total_pages" in locals():
|
elif "total_pages" in locals():
|
||||||
show_next = bool(total_pages > 1 and page < total_pages) # type: ignore[name-defined]
|
show_next = bool(total_pages > 1 and page < total_pages) # type: ignore[name-defined]
|
||||||
|
|
||||||
@@ -3445,7 +3363,12 @@ def _show_latest_episodes(plugin_name: str, page: int = 1) -> None:
|
|||||||
info_labels=info_labels,
|
info_labels=info_labels,
|
||||||
)
|
)
|
||||||
|
|
||||||
if entries:
|
has_more_fn = getattr(plugin, "latest_episodes_has_more", None)
|
||||||
|
if callable(has_more_fn):
|
||||||
|
show_next = bool(has_more_fn(page))
|
||||||
|
else:
|
||||||
|
show_next = False
|
||||||
|
if show_next:
|
||||||
_add_directory_item(handle, "Naechste Seite", "latest_titles",
|
_add_directory_item(handle, "Naechste Seite", "latest_titles",
|
||||||
{"plugin": plugin_name, "page": str(page + 1)}, is_folder=True)
|
{"plugin": plugin_name, "page": str(page + 1)}, is_folder=True)
|
||||||
|
|
||||||
@@ -3842,6 +3765,51 @@ def _is_resolveurl_missing_error(message: str) -> bool:
|
|||||||
return str(message or "").strip().casefold() == "resolveurl missing"
|
return str(message or "").strip().casefold() == "resolveurl missing"
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_stream_with_retry(plugin: BasisPlugin, link: str) -> str | None:
|
||||||
|
"""Löst einen Stream-Link auf mit ResolveURL-Auto-Install und CF-Check.
|
||||||
|
|
||||||
|
Gibt den finalen Link zurück oder None (mit Kodi-Notification bei Fehler).
|
||||||
|
"""
|
||||||
|
resolved_link = plugin.resolve_stream_link(link)
|
||||||
|
if not resolved_link:
|
||||||
|
err = _resolveurl_last_error()
|
||||||
|
if _is_resolveurl_missing_error(err):
|
||||||
|
_log("ResolveURL fehlt: versuche Auto-Installation.", xbmc.LOGWARNING)
|
||||||
|
_ensure_resolveurl_installed(force=True, silent=True)
|
||||||
|
resolved_link = plugin.resolve_stream_link(link)
|
||||||
|
err = _resolveurl_last_error()
|
||||||
|
if _is_cloudflare_challenge_error(err):
|
||||||
|
_log(f"ResolveURL Cloudflare-Challenge: {err}", xbmc.LOGWARNING)
|
||||||
|
xbmcgui.Dialog().notification(
|
||||||
|
"Wiedergabe",
|
||||||
|
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
|
||||||
|
xbmcgui.NOTIFICATION_INFO,
|
||||||
|
4500,
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
if not resolved_link:
|
||||||
|
_log("Stream konnte nicht aufgeloest werden.", xbmc.LOGWARNING)
|
||||||
|
xbmcgui.Dialog().notification(
|
||||||
|
"Wiedergabe",
|
||||||
|
"Stream konnte nicht aufgeloest werden.",
|
||||||
|
xbmcgui.NOTIFICATION_INFO,
|
||||||
|
3000,
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
final_link = normalize_resolved_stream_url(resolved_link, source_url=link)
|
||||||
|
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(
|
||||||
|
"Wiedergabe",
|
||||||
|
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
|
||||||
|
xbmcgui.NOTIFICATION_INFO,
|
||||||
|
4500,
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
return final_link
|
||||||
|
|
||||||
|
|
||||||
def _play_final_link(
|
def _play_final_link(
|
||||||
link: str,
|
link: str,
|
||||||
*,
|
*,
|
||||||
@@ -4086,43 +4054,8 @@ def _play_episode(
|
|||||||
xbmcgui.Dialog().notification("Wiedergabe", "Kein Stream gefunden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
xbmcgui.Dialog().notification("Wiedergabe", "Kein Stream gefunden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
||||||
return
|
return
|
||||||
_log(f"Stream-Link: {link}", xbmc.LOGDEBUG)
|
_log(f"Stream-Link: {link}", xbmc.LOGDEBUG)
|
||||||
resolved_link = plugin.resolve_stream_link(link)
|
final_link = _resolve_stream_with_retry(plugin, link)
|
||||||
if not resolved_link:
|
if not final_link:
|
||||||
err = _resolveurl_last_error()
|
|
||||||
if _is_resolveurl_missing_error(err):
|
|
||||||
_log("ResolveURL fehlt: versuche Auto-Installation.", xbmc.LOGWARNING)
|
|
||||||
_ensure_resolveurl_installed(force=True, silent=True)
|
|
||||||
resolved_link = plugin.resolve_stream_link(link)
|
|
||||||
err = _resolveurl_last_error()
|
|
||||||
if _is_cloudflare_challenge_error(err):
|
|
||||||
_log(f"ResolveURL Cloudflare-Challenge: {err}", xbmc.LOGWARNING)
|
|
||||||
xbmcgui.Dialog().notification(
|
|
||||||
"Wiedergabe",
|
|
||||||
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
|
|
||||||
xbmcgui.NOTIFICATION_INFO,
|
|
||||||
4500,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
if not resolved_link:
|
|
||||||
_log("Stream konnte nicht aufgeloest werden.", xbmc.LOGWARNING)
|
|
||||||
xbmcgui.Dialog().notification(
|
|
||||||
"Wiedergabe",
|
|
||||||
"Stream konnte nicht aufgeloest werden.",
|
|
||||||
xbmcgui.NOTIFICATION_INFO,
|
|
||||||
3000,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
final_link = resolved_link
|
|
||||||
final_link = normalize_resolved_stream_url(final_link, source_url=link)
|
|
||||||
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(
|
|
||||||
"Wiedergabe",
|
|
||||||
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
|
|
||||||
xbmcgui.NOTIFICATION_INFO,
|
|
||||||
4500,
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
finally:
|
finally:
|
||||||
if restore_hosters is not None and callable(preferred_setter):
|
if restore_hosters is not None and callable(preferred_setter):
|
||||||
@@ -4237,43 +4170,8 @@ def _play_episode_url(
|
|||||||
xbmcgui.Dialog().notification("Wiedergabe", "Kein Stream gefunden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
xbmcgui.Dialog().notification("Wiedergabe", "Kein Stream gefunden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
||||||
return
|
return
|
||||||
_log(f"Stream-Link: {link}", xbmc.LOGDEBUG)
|
_log(f"Stream-Link: {link}", xbmc.LOGDEBUG)
|
||||||
resolved_link = plugin.resolve_stream_link(link)
|
final_link = _resolve_stream_with_retry(plugin, link)
|
||||||
if not resolved_link:
|
if not final_link:
|
||||||
err = _resolveurl_last_error()
|
|
||||||
if _is_resolveurl_missing_error(err):
|
|
||||||
_log("ResolveURL fehlt: versuche Auto-Installation.", xbmc.LOGWARNING)
|
|
||||||
_ensure_resolveurl_installed(force=True, silent=True)
|
|
||||||
resolved_link = plugin.resolve_stream_link(link)
|
|
||||||
err = _resolveurl_last_error()
|
|
||||||
if _is_cloudflare_challenge_error(err):
|
|
||||||
_log(f"ResolveURL Cloudflare-Challenge: {err}", xbmc.LOGWARNING)
|
|
||||||
xbmcgui.Dialog().notification(
|
|
||||||
"Wiedergabe",
|
|
||||||
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
|
|
||||||
xbmcgui.NOTIFICATION_INFO,
|
|
||||||
4500,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
if not resolved_link:
|
|
||||||
_log("Stream konnte nicht aufgeloest werden.", xbmc.LOGWARNING)
|
|
||||||
xbmcgui.Dialog().notification(
|
|
||||||
"Wiedergabe",
|
|
||||||
"Stream konnte nicht aufgeloest werden.",
|
|
||||||
xbmcgui.NOTIFICATION_INFO,
|
|
||||||
3000,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
final_link = resolved_link
|
|
||||||
final_link = normalize_resolved_stream_url(final_link, source_url=link)
|
|
||||||
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(
|
|
||||||
"Wiedergabe",
|
|
||||||
"Hoster durch Cloudflare geschuetzt. Bitte spaeter erneut probieren.",
|
|
||||||
xbmcgui.NOTIFICATION_INFO,
|
|
||||||
4500,
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
finally:
|
finally:
|
||||||
if restore_hosters is not None and callable(preferred_setter):
|
if restore_hosters is not None and callable(preferred_setter):
|
||||||
@@ -4945,21 +4843,11 @@ def _route_plugin_search(params: dict[str, str]) -> None:
|
|||||||
_show_plugin_search(params.get("plugin", ""))
|
_show_plugin_search(params.get("plugin", ""))
|
||||||
|
|
||||||
|
|
||||||
@_router.route("genre_sources")
|
|
||||||
def _route_genre_sources(params: dict[str, str]) -> None:
|
|
||||||
_show_genre_sources()
|
|
||||||
|
|
||||||
|
|
||||||
@_router.route("genres")
|
@_router.route("genres")
|
||||||
def _route_genres(params: dict[str, str]) -> None:
|
def _route_genres(params: dict[str, str]) -> None:
|
||||||
_show_genres(params.get("plugin", ""))
|
_show_genres(params.get("plugin", ""))
|
||||||
|
|
||||||
|
|
||||||
@_router.route("categories")
|
|
||||||
def _route_categories(params: dict[str, str]) -> None:
|
|
||||||
_show_categories(params.get("plugin", ""))
|
|
||||||
|
|
||||||
|
|
||||||
@_router.route("latest_titles")
|
@_router.route("latest_titles")
|
||||||
def _route_latest_titles(params: dict[str, str]) -> None:
|
def _route_latest_titles(params: dict[str, str]) -> None:
|
||||||
_show_latest_titles(params.get("plugin", ""), _parse_positive_int(params.get("page", "1"), default=1))
|
_show_latest_titles(params.get("plugin", ""), _parse_positive_int(params.get("page", "1"), default=1))
|
||||||
@@ -4988,13 +4876,6 @@ def _route_genre_titles_page(params: dict[str, str]) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@_router.route("category_titles_page")
|
|
||||||
def _route_category_titles_page(params: dict[str, str]) -> None:
|
|
||||||
_show_category_titles_page(
|
|
||||||
params.get("plugin", ""), params.get("category", ""),
|
|
||||||
_parse_positive_int(params.get("page", "1"), default=1),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@_router.route("alpha_index")
|
@_router.route("alpha_index")
|
||||||
def _route_alpha_index(params: dict[str, str]) -> None:
|
def _route_alpha_index(params: dict[str, str]) -> None:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Vorgehen fuer ein neues Plugin:
|
|||||||
3. `search_titles`, `seasons_for`, `episodes_for` gemaess Zielseite implementieren
|
3. `search_titles`, `seasons_for`, `episodes_for` gemaess Zielseite implementieren
|
||||||
4. Optional weitere Methoden implementieren – capabilities deklarieren und Methoden ueberschreiben:
|
4. Optional weitere Methoden implementieren – capabilities deklarieren und Methoden ueberschreiben:
|
||||||
- `popular_series()` + capability 'popular_series'
|
- `popular_series()` + capability 'popular_series'
|
||||||
- `latest_titles(page)` + capability 'latest_titles'
|
- `new_titles()` + `new_titles_page(page)` + capability 'new_titles'
|
||||||
- `genres()` + `titles_for_genre(genre)` + `titles_for_genre_page(genre, page)`
|
- `genres()` + `titles_for_genre(genre)` + `titles_for_genre_page(genre, page)`
|
||||||
- `alpha_index()` + `titles_for_alpha_page(letter, page)`
|
- `alpha_index()` + `titles_for_alpha_page(letter, page)`
|
||||||
- `years_available()` + `titles_for_year(year, page)` + capability 'year_filter'
|
- `years_available()` + `titles_for_year(year, page)` + capability 'year_filter'
|
||||||
@@ -169,7 +169,7 @@ class TemplatePlugin(BasisPlugin):
|
|||||||
|
|
||||||
Bekannte Werte (aus plugin_interface.py):
|
Bekannte Werte (aus plugin_interface.py):
|
||||||
- 'popular_series' – Plugin hat beliebte Serien/Filme
|
- 'popular_series' – Plugin hat beliebte Serien/Filme
|
||||||
- 'latest_titles' – Plugin hat neu hinzugefuegte Titel
|
- 'new_titles' – Plugin hat neu hinzugefuegte Titel
|
||||||
- 'year_filter' – Plugin unterstuetzt Jahr-Filter
|
- 'year_filter' – Plugin unterstuetzt Jahr-Filter
|
||||||
- 'country_filter' – Plugin unterstuetzt Land-Filter
|
- 'country_filter' – Plugin unterstuetzt Land-Filter
|
||||||
- 'collections' – Plugin hat Sammlungen/Filmreihen
|
- 'collections' – Plugin hat Sammlungen/Filmreihen
|
||||||
|
|||||||
@@ -1025,7 +1025,7 @@ class AniworldPlugin(BasisPlugin):
|
|||||||
_session_cache_set(self._season_episodes_cache_name(season_url), payload)
|
_session_cache_set(self._season_episodes_cache_name(season_url), payload)
|
||||||
|
|
||||||
def capabilities(self) -> set[str]:
|
def capabilities(self) -> set[str]:
|
||||||
return {"popular_series", "genres", "latest_episodes", "latest_titles"}
|
return {"popular_series", "genres", "latest_episodes", "new_titles"}
|
||||||
|
|
||||||
def _find_series_by_title(self, title: str) -> Optional[SeriesResult]:
|
def _find_series_by_title(self, title: str) -> Optional[SeriesResult]:
|
||||||
title = (title or "").strip()
|
title = (title or "").strip()
|
||||||
@@ -1345,7 +1345,7 @@ class AniworldPlugin(BasisPlugin):
|
|||||||
except Exception:
|
except Exception:
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def latest_titles(self, page: int = 1) -> List[str]:
|
def new_titles_page(self, page: int = 1) -> List[str]:
|
||||||
"""Liefert neu hinzugefuegte Anime vom Animekalender."""
|
"""Liefert neu hinzugefuegte Anime vom Animekalender."""
|
||||||
if not self._requests_available:
|
if not self._requests_available:
|
||||||
return []
|
return []
|
||||||
@@ -1372,6 +1372,9 @@ class AniworldPlugin(BasisPlugin):
|
|||||||
except Exception:
|
except Exception:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
def new_titles(self) -> List[str]:
|
||||||
|
return self.new_titles_page(1)
|
||||||
|
|
||||||
def _season_label(self, number: int) -> str:
|
def _season_label(self, number: int) -> str:
|
||||||
return f"Staffel {number}"
|
return f"Staffel {number}"
|
||||||
|
|
||||||
|
|||||||
@@ -1019,7 +1019,7 @@ class EinschaltenPlugin(BasisPlugin):
|
|||||||
return resolve_via_resolveurl(link, fallback_to_link=True)
|
return resolve_via_resolveurl(link, fallback_to_link=True)
|
||||||
|
|
||||||
def capabilities(self) -> Set[str]:
|
def capabilities(self) -> Set[str]:
|
||||||
return {"new_titles", "genres", "popular_series", "latest_titles"}
|
return {"new_titles", "genres", "popular_series"}
|
||||||
|
|
||||||
def popular_series(self) -> List[str]:
|
def popular_series(self) -> List[str]:
|
||||||
"""Liefert die am besten bewerteten Filme (nach voteAverage sortiert)."""
|
"""Liefert die am besten bewerteten Filme (nach voteAverage sortiert)."""
|
||||||
@@ -1042,14 +1042,6 @@ class EinschaltenPlugin(BasisPlugin):
|
|||||||
titles.append(movie.title)
|
titles.append(movie.title)
|
||||||
return titles
|
return titles
|
||||||
|
|
||||||
def latest_titles(self, page: int = 1) -> List[str]:
|
|
||||||
"""Liefert neu hinzugefügte Filme (Alias zu new_titles_page)."""
|
|
||||||
if not REQUESTS_AVAILABLE:
|
|
||||||
return []
|
|
||||||
if not self._get_base_url():
|
|
||||||
return []
|
|
||||||
return self.new_titles_page(max(1, int(page or 1)))
|
|
||||||
|
|
||||||
def new_titles(self) -> List[str]:
|
def new_titles(self) -> List[str]:
|
||||||
if not REQUESTS_AVAILABLE:
|
if not REQUESTS_AVAILABLE:
|
||||||
return []
|
return []
|
||||||
|
|||||||
@@ -525,7 +525,7 @@ class FilmpalastPlugin(BasisPlugin):
|
|||||||
return max_page
|
return max_page
|
||||||
|
|
||||||
def capabilities(self) -> set[str]:
|
def capabilities(self) -> set[str]:
|
||||||
return {"genres", "alpha", "series_catalog", "popular_series", "latest_titles"}
|
return {"genres", "alpha", "series_catalog", "popular_series", "new_titles"}
|
||||||
|
|
||||||
def _parse_alpha_links(self, soup: BeautifulSoupT) -> Dict[str, str]:
|
def _parse_alpha_links(self, soup: BeautifulSoupT) -> Dict[str, str]:
|
||||||
alpha: Dict[str, str] = {}
|
alpha: Dict[str, str] = {}
|
||||||
@@ -1057,7 +1057,7 @@ class FilmpalastPlugin(BasisPlugin):
|
|||||||
except Exception:
|
except Exception:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def latest_titles(self, page: int = 1) -> List[str]:
|
def new_titles_page(self, page: int = 1) -> List[str]:
|
||||||
"""Liefert neu hinzugefuegte Titel von /movies/new."""
|
"""Liefert neu hinzugefuegte Titel von /movies/new."""
|
||||||
if not self._requests_available:
|
if not self._requests_available:
|
||||||
return []
|
return []
|
||||||
@@ -1071,6 +1071,9 @@ class FilmpalastPlugin(BasisPlugin):
|
|||||||
except Exception:
|
except Exception:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
def new_titles(self) -> List[str]:
|
||||||
|
return self.new_titles_page(1)
|
||||||
|
|
||||||
def resolve_stream_link(self, link: str) -> Optional[str]:
|
def resolve_stream_link(self, link: str) -> Optional[str]:
|
||||||
if not link:
|
if not link:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -231,10 +231,13 @@ class NetzkinoPlugin(BasisPlugin):
|
|||||||
# Browsing
|
# Browsing
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
def latest_titles(self, page: int = 1) -> List[str]:
|
def new_titles_page(self, page: int = 1) -> List[str]:
|
||||||
url = _URL_CATEGORY.format(slug="neue-filme")
|
url = _URL_CATEGORY.format(slug="neue-filme")
|
||||||
return self._load_posts(url)
|
return self._load_posts(url)
|
||||||
|
|
||||||
|
def new_titles(self) -> List[str]:
|
||||||
|
return self.new_titles_page(1)
|
||||||
|
|
||||||
def genres(self) -> List[str]:
|
def genres(self) -> List[str]:
|
||||||
# Gibt die Anzeigenamen zurück (sortiert, Browsing-Kategorien)
|
# Gibt die Anzeigenamen zurück (sortiert, Browsing-Kategorien)
|
||||||
return sorted(CATEGORIES.values())
|
return sorted(CATEGORIES.values())
|
||||||
@@ -248,4 +251,4 @@ class NetzkinoPlugin(BasisPlugin):
|
|||||||
return self._load_posts(url)
|
return self._load_posts(url)
|
||||||
|
|
||||||
def capabilities(self) -> set[str]:
|
def capabilities(self) -> set[str]:
|
||||||
return {"latest_titles", "genres"}
|
return {"new_titles", "genres"}
|
||||||
|
|||||||
@@ -1167,7 +1167,7 @@ class TopstreamfilmPlugin(BasisPlugin):
|
|||||||
return resolve_via_resolveurl(link, fallback_to_link=True)
|
return resolve_via_resolveurl(link, fallback_to_link=True)
|
||||||
|
|
||||||
def capabilities(self) -> set[str]:
|
def capabilities(self) -> set[str]:
|
||||||
return {"genres", "popular_series", "year_filter", "latest_titles"}
|
return {"genres", "popular_series", "year_filter", "new_titles"}
|
||||||
|
|
||||||
def years_available(self) -> List[str]:
|
def years_available(self) -> List[str]:
|
||||||
"""Liefert verfügbare Erscheinungsjahre (aktuelles Jahr bis 1980)."""
|
"""Liefert verfügbare Erscheinungsjahre (aktuelles Jahr bis 1980)."""
|
||||||
@@ -1207,7 +1207,7 @@ class TopstreamfilmPlugin(BasisPlugin):
|
|||||||
self._save_title_url_cache()
|
self._save_title_url_cache()
|
||||||
return titles
|
return titles
|
||||||
|
|
||||||
def latest_titles(self, page: int = 1) -> List[str]:
|
def new_titles_page(self, page: int = 1) -> List[str]:
|
||||||
"""Liefert neu hinzugefügte Filme.
|
"""Liefert neu hinzugefügte Filme.
|
||||||
|
|
||||||
URL-Muster: /neueste-filme/ oder /neueste-filme/page/{n}/
|
URL-Muster: /neueste-filme/ oder /neueste-filme/page/{n}/
|
||||||
@@ -1238,6 +1238,9 @@ class TopstreamfilmPlugin(BasisPlugin):
|
|||||||
self._save_title_url_cache()
|
self._save_title_url_cache()
|
||||||
return titles
|
return titles
|
||||||
|
|
||||||
|
def new_titles(self) -> List[str]:
|
||||||
|
return self.new_titles_page(1)
|
||||||
|
|
||||||
|
|
||||||
# Alias für die automatische Plugin-Erkennung.
|
# Alias für die automatische Plugin-Erkennung.
|
||||||
Plugin = TopstreamfilmPlugin
|
Plugin = TopstreamfilmPlugin
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# commit-msg: Commit-Message als Changelog-Eintrag in CHANGELOG-DEV.md prependen (nur dev-Branch)
|
# commit-msg: Version in Commit-Message aktualisieren und Changelog-Eintrag prependen (nur dev-Branch)
|
||||||
|
|
||||||
branch=$(git symbolic-ref --short HEAD 2>/dev/null)
|
branch=$(git symbolic-ref --short HEAD 2>/dev/null)
|
||||||
[[ "$branch" != "dev" ]] && exit 0
|
[[ "$branch" != "dev" ]] && exit 0
|
||||||
@@ -7,8 +7,15 @@ branch=$(git symbolic-ref --short HEAD 2>/dev/null)
|
|||||||
root=$(git rev-parse --show-toplevel)
|
root=$(git rev-parse --show-toplevel)
|
||||||
cd "$root"
|
cd "$root"
|
||||||
|
|
||||||
msg=$(cat "$1")
|
# Aktuelle Version aus addon.xml (bereits vom pre-commit Hook hochgezählt)
|
||||||
version=$(grep -oP 'version="\K[0-9]+\.[0-9]+\.[0-9]+[^"]*' addon/addon.xml | head -1)
|
version=$(grep -oP 'version="\K[0-9]+\.[0-9]+\.[0-9]+[^"]*' addon/addon.xml | head -1)
|
||||||
|
|
||||||
|
# Commit-Message: alte Versionsnummern durch aktuelle ersetzen
|
||||||
|
# z.B. "dev: bump to 0.1.72-dev – ..." → "dev: bump to 0.1.73-dev – ..."
|
||||||
|
msg=$(cat "$1")
|
||||||
|
updated_msg=$(echo "$msg" | sed -E "s/bump to [0-9]+\.[0-9]+\.[0-9]+[^ ]*/bump to ${version}/g")
|
||||||
|
echo "$updated_msg" > "$1"
|
||||||
|
|
||||||
today=$(date +%Y-%m-%d)
|
today=$(date +%Y-%m-%d)
|
||||||
|
|
||||||
# Changelog-Eintrag aufbauen
|
# Changelog-Eintrag aufbauen
|
||||||
@@ -19,7 +26,7 @@ today=$(date +%Y-%m-%d)
|
|||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
[[ -z "$line" ]] && continue
|
[[ -z "$line" ]] && continue
|
||||||
echo "- ${line}"
|
echo "- ${line}"
|
||||||
done <<< "$msg"
|
done <<< "$updated_msg"
|
||||||
echo ""
|
echo ""
|
||||||
cat CHANGELOG-DEV.md
|
cat CHANGELOG-DEV.md
|
||||||
} > /tmp/changelog_new.md
|
} > /tmp/changelog_new.md
|
||||||
|
|||||||
Reference in New Issue
Block a user