fp/packages/capture/src/Video.js

69 lines
1.6 KiB
JavaScript

import { execa } from 'execa'
import { tmpdir } from 'os'
import path from 'node:path'
import fs from 'node:fs'
import os from 'node:os'
export class VideoConcatError extends Error {
constructor (msg) {
super(msg || 'Failed to concatenate video')
this.name = 'VideoConcatError'
}
}
export default class Video {
constructor (opts) {
if (typeof opts.filePaths === 'undefined') throw new Error('Video must be called with opts.filePaths');
if (typeof opts.cwd === 'undefined') throw new Error('Video must be called with opts.cwd');
this.filePaths = opts.filePaths
this.cwd = opts.cwd
this.room = opts.room || 'projektmelody'
this.execa = opts.execa || execa
}
getFilesTxt () {
return this.filePaths
.sort((a, b) => a.timestamp - b.timestamp)
.map((d) => `file '${d.file}'`)
.join('\n')
.concat('\n')
}
getFilesFile () {
const p = path.join(this.cwd, 'files.txt')
fs.writeFileSync(
p,
this.getFilesTxt(this.filePaths),
{ encoding: 'utf-8' }
)
return p
}
async concat () {
const target = path.join(this.cwd, `${this.room}-chaturbate-${new Date().valueOf()}.mp4`)
const { exitCode, killed, stdout, stderr } = await this.execa('ffmpeg', [
'-y',
'-f', 'concat',
'-safe', '0',
'-i', this.getFilesFile(this.filePaths),
'-c', 'copy',
target
], {
cwd: this.cwd
});
if (exitCode !== 0 || killed !== false) {
throw new VideoConcatError(`exitCode:${exitCode}, killed:${killed}, stdout:${stdout}, stderr:${stderr}`);
}
return target
}
}