mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-11-14 21:29:34 +00:00
fix(engine): give the ffmpeg children the signal that actually aborts them
The internal AbortController exists so a fatal FX error can cancel in-flight
ffmpeg before the finally-block deletes workDir. It reached the entry guard and
applyAudioFxChain, and nothing else: the trim, the video extract and the
download were all still handed the CALLER's signal. So `internalController
.abort()` cancelled nothing, and the `rmSync(workDir, { recursive: true })`
immediately after it ran while those children were still writing into the
directory.
Concretely: track A's FX render fails while track B is mid-trim, with no
external cancellation. B's ffmpeg keeps writing `${id}-trimmed.wav` into an
unlinked directory until it finishes. The comment above the controller
describes a mechanism that was never wired to the processes it names.
The download also had no signal at all, though downloadToTemp accepts one.
No test: the three changes are which variable is passed to a child process
helper, and observing the difference means driving real ffmpeg to the point of
cancellation. The old wiring's inertness is provable by reading — the caller's
signal is never aborted by this function — and the new wiring is the same three
calls with the controller the function already built.
This commit is contained in:
@@ -765,6 +765,12 @@ export async function processCompositionAudio(
|
||||
// be able to abort the in-flight ffmpeg runs before the finally-block removes
|
||||
// workDir out from under them. Chained off the caller's signal so external
|
||||
// cancellation still behaves as before.
|
||||
//
|
||||
// Every child that can outlive a sibling's failure has to be given THIS
|
||||
// signal, not the caller's: the trim, the video extract and the download all
|
||||
// took `signal`, so `internalController.abort()` cancelled nothing and the
|
||||
// `rmSync(workDir)` on the next line ran while their ffmpeg children were
|
||||
// still writing into it.
|
||||
const internalController = new AbortController();
|
||||
const effectiveSignal = internalController.signal;
|
||||
if (signal) {
|
||||
@@ -795,7 +801,7 @@ export async function processCompositionAudio(
|
||||
|
||||
if (isHttpUrl(srcPath)) {
|
||||
try {
|
||||
srcPath = await downloadToTemp(srcPath, workDir);
|
||||
srcPath = await downloadToTemp(srcPath, workDir, undefined, effectiveSignal);
|
||||
} catch (err: unknown) {
|
||||
failures.push(
|
||||
downloadFailure(err instanceof Error ? err.message : String(err), element.id),
|
||||
@@ -842,7 +848,7 @@ export async function processCompositionAudio(
|
||||
startTime: element.mediaStart,
|
||||
duration: element.end - element.start,
|
||||
},
|
||||
signal,
|
||||
effectiveSignal,
|
||||
config,
|
||||
);
|
||||
if (!extractResult.success) {
|
||||
@@ -868,7 +874,7 @@ export async function processCompositionAudio(
|
||||
trimmedPath,
|
||||
element.mediaStart,
|
||||
element.end - element.start,
|
||||
signal,
|
||||
effectiveSignal,
|
||||
config,
|
||||
);
|
||||
if (!prepResult.success) {
|
||||
|
||||
Reference in New Issue
Block a user