hazy
0a01583f56
Make CLI arguments use flags, support webm output encoding, allow running from any working directory, add optional profiles, add audio to spliced-in clips, various small fixes
54 lines
2.4 KiB
JavaScript
54 lines
2.4 KiB
JavaScript
import fs from 'fs'
|
|
import exec from './exec.js'
|
|
import getArgument from './getArgument.js'
|
|
|
|
function getExtension(filename) {
|
|
return filename.split('/').pop().split('.').pop()
|
|
}
|
|
|
|
export function getArgs(filename) {
|
|
let extension = getExtension(filename)
|
|
if (extension=='webm') return `-c:v libvpx-vp9 -crf 30 -cpu-used 5 -deadline realtime -vsync 2`
|
|
return `-c:v libx264 -x264-params crf=25 -ar 44100 -vsync 2`
|
|
}
|
|
|
|
export default async function concatenateVideos(filenames, outfile) {
|
|
const chunkSize = getArgument('chunk-size')??40
|
|
console.log(`Joining every ${chunkSize} clips`)
|
|
let chunks = []
|
|
for (let i = 0; i < filenames.length; i += chunkSize) {
|
|
const ffmpegArgs = getArgs(outfile)
|
|
const filenamesSlice = filenames.slice(i, i + chunkSize)
|
|
const inputs = filenamesSlice.map(e => `-i "${e}"`).join(' ')
|
|
const filter = filenamesSlice.map((e, i) => `[${i}:v:0][${i}:a:0]`).join('')
|
|
const chunkFile = `${__dirname}/tmp/chunk-${i}.${getExtension(outfile)}`
|
|
const execScript = `ffmpeg -y ${inputs} -filter_complex "${filter}concat=n=${filenamesSlice.length}:v=1:a=1[outv][outa]" -map "[outv]" -map "[outa]" ${ffmpegArgs} "${chunkFile}"`
|
|
process.stdout.clearLine()
|
|
process.stdout.cursorTo(0)
|
|
process.stdout.write(`Starting chunk ${Math.floor(i/chunkSize)+1} of ${Math.ceil(filenames.length/chunkSize)}`)
|
|
process.stdout.cursorTo(0)
|
|
await exec(execScript, {
|
|
maxBuffer: 5 * 1024 * 1024
|
|
})
|
|
process.stdout.clearLine()
|
|
process.stdout.cursorTo(0)
|
|
process.stdout.write(`Completed chunk ${Math.floor(i/chunkSize)+1} of ${Math.ceil(filenames.length/chunkSize)}`)
|
|
process.stdout.cursorTo(0)
|
|
chunks.push(chunkFile)
|
|
}
|
|
if (chunks.length>1) {
|
|
const ffmpegArgs = getArgs(outfile)
|
|
console.log(`\nJoining all ${Math.ceil(filenames.length/chunkSize)} chunks`)
|
|
const inputs = chunks.map(e => `-i "${e}"`).join(' ')
|
|
const filter = chunks.map((e, i) => `[${i}:v:0][${i}:a:0]`).join('')
|
|
const execScript = `ffmpeg -y ${inputs} -filter_complex "${filter}concat=n=${chunks.length}:v=1:a=1[outv][outa]" -map "[outv]" -map "[outa]" ${ffmpegArgs} "${outfile}"`
|
|
console.log(execScript)
|
|
await exec(execScript, {
|
|
maxBuffer: 5 * 1024 * 1024
|
|
})
|
|
} else {
|
|
fs.promises.rename(chunks[0], outfile)
|
|
}
|
|
console.log('Complete.')
|
|
}
|