Code coverage report for src/command.ts

# All files / src command.ts

69.81% Statements 74/106
72.72% Branches 16/22
80% Functions 4/5
69.81% Lines 74/106

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    1071x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    5x
    5x
    5x
    5x
    5x
    5x
    5x
    5x
    3x
    1x
    1x
    2x
    2x
    2x
    4x
    5x

    4x
    5x
    5x
    5x
    5x
    5x
    5x
    5x
    5x
    5x
    5x
    5x
    5x
    5x
    5x
    5x
    4x
    1x
    1x
    3x
    4x

    4x
    5x
    5x
    3x
    3x
    3x
    5x
    5x
    3x
    3x
    3x
    3x

    3x
    3x

    3x
    3x

    3x
    3x
    3x
    5x
    5x

    5x
     import { Readable, Writable } from 'node:stream'
    import { FfmpegInput, InputDefinition } from './input'
    import { FfmpegOutput, OutputDefinition } from './output'
    import { FfmpegProcess, RunResult, RunOptions, ProcessOptions } from './process'
    import { FilterGraph, generateFilterGraph } from './utils/filters'

    export type CommandOptions = {
      input?: InputDefinition
      inputs?: InputDefinition[]
      output?: OutputDefinition
      outputs?: OutputDefinition[]
      complexFilters?: FilterGraph
      customArgs?: string[]
    }

    export class FfmpegCommand implements CommandOptions {
      inputs: FfmpegInput[]
      outputs: FfmpegOutput[]
      complexFilters?: FilterGraph
      customArgs: string[]

      constructor(options: CommandOptions) {
        if (options.input) {
          if (options.inputs) {
            throw new Error("Cannot specify both 'input' and 'inputs'")
          }

          options.inputs = [options.input]
        }

        if (options.output) {
          if (options.outputs) {
            throw new Error("Cannot specify both 'output' and 'outputs'")
          }

          options.outputs = [options.output]
        }

        this.inputs = (options.inputs || []).map(
          (inputOptions) => new FfmpegInput(inputOptions)
        )

        this.outputs = (options.outputs || []).map(
          (outputOptions) => new FfmpegOutput(outputOptions)
        )

        this.complexFilters = options.complexFilters
        this.customArgs = options.customArgs || []

        this.#validateIO()
      }

      #validateIO(): void {
        if (this.inputs.filter((i) => i.isStream).length > 1) {
          throw new Error(`At most one stream input is supported`)
        }

        if (this.outputs.filter((o) => o.isStream).length > 1) {
          throw new Error(`At most one stream output is supported`)
        }
      }

      getFfmpegArguments(): string[] {
        let args: string[] = []

        for (let input of this.inputs) {
          args.push(...input.getFfmpegArguments())
        }

        args.push(...this.customArgs)

        if (this.complexFilters) {
          args.push('-filter_complex', generateFilterGraph(this.complexFilters))
        }

        if (this.outputs.some((o) => o.isLocalFile)) {
          // Force overwrite outputs
          args.push('-y')
        }

        for (let output of this.outputs) {
          args.push(...output.getFfmpegArguments())
        }

        return args
      }

      run(options: RunOptions): Promise<RunResult> {
        let procOtions: ProcessOptions = {
          args: this.getFfmpegArguments(),
          ...options
        }

        let streamInput = this.inputs.find((i) => i.isStream)
        if (streamInput) {
          procOtions.inputStream = streamInput.source as Readable
        }

        let streamOutput = this.outputs.find((i) => i.isStream)
        if (streamOutput) {
          procOtions.outputStream = streamOutput.target as Writable
        }

        return new FfmpegProcess(procOtions).run()
      }
    }