H3: probe.ts now imports fmtBytes from lib/formatters instead of download.ts,
breaking the circular probe->download dependency direction.
M33: ipc.ts comment corrected to reflect that no batch cap exists; notes the
known M33 issue for future implementation.
L25: App.tsx taskbar subscriber skips IPC when fraction/mode/badge unchanged,
eliminating redundant roundtrips on every progress tick.
L32: paletteActions wrapped in useMemo so CommandPalette sees a stable array
reference; only rebuilds when isDark changes.
typecheck + 242 tests + eslint + prettier green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
H5: HistoryEntry now stores formatId+formatHasAudio; redownload passes them
back to addFromUrl so the original yt-dlp format is reused, not guessed.
Compound quality labels stripped to the preset prefix for old history.
L72: thumbnail now passed in redownload so re-queued rows keep their art.
H6: TerminalView log capped at MAX_LOG_LINES=2000 to prevent unbounded growth.
M21: --audio-quality omitted for lossless audioFormats (flac/wav).
L166: fmtEta hour rollover fixed in all 3 locations -- 2h00:00 not 120:00.
L165: queueStats formatSpeed precision aligned with fmtBytes.
L55: QueueItem suppresses sizeLabel when already in probed-format quality label.
L47: probeMeta null sends empty meta event so Resolving clears via SR6.
L68: notifiedBackground resets on window show for subsequent close-while-downloading.
L76: dup warning falls back to URL when title is still a placeholder.
L91: SettingsView onFormatSelect uses indexOf instead of unsafe tuple cast.
L98: Embed chapters field gets a hint text.
L108: BrowserWindow created with explicit title AeroFetch.
L37: pure formatters extracted to src/main/lib/formatters.ts and unit-tested.
L38: buildArgs test covers webm thumbnail suppression.
L39: CROP_SQUARE_PPA test is structural not exact-string.
L40: looksLikeUrl/looksLikeSingleVideo extracted to src/renderer/src/lib/urlHelpers.ts.
typecheck + 242 tests + eslint + prettier green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reformat from a one-off security report into a living checklist.
Adds the 2026-06-29 architectural review findings and a second
polish pass; all original security findings marked completed.
Items carry stable IDs for tracking across sessions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Generate build/icon.svg — a white download glyph on the teal brand square,
mirroring the in-app brand mark (Onboarding.tsx) — and rasterise it to a
multi-size build/icon.ico (256/128/64/48/32/16) via ImageMagick. Uncomment
win.icon in electron-builder.yml so installers stop falling back to the default
Electron icon. Marks CODE-AUDIT.md M3 placeholder-done; a designed asset is
still wanted before v1.0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A live smoke test of buildArgs' argv against the bundled yt-dlp + ffmpeg found
that ffprobe.exe was never bundled (resources/bin/ shipped only ffmpeg.exe).
yt-dlp resolves both from --ffmpeg-location, so duration-aware post-processing
failed at runtime for every user with "ffprobe not found": --sponsorblock-remove,
--force-keyframes-at-cuts, and --split-chapters (CODE-AUDIT C1).
- Bundle ffprobe.exe (matching n8.1.2 LGPL build, sha256-verified against the
already-bundled ffmpeg.exe) and document it in resources/bin/README.md as a
required binary; correct the stale "not committed to git" note.
- Guard startDownload against a missing ffmpeg.exe/ffprobe.exe up front so the
failure is a clear AeroFetch error, not a cryptic yt-dlp postprocessing one
(new getFfprobePath() in binaries.ts).
- Expand test/real-download.integration.test.ts from 2 to 8 live cases: crop,
sponsorblock-remove, audio opus re-encode, mkv+vp9 merge, subtitle+chapter
embed, restrict-filenames, download-archive skip, and Phase C extra-args.
All 8 pass against live yt-dlp + ffmpeg.
- ROADMAP: mark the Phase A/B/C smoke-test caveats done. CODE-AUDIT: add C1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Security:
- S1 (backup.ts): require explicit confirmation before enabling custom-command
templates from a backup file; import templates in a disabled state if declined
- S2 (cookies.ts): deny non-http/https popups in cookie login window, matching
the main window's setWindowOpenHandler defence
- S3 (download.ts): main-process concurrency cap — startDownload now rejects
spawns when active.size >= maxConcurrent (defence-in-depth; renderer already
enforces this but should not be the only gate)
- S4 (settings.ts, validation.ts): reject filenameTemplate values containing
path traversal (.. segments or absolute paths); validate outputDir is absolute
- S5 (history.ts, errorlog.ts, templates.ts, validation.ts): per-field
validation on JSON reads — invalid entries dropped rather than blindly trusted
Performance:
- P1 (settings.ts): getSettings() now only writes to disk when sanitization
actually changed a value; removes synchronous file I/O on every hot-path read
- P2 (ipc.ts, download.ts, downloads.ts): renderer forwards its pre-probed
metadata (title/channel/duration) via StartDownloadOptions.meta; main uses
it directly instead of spawning a redundant second yt-dlp probe
Maintainability:
- M1 (log.ts, download.ts, probe.ts): extract duplicated cleanError() to
src/main/log.ts; both callers import from the shared module
- M2 (buildArgs.ts): document parseExtraArgs escape-sequence limitations
- M3 (electron-builder.yml): clarify icon TODO as a pre-release action
- P3 (downloads.ts): document that lowering maxConcurrent mid-flight does
not pause active downloads (intended behaviour, now written down)
Tests:
- Add test/validation.test.ts covering isSafeFilenameTemplate, isSafeOutputDir,
isValidHistoryEntry, isValidErrorLogEntry, isTemplateLike (76 tests pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>