Bash Script to Clip Ripped Home Movies
Bash script I wrote to assist in clipping home movies. Still some more to do, but here's a summary.
Particularly useful was the code I used to find the difference under bash between start and end points using date...and keeping the result in a format ffmpeg could understand.
#!/bin/bash
#
# Title: HomeMoviesClipper.sh
# Description: Use ffmpeg to split raw home movie captures into clips
# Original Author: Joseph Gullo (surfrock66) (surfrock66@surfrock66.com)
#
# This script works with a very specific set of constraints and formats to
# take raw video captures and split them into clips of events. Each of
# these movies was captured, using a Dazzle USB video capture device,
# into h264 video and mp3 audio, using VLC. These original tapes were
# typically close to 2 hours long, however capture was often left to run,
# creating videos that were 8+ hours long. The capture device produced
# video that was 480x480 and audio that only contains 1 channel (recorded
# through stereo), and the output video will retain this. The desired
# format is high-bitrate h264 and aac audio, encapsulated in .mp4 files.
# This script also works within a predictable directory environment
# because of how specific the task is, and because I'm a lazy bastard
# that doesn't want to code bunch of directory logic. The script first
# queries the input file, then works on this file recursing the steps
# for each clip. Each clip requires an output name, a start timecode
# (formatted hh:mm:ss.nnn) and an ending timecode. This is part of
# some of the headache, as ffmpeg processes clips with a start time
# and a duration, meaning you have to do the difference math yourself.
# I take care of this in there. After producing a clip, the script
# will ask the user if they want to create another clip from the same
# source file, at which it recurses through a prompting loop. The
# script also can be run from the command line...however in CLI mode,
# the intent is to NOT allow the script to re-prompt for additional cips
# from the same source video.
#
# The script can optionally take 4 command line arguments:
# 1) The filename for the source video, without extension
# 2) The desired resulting clip name, without extension
# 3) The start timecode for the clip, format hh:mm:ss.nnn
# 4) The end timecode for the clip, format hh:mm:ss.nnn
#
# Variables:
# INPUTFILE - Input video filename, without extension
# OUTPUTFILE - Clip output filename, without extension
# STARTTIME - Clip starting timecode, format hh:mm:ss.nnn
# ENDTIME - Clip ending timecode, format hh:mm:ss.nnn
# NANODIFF - Difference between start and end timecode, in milliseconds
# NUMSECS - Timecoe difference in whole seconds
# NUMNANO - Timecode difference remainder in milliseconds
# REPLY - Flag for checking if the script should create another clip
# CLIFLAG - Flag set if command line mode is detected, to prevent prompting
#
# Prompt the user for all parameters, by asking for the source clip name, then calling
# the function which prompts for the clip info. This can safely call the clip-info
# prompting function, as this should only be used once per execution, if at all.
PROMPTFULL() {
echo "---What is the source video name? (No Extension) (Inside /home/surfrock66/Videos/HomeMoviesRaw/):"
# First point of possible declaration for INPUTFILE
read INPUTFILE
# Execute the PROMPCLIP function
PROMPTCLIP
}
# Prompt the user for the clip name, start time, and end time.
PROMPTCLIP() {
echo "---What is the output filename for this clip? (No Extension):"
# First point of possible declaration for OUTPUTFILE
read OUTPUTFILE
echo "---What is the timecode for the start of this clip? (##:##:##.###):"
# First point of possible declaration for STARTTIME
read STARTTIME
echo "---What is the timecode for the end of this clip? (##:##:##.###):"
# First point of possible declaration for ENDTIME
read ENDTIME
}
# This function handles the timecode calculations to find the clip duration,
# then executes the actial ffmpeg command.
PROCESSVID() {
# Calculate the actuall difference, in milliseconds (but we'll call them
# nanoseconds, because that's how the date command works) between the
# start time and end time. This relies heavily on the "date" command.
# It can convert formatted timecodes between different format
# conventions, but to go from something resembling a standard video
# editing timecode to a raw nanosecond count is a PAIN IN THE ASS.
# This strips out the amount of hours in milliseconds, then the number
# of minutes in milliseconds, then the number of seconds in milliseconds,
# then the number of nanoseconds in milliseconds. It then adds them
# together, creates a full millisecond count for the start an end time,
# and then finds the difference, in milliseconds.
NANODIFF=$(($(($(($(date -d $ENDTIME +%-H) * 3600000)) + $(($(date -d $ENDTIME +%-M) * 60000)) + $(($(date -d $ENDTIME +%-S) * 1000)) + $(($(date -d $ENDTIME +%-N) / 1000000)))) - $(($(($(date -d $STARTTIME +%-H) * 3600000)) + $(($(date -d $STARTTIME +%-M) * 60000)) + $(($(date -d $STARTTIME +%-S) * 1000)) + $(($(date -d $STARTTIME +%-N) / 1000000))))))
# Convert the resulting difference, in millisecodns, into seconds
NUMSECS=$(($NANODIFF / 1000))
# Convert the reaminder of the resulting difference, in milliseconds,
# to milliseconds
NUMNANO=$(($NANODIFF % 1000))
# Print out the ffmpeg command that is generated, mostly for debugging
echo "\nCommand: ffmpeg -i \"/home/surfrock66/Videos/HomeMoviesRaw/$INPUTFILE.mp4\" -ss $STARTTIME -t $NUMSECS.$NUMNANO -vcodec libx264 -b 1500k -s 480x480 -acodec libfaac -ab 192k -ac 1 -threads 8 \"/home/surfrock66/Videos/HomeMoviesRaw/$INPUTFILE/$OUTPUTFILE.mp4\"\n"
# Execute the ffmpeg command, this is the big kahuna.
ffmpeg -i "/home/surfrock66/Videos/HomeMoviesRaw/$INPUTFILE.mp4" -ss $STARTTIME -t $NUMSECS.$NUMNANO -vcodec libx264 -b 1500k -s 480x480 -acodec libfaac -ab 192k -ac 1 -threads 8 "/home/surfrock66/Videos/HomeMoviesRaw/$INPUTFILE/$OUTPUTFILE.mp4"
}
# Start of the main script. Section 1: Input validation
# Check that there is a first parameter. If not, Prompt the user for the
# parameters. If there is one, validate the rest of the params, then
# either prompt the user, or use the passed params.
if [ -z "$1" ]
then
# Call the prompt function, including asking for the source video
PROMPTFULL
else
# Assuming the first parameter exists, check for parameters 2,3, and
# 4. I'm not validating these strings, mostly because ffmpeg will
# flip out if there's something wrong with them...as you know, it's
# quite verbose.
if [ -z "$2" -o -z "$3" -o -z "$4" ]
then
# If any parameters are invalid, prompt for them all. Even if param 1
# is successfully detected, re-ask for it.
echo "--Parameters are incomplete, switching to prompt mode."
PROMPTFULL
else
# Pass all the command line parameters to the variables
INPUTFILE="$1"
OUTPUTFILE="$2"
STARTTIME="$3"
ENDTIME="$4"
# Change the flag to indicate later that this initiated by CLI
CLIFLAG="yes"
fi
fi
# Create the default output directory, if it's not already created
if [ ! -d /home/surfrock66/Videos/HomeMoviesRaw/$INPUTFILE ]; then
mkdir /home/surfrock66/Videos/HomeMoviesRaw/$INPUTFILE
fi
# Now...initiate the final video processing
# Declare and initiate the REPLY flag...can be set to anything byt n/N
REPLY="y"
# As long as the REPLY flag isn't n or N, repeat the loop
while [ "$REPLY" != "n" ] && [ "$REPLY" != "N" ]
do
# Call the function PROCESSVID
PROCESSVID
# Check to see if the script was run with command line parameters,
# If not, issue a bunch of logic to see if we want to re-run the
# code for new clips
if [ -z "$CLIFLAG" ]
then
# Now that we know the script was run in prompt mode, ask the user
# if they want to make another clip, repeating this loop
echo "---Would you like to create another clip from this input video? {Y/n}"
read REPLY
if [ "$REPLY" != "n" ] && [ "$REPLY" != "N" ]
then
PROMPTCLIP
fi
# If it was run by command line, essentially abort the loop,
# and the script.
else
REPLY="n"
fi
done