loading...
Cover image for A update to my automated silence trimming script!

A update to my automated silence trimming script!

dak425 profile image Donald Feury ・3 min read

Automated Video Editing (11 Part Series)

1) Cut up video and audio with just FFMPEG! 2) Add Fade In and Fade Out Effects With FFMPEG! 3 ... 9 3) Easily remove or extract the audio from a video with FFMPEG 4) Save yourself hours of editing when working with recordings of work (art, coding, etc) 5) Concatenate Videos Together Using FFMPEG! 6) Add an animation overlay on a video with FFMPEG! 7) How I Completely Automated My YouTube Editing 8) How to stack videos together with FFMPEG! 9) Automatically Trim Silence from Video with FFMPEG and Python! 10) How to replace the audio in a video with the audio from another video with ffmpeg! 11) A update to my automated silence trimming script!

Follow me on YouTube and Twitter for more video editing tricks with ffmpeg and a little python now!

I recently updated my python script for automatically removing the silence parts of a video.

Previously, I had to call the shell script separate to generate the silence timestamps.

Now, the python script grabs the output of the shell script directly using subprocess run.

Script

#!/usr/bin/env python

import sys
import subprocess
import os
from moviepy.editor import VideoFileClip, concatenate_videoclips

input_path = sys.argv[1]
out_path = sys.argv[2]
threshold = sys.argv[3]
duration = sys.argv[4]

try:
    ease = float(sys.argv[5])
except IndexError:
    ease = 0.2

minimum_duration = 1.0

def generate_timestamps(path, threshold, duration):
    command = "detect_silence {} {} {}".format(input_path, threshold, duration)
    output = subprocess.run(command, shell=True, capture_output=True, text=True)
    return output.stdout.split('\n')[:-1]


def main():
    count = 0
    last = 0
    timestamps = generate_timestamps(input_path, threshold, duration)
    print("Timestamps: {}".format(timestamps))
    video = VideoFileClip(input_path)
    full_duration = video.duration
    clips = []

    for times in timestamps:
        end,dur = times.strip().split()
        print("End: {}, Duration: {}".format(end, dur))

        to = float(end) - float(dur) + ease

        start = float(last)
        clip_duration = float(to) - start
        # Clips less than one seconds don't seem to work
        print("Clip Duration: {} seconds".format(clip_duration))

        if clip_duration < minimum_duration:
            continue

        if full_duration - to < minimum_duration:
            continue


        print("Clip {} (Start: {}, End: {})".format(count, start, to))
        clip = video.subclip(start, to)
        clips.append(clip)
        last = end
        count += 1

    if not clips:
        print("No silence detected, exiting...")
        return


    if full_duration - float(last) > minimum_duration:
        print("Clip {} (Start: {}, End: {})".format(count, last, 'EOF'))
        clips.append(video.subclip(last))

    processed_video = concatenate_videoclips(clips)
    processed_video.write_videofile(
        out_path,
        fps=60,
        preset='ultrafast',
        codec='libx264',
        audio_codec='aac'
    )

    video.close()


main()

I won't go over this in full detail, as I did that in the last post about the silence trimming script. I will break down the changes I made.

For a break down of the scripts in more detail, check out the last post I made about it.

Changes

def generate_timestamps(path, threshold, duration):
    command = "detect_silence {} {} {}".format(input_path, threshold, duration)
    output = subprocess.run(command, shell=True, capture_output=True, text=True)
    return output.stdout.split('\n')[:-1]

Here I created a function to pass in the arguments needed by the detect silence script, and execute it using subprocess.run.

It needs the capture_output=True to actually save the output, and text=True to make the output be in the form of a string, otherwise its returned as raw bytes.

I then split on the newlines and remove the last entry, as its an empty string that is not needed.

Since we are grabbing the script output straight from stdout, we no longer have to open and read an arbitrary text file to get the timestamps.

One last change was, before I was adding a padding to the start of the next clip, to make the transitions less abrupt. Now I add it the end of the last clip, as it seems more natural.

if not clips:
        print("No silence detected, exiting...")
        return

I also added this sanity check to make sure there were actually clips generated, can't concatenate clips that don't exist.

Thats it! Now I can remove the silence parts of a video by calling only script! It also avoids having to create the intermittent timestamp file as well.

Automated Video Editing (11 Part Series)

1) Cut up video and audio with just FFMPEG! 2) Add Fade In and Fade Out Effects With FFMPEG! 3 ... 9 3) Easily remove or extract the audio from a video with FFMPEG 4) Save yourself hours of editing when working with recordings of work (art, coding, etc) 5) Concatenate Videos Together Using FFMPEG! 6) Add an animation overlay on a video with FFMPEG! 7) How I Completely Automated My YouTube Editing 8) How to stack videos together with FFMPEG! 9) Automatically Trim Silence from Video with FFMPEG and Python! 10) How to replace the audio in a video with the audio from another video with ffmpeg! 11) A update to my automated silence trimming script!

Posted on May 13 by:

dak425 profile

Donald Feury

@dak425

Appalachian Boi, former Software Developer turned Content Creator

Discussion

markdown guide