erwinv / yaffu

Yet Another FFmpeg Util
2 stars 1 forks source link

Unrecognized option '/filter_complex'. Error splitting the argument list: Option not found #5

Open koyukan opened 1 week ago

koyukan commented 1 week ago

Hey Erwin, I see that you pushed a new version, thanks for your work. When I followed your instructions and try to run one of the examples, I get the below error.

Unrecognized option '/filter_complex'.
Error splitting the argument list: Option not found
file:///home/prometheus/Desktop/yaffu/example/node_modules/.pnpm/yaffu@file+../node_modules/yaffu/build/lib/ffmpeg.js:107
                    errorHandler(new FFmpegError(code ?? NaN));
                                 ^

FFmpegError: FFmpeg exited with failure code: 8
    at ChildProcess.<anonymous> (file:///home/prometheus/Desktop/yaffu/example/node_modules/.pnpm/yaffu@file+../node_modules/yaffu/build/lib/ffmpeg.js:107:34)
    at ChildProcess.emit (node:events:518:28)
    at maybeClose (node:internal/child_process:1105:16)
    at ChildProcess._handle.onexit (node:internal/child_process:305:5) {
  exitCode: 8
}

Node.js v20.12.0

Also I couldn't figure out exactly what the issue was but below changes solved the issue for me. Thanks again.

  graph: FilterGraph,
  verbose = true,
  bestEffort = false,
) {
  if (graph.outputs.size === 0) throw new Error('No defined output/s')

  const inputs = graph.inputs
  const outputs = [...graph.outputs.entries()]

  console.table(inputs)
  for (const [path, streams] of outputs) {
    console.info(path)
    console.table(streams)
  }
  console.dir(graph.pipes.map((p) => p.serialize()))

  // Generate the filter_complex string
  let filterComplex = graph.serialize()

  // If filterComplex is an array, join it; otherwise, use it as is
  if (Array.isArray(filterComplex)) {
    filterComplex = filterComplex.join(';')
  }

  // Escape special characters in the filter complex string
  const escapedFilterComplex = filterComplex.replace(/'/g, "'\\''")

  const ffmpegArgs = [
    verbose ? '-hide_banner' : '-v', 'warning',
    ...inputs.flatMap((input) => [
      ...(input.opts ?? []),
      '-i', input.path,
    ]),
    '-filter_complex', `'${escapedFilterComplex}'`,
    ...outputs.flatMap(([outputPath, streams]) => [
      ...streams.map((stream) => stream.serialize()),
      '-y',
      outputPath,
    ]),
  ]

  console.log('FFmpeg command:', ffmpegArgs.join(' '))

  const ffmpeg = spawn('ffmpeg', ffmpegArgs, {
    shell: true,
    stdio: ['pipe', process.stderr, 'inherit'],
  })

  try {
    await new Promise<void>((resolve, reject) => {
      const ignoreError = bestEffort
      const errorHandler = ignoreError ? console.error : reject

      ffmpeg.on('error', errorHandler)
      ffmpeg.on('close', (code) => {
        if (code !== 0) {
          errorHandler(new FFmpegError(code ?? NaN))
          if (bestEffort) resolve()
        } else resolve()
      })
    })
  } catch (e) {
    await Promise.all(outputs.map(([outputPath]) => unlinkNoThrow(outputPath)))
    throw e
  }
}
erwinv commented 1 week ago

Hey there! Sorry about that. I think I am using an FFmpeg version that is more recent/up-to-date than yours. This new FFmpeg syntax is explained here: https://ffmpeg.org/ffmpeg.html#Options

Options that take arguments support a special syntax where the argument given on the command line is interpreted as a path to the file from which the actual argument value is loaded. To use this feature, add a forward slash ’/’ immediately before the option name (after the leading dash). E.g.

ffmpeg -i INPUT -/filter:v filter.script OUTPUT

will load a filtergraph description from the file named filter.script.

-/filter_complex pipe: just loads the complex filter from a "file" which, in this case, is piped from stdin.