e19f988c72
The watched live session for the six deferred lifecycle items, each verified against real yt-dlp/Electron behavior on Windows: - R4: cancel now deletes the download's orphaned partials. The engine captures the final output path via a new `--print before_dl:dest|%(filename)s` (empirically %(filepath)s is still 'NA' that early) and the close handler sweeps only unambiguous intermediates (.part / .part-Frag* / .ytdl / <stem>.fNNN.<ext>) via the pure, unit-tested lib/partials.ts — never a bare <stem>.<ext> or another download's files. Live-verified with decoys. - L65: verified-acceptable, no code change — a taskkill /F pause leaves the .part byte-intact and yt-dlp --continue resumes at the exact byte offset. - L139: spawn↔self-update interlock (new ytdlpLock.ts). The updater refuses while any yt-dlp spawn is live; download/terminal IPC handlers hold (await) behind an in-progress exe rewrite instead of failing. - B2: source indexing is cancellable — AbortSignal through the probe walk (kills the in-flight child, live-verified via tasklist), sources:index-cancel IPC, and a Cancel button in the Library add-source row. - B6: app-update download is cancellable — app:update-cancel routes through the existing finish() teardown (abort + destroy + unlink, live-verified on Electron net.request); Cancel button under the update progress bar. The checksum phase is cancelable too. - L143: closing the window mid-download (non-tray mode) now offers a native tray-independent "Quit anyway / Keep downloading" dialog, replacing the passive "use the tray to quit" notification. Peer-review pass caught and fixed: a non-reentrant update lock (concurrent begin clobbered the settle promise), a dead Cancel window during the B6 checksum fetch + a canceller slot-ownership bug (L140 shape), and a persist-after-cancel hole in the index walk. typecheck + 304 tests + eslint + production build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
40 lines
2.0 KiB
TypeScript
40 lines
2.0 KiB
TypeScript
/**
|
|
* Pure identification of a download's orphaned yt-dlp intermediate files (R4).
|
|
*
|
|
* When a running download is canceled, yt-dlp is tree-killed and leaves partial
|
|
* files behind in the output folder: the growing `.part`, fragment pieces
|
|
* (`.part-Frag*`) and the `.ytdl` fragment-state file for fragmented formats, plus
|
|
* any already-finished per-stream file (`<stem>.fNNN.<ext>` — e.g. the video half
|
|
* of a video+audio download whose audio was still in flight). A plain cancel never
|
|
* removes these, so they accumulate. download.ts captures the resolved FINAL output
|
|
* path from the before_dl `dest|` print; this module turns that path + a folder
|
|
* listing into the exact set to delete.
|
|
*
|
|
* Kept free of any electron/node-runtime chain (only `path.parse`) so it's
|
|
* unit-testable in isolation, the same posture as lib/formatters.ts (L37).
|
|
*
|
|
* SAFETY: it returns only unambiguous yt-dlp intermediates, matched on the output
|
|
* stem. It never returns a bare `<stem>.<ext>` completed file — so a pre-existing
|
|
* same-title download the user kept in the same folder is safe — nor any file
|
|
* belonging to a different download (different stem). This is the guard against the
|
|
* audit's "delete the wrong file if the destination capture is even slightly off".
|
|
*/
|
|
import { parse } from 'path'
|
|
|
|
export function orphanPartials(outputPath: string, entries: string[]): string[] {
|
|
// `name` is the basename without the final extension; a title containing dots is
|
|
// preserved (only the trailing `.<ext>` is stripped), so the stem stays exact.
|
|
const stem = parse(outputPath).name
|
|
if (!stem) return []
|
|
const stemDot = `${stem}.`
|
|
return entries.filter((entry) => {
|
|
if (!entry.startsWith(stemDot)) return false
|
|
return (
|
|
entry.endsWith('.part') || // <stem>….part — in-progress stream or merge
|
|
entry.includes('.part-Frag') || // fragment pieces (HLS/DASH)
|
|
entry.endsWith('.ytdl') || // fragment-download state file
|
|
/\.f\d+\.[^.]+$/i.test(entry) // finished per-stream file, e.g. <stem>.f399.mp4
|
|
)
|
|
})
|
|
}
|