BingeStats/.zcode/plans/plan-sess_b4edee64-dc94-41eb-b331-7a42ff971024.md
Tronax f55ff411c6
feat: add movies stats screen and improve provider sync
- Add MoviesScreen and corresponding navigation
- Implement movie data aggregation in ViewingDao and repository
- Enhance NetflixParser to support multiple JSON payload shapes
- Apply custom app theme and update launcher icons
- Add back navigation handling to ProviderSyncScreen
2026-08-04 17:46:57 +02:00

4.9 KiB

Plan: Filme-Tab + Web-Sync-Reparatur + Navigations-Bug

Drei unabhängige Fixes, alle ohne Datenbank-Migration (Room fallbackToDestructiveMigration ist aktiv, Schema bleibt v1, nur neue Queries).


Fix 1: Filme als eigener Tab in der Bottom-Navi

Aktuell ist das Dashboard "Filme"-Zählen kaputt (itemCount - topShows.episodeCount mit LIMIT 20) und Filme tauchen im Serien-Ranking auf.

1a. Neue DAO-Queries (ViewingDao.kt) — Room speichert contentType als TEXT ('MOVIE'/'SERIES'), daher String-Filter:

  • getTopMovies(limit)SELECT ... WHERE contentType = 'MOVIE' GROUP BY showTitle ORDER BY totalMinutes DESC
  • getTopShows(limit) → ergänze WHERE contentType != 'MOVIE' damit Filme aus dem Serien-Ranking verschwinden
  • getMoviesCount()SELECT COUNT(*) ... WHERE contentType = 'MOVIE'
  • getSeriesEpisodesCount()SELECT COUNT(*) ... WHERE contentType != 'MOVIE'

1b. WatchStats korrekt befüllen (StatsRepository.kt) — combine von 7 Flows (Vararg-Variante wie bereits vorhanden), echte Counts statt Heuristik. moviesCount und seriesEpisodesCount direkt aus der DB.

1c. Neue Datenmodelle (WatchStats.kt) — MovieSummary (analog ShowSummary) + topMovies: List<MovieSummary>-Feld in WatchStats.

1d. Neuer Screen MoviesScreen.kt — Clone von SeriesStatsScreen.kt mit MovieRankItem (zeigt Dauer statt Folgenanzahl, Movie-Icon statt "X Folgen"). Wiederverwendet ProviderBadge.

1e. Bottom-Navi erweitern (MainActivity.kt) — Neuer Screen.Movies("movies", "Filme", Icons.Default.Movie) zwischen Series und Analytics. NavHost erhält die neue Route.


Fix 2: Web-Sync JS-Extraktion reparieren

Root Cause: Der Button nimmt die komplette HTML-Seite (document.documentElement.outerHTML) und füttert sie dem CSV-Parser → 1 Müll-Eintrag mit <meta>-Tag-Text.

Recherche zeigt: DOM-Klassen sind bei Netflix gehasht (brittel). Der robuste Weg ist die Netflix Shakti-API aus dem WebView heraus, da die Session-Cookies (NetflixId) automatisch vorliegen.

2a. Provider-spezifische JS-Extraktion (ProviderSyncScreen.ktWebViewSyncSection):

  • Netflix: Injiziere JS, das window.netflix.reactContext.models.serverDefs.data.BUILD_IDENTIFIER + authURL liest, dann fetch('/api/shakti/'+buildId+'/viewingactivity?pg='+n) paginiert (Schleife bis vhSize erreicht) und ein sauberes JSON-Array von {title, seriesTitle, episodeTitle, date, duration} zurückgibt.
  • Prime Video: Parse inline-JSON aus script[type="text/template"] (props→widgets→content.content.titles[]), fallback auf [data-automation-id^="wh-date"].
  • Andere (Disney+/Apple): Greaceful-Degradation-Meldung "Web-Sync noch nicht unterstützt, bitte CSV verwenden".

2b. Toast-Feedback statt HTML-String: Der Button zeigt Fortschritt ("Lade Seite N…"). Die extrahierten JSON-Daten gehen an viewModel.importScrapedWebHistory(json, provider) — bereits vorhanden, leitet an NetflixParser.parseJsonApiPayload weiter.

2c. parseJsonApiPayload robuster machen (NetflixParser.kt): Akzeptiert jetzt sowohl das Netflix-viewedItems-Array als auch ein flaches Array (für den Prime-Fall). Field-Reading defensiv (seriesTitle für Episoden, title für Filme → korrekter contentType). Dauer aus dem duration-Feld (ms → Minuten) falls vorhanden, sonst Default.


Fix 3: Navigations-Bug (hängt nach "Verlauf Auslesen" fest)

Symptom: Nach dem Sync-Screen kommt man nicht mehr aufs Dashboard. Ursachen: WebView wird nicht disposed, update-Lambda erzwingt Reloads, und das alte Parsing der kompletten HTML lief auf dem Main-Thread.

3a. WebView sauber lifecycle-gebunden (WebViewSyncSection):

  • DisposableEffect / remember { ...; onDispose { webView.destroy() } } statt freischwebendem Ref — verhindert, dass der WebView den Screen blockiert.
  • Reload-Schleife in update-Lambda entfernen (vergleicht nur noch, ob sich der Provider geändert hat, nicht jeden Frame).

3b. Parsing off-main: Die extrahierten JSON-Daten werden ohnehin über viewModelScope.launch(Dispatchers.IO) verarbeitet (bereits in importScrapedWebHistory), aber das JS-evaluateJavascript-Ergebnis wird vor dem Parsen als String geprüft — keine riesige HTML-Seite mehr, die den Main-Thread blockiert.

3c. Navi-Zurück-Verhalten: BackHandler im Sync-Screen, der bei geladenem WebView zuerst webView.goBack() macht (falls History vorhanden) und erst dann navController.popBackStack(). So kommt man sicher vom Sync-Screen weg.


Validierung

  • ./gradlew assembleDebug (Java 21) baut fehlerfrei.
  • adb install -r auf das S24 Ultra.
  • Manueller Smoke-Test: Sample-Daten → Filme-Tab zeigt Filme, Serien-Tab nur Serien, Dashboard zählt korrekt. Navi nach Sync funktioniert.

Alle drei Fixes sind voneinander unabhängig und werden in einem Build-Zyklus umgesetzt. Keine DB-Schema-Änderung (nur neue Queries auf bestehenden Spalten).