Skip to content

Bug: setFfmpegPath / setFfprobePath leaves stale capability cache #39

@isamu

Description

@isamu

Summary

After a successful run populates the capability cache (codecs / encoders / formats / filters), calling setFfmpegPath(differentPath) only swaps the binary path — the cache stays. The next run silently uses stale capability data scraped from the previous binary, and any downstream _checkCapabilities check passes against information that no longer reflects reality.

In the worst case the new binary is a totally different ffmpeg build (e.g. a stripped-down statically-linked binary that lacks the codecs the cache claims are available); ffmpeg fails to spawn with an obscure error, while a fresh probe would have surfaced the problem cleanly.

Upstream issues

Reproduction

ffmpeg.setFfmpegPath("/usr/bin/ffmpeg");
await encodeOnce();              // populates cache.codecs, cache.formats, ...

ffmpeg.setFfmpegPath("/some/stripped/build/ffmpeg");
await encodeAgain();             // _checkCapabilities passes against stale data
                                 // → ffmpeg actually crashes during exec

Fix plan

In lib/capabilities.ts setFfmpegPath and setFfprobePath, invalidate the parse caches by assigning undefined (not delete), so @typescript-eslint/no-dynamic-delete is not triggered. The existing readers test the parse caches via truthy check (if (cache.codecs), if (cache.filters) etc.), which return false for an undefined slot — semantically identical to the original delete-based reset for these particular keys.

 proto.setFfmpegPath = function (this: FfmpegCommandThis, ffmpegPath: string) {
   cache.ffmpegPath = ffmpegPath;
+  cache.codecs = undefined;
+  cache.encoders = undefined;
+  cache.formats = undefined;
+  cache.filters = undefined;
   return this;
 };

PathCache.codecs etc. are already declared ?: Record<string, …>, so undefined assignment is type-safe.

Note the existing path caches (ffmpegPath / ffprobePath / flvtoolPath) are NOT invalidated here — those are gated by 'X' in cache checks (line 294, 305, 325), where the difference between delete and undefined is observable. We keep those as-is; setFfmpegPath is re-setting cache.ffmpegPath rather than invalidating it, so the resolution check still reads the new path correctly.

setFfprobePath similarly invalidates only cache.codecs / cache.formats (less critical because the probe sidecar uses them less, but symmetrical).

Test plan

A unit test that:

  1. Calls setFfmpegPath('/path/a'), then internally seeds cache.codecs, cache.formats, etc. via availableCodecs / availableFormats.
  2. Calls setFfmpegPath('/path/b').
  3. Asserts cache.codecs === undefined && cache.formats === undefined.

Caveat

Cache flushing on every path-set means consumers who set the path multiple times incur an extra capability query. Trade-off is correctness for ~50ms of one-time spawn — acceptable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions