63687aaec3
schedule.ts keeps its own schtasks wrapper because it needs the numeric exit code (getScheduledSync checks r.code === 0), which execFileAsync deliberately doesn't surface. The 5 migrated sites are probe/ytdlp(x2)/ffmpeg/indexer/ download.probeMeta. Comment-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
54 lines
2.1 KiB
TypeScript
54 lines
2.1 KiB
TypeScript
import { execFile, type ExecFileOptions } from 'child_process'
|
||
|
||
/**
|
||
* Normalized result of a one-shot child-process run. `ok` is true on a clean
|
||
* exit-0; `timedOut` distinguishes a kill-by-timeout (Node sets `err.killed` when
|
||
* it terminates the process on the `timeout` option) from an ordinary non-zero
|
||
* exit, so callers can show "Timed out …" versus the captured stderr.
|
||
*/
|
||
export interface ExecResult {
|
||
ok: boolean
|
||
stdout: string
|
||
stderr: string
|
||
timedOut: boolean
|
||
/** The raw error on a non-zero exit / spawn failure (undefined when ok). */
|
||
error?: Error
|
||
}
|
||
|
||
/**
|
||
* `execFile` as a promise that never rejects (audit SIMP2/CC4/CC5). Replaces the
|
||
* six hand-rolled `new Promise((resolve) => execFile(…, cb))` wrappers across
|
||
* probe / ytdlp (×2) / ffmpeg / indexer / download.probeMeta — each caller now maps
|
||
* this normalized result to its own shape instead of repeating the `err.killed`
|
||
* timeout check and the resolve/reject plumbing. (schedule.ts keeps its own wrapper:
|
||
* it needs the numeric exit code, which this helper deliberately doesn't surface.)
|
||
*
|
||
* `windowsHide` defaults on (every caller wants the console window suppressed);
|
||
* pass `timeout` / `maxBuffer` per call exactly as before.
|
||
*/
|
||
export function execFileAsync(
|
||
file: string,
|
||
args: readonly string[],
|
||
options: ExecFileOptions = {}
|
||
): Promise<ExecResult> {
|
||
return new Promise((resolve) => {
|
||
execFile(file, [...args], { windowsHide: true, ...options }, (err, stdout, stderr) => {
|
||
// With the default (utf8) encoding stdout/stderr are strings, but ExecFileOptions
|
||
// permits a Buffer encoding, so normalize defensively.
|
||
const out = typeof stdout === 'string' ? stdout : stdout.toString()
|
||
const errOut = typeof stderr === 'string' ? stderr : stderr.toString()
|
||
if (err) {
|
||
resolve({
|
||
ok: false,
|
||
stdout: out,
|
||
stderr: errOut,
|
||
timedOut: (err as { killed?: boolean }).killed === true,
|
||
error: err
|
||
})
|
||
} else {
|
||
resolve({ ok: true, stdout: out, stderr: errOut, timedOut: false })
|
||
}
|
||
})
|
||
})
|
||
}
|