Skip to content

Instantly share code, notes, and snippets.

@mattpocock
Created October 31, 2022 13:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattpocock/f78af20137f0bdd1dc565ce89c59f30c to your computer and use it in GitHub Desktop.
Save mattpocock/f78af20137f0bdd1dc565ce89c59f30c to your computer and use it in GitHub Desktop.
// 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