Show search progress per plugin during global search

This commit is contained in:
2026-02-01 23:26:12 +01:00
parent 1e3c6ffdf6
commit 372d443cb2

View File

@@ -147,6 +147,45 @@ def _busy_dialog():
_busy_close()
@contextmanager
def _progress_dialog(heading: str, message: str = ""):
"""Zeigt einen Fortschrittsdialog in Kodi und liefert eine Update-Funktion."""
dialog = None
try: # pragma: no cover - Kodi runtime
if xbmcgui is not None and hasattr(xbmcgui, "DialogProgress"):
dialog = xbmcgui.DialogProgress()
dialog.create(heading, message)
except Exception:
dialog = None
def _update(percent: int, text: str = "") -> bool:
if dialog is None:
return False
percent = max(0, min(100, int(percent)))
try: # Kodi Matrix/Nexus
dialog.update(percent, text)
except TypeError:
try: # Kodi Leia fallback
dialog.update(percent, text, "", "")
except Exception:
pass
except Exception:
pass
try:
return bool(dialog.iscanceled())
except Exception:
return False
try:
yield _update
finally:
if dialog is not None:
try:
dialog.close()
except Exception:
pass
def _get_handle() -> int:
return int(sys.argv[1]) if len(sys.argv) > 1 else -1
@@ -919,21 +958,27 @@ def _show_plugin_search_results(plugin_name: str, query: str) -> None:
_set_content(handle, "movies" if plugin_name.casefold() == "einschalten" else "tvshows")
_log(f"Suche nach Titeln (Plugin={plugin_name}): {query}")
list_items: list[dict[str, object]] = []
canceled = False
try:
with _progress_dialog("Suche läuft", f"{plugin_name} (1/1) starte…") as progress:
canceled = progress(5, f"{plugin_name} (1/1) Suche…")
results = _run_async(plugin.search_titles(query))
except Exception as exc:
_log(f"Suche fehlgeschlagen ({plugin_name}): {exc}", xbmc.LOGWARNING)
xbmcgui.Dialog().notification("Suche", "Suche fehlgeschlagen.", xbmcgui.NOTIFICATION_INFO, 3000)
xbmcplugin.endOfDirectory(handle)
return
results = [str(t).strip() for t in (results or []) if t and str(t).strip()]
results.sort(key=lambda value: value.casefold())
tmdb_prefetched: dict[str, tuple[dict[str, str], dict[str, str], list[TmdbCastMember]]] = {}
if results:
with _busy_dialog():
if results and not canceled:
canceled = progress(35, f"{plugin_name} (1/1) Metadaten…")
tmdb_prefetched = _tmdb_labels_and_art_bulk(list(results))
for title in results:
total_results = max(1, len(results))
for index, title in enumerate(results, start=1):
if canceled:
break
if index == 1 or index == total_results or (index % 10 == 0):
pct = 35 + int((index / float(total_results)) * 60)
canceled = progress(pct, f"{plugin_name} (1/1) aufbereiten {index}/{total_results}")
info_labels, art, cast = tmdb_prefetched.get(title, _tmdb_labels_and_art(title))
info_labels = dict(info_labels or {})
info_labels.setdefault("mediatype", "tvshow")
@@ -945,15 +990,37 @@ def _show_plugin_search_results(plugin_name: str, query: str) -> None:
display_label = _label_with_playstate(display_label, playstate)
direct_play = bool(plugin_name.casefold() == "einschalten" and _get_setting_bool("einschalten_enable_playback", default=False))
extra_params = _series_url_params(plugin, title)
list_items.append(
{
"label": display_label,
"action": "play_movie" if direct_play else "seasons",
"params": {"plugin": plugin_name, "title": title, **extra_params},
"is_folder": (not direct_play),
"info_labels": merged_info,
"art": art,
"cast": cast,
}
)
except Exception as exc:
_log(f"Suche fehlgeschlagen ({plugin_name}): {exc}", xbmc.LOGWARNING)
xbmcgui.Dialog().notification("Suche", "Suche fehlgeschlagen.", xbmcgui.NOTIFICATION_INFO, 3000)
xbmcplugin.endOfDirectory(handle)
return
if canceled and not list_items:
xbmcgui.Dialog().notification("Suche", "Suche abgebrochen.", xbmcgui.NOTIFICATION_INFO, 2500)
xbmcplugin.endOfDirectory(handle)
return
for item in list_items:
_add_directory_item(
handle,
display_label,
"play_movie" if direct_play else "seasons",
{"plugin": plugin_name, "title": title, **extra_params},
is_folder=not direct_play,
info_labels=merged_info,
art=art,
cast=cast,
str(item["label"]),
str(item["action"]),
dict(item["params"]),
is_folder=bool(item["is_folder"]),
info_labels=item["info_labels"],
art=item["art"],
cast=item["cast"],
)
xbmcplugin.endOfDirectory(handle)
@@ -1058,18 +1125,42 @@ def _show_search_results(query: str) -> None:
xbmcgui.Dialog().notification("Suche", "Keine Plugins gefunden.", xbmcgui.NOTIFICATION_INFO, 3000)
xbmcplugin.endOfDirectory(handle)
return
for plugin_name, plugin in plugins.items():
list_items: list[dict[str, object]] = []
canceled = False
plugin_entries = list(plugins.items())
total_plugins = max(1, len(plugin_entries))
with _progress_dialog("Suche läuft", "Suche gestartet…") as progress:
for plugin_index, (plugin_name, plugin) in enumerate(plugin_entries, start=1):
range_start = int(((plugin_index - 1) / float(total_plugins)) * 100)
range_end = int((plugin_index / float(total_plugins)) * 100)
canceled = progress(range_start, f"{plugin_name} ({plugin_index}/{total_plugins}) Suche…")
if canceled:
break
try:
results = _run_async(plugin.search_titles(query))
except Exception as exc:
_log(f"Suche fehlgeschlagen ({plugin_name}): {exc}", xbmc.LOGWARNING)
continue
results = [str(t).strip() for t in (results or []) if t and str(t).strip()]
_log(f"Treffer ({plugin_name}): {len(results)}", xbmc.LOGDEBUG)
tmdb_prefetched: dict[str, tuple[dict[str, str], dict[str, str], list[TmdbCastMember]]] = {}
if results:
with _busy_dialog():
canceled = progress(
range_start + int((range_end - range_start) * 0.35),
f"{plugin_name} ({plugin_index}/{total_plugins}) Metadaten…",
)
if canceled:
break
tmdb_prefetched = _tmdb_labels_and_art_bulk(list(results))
for title in results:
total_results = max(1, len(results))
for title_index, title in enumerate(results, start=1):
if title_index == 1 or title_index == total_results or (title_index % 10 == 0):
canceled = progress(
range_start + int((range_end - range_start) * (0.35 + 0.65 * (title_index / float(total_results)))),
f"{plugin_name} ({plugin_index}/{total_plugins}) aufbereiten {title_index}/{total_results}",
)
if canceled:
break
info_labels, art, cast = tmdb_prefetched.get(title, _tmdb_labels_and_art(title))
info_labels = dict(info_labels or {})
info_labels.setdefault("mediatype", "tvshow")
@@ -1084,15 +1175,36 @@ def _show_search_results(query: str) -> None:
plugin_name.casefold() == "einschalten" and _get_setting_bool("einschalten_enable_playback", default=False)
)
extra_params = _series_url_params(plugin, title)
list_items.append(
{
"label": label,
"action": "play_movie" if direct_play else "seasons",
"params": {"plugin": plugin_name, "title": title, **extra_params},
"is_folder": (not direct_play),
"info_labels": merged_info,
"art": art,
"cast": cast,
}
)
if canceled:
break
if not canceled:
progress(100, "Suche abgeschlossen")
if canceled and not list_items:
xbmcgui.Dialog().notification("Suche", "Suche abgebrochen.", xbmcgui.NOTIFICATION_INFO, 2500)
xbmcplugin.endOfDirectory(handle)
return
for item in list_items:
_add_directory_item(
handle,
label,
"play_movie" if direct_play else "seasons",
{"plugin": plugin_name, "title": title, **extra_params},
is_folder=not direct_play,
info_labels=merged_info,
art=art,
cast=cast,
str(item["label"]),
str(item["action"]),
dict(item["params"]),
is_folder=bool(item["is_folder"]),
info_labels=item["info_labels"],
art=item["art"],
cast=item["cast"],
)
xbmcplugin.endOfDirectory(handle)