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>
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
closeTopmostOpenModaldamit 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-crawlerModul: Helix/clipsAPI + 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".fetchImplinjizierbar fuer Tests (kein echter HTTP-Call in CI).- 9 Tests (sortierung, snake-case→camelCase Mapping, query-string, clamping, error paths).
- Konfigurierbar:
5.1.0-alpha.0 (2026-05-11)
Pillar 1 — Live Recording (Storage-Layer)
integrity-checkModul: 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-storeModul: CRUD aufarchive_filesTabelle.- 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.tsmit 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.tsWrapper (src/main/infra/):- better-sqlite3 unter der Haube, WAL-Journal + busy_timeout 5000ms + foreign_keys ON.
- Public
DbHandleInterface: 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-backupCopy 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 instreamersmit 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 | nullModul-scope, geoeffnet imapp.whenReadyBlock. getAppDb()Getter exportiert (Voraussetzung fuer kommende Recorder-Integration).shutdownCleanupschliesst die DB vor dem Debug-Log-Flush -> WAL-Checkpoint sauber.- Fail-soft: bei better-sqlite3 Native-Build-Fehler bleibt
appDb = nullund die App startet trotzdem (JSON-Pfad ist weiterhin der Master). - Logger-Output: JSON-serialisiertes MigrationResult landet in
debug.logals[ts] sqlite-migrator | {...}.
- Long-lived
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-storageModul (src/main/infra/):SecureStorageInterface (isEncryptionAvailable, encrypt, decrypt).MemorySecureStorage(base64 ohne echte Crypto, fuer Tests / Headless-Envs — meldetisEncryptionAvailable()=false).createElectronSecureStorage()wrapptelectron.safeStorage(Win Credential Manager / macOS Keychain / Linux libsecret).- 7 Tests.
-
token-storeModul (src/main/domain/):- CRUD auf
oauth_accountsmit 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).
- CRUD auf
-
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})returntURLSearchParamsoder 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 anid.twitch.tv/oauth2/tokenmit PKCE verifier.fetchTwitchUserInfo(accessToken, clientId)-> Helix/users-> twitch_user_id + login + display_name.- Alle Helix-Calls nehmen
fetchImplals optionalen Parameter (Testbarkeit ohne echten HTTP-Call). - 9 Tests.
Pillar 6 — Smart-Resume (Foundation)
-
chunk-hashModul (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-storeModul (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.
- CRUD auf
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):
vitestals Test-Runner (replacements/erweiterung der bisherigen Playwright-only Suite).test:unit+test:unit:watchScripts;test:e2e:releasekettet 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 vonsrc/,normalizeUpdateVersion/compareUpdateVersions/isNewerUpdateVersion. 16 Tests.src/main/domain/i18n-backend.ts—BACKEND_MESSAGES(DE/EN, 38 keys) + puretBackend(key, params, lang). main.ts hat 2-arg Adapter derconfig.languageinjiziert. 8 Tests.src/main/domain/config-normalize.ts— 8 pure Normalizer (normalizeAutoRecordPollSeconds,normalizeAutoRecordList,normalizeStreamlinkQuality,normalizeFilenameTemplate,normalizeMetadataCacheMinutes,normalizePerformanceMode,isPlainObject,normalizeLogin) +VALID_STREAMLINK_QUALITIES+PerformanceModetype. 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:
- App starten — Migrator laeuft automatisch, fail-soft.
- Pruefen:
%PROGRAMDATA%\Twitch_VOD_Manager\app.dbexistiert. - Pruefen:
config.json.v4-backupunddownload_queue.json.v4-backupliegen daneben. - debug.log nach
sqlite-migrator |Zeile suchen — sollte JSON mitconfigMigrated:true, queueMigrated:true, downloadedVodsCount:N, streamersCount:Nenthalten. - 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)