nightly: fix movie search flow and add source metadata fallbacks
This commit is contained in:
204
addon/default.py
204
addon/default.py
@@ -1214,7 +1214,7 @@ def _show_plugin_search_results(plugin_name: str, query: str) -> None:
|
||||
except Exception:
|
||||
pass
|
||||
raise
|
||||
results = [str(t).strip() for t in (results or []) if t and str(t).strip()]
|
||||
results = _clean_search_titles([str(t).strip() for t in (results or []) if t and str(t).strip()])
|
||||
results.sort(key=lambda value: value.casefold())
|
||||
|
||||
use_source, show_tmdb, prefer_source = _metadata_policy(
|
||||
@@ -1406,6 +1406,33 @@ def _series_url_params(plugin: BasisPlugin, title: str) -> dict[str, str]:
|
||||
return {"series_url": series_url} if series_url else {}
|
||||
|
||||
|
||||
def _clean_search_titles(values: list[str]) -> list[str]:
|
||||
"""Filtert offensichtliche Platzhalter und dedupliziert Treffer."""
|
||||
blocked = {
|
||||
"stream",
|
||||
"streams",
|
||||
"film",
|
||||
"movie",
|
||||
"play",
|
||||
"details",
|
||||
"details/play",
|
||||
}
|
||||
cleaned: list[str] = []
|
||||
seen: set[str] = set()
|
||||
for raw in values:
|
||||
title = (raw or "").strip()
|
||||
if not title:
|
||||
continue
|
||||
key = title.casefold()
|
||||
if key in blocked:
|
||||
continue
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
cleaned.append(title)
|
||||
return cleaned
|
||||
|
||||
|
||||
def _show_search() -> None:
|
||||
_log("Suche gestartet.")
|
||||
dialog = xbmcgui.Dialog()
|
||||
@@ -1453,7 +1480,7 @@ def _show_search_results(query: str) -> None:
|
||||
pass
|
||||
_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()]
|
||||
results = _clean_search_titles([str(t).strip() for t in (results or []) if t and str(t).strip()])
|
||||
_log(f"Treffer ({plugin_name}): {len(results)}", xbmc.LOGDEBUG)
|
||||
use_source, show_tmdb, prefer_source = _metadata_policy(
|
||||
plugin_name, plugin, allow_tmdb=_tmdb_enabled()
|
||||
@@ -1537,6 +1564,73 @@ def _show_search_results(query: str) -> None:
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
|
||||
|
||||
def _movie_seed_for_title(plugin: BasisPlugin, title: str, seasons: list[str]) -> tuple[str, str] | None:
|
||||
"""Ermittelt ein Film-Seed (Season/Episode), um direkt Provider anzeigen zu können."""
|
||||
if not seasons or len(seasons) != 1:
|
||||
return None
|
||||
season = str(seasons[0] or "").strip()
|
||||
if not season:
|
||||
return None
|
||||
try:
|
||||
episodes = [str(value or "").strip() for value in (plugin.episodes_for(title, season) or [])]
|
||||
except Exception:
|
||||
return None
|
||||
episodes = [value for value in episodes if value]
|
||||
if len(episodes) != 1:
|
||||
return None
|
||||
episode = episodes[0]
|
||||
season_key = season.casefold()
|
||||
episode_key = episode.casefold()
|
||||
title_key = (title or "").strip().casefold()
|
||||
generic_seasons = {"film", "movie", "stream"}
|
||||
generic_episodes = {"stream", "film", "play", title_key}
|
||||
if season_key in generic_seasons and episode_key in generic_episodes:
|
||||
return (season, episode)
|
||||
return None
|
||||
|
||||
|
||||
def _show_movie_streams(
|
||||
plugin_name: str,
|
||||
title: str,
|
||||
season: str,
|
||||
episode: str,
|
||||
*,
|
||||
series_url: str = "",
|
||||
) -> None:
|
||||
handle = _get_handle()
|
||||
plugin = _discover_plugins().get(plugin_name)
|
||||
if plugin is None:
|
||||
xbmcgui.Dialog().notification("Streams", "Quelle nicht gefunden.", xbmcgui.NOTIFICATION_INFO, 3000)
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
return
|
||||
|
||||
if series_url:
|
||||
remember_series_url = getattr(plugin, "remember_series_url", None)
|
||||
if callable(remember_series_url):
|
||||
try:
|
||||
remember_series_url(title, series_url)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
xbmcplugin.setPluginCategory(handle, f"{title} - Streams")
|
||||
_set_content(handle, "videos")
|
||||
|
||||
base_params = {"plugin": plugin_name, "title": title, "season": season, "episode": episode}
|
||||
if series_url:
|
||||
base_params["series_url"] = series_url
|
||||
|
||||
# Hoster bleiben im Auswahldialog der Wiedergabe (wie bisher).
|
||||
_add_directory_item(
|
||||
handle,
|
||||
title,
|
||||
"play_episode",
|
||||
dict(base_params),
|
||||
is_folder=False,
|
||||
info_labels={"title": title, "mediatype": "movie"},
|
||||
)
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
|
||||
|
||||
def _show_seasons(plugin_name: str, title: str, series_url: str = "") -> None:
|
||||
handle = _get_handle()
|
||||
_log(f"Staffeln laden: {plugin_name} / {title}")
|
||||
@@ -1553,60 +1647,6 @@ def _show_seasons(plugin_name: str, title: str, series_url: str = "") -> None:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Einschalten liefert Filme. Für Playback soll nach dem Öffnen des Titels direkt ein
|
||||
# einzelnes abspielbares Item angezeigt werden: <Titel> -> (<Titel> abspielbar).
|
||||
# Wichtig: ohne zusätzliche Netzwerkanfragen (sonst bleibt Kodi ggf. im Busy-Spinner hängen).
|
||||
if (plugin_name or "").casefold() == "einschalten" and _get_setting_bool("einschalten_enable_playback", default=False):
|
||||
xbmcplugin.setPluginCategory(handle, title)
|
||||
_set_content(handle, "movies")
|
||||
playstate = _title_playstate(plugin_name, title)
|
||||
info_labels: dict[str, object] = {"title": title, "mediatype": "movie"}
|
||||
info_labels = _apply_playstate_to_info(info_labels, playstate)
|
||||
display_label = _label_with_playstate(title, playstate)
|
||||
movie_params = {"plugin": plugin_name, "title": title}
|
||||
if series_url:
|
||||
movie_params["series_url"] = series_url
|
||||
_add_directory_item(
|
||||
handle,
|
||||
display_label,
|
||||
"play_movie",
|
||||
movie_params,
|
||||
is_folder=False,
|
||||
info_labels=info_labels,
|
||||
)
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
return
|
||||
|
||||
# Optional: Plugins können schnell (ohne Detail-Request) sagen, ob ein Titel ein Film ist.
|
||||
# Dann zeigen wir direkt ein einzelnes abspielbares Item: <Titel> -> (<Titel>).
|
||||
is_movie = getattr(plugin, "is_movie", None)
|
||||
if callable(is_movie):
|
||||
try:
|
||||
if bool(is_movie(title)):
|
||||
xbmcplugin.setPluginCategory(handle, title)
|
||||
_set_content(handle, "movies")
|
||||
playstate = _title_playstate(plugin_name, title)
|
||||
info_labels: dict[str, object] = {"title": title, "mediatype": "movie"}
|
||||
info_labels = _apply_playstate_to_info(info_labels, playstate)
|
||||
display_label = _label_with_playstate(title, playstate)
|
||||
movie_params = {"plugin": plugin_name, "title": title}
|
||||
if series_url:
|
||||
movie_params["series_url"] = series_url
|
||||
else:
|
||||
movie_params.update(_series_url_params(plugin, title))
|
||||
_add_directory_item(
|
||||
handle,
|
||||
display_label,
|
||||
"play_movie",
|
||||
movie_params,
|
||||
is_folder=False,
|
||||
info_labels=info_labels,
|
||||
)
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
use_source, show_tmdb, _prefer_source = _metadata_policy(
|
||||
plugin_name, plugin, allow_tmdb=_tmdb_enabled()
|
||||
)
|
||||
@@ -1636,6 +1676,26 @@ def _show_seasons(plugin_name: str, title: str, series_url: str = "") -> None:
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
return
|
||||
|
||||
movie_seed = _movie_seed_for_title(plugin, title, seasons)
|
||||
if movie_seed is not None:
|
||||
# Dieser Action-Pfad wurde als Verzeichnis aufgerufen. Ohne endOfDirectory()
|
||||
# bleibt Kodi im Busy-Zustand, auch wenn wir direkt in die Wiedergabe springen.
|
||||
try:
|
||||
xbmcplugin.endOfDirectory(handle, succeeded=False)
|
||||
except Exception:
|
||||
try:
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
except Exception:
|
||||
pass
|
||||
_play_episode(
|
||||
plugin_name,
|
||||
title,
|
||||
movie_seed[0],
|
||||
movie_seed[1],
|
||||
series_url=series_url,
|
||||
)
|
||||
return
|
||||
|
||||
count = len(seasons)
|
||||
suffix = "Staffel" if count == 1 else "Staffeln"
|
||||
xbmcplugin.setPluginCategory(handle, f"{title} ({count} {suffix})")
|
||||
@@ -3210,12 +3270,29 @@ def _track_playback_and_update_state(key: str) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def _track_playback_and_update_state_async(key: str) -> None:
|
||||
"""Startet Playstate-Tracking im Hintergrund, damit die UI nicht blockiert."""
|
||||
key = (key or "").strip()
|
||||
if not key:
|
||||
return
|
||||
|
||||
def _worker() -> None:
|
||||
try:
|
||||
_track_playback_and_update_state(key)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
worker = threading.Thread(target=_worker, name="viewit-playstate-tracker", daemon=True)
|
||||
worker.start()
|
||||
|
||||
|
||||
def _play_episode(
|
||||
plugin_name: str,
|
||||
title: str,
|
||||
season: str,
|
||||
episode: str,
|
||||
*,
|
||||
forced_hoster: str = "",
|
||||
episode_url: str = "",
|
||||
series_url: str = "",
|
||||
resolve_handle: int | None = None,
|
||||
@@ -3260,10 +3337,16 @@ def _play_episode(
|
||||
_log(f"Hoster laden fehlgeschlagen ({plugin_name}): {exc}", xbmc.LOGWARNING)
|
||||
|
||||
selected_hoster: str | None = None
|
||||
forced_hoster = (forced_hoster or "").strip()
|
||||
if available_hosters:
|
||||
if len(available_hosters) == 1:
|
||||
if forced_hoster:
|
||||
for hoster in available_hosters:
|
||||
if hoster.casefold() == forced_hoster.casefold():
|
||||
selected_hoster = hoster
|
||||
break
|
||||
if selected_hoster is None and len(available_hosters) == 1:
|
||||
selected_hoster = available_hosters[0]
|
||||
else:
|
||||
elif selected_hoster is None:
|
||||
selected_index = xbmcgui.Dialog().select("Hoster waehlen", available_hosters)
|
||||
if selected_index is None or selected_index < 0:
|
||||
_log("Play abgebrochen (kein Hoster gewählt).", xbmc.LOGDEBUG)
|
||||
@@ -3308,7 +3391,7 @@ def _play_episode(
|
||||
cast=cast,
|
||||
resolve_handle=resolve_handle,
|
||||
)
|
||||
_track_playback_and_update_state(
|
||||
_track_playback_and_update_state_async(
|
||||
_playstate_key(plugin_name=plugin_name, title=title, season=season, episode=episode)
|
||||
)
|
||||
|
||||
@@ -3396,7 +3479,7 @@ def _play_episode_url(
|
||||
cast=cast,
|
||||
resolve_handle=resolve_handle,
|
||||
)
|
||||
_track_playback_and_update_state(
|
||||
_track_playback_and_update_state_async(
|
||||
_playstate_key(plugin_name=plugin_name, title=title, season=season_label, episode=episode_label)
|
||||
)
|
||||
|
||||
@@ -3496,6 +3579,7 @@ def run() -> None:
|
||||
params.get("title", ""),
|
||||
params.get("season", ""),
|
||||
params.get("episode", ""),
|
||||
forced_hoster=params.get("hoster", ""),
|
||||
episode_url=params.get("url", ""),
|
||||
series_url=params.get("series_url", ""),
|
||||
resolve_handle=_get_handle(),
|
||||
|
||||
Reference in New Issue
Block a user