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

