Video Adding Script

I had a folder with some video in it, and I decided that I wanted to know how long all the videos were in total. The folder I had was not too large but even with only 25 videos, running an ffprobe command on each file and then manually adding the duration would be tedious. So I wrote a script to do this automatically, you can download it here. I am going to walk through how this script works below.

#!/bin/sh
#Call me from the directory which you would like to add the lengths of video and gifs. ffmpeg is a dependency. Additional formats can be supported by adding them to the ls command.
hours=0
minutes=0
seconds=0
for line in $(ls *.webm *.mp4 *.gif)
do
    currentTime=$(ffprobe -show_format -pretty -loglevel quiet $line | grep duration | sed -e 's/duration=//' -e 's/:/ /g' -e 's/\([0-9 ]*\).*/\1/')
    echo $line
    seconds=$(echo "$seconds + $(echo $currentTime | awk '{print $3}')" | bc)
    minutes=$(echo "$minutes + $(echo $currentTime | awk '{print $2}')" | bc)
    hours=$(echo "$hours + $(echo $currentTime | awk '{print $1}')" | bc)
done
[[ $seconds -ge 60 ]] && minutes=$(echo "$minutes + $seconds/60" | bc) && seconds=$(echo "$seconds%60" | bc)
[[ $minutes -ge 60 ]] && hours=$(echo "$hours + $minutes/60" | bc) && minutes=$(echo "$minutes%60" | bc)
echo "Hours:$hours Minutes:$minutes Seconds:$seconds"

The script needs to be run from the directory which contains the videos you want to add. Alternatively I could take the name of the directory as an argument but I do not see a great advantage to doing that. This script will run on any Linux, BSD, or Mac operating system (basically anything but Windows unless you use something like WSL or cygwin) and requires ffmpeg to be installed on your system. The command to make a file executable is chmod +x file

The first thing that this script does is assign 3 variables for hours, minutes and seconds to the value of zero so that we can add to these later. I then start a for loop with the iterating variable "line" which iterates through the output of the ls command. The ls command finds files or folders in the current working directory. If the ffprobe command operates on a file which is not a video or gif, it will return a blank space which can't be added to an integer. So for this script to work, we specify in the ls command to only list webm, mp4 and gif files. Any file type that is supported by ffmpeg can be added here as well. Inside the for loop, we assign the currentTime variable to the output of a ffprobe command. ffprobe returns information on the video file, the options "-show_format -pretty -loglevel quiet" allow the output to be piped into the next command. Piping on a unix system allows you to direct the output of one command to the input of another command. This is a very powerful feature of a unix like operating system that allows you to easily build complex tools using simple programs that do one thing and do it very well. The grep command takes the output of the ffprobe command and prints only the line which has the word "duration" and then this line is piped to sed. Sed is the stream editor which can be used to filter text, this is very powerful as everything on a unix system is treated as text. The '-e' option indicates that there are multiple sed commands to be run one after another on the same string of text. This is equivalent to running "sed 's/duration=//' | sed 's/:/ /g' | sed 's/\([0-9 ]*\).*/\1/'" except that it looks neater and I believe it runs more efficiently. At the very least, the file is smaller. The first sed command uses the substitution utility and replaces "duration=" with nothing. This is useful because we are just after the numbers. The next section replaces each colon with a space, the g on the end ensures this is done to each colon in the stream and not just the first one. The reason we want the numbers separated by a space is because that allows us to access each number separately using the awk tool. So now we have the number formatted as "Hours Minutes Seconds". The final section of the sed command removes everything to the right of and including the decimal point. This is not absolutely necessary since we are using bc for arithmetic which does support floating point values, but I thought it would look neater using integers and the fractions of a second were not a concern for me. You can see the flow of these commands using an example file below. In the for loop we do this to each video and assign the output to the currentTime variable.

The next line is simple, it prints the name of the file which is currently being counted. This is not necessary for the script to function, but just a design choice. Next we echo the currentTime variable and pipe the output to awk to print the 3rd column which is the seconds output for the particular video. This is added to the seconds variable which is 0 on the first iteration of the loop. The bc command can be piped a string as input such as "12 + 13" and it will output the solution "25". We do the same thing for minutes and hours. This completes the for loop. After the for loop, I check to see if the $seconds variable is more than or equal to 60 using the syntax [[ NUM -ge NUM ]]. Other comparison operators are -gt for greater than, -eq for equal to, -ne for not equal to, -le for less than or equal to and -lt for less than. If the seconds variable is greater than or equal to 60, then I add to the $minutes variable the result of the $seconds variable divided by 60. Finally the $seconds variable is set to its old value modulo 60. Modulo or the '%' operator performs a division operation and returns the remainder of the division. So "65 % 60" would return 5. I do the same thing for minutes to hours which is the largest unit of time that I care to measure for this program. Finally I print these 3 variables to complete the script. You can see the output of this script below. As you can see, if there are formats present in the ls command which are not in the folder, that will create an error before going through the files. This is annoying but does not effect the functionality of the program.

If you see any errors or a way to optimize/improve this script, let me know!