Spaces:
Running
Running
| import _ from 'lodash' | |
| import fs from 'fs' | |
| import { Plugin } from './plugin.js'; | |
| import { FFMpegUtils } from 'common-utils'; | |
| export class SpeedPlugin extends Plugin { | |
| constructor(name, options) { | |
| super(name, options); | |
| } | |
| async applyPrerender(originalManuscript, jobId) { | |
| } | |
| async applyPostrender(originalManuscript, jobId, outFiles) { | |
| for (let outFile of outFiles) { | |
| const speedFactor = this.options.speed || 1.0; | |
| if (speedFactor !== 1.0) { | |
| const tempFile = outFile + '.original.mp4'; | |
| fs.renameSync(outFile, tempFile); | |
| try { | |
| const pts = 1.0 / speedFactor; | |
| if (pts <= 0) { | |
| return | |
| } | |
| // atempo only supports 0.5-2.0, so we chain filters to approximate the target | |
| let remaining = speedFactor; | |
| const atempoFilters = []; | |
| while (remaining > 2.0) { | |
| atempoFilters.push('atempo=2.0'); | |
| remaining /= 2.0; | |
| } | |
| while (remaining < 0.5) { | |
| atempoFilters.push('atempo=0.5'); | |
| remaining *= 2.0; | |
| } | |
| atempoFilters.push(`atempo=${remaining.toFixed(6)}`); | |
| const audioFilter = atempoFilters.join(','); | |
| this.log(`Speeding the video ${outFile} by a factor of x${speedFactor}`); | |
| const cmd = `ffmpeg -i ${tempFile} -filter_complex "[0:v]setpts=${pts.toFixed(6)}*PTS[v];[0:a]${audioFilter}[a]" -map "[v]" -map "[a]" ${outFile}`; | |
| await FFMpegUtils.execute(cmd); | |
| fs.unlinkSync(tempFile); | |
| } catch (err) { | |
| this.log(`Error applying speed change: ${err}`); | |
| if (fs.existsSync(outFile)) { | |
| fs.unlinkSync(outFile); | |
| } | |
| fs.renameSync(tempFile, outFile); | |
| } | |
| } | |
| } | |
| } | |
| } |