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

13 KiB

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 (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.tswriteFileAtomicSync (tmp+rename, EPERM-Retry-Pattern, Windows-Fallback copy+unlink). 6 Tests.
    • src/main/infra/duration.tsparseDuration, formatDuration, formatDurationDashed. 18 Tests.
    • src/main/domain/update-version-utils.ts — verschoben von src/, normalizeUpdateVersion/compareUpdateVersions/isNewerUpdateVersion. 16 Tests.
    • src/main/domain/i18n-backend.tsBACKEND_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)