dev: YouTube Fixes, Trakt Credentials fest, Upcoming Ansicht, Watchlist Kontextmenue

This commit is contained in:
2026-03-14 12:50:39 +01:00
parent 4b9ba6a01a
commit d51505e004
6 changed files with 276 additions and 55 deletions

View File

@@ -227,14 +227,14 @@ def _trakt_save_token(token) -> None:
addon.setSetting("trakt_token_expires", str(token.expires_at))
TRAKT_CLIENT_ID = "5f1a46be11faa2ef286d6a5d4fbdcdfe3b19c87d3799c11af8cf25dae5b802e9"
TRAKT_CLIENT_SECRET = "7b694c47c13565197c3549c7467e92999f36fb2d118f7c185736ec960af22405"
def _trakt_get_client():
"""Erstellt einen TraktClient falls client_id und client_secret konfiguriert sind."""
client_id = _get_setting_string("trakt_client_id").strip()
client_secret = _get_setting_string("trakt_client_secret").strip()
if not client_id or not client_secret:
return None
"""Erstellt einen TraktClient mit den fest hinterlegten Credentials."""
from core.trakt import TraktClient
return TraktClient(client_id, client_secret, log=lambda m: _log(m, xbmc.LOGDEBUG))
return TraktClient(TRAKT_CLIENT_ID, TRAKT_CLIENT_SECRET, log=lambda m: _log(m, xbmc.LOGDEBUG))
def _trakt_get_valid_token() -> str:
@@ -1167,6 +1167,7 @@ def _add_directory_item(
info_labels: dict[str, str] | None = None,
art: dict[str, str] | None = None,
cast: list[TmdbCastMember] | None = None,
context_menu: list[tuple[str, str]] | None = None,
) -> None:
"""Fuegt einen Eintrag (Folder oder Playable) in die Kodi-Liste ein."""
query: dict[str, str] = {"action": action}
@@ -1187,6 +1188,11 @@ def _add_directory_item(
setter(art)
except Exception:
pass
if context_menu:
try:
item.addContextMenuItems(context_menu)
except Exception:
pass
xbmcplugin.addDirectoryItem(handle=handle, url=url, listitem=item, isFolder=is_folder)
@@ -4015,21 +4021,27 @@ def _trakt_scrobble_start_async(media: dict[str, object]) -> None:
from core.trakt import TraktClient
except Exception:
return
client_id = _get_setting_string("trakt_client_id").strip()
client_secret = _get_setting_string("trakt_client_secret").strip()
access_token = _get_setting_string("trakt_access_token").strip()
if not client_id or not client_secret or not access_token:
if not access_token:
return
client = TraktClient(client_id, client_secret, log=lambda m: _log(m, xbmc.LOGDEBUG))
client = TraktClient(TRAKT_CLIENT_ID, TRAKT_CLIENT_SECRET, log=lambda m: _log(m, xbmc.LOGDEBUG))
media_type = str(media.get("kind", "movie"))
tmdb_id = int(media.get("tmdb_id", 0))
imdb_id = str(media.get("imdb_id", ""))
client.scrobble_start(
access_token,
media_type=str(media.get("kind", "movie")),
media_type=media_type,
title=str(media.get("title", "")),
tmdb_id=int(media.get("tmdb_id", 0)),
imdb_id=str(media.get("imdb_id", "")),
tmdb_id=tmdb_id,
imdb_id=imdb_id,
season=int(media.get("season", 0)),
episode=int(media.get("episode", 0)),
)
if _get_setting_bool("trakt_auto_watchlist", default=False) and (tmdb_id or imdb_id):
try:
client.add_to_watchlist(access_token, media_type=media_type, tmdb_id=tmdb_id, imdb_id=imdb_id)
except Exception:
pass
threading.Thread(target=_do, daemon=True).start()
@@ -4040,12 +4052,10 @@ def _trakt_scrobble_stop_async(media: dict[str, object], progress: float = 100.0
from core.trakt import TraktClient
except Exception:
return
client_id = _get_setting_string("trakt_client_id").strip()
client_secret = _get_setting_string("trakt_client_secret").strip()
access_token = _get_setting_string("trakt_access_token").strip()
if not client_id or not client_secret or not access_token:
if not access_token:
return
client = TraktClient(client_id, client_secret, log=lambda m: _log(m, xbmc.LOGDEBUG))
client = TraktClient(TRAKT_CLIENT_ID, TRAKT_CLIENT_SECRET, log=lambda m: _log(m, xbmc.LOGDEBUG))
client.scrobble_stop(
access_token,
media_type=str(media.get("kind", "movie")),
@@ -4652,13 +4662,11 @@ def _trakt_authorize() -> None:
time.sleep(code.interval)
from core.trakt import TraktClient
# Einzelversuch (kein internes Polling wir steuern die Schleife selbst)
client_id = _get_setting_string("trakt_client_id").strip()
client_secret = _get_setting_string("trakt_client_secret").strip()
tmp_client = TraktClient(client_id, client_secret, log=lambda m: _log(m, xbmc.LOGDEBUG))
tmp_client = TraktClient(TRAKT_CLIENT_ID, TRAKT_CLIENT_SECRET, log=lambda m: _log(m, xbmc.LOGDEBUG))
status, payload = tmp_client._post("/oauth/device/token", {
"code": code.device_code,
"client_id": client_id,
"client_secret": client_secret,
"client_id": TRAKT_CLIENT_ID,
"client_secret": TRAKT_CLIENT_SECRET,
})
if status == 200 and isinstance(payload, dict):
from core.trakt import TraktToken
@@ -4775,6 +4783,15 @@ def _show_trakt_history(page: int = 1) -> None:
info_labels["plot"] = item.episode_overview
info_labels["mediatype"] = "episode" if is_episode else "tvshow"
# Kontextmenue: Zur Watchlist hinzufuegen
ctx: list[tuple[str, str]] = []
if item.ids and (item.ids.tmdb or item.ids.imdb):
wl_type = "movie" if item.media_type == "movie" else "tv"
wl_params = urlencode({"action": "trakt_watchlist_add", "type": wl_type,
"tmdb_id": str(item.ids.tmdb), "imdb_id": item.ids.imdb})
ctx.append(("Zur Trakt-Watchlist hinzufuegen",
f"RunPlugin({sys.argv[0]}?{wl_params})"))
# Navigation: Episoden direkt abspielen, Serien zur Staffelauswahl
match = _trakt_find_in_plugins(item.title)
if match:
@@ -4787,13 +4804,13 @@ def _show_trakt_history(page: int = 1) -> None:
"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)
_add_directory_item(handle, label, action, params, is_folder=False, info_labels=info_labels, art=art, context_menu=ctx or None)
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)
_add_directory_item(handle, label, action, params, is_folder=True, info_labels=info_labels, art=art, context_menu=ctx or None)
else:
_add_directory_item(handle, label, "search", {"query": item.title}, is_folder=True, info_labels=info_labels, art=art)
_add_directory_item(handle, label, "search", {"query": item.title}, is_folder=True, info_labels=info_labels, art=art, context_menu=ctx or None)
if len(items) >= LIST_PAGE_SIZE:
_add_directory_item(handle, "Naechste Seite >>", "trakt_history", {"page": str(page + 1)}, is_folder=True)
@@ -4813,7 +4830,7 @@ def _show_trakt_upcoming() -> None:
return
xbmcplugin.setPluginCategory(handle, "Trakt: Upcoming")
_set_content(handle, "episodes")
_set_content(handle, "tvshows")
try:
from core.trakt import TraktCalendarItem as _TCI # noqa: F401
@@ -4829,20 +4846,50 @@ def _show_trakt_upcoming() -> None:
xbmcplugin.endOfDirectory(handle)
return
from datetime import datetime, date as _date
_WEEKDAYS = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"]
today = _date.today()
# Datum pro Item berechnen und nach Datum gruppieren
dated_items: list[tuple[_date, object]] = []
for item in items:
# Datum aufbereiten: ISO -> lesbares Datum
airdate = ""
airdate = today
if item.first_aired:
try:
from datetime import datetime, timezone
dt = datetime.fromisoformat(item.first_aired.replace("Z", "+00:00"))
airdate = dt.astimezone(tz=None).strftime("%d.%m.%Y")
airdate = dt.astimezone(tz=None).date()
except Exception:
airdate = item.first_aired[:10]
pass
dated_items.append((airdate, item))
last_date: _date | None = None
for airdate, item in dated_items:
# Datums-Ueberschrift einfuegen
if airdate != last_date:
last_date = airdate
delta = (airdate - today).days
if delta == 0:
heading = "Heute"
elif delta == 1:
heading = "Morgen"
elif 2 <= delta <= 6:
heading = _WEEKDAYS[airdate.weekday()]
else:
heading = f"{_WEEKDAYS[airdate.weekday()]} {airdate.strftime('%d.%m.')}"
sep = xbmcgui.ListItem(label=f"[B]{heading}[/B]")
sep.setProperty("IsPlayable", "false")
try:
_apply_video_info(sep, {"title": heading, "mediatype": "video"}, None)
except Exception:
pass
xbmcplugin.addDirectoryItem(handle=handle, url="", listitem=sep, isFolder=False)
# Episoden-Label mit Titel
ep_title = item.episode_title or ""
label = f"{item.show_title} \u2013 S{item.season:02d}E{item.episode:02d}"
if airdate:
label = f"{label} ({airdate})"
if ep_title:
label = f"{label}: {ep_title}"
info_labels: dict[str, object] = {
"title": label,
@@ -4855,15 +4902,18 @@ def _show_trakt_upcoming() -> None:
info_labels["year"] = item.show_year
if item.episode_overview:
info_labels["plot"] = item.episode_overview
if ep_title:
info_labels["tagline"] = ep_title
# Artwork: Trakt-Bilder als Basis, TMDB ergänzt fehlende Keys
# Artwork: Trakt-Bilder als Basis, TMDB ergaenzt 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["thumb"] = item.show_poster
art["poster"] = item.show_poster
if item.episode_thumb:
art["fanart"] = item.episode_thumb
elif item.show_fanart:
art["fanart"] = item.show_fanart
_, tmdb_art, _ = _tmdb_labels_and_art(item.show_title)
for _k, _v in tmdb_art.items():
art.setdefault(_k, _v)