Open video-speed in Script Kit

Very useful for making those AI screen recordings look better (=faster :D)

image

// Name: Speed Up Video
// Description: Speed up a video, optionally keeping the first and last X seconds at original speed
// Author: Strajk
import '@johnlindquist/kit';
// Get the video file path, either from selection or prompt
import { Choice } from '@johnlindquist/kit';
const videoPath = await getSelectedFile() || await path({ placeholder: 'Select a video file' });
const { stdout: videoDurationRaw } = await $`ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 ${videoPath}`
const videoDuration = parseFloat(videoDurationRaw)
// Not sure if best way of handling error states
if (!videoDuration || videoDuration < 1) {
await div(`${videoPath} does not look like a video file, exiting`)
exit()
}
const { dir, name, ext } = path.parse(videoPath);
const outputPath = path.join(dir, `${name}-adjusted${ext}`);
const speedOptions: Choice[] = [
{ name: `2x (${Math.round(videoDuration / 2)}s)`, value: 2 },
{ name: `3x (${Math.round(videoDuration / 3)}s)`, value: 3 },
{ name: `4x (${Math.round(videoDuration / 4)}s)`, value: 4 },
{ name: `5x (${Math.round(videoDuration / 5)}s)`, value: 5 },
];
// Prompt user to select a speed
const speedRaw = await arg({
placeholder: `Select new playback speed, or enter a custom value`,
hint: `Any number or ";5;3;6" for 5x speed-up, but first 3 and last 6s are kept at original speed`,
choices: speedOptions,
strict: false, // allow arbitrary input
});
let mode: 'simple' | 'complex' = speedRaw.includes?.(';') ? 'complex' : 'simple'
if (mode === 'simple') {
let speed: number = parseFloat(speedRaw)
// -filter:v means video filter
// -filter:a means audio filter
// for uploading to twitter, i'm setting fps to 30
await term(`ffmpeg \
-i "${videoPath}" \
-filter:v "setpts=${1 / speed}*PTS,fps=30" \
-filter:a "atempo=${speed}" \
-y "${outputPath}"
`)
} else {
let [speedStr, introStr, outroStr] = speedRaw.split(';')
let speed = parseFloat(speedStr)
let intro = parseInt(introStr)
let outro = parseInt(outroStr)
// Note: ffmpeg is savage
await term(`ffmpeg -i "${videoPath}" -filter_complex "
[0:v]split=3[v1][v2][v3];
[v1]trim=0:${intro},setpts=PTS-STARTPTS,fps=30[first];
[v2]trim=${intro}:${videoDuration - outro},setpts=PTS-STARTPTS,setpts=${1 / speed}*PTS,fps=30[middle];
[v3]trim=${videoDuration - outro},setpts=PTS-STARTPTS,fps=30[last];
[first][middle][last]concat=n=3:v=1:a=0[outv]
" -y -map "[outv]" "${outputPath}"
`)
}
// Reveal output file in the system's file explorer
await revealFile(outputPath);