From 3c0891b6382d22cf5d60116c2c76b1d11f04c5e7 Mon Sep 17 00:00:00 2001 From: "itdrui.de" Date: Sun, 1 Mar 2026 19:17:58 +0100 Subject: [PATCH] =?UTF-8?q?dev:=20bump=20to=200.1.71-dev=20=E2=80=93=20vol?= =?UTF-8?q?lst=C3=A4ndiges=20Trakt-Scrobbling=20mit=20stop-Monitor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG-DEV.md | 7 +++++ addon/addon.xml | 2 +- addon/default.py | 74 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/CHANGELOG-DEV.md b/CHANGELOG-DEV.md index f975c9b..6c1f520 100644 --- a/CHANGELOG-DEV.md +++ b/CHANGELOG-DEV.md @@ -1,5 +1,12 @@ # Changelog (Dev) +## 0.1.71-dev - 2026-03-01 + +- Trakt Scrobble vollständig: scrobble/stop wird nach Wiedergabe-Ende mit berechnetem Fortschritt gesendet. +- Neue Funktion `_trakt_scrobble_stop_async()` und `_trakt_monitor_playback()`. +- Monitor blockiert den Plugin-Prozess bis Wiedergabe endet → Fortschritt wird korrekt berechnet. +- Damit wird Trakt "als gesehen" erst ab ≥ 80% Fortschritt markiert. + ## 0.1.70-dev - 2026-03-01 - Suchergebnisse werden über alle Plugins hinweg nach Titel gruppiert. diff --git a/addon/addon.xml b/addon/addon.xml index efd420e..d361f00 100644 --- a/addon/addon.xml +++ b/addon/addon.xml @@ -1,5 +1,5 @@ - + diff --git a/addon/default.py b/addon/default.py index ef9a4ee..ab3b3dd 100644 --- a/addon/default.py +++ b/addon/default.py @@ -3814,9 +3814,10 @@ def _play_final_link( player = xbmc.Player() player.play(item=link, listitem=list_item) - # Trakt Scrobble Start (Hintergrund-Thread) + # Trakt Scrobble: Start senden, dann blockierend auf Wiedergabe-Ende warten if trakt_media and _get_setting_bool("trakt_enabled", default=False): _trakt_scrobble_start_async(trakt_media) + _trakt_monitor_playback(trakt_media) def _trakt_scrobble_start_async(media: dict[str, object]) -> None: @@ -3844,6 +3845,77 @@ def _trakt_scrobble_start_async(media: dict[str, object]) -> None: threading.Thread(target=_do, daemon=True).start() +def _trakt_scrobble_stop_async(media: dict[str, object], progress: float = 100.0) -> None: + """Sendet scrobble/stop an die Trakt-API in einem Hintergrund-Thread.""" + def _do() -> None: + try: + 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: + return + client = TraktClient(client_id, client_secret, log=lambda m: _log(m, xbmc.LOGDEBUG)) + client.scrobble_stop( + access_token, + media_type=str(media.get("kind", "movie")), + title=str(media.get("title", "")), + tmdb_id=int(media.get("tmdb_id", 0)), + imdb_id=str(media.get("imdb_id", "")), + season=int(media.get("season", 0)), + episode=int(media.get("episode", 0)), + progress=progress, + ) + _log(f"Trakt scrobble/stop: {media.get('title')} progress={progress:.0f}%", xbmc.LOGDEBUG) + threading.Thread(target=_do, daemon=True).start() + + +def _trakt_monitor_playback(media: dict[str, object]) -> None: + """Blockiert bis die Wiedergabe endet, berechnet Fortschritt und sendet scrobble/stop. + + Muss im Haupt-Thread nach player.play() / setResolvedUrl() aufgerufen werden, + damit der Plugin-Prozess bis zum Wiedergabe-Ende aktiv bleibt. + """ + monitor = xbmc.Monitor() + player = xbmc.Player() + + # Warten bis Wiedergabe startet (max 15 Sekunden) + timeout = 0 + while not player.isPlaying() and timeout < 15: + if monitor.waitForAbort(1): + return + timeout += 1 + if not player.isPlaying(): + _log("Trakt monitor: Wiedergabe nicht gestartet.", xbmc.LOGDEBUG) + return + + last_pos: float = 0.0 + total_time: float = 0.0 + try: + total_time = player.getTotalTime() + except Exception: + pass + + # Wiedergabe verfolgen (alle 5 Sekunden) + while player.isPlaying() and not monitor.abortRequested(): + try: + last_pos = player.getTime() + if not total_time: + total_time = player.getTotalTime() + except Exception: + pass + monitor.waitForAbort(5) + + if monitor.abortRequested(): + return + + progress = min(100.0, (last_pos / total_time * 100.0)) if total_time > 0 else 100.0 + _log(f"Trakt monitor: Wiedergabe beendet, progress={progress:.0f}%", xbmc.LOGDEBUG) + _trakt_scrobble_stop_async(media, progress=progress) + + def _track_playback_and_update_state_async(key: str) -> None: # Eigenes Resume/Watched ist deaktiviert; Kodi verwaltet das selbst. return