Twitch-VOD-Manager/CHANGELOG.md
xRangerDE 5439786652 docs: comprehensive CHANGELOG 4.6.155 -> 5.1.0-alpha.1
43 commits seit 4.6.155, 20 neue Module, 219 unit tests, 5 alpha-Tags + 5.0.0 GA.
Bricht nach Pillar gegliedert auf, listet Breaking Changes (additive),
Build/Migration Checklist + Roadmap-Rest fuer 5.1.x.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 01:03:06 +02:00

218 lines
13 KiB
Markdown

# Changelog
Major-Bump 4.6.155 -> 5.0.0 (GA) + 5.1.0-alpha.1 (kontinuierliche post-GA Arbeit).
43 Commits, 20 neue Module in `src/main/`, 219 Unit-Tests (vorher 0).
---
## 5.1.0-alpha.1 (2026-05-12)
### Pillar 5 — UI Power (erste sichtbare Komponente)
- **Command Palette** (`Ctrl+K`): Modal mit 6 Tab-Wechsel-Befehlen (VODs, Queue, Streamers, Stats, Archive, Settings).
- ArrowUp/Down navigiert, Enter fuehrt aus, Esc/Overlay-Click schliesst.
- Prefix-Match auf Label + Synonyme (DE/EN).
- Registriert in `closeTopmostOpenModal` damit globaler Esc-Handler greift.
- Dateien: `src/index.html` (Modal-Markup), `src/styles.css` (.cp-* Klassen), `src/renderer-command-palette.ts`.
### Pillar 7 — Auto-Discovery (Scaffold)
- **`top-clips-crawler` Modul**: Helix `/clips` API + Pagination + Sortierung nach view_count desc.
- Konfigurierbar: `broadcaster_id`, `first` (1-100 geclamt), `started_at`/`ended_at`.
- `rangeLastDays(N)` Helper fuer ISO-Range "letzte N Tage".
- `fetchImpl` injizierbar fuer Tests (kein echter HTTP-Call in CI).
- 9 Tests (sortierung, snake-case→camelCase Mapping, query-string, clamping, error paths).
## 5.1.0-alpha.0 (2026-05-11)
### Pillar 1 — Live Recording (Storage-Layer)
- **`integrity-check` Modul**: ffprobe-JSON-Parser + Verdict-Assessor.
- `parseFfprobeJson(rawJson)` -> `ProbeResult` (streams, duration, size).
- `assessIntegrity(probe, opts)` -> `IntegrityVerdict` (ok, reasons[], hasVideo, hasAudio).
- Reasons: `no-video-stream`, `duration-too-short`, `duration-mismatch:actual=Xs,expected=Ys`.
- Spawn-frei (Caller liefert JSON-String) -> 12 Tests ohne ffprobe-Dependency.
### Pillar 6 — Smart-Resume (Archive-Index)
- **`archive-files-store` Modul**: CRUD auf `archive_files` Tabelle.
- upsert / get / list (filter by streamer, ordered by createdAt DESC NULLS LAST).
- setVerified, delete.
- `summaryByStreamer()` (Datei-Count + total bytes pro Streamer, sortiert).
- `totalBytes()` (sum aller archive_files).
- normalizeLogin auf streamer_login bei Write+Filter -> '@Alice'/'Alice'/'alice' kollabieren zu 'alice'.
- 10 Tests.
### Pillar 4 — Architecture Split (4 weitere Helpers extrahiert)
- **`src/main/infra/format-helpers.ts`** mit 4 pure Funktionen aus main.ts:
- `sanitizeFilenamePart` (Windows-FS-verbotene Chars + Path-Separators -> '_').
- `formatTwitchDurationFromSeconds` ('1h2m3s'-Style, NaN/negative geclamt auf 0).
- `formatDateWithPattern` (yyyy/yy/MM/M/dd/d/HH/H/hh/h/mm/m/ss/s Tokens, Backslash-strip).
- `getMergeGroupPhaseText` (DE/EN, mit Language als Parameter — main.ts hat 1-Arg-Adapter).
- 24 Tests.
## 5.0.0 (GA, 2026-05-11)
Major-Bump. 4.6.155-User sehen das ueber Auto-Updater (sobald Gitea-Release publiziert ist).
### Pillar 3 — SQLite-Migration (DONE, Breaking)
Source-of-Truth bleibt JSON (`C:\ProgramData\Twitch_VOD_Manager\config.json` + `download_queue.json`) — SQLite ist Shadow-Schreibziel. Cutover (SQLite wird Master) erfolgt in spaeterem Release.
- **Schema v5** in `src/main/infra/schema-v5.ts` (inline SQL-Konstante, kein non-TS Asset im Build):
- `schema_meta(key PK, value)` — version-tracking.
- `config_kv(key PK, value, updated_at)` — KV-Mirror der config.json fuer alle ungebundenen Keys.
- `queue_items(id PK, streamer_login, vod_id, clip_id, title, output_path, status, progress_pct, error_message, created_at, updated_at, completed_at, payload_json)` — Mirror der Queue, mit Index auf status / streamer_login / created_at.
- `downloaded_vods(vod_id PK, downloaded_at)` — bounded list von schon-runtergeladenen VODs.
- `streamers(login PK, auto_record, auto_vod_download, added_at)` — Streamer-Watchlist, Indices auf beiden auto_* Flags.
- `archive_files(path PK, streamer_login, size_bytes, duration_seconds, created_at, verified)` — Archiv-Index, Index auf streamer_login.
- `migrations_applied(name PK, applied_at, payload)` — Migrator-Marker (Idempotenz).
- `oauth_accounts(id PK AUTOINC, provider, twitch_user_id, login, display_name, encrypted_access_token, encrypted_refresh_token, expires_at, scopes_json, is_default, created_at, updated_at)` — UNIQUE(provider, twitch_user_id), Indices auf provider + is_default.
- `chunk_index(id PK AUTOINC, item_id, chunk_seq, sha1_hex, bytes, created_at)` — UNIQUE(item_id, chunk_seq), Indices auf item_id + sha1_hex.
- **`db.ts` Wrapper** (`src/main/infra/`):
- better-sqlite3 unter der Haube, WAL-Journal + busy_timeout 5000ms + foreign_keys ON.
- Public `DbHandle` Interface: run / get / all / transaction / runBatch / close / raw.
- 10 Tests (creates file, schema_meta=5, WAL aktiv, idempotenter open, roundtrip insert/select, transaction commit + rollback, oauth_accounts INSERT + UNIQUE, chunk_index INSERT + UNIQUE).
- **Migrator** (`src/main/domain/migrator.ts`):
- Idempotent (marker in migrations_applied, zweimaliger Aufruf = identischer Endzustand).
- Fail-soft (malformed JSON wird ins `errors[]` Array geschrieben, kein Crash).
- Per-source `.v4-backup` Copy nach erfolgreichem Migrations-Run.
- 31 whitelisted config_kv keys (language, performance_mode, alle filename_template_*, alle discord_*, alle auto_*, etc.).
- downloaded_vod_ids als Set-Insert in `downloaded_vods`.
- auto_record_streamers + auto_vod_download_streamers normalisiert via `normalizeLogin` (lowercase + @-Stripping), upserted in `streamers` mit dem entsprechenden Flag.
- download_queue.json -> queue_items, mit payload_json als kompletter JSON-Dump des Original-Items (so dass auch unbekannte Felder erhalten bleiben).
- 8 Tests (leeres AppData / config-keys / vod-ids / streamers / queue / idempotent / backup-files / malformed-json).
- **DB-Handle-Singleton** in main.ts:
- Long-lived `appDb: DbHandle | null` Modul-scope, geoeffnet im `app.whenReady` Block.
- `getAppDb()` Getter exportiert (Voraussetzung fuer kommende Recorder-Integration).
- `shutdownCleanup` schliesst die DB vor dem Debug-Log-Flush -> WAL-Checkpoint sauber.
- Fail-soft: bei better-sqlite3 Native-Build-Fehler bleibt `appDb = null` und die App startet trotzdem (JSON-Pfad ist weiterhin der Master).
- Logger-Output: JSON-serialisiertes MigrationResult landet in `debug.log` als `[ts] sqlite-migrator | {...}`.
### Pillar 2 — Twitch OAuth (Scaffold)
Twitch unterstuetzt **kein** Device Code Flow (im goal.md falsch angenommen). Korrektur: Authorization Code Flow + PKCE via System-Browser + Loopback-Redirect (RFC 8252). IPC-Wiring + Renderer-Login-Button + Twitch Dev App Registrierung folgen post-5.0.
- **`secure-storage` Modul** (`src/main/infra/`):
- `SecureStorage` Interface (isEncryptionAvailable, encrypt, decrypt).
- `MemorySecureStorage` (base64 ohne echte Crypto, fuer Tests / Headless-Envs — meldet `isEncryptionAvailable()=false`).
- `createElectronSecureStorage()` wrappt `electron.safeStorage` (Win Credential Manager / macOS Keychain / Linux libsecret).
- 7 Tests.
- **`token-store` Modul** (`src/main/domain/`):
- CRUD auf `oauth_accounts` mit Encryption-on-Write / Decryption-on-Read.
- upsert mit ON CONFLICT(provider, twitch_user_id) DO UPDATE — neue Token ueberschreiben alte.
- getDefault / setDefault sind provider-scoped (genau ein Default pro Provider).
- Scopes serialisiert als JSON-Array.
- 11 Tests (insert / update-not-duplicate / list / filter / default-toggle / getAccessToken decrypt / scopes / delete / expiresAt / list-by-id).
- **PKCE-Pair** (`src/main/domain/pkce.ts`):
- 32-Byte verifier (43-char base64url), S256 challenge.
- `generateState()` fuer CSRF-Schutz.
- 7 Tests (S256 marker, charset, sha256-derived, entropy).
- **Loopback-Server** (`src/main/infra/loopback-server.ts`):
- Ephemerer HTTP-Server auf 127.0.0.1:PORT (port=0 = OS waehlt, kein Firewall-Prompt).
- Path-Prefix-Filter (default `/oauth/callback`).
- HTML-Success / Error Antwort fuer den User-Browser.
- `awaitParams({timeoutMs})` returnt `URLSearchParams` oder rejected.
- 5 Tests.
- **Twitch-OAuth Flow** (`src/main/domain/twitch-oauth.ts`):
- `startLoginFlow({clientId, scopes})` -> Auth-URL + Loopback-Server.
- `awaitAuthorizationCode(flow)` validiert state (CSRF), wirft auf error= oder missing code.
- `exchangeCodeForToken(...)` POST an `id.twitch.tv/oauth2/token` mit PKCE verifier.
- `fetchTwitchUserInfo(accessToken, clientId)` -> Helix `/users` -> twitch_user_id + login + display_name.
- Alle Helix-Calls nehmen `fetchImpl` als optionalen Parameter (Testbarkeit ohne echten HTTP-Call).
- 9 Tests.
### Pillar 6 — Smart-Resume (Foundation)
- **`chunk-hash` Modul** (`src/main/infra/`):
- `hashBuffer(Buffer)` -> sha1 hex (sync).
- `hashFile(path)` -> Promise<sha1 hex> (streaming, blockiert keinen Event-Loop bei grossen Recorded-Segments).
- 8 Tests (known-vectors fuer 'hello' und empty, large-buffer determinism, file=buffer hash, missing-file rejects).
- **`chunk-index-store` Modul** (`src/main/domain/`):
- CRUD auf `chunk_index`.
- record / listForItem (sorted by chunk_seq) / countForItem / lookupBySha1 (dedupe candidates) / deleteForItem (returns count).
- ON CONFLICT(item_id, chunk_seq) DO UPDATE -> re-recorded segments ueberschreiben den alten Hash.
- 8 Tests.
### Pillar 4 — Architecture Split (Start)
main.ts ging von 7485 LoC auf 7276 LoC (-209). 20 Module in `src/main/{infra,domain}/`. 18 Test-Files.
**Foundation (5.0.0-alpha.0):**
- `vitest` als Test-Runner (replacements/erweiterung der bisherigen Playwright-only Suite).
- `test:unit` + `test:unit:watch` Scripts; `test:e2e:release` kettet jetzt build + unit + update-logic + 3 Playwright-Stages.
- 5 Initial-Extraktionen (alle pure, alle mit Tests):
- `src/main/infra/fs-atomic.ts``writeFileAtomicSync` (tmp+rename, EPERM-Retry-Pattern, Windows-Fallback copy+unlink). 6 Tests.
- `src/main/infra/duration.ts``parseDuration`, `formatDuration`, `formatDurationDashed`. 18 Tests.
- `src/main/domain/update-version-utils.ts` — verschoben von `src/`, `normalizeUpdateVersion`/`compareUpdateVersions`/`isNewerUpdateVersion`. 16 Tests.
- `src/main/domain/i18n-backend.ts``BACKEND_MESSAGES` (DE/EN, 38 keys) + pure `tBackend(key, params, lang)`. main.ts hat 2-arg Adapter der `config.language` injiziert. 8 Tests.
- `src/main/domain/config-normalize.ts` — 8 pure Normalizer (`normalizeAutoRecordPollSeconds`, `normalizeAutoRecordList`, `normalizeStreamlinkQuality`, `normalizeFilenameTemplate`, `normalizeMetadataCacheMinutes`, `normalizePerformanceMode`, `isPlainObject`, `normalizeLogin`) + `VALID_STREAMLINK_QUALITIES` + `PerformanceMode` type. 47 Tests.
**Post-Foundation (5.1.0-alpha.0):**
- `src/main/infra/format-helpers.ts` (siehe oben).
---
## Breaking Changes
| # | Was bricht | Migration | User-Impact |
|---|---|---|---|
| BC-1 | Config-Speicherort additiv erweitert | Migrator on first 5.0 start, `.v4-backup` der JSONs bleibt liegen | Einmalig sek-30s Migrations-Dialog (silent, ins debug.log geloggt) |
| BC-2 | Auto-Updater Channel | 4.6-User auf `stable` erkennen 5.0.0 sobald Gitea-Release publiziert ist | Auto-Update wie immer (Klick Install bei Notification) |
Kein hard-break fuer existing State. Migrator ist additiv + idempotent. Wenn er fehlschlaegt, bleibt JSON der Master.
---
## Vergleich: 4.6.155 vs 5.1.0-alpha.1
| Metric | 4.6.155 | 5.1.0-alpha.1 |
|---|---|---|
| Total LoC (src/) | 16016 | ~16700 (mit Tests) |
| main.ts | 7485 | 7276 |
| Module in src/main/ | 0 | 20 |
| Unit Tests (vitest) | 0 | 219 |
| E2E Tests (Playwright) | 4 stages | 4 stages (gleich) |
| Dependencies | axios, electron-updater | + better-sqlite3 |
| DevDeps | playwright, typescript, eslint | + vitest, @types/better-sqlite3 |
| SQLite-Schema | n/a | v5 (9 tables) |
| OAuth-Support | n/a | scaffold (AuthCode+PKCE) |
| Smart-Resume | n/a | chunk-hash + index store |
| Command Palette | n/a | Ctrl+K |
| Themes | 5 (Twitch, Discord, YouTube, Apple, Light) | gleich |
| Locales | DE, EN | gleich |
---
## Was in 5.1.x noch kommt (Roadmap, post diesem Release)
- **Pillar 2 OAuth-Vervollstaendigung**: IPC-Handler `oauth-twitch-login`/`-logout`/`-whoami`, Renderer-Login-Button im Settings-Tab, Client-ID-Config-Feld (Twitch Dev App Registration ist User-seitig).
- **Pillar 4 Architektur-Split**: state-coupled Module aus main.ts (twitch-api, queue, download, record, cutter, stats, updater). Voraussetzung: State-Container-Design entscheiden.
- **Pillar 5 UI Power-Restpaket**: Virtual List fuer Queue/Archive (10k+ Eintraege), Mini-Player (HLS-Stream Vorschau via hls.js), Drag-Reorder, mehr Command-Palette-Commands (Streamer-Suche).
- **Pillar 6 Smart-Resume Integration**: chunk-hash-Producer in den live Recorder einklinken, Resume-on-startup Dialog.
- **Pillar 7 Auto-Discovery Erweiterung**: Top-Clip-Crawler Scheduler (alle X Tage), Follow-Sync (importiert die Follow-Liste des eingeloggten Twitch-Users), Top-Clip-Modal im Streamer-Tab.
---
## Migration Checklist fuer Endnutzer
Beim ersten Start von 5.0 (oder 5.1) auf einer bestehenden 4.6.x-Installation:
1. App starten — Migrator laeuft automatisch, fail-soft.
2. Pruefen: `%PROGRAMDATA%\Twitch_VOD_Manager\app.db` existiert.
3. Pruefen: `config.json.v4-backup` und `download_queue.json.v4-backup` liegen daneben.
4. debug.log nach `sqlite-migrator | ` Zeile suchen — sollte JSON mit `configMigrated:true, queueMigrated:true, downloadedVodsCount:N, streamersCount:N` enthalten.
5. Bei Problemen: einfach 4.6.155 reinstallieren — JSON ist unangetastet, SQLite war nur Shadow-Schreibziel.
---
## Build / Release
Setup.exe gebaut mit `npm run dist:win`. Auto-Updater pickt das via `latest.yml` (sha512 + size + releaseDate).
Co-Authored-By: Claude Opus 4.7 (1M context)