Format code with Prettier (8 modified files from H1 decomposition)

Aligns with CC2 enforcement: ESLint + Prettier + noImplicitAny now enforced.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-07-01 07:54:02 -04:00
parent 9e0064aab2
commit 36699531cf
38 changed files with 1004 additions and 458 deletions
+49 -24
View File
@@ -13,7 +13,8 @@ import {
getAppIconImage,
YTDLP_MISSING_MSG
} from './binaries'
import { getSettings, getDownloadArchivePath, getDefaultMediaDir } from './settings'
import { getSettings } from './settings'
import { getDownloadArchivePath, getDefaultMediaDir } from './paths'
import { ensureManagedYtdlp } from './ytdlp'
import { materializeCookies, hasStoredCookies } from './cookies'
import { listTemplates } from './templates'
@@ -29,6 +30,12 @@ import {
} from './buildArgs'
import { cleanError } from './log'
import { addErrorLog } from './errorlog'
import {
STALL_TIMEOUT_MS,
META_PROBE_TIMEOUT_MS,
META_MAX_BUFFER,
STDERR_TAIL_BYTES
} from './constants'
import {
IpcChannels,
type StartDownloadOptions,
@@ -62,14 +69,6 @@ function releaseActive(id: string, rec: ActiveDownload): void {
if (active.get(id) === rec) active.delete(id)
}
// Backstop for B1: if a spawned yt-dlp goes completely silent — no stdout or
// stderr — for this long, treat it as wedged, kill it, and error the item so the
// concurrency slot frees instead of leaking forever. Generous on purpose so a
// long, output-less post-processing step (e.g. a large ffmpeg merge) isn't
// mistaken for a stall; --socket-timeout (buildArgs) handles the common
// dead-connection case at the network layer.
const STALL_TIMEOUT_MS = 5 * 60_000
/**
* Whether any yt-dlp download is currently running. Used by the window's close
* handler to keep the app alive in the tray (instead of quitting and killing the
@@ -137,7 +136,7 @@ function probeMeta(ytdlp: string, url: string): Promise<DownloadMeta | null> {
'--',
url
],
{ windowsHide: true, maxBuffer: 4 * 1024 * 1024, timeout: 30_000 },
{ windowsHide: true, maxBuffer: META_MAX_BUFFER, timeout: META_PROBE_TIMEOUT_MS },
(err, stdout) => {
if (err) return resolve(null)
const [title, uploader, duration] = stdout.split(META_SEP).map((l) => l.trim())
@@ -211,7 +210,7 @@ export function buildCommand(opts: StartDownloadOptions, cookiesFile?: string):
youtubePoToken: settings.youtubePoToken
}
const extraArgs = resolveExtraArgs(opts, settings)
return buildArgs(opts, outputTemplate, options, getBinDir(), access, extraArgs)
return buildArgs({ opts, outputTemplate, options, binDir: getBinDir(), access, extraArgs })
}
/** Build the exact command line for the current form state, without running it. */
@@ -333,6 +332,34 @@ export function startDownload(wc: WebContents, opts: StartDownloadOptions): Star
})
}
// Wire the child's streams + teardown (CL3). resolvedTitle is passed as a getter
// because the parallel probeMeta above may fill it in after this returns.
wireChildProcess({ wc, opts, rec, cleanup: cleanupCookies, getTitle: () => resolvedTitle })
return { ok: true }
}
// --- Child-process wiring ---------------------------------------------------
/**
* Wire a spawned yt-dlp child's stdout/stderr/close/error to download events, and
* run the B1 idle watchdog. Extracted from startDownload so that function reads as
* a linear spawn + pre-flight and this owns the streaming/teardown lifecycle (CL3).
*
* `getTitle` is read lazily on each event: the completion/error paths need the
* best title known *at settle time*, which the parallel metadata probe may only
* fill in after wiring is set up.
*/
function wireChildProcess(params: {
wc: WebContents
opts: StartDownloadOptions
rec: ActiveDownload
cleanup: () => void
getTitle: () => string | undefined
}): void {
const { wc, opts, rec, cleanup, getTitle } = params
const child = rec.child
let stdoutBuf = ''
let stderrTail = ''
let filePath: string | undefined
@@ -360,13 +387,13 @@ export function startDownload(wc: WebContents, opts: StartDownloadOptions): Star
if (settled || rec.canceled || rec.paused) return
settled = true
clearWatchdog()
cleanupCookies()
cleanup()
releaseActive(opts.id, rec)
killTree(rec)
const msg = `Download stalled — no activity for ${Math.round(STALL_TIMEOUT_MS / 60_000)} min. Stopped; retry to resume.`
send(wc, { type: 'error', id: opts.id, error: msg })
logFailure(opts, resolvedTitle, msg)
notify(wc, resolvedTitle ?? 'Download failed', msg)
logFailure(opts, getTitle(), msg)
notify(wc, getTitle() ?? 'Download failed', msg)
}, STALL_TIMEOUT_MS)
}
bumpWatchdog()
@@ -397,20 +424,20 @@ export function startDownload(wc: WebContents, opts: StartDownloadOptions): Star
child.stderr?.on('data', (chunk: Buffer) => {
bumpWatchdog()
stderrTail = (stderrTail + chunk.toString()).slice(-4000)
stderrTail = (stderrTail + chunk.toString()).slice(-STDERR_TAIL_BYTES)
})
child.on('error', (err) => {
if (settled) return
settled = true
clearWatchdog()
cleanupCookies()
cleanup()
releaseActive(opts.id, rec)
// A paused download was killed on purpose — stay silent, like a cancel.
if (!rec.canceled && !rec.paused) {
send(wc, { type: 'error', id: opts.id, error: err.message })
logFailure(opts, resolvedTitle, err.message)
notify(wc, resolvedTitle ?? 'Download failed', err.message)
logFailure(opts, getTitle(), err.message)
notify(wc, getTitle() ?? 'Download failed', err.message)
}
})
@@ -418,23 +445,21 @@ export function startDownload(wc: WebContents, opts: StartDownloadOptions): Star
if (settled) return
settled = true
clearWatchdog()
cleanupCookies()
cleanup()
releaseActive(opts.id, rec)
// Canceled: renderer already showed 'canceled'. Paused: renderer showed
// 'paused' and keeps the .part for a later resume. Either way, no event.
if (rec.canceled || rec.paused) return
if (code === 0) {
send(wc, { type: 'done', id: opts.id, filePath })
notify(wc, resolvedTitle ?? 'Download complete', 'Finished downloading.')
notify(wc, getTitle() ?? 'Download complete', 'Finished downloading.')
} else {
const msg = cleanError(stderrTail) || `yt-dlp exited with code ${code}`
send(wc, { type: 'error', id: opts.id, error: msg })
logFailure(opts, resolvedTitle, msg)
notify(wc, resolvedTitle ?? 'Download failed', msg)
logFailure(opts, getTitle(), msg)
notify(wc, getTitle() ?? 'Download failed', msg)
}
})
return { ok: true }
}
// Kill the whole process tree (/T) so the spawned ffmpeg child dies too. Resolve