dev: bump to 0.1.71-dev – Trakt History direkt abspielen, Metadaten + Plugin-Bugfixes
- Trakt History: Episoden starten direkt (kein Staffel-Dialog mehr) - Trakt History: Episodentitel, Plot und Artwork bereits in der Übersicht - TraktItem um episode_title, episode_overview, episode_thumb, show_poster, show_fanart erweitert - get_history() nutzt jetzt ?extended=full,images - Slash-Commands /check und /deploy angelegt - build_install_addon.sh deployt jetzt auch nach ~/.kodi/addons/ - filmpalast_plugin: return-Tuple-Bug gefixt (return "", "", "") - dokustreams_plugin: Regex-Escaping für clean_name() korrigiert - aniworld_plugin: raise_for_status() in resolve_redirect() ergänzt - serienstream_plugin: Toter Code und unnötigen Regex-Backslash entfernt
This commit is contained in:
172
addon/default.py
172
addon/default.py
@@ -131,6 +131,7 @@ _MEDIA_TYPE_CACHE: dict[str, str] = {}
|
||||
_TMDB_SEASON_CACHE: dict[tuple[int, int, str, str], dict[int, tuple[dict[str, str], dict[str, str]]]] = {}
|
||||
_TMDB_SEASON_SUMMARY_CACHE: dict[tuple[int, int, str, str], tuple[dict[str, str], dict[str, str]]] = {}
|
||||
_TMDB_EPISODE_CAST_CACHE: dict[tuple[int, int, int, str], list[TmdbCastMember]] = {}
|
||||
_TRAKT_SEASON_META_CACHE: dict = {}
|
||||
_TMDB_LOG_PATH: str | None = None
|
||||
_GENRE_TITLES_CACHE: dict[tuple[str, str], list[str]] = {}
|
||||
_ADDON_INSTANCE = None
|
||||
@@ -987,7 +988,9 @@ def _tmdb_episode_labels_and_art(*, title: str, season_label: str, episode_label
|
||||
_tmdb_labels_and_art(title)
|
||||
tmdb_id = _tmdb_cache_get(_TMDB_ID_CACHE, title_key)
|
||||
if not tmdb_id:
|
||||
return {"title": episode_label}, {}
|
||||
return _trakt_episode_labels_and_art(
|
||||
title=title, season_label=season_label, episode_label=episode_label
|
||||
)
|
||||
|
||||
season_number = _extract_first_int(season_label)
|
||||
episode_number = _extract_first_int(episode_label)
|
||||
@@ -1003,7 +1006,9 @@ def _tmdb_episode_labels_and_art(*, title: str, season_label: str, episode_label
|
||||
if cached_season is None:
|
||||
api_key = _get_setting_string("tmdb_api_key").strip()
|
||||
if not api_key or api_key == "None":
|
||||
return {"title": episode_label}, {}
|
||||
return _trakt_episode_labels_and_art(
|
||||
title=title, season_label=season_label, episode_label=episode_label
|
||||
)
|
||||
log_requests = _get_setting_bool("tmdb_log_requests", default=False)
|
||||
log_responses = _get_setting_bool("tmdb_log_responses", default=False)
|
||||
log_fn = _tmdb_file_log if (log_requests or log_responses) else None
|
||||
@@ -1038,6 +1043,66 @@ def _tmdb_episode_labels_and_art(*, title: str, season_label: str, episode_label
|
||||
return cached_season.get(episode_number, ({"title": episode_label}, {}))
|
||||
|
||||
|
||||
def _trakt_episode_labels_and_art(
|
||||
*, title: str, season_label: str, episode_label: str
|
||||
) -> tuple[dict[str, str], dict[str, str]]:
|
||||
"""Trakt-Fallback für Episoden-Metadaten wenn TMDB nicht verfügbar.
|
||||
Lädt Staffel-Episodendaten per Batch (extended=full,images) und optionale
|
||||
deutsche Übersetzung per Episode (Translations-Endpunkt).
|
||||
"""
|
||||
client = _trakt_get_client()
|
||||
if not client:
|
||||
return {"title": episode_label}, {}
|
||||
season_number = _extract_first_int(season_label)
|
||||
episode_number = _extract_first_int(episode_label)
|
||||
if season_number is None or episode_number is None:
|
||||
return {"title": episode_label}, {}
|
||||
|
||||
cache_key = (title.strip().casefold(), season_number)
|
||||
cached = _tmdb_cache_get(_TRAKT_SEASON_META_CACHE, cache_key)
|
||||
if cached is None:
|
||||
slug = client.search_show(title)
|
||||
if not slug:
|
||||
_tmdb_cache_set(_TRAKT_SEASON_META_CACHE, cache_key, {})
|
||||
return {"title": episode_label}, {}
|
||||
meta = client.lookup_tv_season(slug, season_number)
|
||||
_tmdb_cache_set(_TRAKT_SEASON_META_CACHE, cache_key, {"slug": slug, "episodes": meta or {}})
|
||||
cached = _tmdb_cache_get(_TRAKT_SEASON_META_CACHE, cache_key)
|
||||
|
||||
slug = (cached or {}).get("slug", "")
|
||||
episodes: dict = (cached or {}).get("episodes", {})
|
||||
ep = episodes.get(episode_number)
|
||||
if not ep:
|
||||
return {"title": episode_label}, {}
|
||||
|
||||
ep_title = ep.title or episode_label
|
||||
ep_overview = ep.overview
|
||||
|
||||
language = _get_setting_string("tmdb_language").strip() or "de-DE"
|
||||
lang_code = language[:2]
|
||||
if slug and lang_code and lang_code != "en":
|
||||
trans_key = (cache_key, episode_number, lang_code)
|
||||
trans_cached = _tmdb_cache_get(_TRAKT_SEASON_META_CACHE, trans_key)
|
||||
if trans_cached is None:
|
||||
t_title, t_overview = client.get_episode_translation(slug, season_number, episode_number, lang_code)
|
||||
trans_cached = {"title": t_title, "overview": t_overview}
|
||||
_tmdb_cache_set(_TRAKT_SEASON_META_CACHE, trans_key, trans_cached)
|
||||
if trans_cached.get("title"):
|
||||
ep_title = trans_cached["title"]
|
||||
if trans_cached.get("overview"):
|
||||
ep_overview = trans_cached["overview"]
|
||||
|
||||
info: dict[str, str] = {"title": ep_title}
|
||||
if ep_overview:
|
||||
info["plot"] = ep_overview
|
||||
if ep.runtime_minutes:
|
||||
info["duration"] = str(ep.runtime_minutes * 60)
|
||||
art: dict[str, str] = {}
|
||||
if ep.thumb:
|
||||
art["thumb"] = ep.thumb
|
||||
return info, art
|
||||
|
||||
|
||||
def _tmdb_episode_cast(*, title: str, season_label: str, episode_label: str) -> list[TmdbCastMember]:
|
||||
if not _tmdb_enabled():
|
||||
return []
|
||||
@@ -4526,12 +4591,29 @@ def _show_trakt_watchlist(media_type: str = "") -> None:
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
return
|
||||
|
||||
_set_content(handle, "tvshows")
|
||||
items = client.get_watchlist(token, media_type=media_type)
|
||||
for item in items:
|
||||
label = f"{item.title}"
|
||||
label = f"{item.title} ({item.year})" if item.year else item.title
|
||||
|
||||
tmdb_info, art, _ = _tmdb_labels_and_art(item.title)
|
||||
info_labels: dict[str, object] = dict(tmdb_info)
|
||||
info_labels["title"] = label
|
||||
info_labels["tvshowtitle"] = item.title
|
||||
if item.year:
|
||||
label = f"{item.title} ({item.year})"
|
||||
_add_directory_item(handle, label, "search", {"query": item.title}, is_folder=True)
|
||||
info_labels["year"] = item.year
|
||||
info_labels["mediatype"] = "tvshow"
|
||||
|
||||
match = _trakt_find_in_plugins(item.title)
|
||||
if match:
|
||||
plugin_name, matched_title = match
|
||||
action = "seasons"
|
||||
params: dict[str, str] = {"plugin": plugin_name, "title": matched_title}
|
||||
else:
|
||||
action = "search"
|
||||
params = {"query": item.title}
|
||||
|
||||
_add_directory_item(handle, label, action, params, 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)
|
||||
@@ -4546,14 +4628,66 @@ def _show_trakt_history(page: int = 1) -> None:
|
||||
xbmcplugin.endOfDirectory(handle)
|
||||
return
|
||||
|
||||
xbmcplugin.setPluginCategory(handle, "Trakt: Zuletzt gesehen")
|
||||
_set_content(handle, "episodes")
|
||||
|
||||
items = client.get_history(token, page=page, limit=LIST_PAGE_SIZE)
|
||||
for item in items:
|
||||
label = item.title
|
||||
if item.media_type == "episode" and item.season and item.episode:
|
||||
label = f"{item.title} - S{item.season:02d}E{item.episode:02d}"
|
||||
is_episode = item.media_type == "episode" and item.season and item.episode
|
||||
|
||||
# Label mit Episodentitel wenn vorhanden
|
||||
if is_episode:
|
||||
ep_title = item.episode_title or f"Episode {item.episode}"
|
||||
label = f"{item.title} – S{item.season:02d}E{item.episode:02d}: {ep_title}"
|
||||
elif item.year:
|
||||
label = f"{item.title} ({item.year})"
|
||||
_add_directory_item(handle, label, "search", {"query": item.title}, is_folder=True)
|
||||
else:
|
||||
label = item.title
|
||||
|
||||
# Artwork: Trakt-Bilder als Basis, TMDB ergänzt fehlende Keys
|
||||
art: dict[str, str] = {}
|
||||
if item.episode_thumb:
|
||||
art["thumb"] = item.episode_thumb
|
||||
if item.show_fanart:
|
||||
art["fanart"] = item.show_fanart
|
||||
if item.show_poster:
|
||||
art["poster"] = item.show_poster
|
||||
_, tmdb_art, _ = _tmdb_labels_and_art(item.title)
|
||||
for _k, _v in tmdb_art.items():
|
||||
art.setdefault(_k, _v)
|
||||
|
||||
# Info-Labels
|
||||
info_labels: dict[str, object] = {}
|
||||
info_labels["title"] = item.episode_title if is_episode and item.episode_title else label
|
||||
info_labels["tvshowtitle"] = item.title
|
||||
if item.year:
|
||||
info_labels["year"] = item.year
|
||||
if is_episode:
|
||||
info_labels["season"] = item.season
|
||||
info_labels["episode"] = item.episode
|
||||
if item.episode_overview:
|
||||
info_labels["plot"] = item.episode_overview
|
||||
info_labels["mediatype"] = "episode" if is_episode else "tvshow"
|
||||
|
||||
# Navigation: Episoden direkt abspielen, Serien zur Staffelauswahl
|
||||
match = _trakt_find_in_plugins(item.title)
|
||||
if match:
|
||||
plugin_name, matched_title = match
|
||||
if is_episode:
|
||||
action = "play_episode"
|
||||
params: dict[str, str] = {
|
||||
"plugin": plugin_name,
|
||||
"title": matched_title,
|
||||
"season": f"Staffel {item.season}",
|
||||
"episode": f"Episode {item.episode}",
|
||||
}
|
||||
_add_directory_item(handle, label, action, params, is_folder=False, info_labels=info_labels, art=art)
|
||||
else:
|
||||
action = "seasons"
|
||||
params = {"plugin": plugin_name, "title": matched_title}
|
||||
_add_directory_item(handle, label, action, params, is_folder=True, info_labels=info_labels, art=art)
|
||||
else:
|
||||
_add_directory_item(handle, label, "search", {"query": item.title}, is_folder=True, info_labels=info_labels, art=art)
|
||||
|
||||
if len(items) >= LIST_PAGE_SIZE:
|
||||
_add_directory_item(handle, "Naechste Seite >>", "trakt_history", {"page": str(page + 1)}, is_folder=True)
|
||||
@@ -4613,8 +4747,20 @@ def _show_trakt_upcoming() -> None:
|
||||
}
|
||||
if item.show_year:
|
||||
info_labels["year"] = item.show_year
|
||||
if item.episode_overview:
|
||||
info_labels["plot"] = item.episode_overview
|
||||
|
||||
_, art, _ = _tmdb_labels_and_art(item.show_title)
|
||||
# Artwork: Trakt-Bilder als Basis, TMDB ergänzt fehlende Keys
|
||||
art: dict[str, str] = {}
|
||||
if item.episode_thumb:
|
||||
art["thumb"] = item.episode_thumb
|
||||
if item.show_fanart:
|
||||
art["fanart"] = item.show_fanart
|
||||
if item.show_poster:
|
||||
art["poster"] = item.show_poster
|
||||
_, tmdb_art, _ = _tmdb_labels_and_art(item.show_title)
|
||||
for _k, _v in tmdb_art.items():
|
||||
art.setdefault(_k, _v)
|
||||
|
||||
match = _trakt_find_in_plugins(item.show_title)
|
||||
if match:
|
||||
@@ -4736,7 +4882,11 @@ def _show_trakt_continue_watching() -> None:
|
||||
|
||||
@_router.route("search")
|
||||
def _route_search(params: dict[str, str]) -> None:
|
||||
_show_search()
|
||||
query = params.get("query", "").strip()
|
||||
if query:
|
||||
_show_search_results(query)
|
||||
else:
|
||||
_show_search()
|
||||
|
||||
|
||||
@_router.route("plugin_menu")
|
||||
|
||||
Reference in New Issue
Block a user