export def cmd [ inputs: list outputs: list ]: nothing -> record { { input: $inputs filters: [] output: $outputs args: [] options: { chain_filters: false } } } export def "cmd to-args" []: record -> list { let command = $in; [ ...($command.input | reduce -f [] { |it, acc| $acc | append ['-i', $it ] }) ...$command.args ...['-filter_complex' ($command.filters | filtergraph to-string)] ...($command.output) ] } # Run the given command # Serves as a sink for a filter pipeline. export def "run" [ --dry-run (-d) # Print the command that would be run ] { if $dry_run { [ffmpeg, ...($in | cmd to-args)] } else { ffmpeg ...($in | cmd to-args) } } def "append last" [ items: list ]: list -> list { update (($in | length) - 1) { $in | append $items } } export def "cmd filters append" [ complex_filters: list ]: record -> record { update filters { |cmd| let filters = $in; if ($cmd.options.chain_filters) { $filters | append last $complex_filters } else { $filters | append [$complex_filters] } } } export def "parse filtergraph" [ ]: string -> list>> { split row ';' | each { parse filterchain } } export def "parse filterchain" [ ]: string -> table> { split row ',' | each { parse filter } } export def "parse filter" [ ]: string -> table> { parse --regex '^\s*(?:\[(?[^\s]+)\]\s*)?(?[^=\s\[]+)\s*(?:=(?[^\[\s,;]*)\s*)?(?:\[(?[^\s,;]+)\])?' | first | update params { parse --regex `(?:(?[^=]+)=)?(?[^:]+):?` } | update input { split row '][' | filter { not ($in | is-empty) } } | update output { split row '][' | filter { not ($in | is-empty) } } } # TODO: Remove export export def "filtergraph to-string" [] { # : list -> string { $in | each { filterchain to-string } | str join ';' } def "filterchain to-string" []: table -> string { $in | each { filter to-string }| str join ',' } def "filter to-string" []: record name: string params: table output: list> -> string { $in | update input { str join '][' } | update output { str join '][' } | update params { each { update value { match ($in | describe) { 'bool' => (if ($in) { '1' } else { '0' }) _ => $in, } } | format pattern '{param}={value}' } | str join ':' | str replace -ar '(?<=^|:)=' '' } | format pattern '[{input}]{name}={params}[{output}]' | str replace -ar '\[\]|=(?=[\[,;])' '' } # Add a chain of filters to the command's filtergraph export def filterchain [ --input (-i): list # sets the input of the last filterchain's first filter --output (-o): list # sets the output of the last filterchain's last filter filter: closure # The filter, or filters to append to the filtergraph ] { let cmd = $in; let original_option = $cmd.options.chain_filters; ( $cmd | update options.chain_filters { not $in } | update filters { let it = $in; if ($it | describe | str starts-with 'table') { [$it [] ] } else { $it | append [[]] } } | do $filter | if ($input | is-empty | not $in) { update filters { update (($in | length) - 1) { update 0.input $input } } } else { $in } | if ($output | is-empty | not $in) { update filters { update (($in | length) - 1) { update (($in | length) - 1) { update output $output } } } } else { $in } | update options.chain_filters $original_option ); } # Build a record representaion of a complex filter export def complex-filter [ --input (-i): list = [] --output (-o): list = [] name: string params: record = {} ]: nothing -> record name: string params: table output: list> { { input: $input name: $name params: ($params | transpose param value | compact param value) output: $output } }