Skip to content

Bug: setFfmpegPath / setFfprobePath leaves stale capability cache #39

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions