|
|
|
|
@@ -2095,8 +2095,17 @@ def _run_async(coro):
|
|
|
|
|
"""Fuehrt eine Coroutine aus, auch wenn Kodi bereits einen Event-Loop hat."""
|
|
|
|
|
_ensure_windows_selector_policy()
|
|
|
|
|
|
|
|
|
|
def _run_with_asyncio_run():
|
|
|
|
|
return asyncio.run(coro)
|
|
|
|
|
def _run_without_asyncio_run():
|
|
|
|
|
# asyncio.run() wuerde cancel_all_tasks() aufrufen, was auf Android TV
|
|
|
|
|
# wegen eines kaputten _weakrefset.py-Builds zu NameError: 'len' fuehrt.
|
|
|
|
|
loop = asyncio.new_event_loop()
|
|
|
|
|
try:
|
|
|
|
|
return loop.run_until_complete(coro)
|
|
|
|
|
finally:
|
|
|
|
|
try:
|
|
|
|
|
loop.close()
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
running_loop = asyncio.get_running_loop()
|
|
|
|
|
@@ -2109,7 +2118,7 @@ def _run_async(coro):
|
|
|
|
|
|
|
|
|
|
def _worker() -> None:
|
|
|
|
|
try:
|
|
|
|
|
result_box["value"] = _run_with_asyncio_run()
|
|
|
|
|
result_box["value"] = _run_without_asyncio_run()
|
|
|
|
|
except BaseException as exc: # pragma: no cover - defensive
|
|
|
|
|
error_box["error"] = exc
|
|
|
|
|
|
|
|
|
|
@@ -2120,7 +2129,7 @@ def _run_async(coro):
|
|
|
|
|
raise error_box["error"]
|
|
|
|
|
return result_box.get("value")
|
|
|
|
|
|
|
|
|
|
return _run_with_asyncio_run()
|
|
|
|
|
return _run_without_asyncio_run()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _series_url_params(plugin: BasisPlugin, title: str) -> dict[str, str]:
|
|
|
|
|
@@ -4414,13 +4423,15 @@ def _play_episode(
|
|
|
|
|
preferred_setter([selected_hoster])
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
link = plugin.stream_link_for(title, season, episode)
|
|
|
|
|
with _busy_dialog("Stream wird gesucht..."):
|
|
|
|
|
link = plugin.stream_link_for(title, season, episode)
|
|
|
|
|
if not link:
|
|
|
|
|
_log("Kein Stream gefunden.", xbmc.LOGWARNING)
|
|
|
|
|
xbmcgui.Dialog().notification("Wiedergabe", "Kein Stream gefunden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
|
|
|
|
return
|
|
|
|
|
_log(f"Stream-Link: {link}", xbmc.LOGDEBUG)
|
|
|
|
|
final_link = _resolve_stream_with_retry(plugin, link)
|
|
|
|
|
with _busy_dialog("Stream wird aufgelöst..."):
|
|
|
|
|
final_link = _resolve_stream_with_retry(plugin, link)
|
|
|
|
|
if not final_link:
|
|
|
|
|
return
|
|
|
|
|
finally:
|
|
|
|
|
@@ -4815,11 +4826,33 @@ def _show_tag_titles_page(plugin_name: str, tag: str, page: int = 1) -> None:
|
|
|
|
|
xbmcplugin.endOfDirectory(handle)
|
|
|
|
|
return
|
|
|
|
|
titles = [str(t).strip() for t in titles if t and str(t).strip()]
|
|
|
|
|
for title in titles:
|
|
|
|
|
_add_directory_item(handle, title, "seasons",
|
|
|
|
|
{"plugin": plugin_name, "title": title, **_series_url_params(plugin, title)},
|
|
|
|
|
is_folder=True)
|
|
|
|
|
if titles:
|
|
|
|
|
use_source, show_tmdb, prefer_source = _metadata_policy(
|
|
|
|
|
plugin_name, plugin, allow_tmdb=_tmdb_list_enabled()
|
|
|
|
|
)
|
|
|
|
|
plugin_meta = _collect_plugin_metadata(plugin, titles) if use_source else {}
|
|
|
|
|
show_plot = _get_setting_bool("tmdb_show_plot", default=True)
|
|
|
|
|
show_art = _get_setting_bool("tmdb_show_art", default=True)
|
|
|
|
|
tmdb_prefetched: dict[str, tuple[dict[str, str], dict[str, str], list[TmdbCastMember]]] = {}
|
|
|
|
|
tmdb_titles = list(titles) if show_tmdb else []
|
|
|
|
|
if show_tmdb and prefer_source and use_source:
|
|
|
|
|
tmdb_titles = [
|
|
|
|
|
t for t in titles
|
|
|
|
|
if _needs_tmdb((plugin_meta.get(t) or ({},))[0], (plugin_meta.get(t) or ({}, {}))[1],
|
|
|
|
|
want_plot=show_plot, want_art=show_art)
|
|
|
|
|
]
|
|
|
|
|
if show_tmdb and tmdb_titles:
|
|
|
|
|
with _busy_dialog("Schlagwort-Liste wird geladen..."):
|
|
|
|
|
tmdb_prefetched = _tmdb_labels_and_art_bulk(tmdb_titles)
|
|
|
|
|
for title in titles:
|
|
|
|
|
tmdb_info, tmdb_art, tmdb_cast = tmdb_prefetched.get(title, ({}, {}, [])) if show_tmdb else ({}, {}, [])
|
|
|
|
|
meta = plugin_meta.get(title)
|
|
|
|
|
info_labels, art, cast = _merge_metadata(title, tmdb_info, tmdb_art, tmdb_cast, meta)
|
|
|
|
|
info_labels = dict(info_labels or {})
|
|
|
|
|
info_labels.setdefault("mediatype", "tvshow")
|
|
|
|
|
_add_directory_item(handle, title, "seasons",
|
|
|
|
|
{"plugin": plugin_name, "title": title, **_series_url_params(plugin, title)},
|
|
|
|
|
is_folder=True, info_labels=info_labels, art=art, cast=cast)
|
|
|
|
|
_add_directory_item(handle, "Naechste Seite", "tag_titles_page",
|
|
|
|
|
{"plugin": plugin_name, "tag": tag, "page": str(page + 1)}, is_folder=True)
|
|
|
|
|
xbmcplugin.endOfDirectory(handle)
|
|
|
|
|
@@ -4929,7 +4962,7 @@ def _show_trakt_watchlist(media_type: str = "") -> None:
|
|
|
|
|
_add_directory_item(handle, label, "search", {"query": item.title}, is_folder=True, info_labels=info_labels, art=art)
|
|
|
|
|
if not items:
|
|
|
|
|
xbmcgui.Dialog().notification("Trakt", "Watchlist ist leer.", xbmcgui.NOTIFICATION_INFO, 3000)
|
|
|
|
|
xbmcplugin.endOfDirectory(handle)
|
|
|
|
|
xbmcplugin.endOfDirectory(handle, cacheToDisc=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _show_trakt_history(page: int = 1) -> None:
|
|
|
|
|
@@ -4999,7 +5032,7 @@ def _show_trakt_history(page: int = 1) -> None:
|
|
|
|
|
_add_directory_item(handle, "Naechste Seite >>", "trakt_history", {"page": str(page + 1)}, is_folder=True)
|
|
|
|
|
if not items and page == 1:
|
|
|
|
|
xbmcgui.Dialog().notification("Trakt", "Keine History vorhanden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
|
|
|
|
xbmcplugin.endOfDirectory(handle)
|
|
|
|
|
xbmcplugin.endOfDirectory(handle, cacheToDisc=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _show_trakt_upcoming() -> None:
|
|
|
|
|
@@ -5110,7 +5143,7 @@ def _show_trakt_upcoming() -> None:
|
|
|
|
|
|
|
|
|
|
_add_directory_item(handle, label, action, params, is_folder=True, info_labels=info_labels, art=art)
|
|
|
|
|
|
|
|
|
|
xbmcplugin.endOfDirectory(handle)
|
|
|
|
|
xbmcplugin.endOfDirectory(handle, cacheToDisc=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _show_trakt_continue_watching() -> None:
|
|
|
|
|
@@ -5127,21 +5160,17 @@ def _show_trakt_continue_watching() -> None:
|
|
|
|
|
_set_content(handle, "episodes")
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
history = client.get_history(token, media_type="episodes", limit=100)
|
|
|
|
|
watched = client.get_watched_shows(token)
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
_log(f"Trakt History fehlgeschlagen: {exc}", xbmc.LOGWARNING)
|
|
|
|
|
xbmcgui.Dialog().notification("Trakt", "History konnte nicht geladen werden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
|
|
|
|
_log(f"Trakt Watched fehlgeschlagen: {exc}", xbmc.LOGWARNING)
|
|
|
|
|
xbmcgui.Dialog().notification("Trakt", "Watched-Liste konnte nicht geladen werden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
|
|
|
|
xbmcplugin.endOfDirectory(handle)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Pro Serie nur den zuletzt gesehenen Eintrag behalten (History ist absteigend sortiert)
|
|
|
|
|
seen: dict[str, object] = {}
|
|
|
|
|
for item in history:
|
|
|
|
|
if item.title and item.title not in seen:
|
|
|
|
|
seen[item.title] = item
|
|
|
|
|
seen: dict[str, object] = {item.title: item for item in watched if item.title}
|
|
|
|
|
|
|
|
|
|
if not seen:
|
|
|
|
|
xbmcgui.Dialog().notification("Trakt", "Keine History vorhanden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
|
|
|
|
xbmcgui.Dialog().notification("Trakt", "Keine gesehenen Serien vorhanden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
|
|
|
|
xbmcplugin.endOfDirectory(handle)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
@@ -5169,7 +5198,7 @@ def _show_trakt_continue_watching() -> None:
|
|
|
|
|
_, art, _ = tmdb_prefetched.get(last.title, ({}, {}, []))
|
|
|
|
|
_add_directory_item(handle, display_label, "search", {"query": last.title}, is_folder=True, info_labels=info_labels, art=art)
|
|
|
|
|
|
|
|
|
|
xbmcplugin.endOfDirectory(handle)
|
|
|
|
|
xbmcplugin.endOfDirectory(handle, cacheToDisc=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|