Skip to content

Instantly share code, notes, and snippets.

@tayiorbeii
Created June 7, 2023 15:32
Show Gist options
  • Save tayiorbeii/e5bf3bbfad0fd82559b8ad7157a505f7 to your computer and use it in GitHub Desktop.
Save tayiorbeii/e5bf3bbfad0fd82559b8ad7157a505f7 to your computer and use it in GitHub Desktop.
// Author: Taylor Bell
// Name: Split video on silences
// Description: detects video silences over a threshold length and deletes the silences
import "@johnlindquist/kit"
const replaceAll = await npm('just-replace-all')
const numbers = await npm('numbers-in-string')
const seconds = await npm('seconds-to-timestamp')
const chunk = await npm('chunk')
const leadingzero = await npm('leadingzero')
const escape = await npm('escape-path-with-spaces')
const MINUTES_OF_SILENCE = 1.25
const silenceInSeconds = MINUTES_OF_SILENCE * 60
/*
* Highlight the long video we are taking clips out of in Finder
*/
let longVideoFile = await getSelectedFile()
cd(path.dirname(longVideoFile))
let filename = longVideoFile.split(path.dirname(longVideoFile)+'/')
/*
* Detect silences in long video file and parse timestamps
*/
await(hide)
let { stdout, stderr } =
await $`ffmpeg -i ${longVideoFile} -af silencedetect=noise=-25dB:d=${silenceInSeconds} -f null -`
let termOutput = stderr.split(/\r\n|\n\r|\n|\r/)
let detectedSilenceLines = termOutput.filter(x => x.includes('[silencedetect'))
let timestamps = chunk([0, ...detectedSilenceLines.map(x => x.split(': ')[1].split(' ')[0]).map(x => numbers(x)).flat()])
let cutsToMake = timestamps.map((time, index) => {
if (time[1] === undefined) {
return {
from: seconds(time[0]),
desc: `part${leadingzero(index+1, 2)}`
}
}
return {
from: seconds(time[0]),
to: seconds(time[1]),
desc: `part${leadingzero(index+1, 2)}`
}
}).filter(x => !(x.from === x.to))
let silentCutsToMake = cutsToMake.map((time, index) => {
if (index+1 == cutsToMake.length) {
return {}
}
return {
from: time['to'],
to: cutsToMake[index+1]['from'],
desc: `silent_part${leadingzero(index+1, 2)}`
}
}).slice(0,-1).filter(x => !(x.from === x.to))
/*
* Split video clips, cutting out silences
*/
for (let cut of cutsToMake) {
let clipFileName = `${replaceAll(cut.desc, ' ', '_')}`
await exec(`ffmpeg -i "${longVideoFile}" -c copy -ss ${cut.from} ${cut.to ? `-to ${cut.to}`: ''} ${clipFileName}.mp4`)
try {
await exec(`ffmpeg -i "${replaceAll(longVideoFile, '.mp4', '.srt')}" -c copy -ss ${cut.from} ${cut.to ? `-to ${cut.to}`: ''} ${clipFileName}.srt`)
} catch(e) {
console.log('no subtitle file')
}
}
/**
* Create silent videos just in case stuff happens
*/
for (let cut of silentCutsToMake) {
let clipFileName = `${replaceAll(cut.desc, ' ', '_')}.mp4`
await exec(`ffmpeg -i "${longVideoFile}" -c copy -ss ${cut.from} ${cut.to ? `-to ${cut.to}`: ''} ${clipFileName}`)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment