Created
October 31, 2022 13:02
-
-
Save mattpocock/f78af20137f0bdd1dc565ce89c59f30c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Name: Trim latest OBS video | |
import "@johnlindquist/kit"; | |
import { z } from "zod"; | |
import { getActiveEditorFilePath } from "./helpers/vscode"; | |
const { stdout } = await $`ls -t ~/Movies/*.mp4 | head -n 1`; | |
const inputVideo = stdout.trim(); | |
const THRESH = "-40"; | |
const DURATION = "1"; | |
const activeEditorFilePath = await getActiveEditorFilePath(); | |
const rawFilename = path.relative(home("repos"), activeEditorFilePath); | |
if (rawFilename.startsWith("..")) { | |
notify("File is not in the repos folder"); | |
exit(1); | |
} | |
const resolvedFilename = home("Movies", rawFilename); | |
await arg({ | |
placeholder: rawFilename, | |
name: "Confirm File name", | |
}); | |
const outputFolder = path.dirname(resolvedFilename); | |
const outputFilename = path.parse(resolvedFilename).name + ".mp4"; | |
const output = | |
await $`ffmpeg -hide_banner -vn -i ${inputVideo} -af "silencedetect=n=${THRESH}dB:d=${DURATION}" -f null - 2>&1 | grep "silence_end" | awk '{print $5 " " $8}'`; | |
let silence = output.stdout | |
.trim() | |
.split("\n") | |
.map((line) => line.split(" ")) | |
.map(([silenceEnd, duration]) => { | |
return { | |
silenceEnd: parseFloat(silenceEnd), | |
duration: parseFloat(duration), | |
}; | |
}); | |
let foundFirstPeriodOfTalking = false; | |
while (!foundFirstPeriodOfTalking) { | |
// Unshift the first silence if the noise afterwards | |
// is less than 1 second long | |
const silenceElem = silence[0]; | |
const nextSilenceElem = silence[1]; | |
const nextSilenceStartTime = | |
nextSilenceElem.silenceEnd - nextSilenceElem.duration; | |
const lengthOfNoise = nextSilenceStartTime - silenceElem.silenceEnd; | |
if (lengthOfNoise < 2) { | |
silence.shift(); | |
} else { | |
foundFirstPeriodOfTalking = true; | |
} | |
} | |
const PADDING = 0.3; | |
const startTime = silence[0].silenceEnd - PADDING; | |
const endTime = | |
silence[silence.length - 1].silenceEnd - | |
silence[silence.length - 1].duration + | |
PADDING; | |
const totalDuration = endTime - startTime; | |
const formatFloatForFFmpeg = (num: number) => { | |
return num.toFixed(3); | |
}; | |
await ensureDir(outputFolder); | |
const outputVideo = path.resolve(outputFolder, outputFilename); | |
await ensureDir(path.resolve(outputFolder, "tests")); | |
const testOutputVideo = path.resolve(outputFolder, "tests", outputFilename); | |
await $`ffmpeg -y -hide_banner -ss ${formatFloatForFFmpeg( | |
startTime, | |
)} -to ${formatFloatForFFmpeg( | |
endTime, | |
)} -i ${inputVideo} -c:v libx264 -profile high -b:v 7000k -pix_fmt yuv420p -maxrate 16000k ${outputVideo}`; | |
await revealInFinder(outputVideo); | |
await $`ffmpeg -y -i ${outputVideo} \ | |
-vf "select='between(t,0,2)+between(t,${(totalDuration - 2).toFixed( | |
1, | |
)},${totalDuration.toFixed(1)})', | |
setpts=N/FRAME_RATE/TB" \ | |
-af "aselect='between(t,0,2)+between(t,${(totalDuration - 2).toFixed( | |
1, | |
)},${totalDuration.toFixed(1)})', | |
asetpts=N/SR/TB" ${testOutputVideo}`; | |
await $`open ${testOutputVideo}`; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment