diff --git a/docs/superpowers/specs/2026-08-26-youtube-guest-quota-design.md b/docs/superpowers/specs/2026-08-26-youtube-guest-quota-design.md index bcc935d..bf82cfb 100644 --- a/docs/superpowers/specs/2026-08-26-youtube-guest-quota-design.md +++ b/docs/superpowers/specs/2026-08-26-youtube-guest-quota-design.md @@ -50,17 +50,97 @@ vorbehalten. | Reset-Zeitpunkt | Kalendertag nach Server-Zeit (Aingrad), kein rollierendes 24h-Fenster | | Suche fürs Limit | Suche bleibt für Gäste unbegrenzt — nur `POST /api/yt-dl` zählt | | Cookies für Gäste | Nein — nur Cloud-Accounts bekommen Dustins YouTube-Cookies; der `cookies`-Parameter wird bei Gast-Token-Anfragen ignoriert | +| Limit für Cloud-Accounts | **Nachtrag 2026-08-29:** 100 Downloads/Kalendertag je Nutzer (Dustin/Baka/Tinker je einzeln gezählt) — ersetzt das ursprüngliche „kein Limit". Grund: dieselbe Absicherung wie beim Gast-Limit, nur großzügiger, da Cloud-Accounts vertraute Nutzer mit Cookie-Zugriff sind. | ## Architektur Drei Zugangsklassen beim Proxy: -1. **Cloud-Account** (`Authorization: Bearer `, wie heute) — unbegrenzt, - Cookies automatisch. +1. **Cloud-Account** (`Authorization: Bearer `, wie heute) — **100 + Downloads/Kalendertag je Nutzer** (Nachtrag 2026-08-29, ersetzt + „unbegrenzt"), Cookies automatisch, Suche unbegrenzt (wie bei Gästen). 2. **Gast** (`X-Guest-Token: `, neu) — 5 Downloads/Kalendertag, Suche unbegrenzt, keine Cookies. 3. **Kein Zugang** — weder Bearer noch gültiger Gast-Token → 401 wie heute. +## Nachtrag 2026-08-29: 100/Tag-Limit auch für Cloud-Accounts + +Dustin hat beim erneuten Durchsprechen der Architektur (nach den +Login-Race-Fixes) bestätigt, dass auch Cloud-Accounts (Dustin, Baka, +Tinker) ein Tages-Limit bekommen sollen — 100 statt der ursprünglich +geplanten 5, aber nicht mehr unbegrenzt. Grund: dieselbe +Missbrauchs-/Kosten-Absicherung wie beim Gast-Limit (YouTube-Rate-Limits, +Serverlast), nur mit großzügigerem Kontingent, weil Cloud-Accounts +vertraute, bekannte Nutzer sind und zusätzlich Cookie-Zugriff haben. + +**Übernommene Entscheidungen des Gast-Limits (analog, keine neue +Diskussion nötig):** Reset zum Kalendertag (Server-Zeit), Suche zählt +nicht, nur `POST /api/yt-dl` zählt, Klartext-429-Fehlermeldung ohne +automatischen Retry. + +**Unterschied zum Gast-Limit:** Cloud-Accounts zählen **pro Nutzername** +(aus dem verifizierten JWT), nicht pro Gerät/Token — ein Nutzer, der die +App auf zwei Geräten installiert hat, teilt sich also ein gemeinsames +Kontingent (folgerichtig, da derselbe Baka-Account). + +### Server-seitige Änderungen (zusätzlich zu den bestehenden Abschnitten oben — Hermes/`claude-server`-Worktree) + +- Neue Tabelle (oder Erweiterung von `guest_quota`, falls strukturell + identisch — Entscheidung liegt bei der Server-Session) analog zu + `guest_quota`, aber mit `username` statt `token` als Schlüssel: + ```sql + CREATE TABLE IF NOT EXISTS cloud_quota ( + username TEXT PRIMARY KEY, + count INTEGER NOT NULL DEFAULT 0, + count_date TEXT NOT NULL + ); + ``` +- `POST /api/yt-dl` bei Cloud-Zugriff (gültiger Bearer-JWT): dieselbe + Tageswechsel-Reset-Logik wie bei Gästen, aber Schlüssel = Nutzername aus + dem JWT. Bei `count >= 100` → `429` mit + `{"error": "Tages-Limit erreicht (100/Tag) — ab morgen wieder verfügbar", "cooldown": }`. + Erfolgsantwort bekommt zusätzlich `"cloud_remaining": ` (nur bei + Cloud-Zugriff, analog zu `guest_remaining` bei Gästen — beide Felder + schließen sich gegenseitig aus, nie beide gleichzeitig gesetzt). +- `GET /api/search` bleibt für Cloud-Accounts unverändert unbegrenzt (wie + für Gäste). + +### App-seitige Änderungen (diese Seite implementiere ich) + +- `BakaAuth` (`lib/services/baka_auth.dart`) bekommt — analog zu + `GastZugang.verbleibend`/`.merkeVerbleibend(n)` — ein neues Feld + `int? verbleibend` (Getter) und `void merkeVerbleibend(int n)`, das den + zuletzt vom Server gemeldeten Kontingent-Stand hält (nur fürs Anzeigen, + `null` vor dem ersten Download in dieser Session). +- `YtDownloadService.herunterladen()` (`lib/services/yt_download_service.dart`): + liest nach einem erfolgreichen `POST /api/yt-dl` zusätzlich + `daten['cloud_remaining']` aus, und ruft bei `!gastAktiv` (also + Cloud-Zugriff) `auth.merkeVerbleibend(cloudRest)` auf — analog zur + bestehenden `gast!.merkeVerbleibend(gastRest)`-Zeile. Ein `429` läuft + bereits durch den bestehenden generischen `_fehlerText(antwort)`-Pfad + (kein neuer Fehlerfall nötig, der zeigt jeden Server-`error`-Text aus dem + 200-fremden Statuscode an — funktioniert für 429 genauso wie für 401 + außerhalb des Spezialfalls). +- UI (`_YouTubeBereich` in `lib/downloads/downloads_screen.dart`, + `YoutubeSearchScreen` in `lib/downloads/youtube_search_screen.dart`): + zeigt analog zum bestehenden Gast-Text („Als Gast unterwegs — noch N von + 5 heute") jetzt auch für angemeldete Cloud-Accounts einen Text „Noch N + von 100 heute", sobald `auth.verbleibend != null` — platziert an + vergleichbarer Stelle wie der Gast-Text, aber nur sichtbar wenn + `auth.istAngemeldet` (nicht für Gäste, die haben ihren eigenen Text). + +### Tests (Nachtrag) + +- **App:** `BakaAuth.merkeVerbleibend`, `YtDownloadService` liest + `cloud_remaining` bei Cloud-Zugriff (nicht bei Gast-Zugriff — die beiden + Felder dürfen sich nicht vermischen), Widget-Test für den neuen + „Noch N von 100 heute"-Text in beiden Bildschirmen. +- **Server** (Hermes): analog zu den bestehenden Gast-Tests, plus ein Test, + der sicherstellt, dass Gast- und Cloud-Kontingente unabhängig + voneinander gezählt werden (ein Cloud-Login verbraucht kein + Gast-Kontingent und umgekehrt — sie sind ohnehin durch unterschiedliche + Auth-Wege getrennt, aber das schadet nicht, explizit zu testen). + ## Server-seitige Komponenten **Betrifft `/home/dustin/scripts/yt_proxy.py` und ggf. `auth_common.py` —