How can I get duration of all video files in a folder containing multiple subfolders?

I have a folder named tutorials. Inside it, there are about 15 folders each containing about 15-20 .mp4 video files. I want to get the total duration of all the files present in the folder tutorials. This is the code I have written so far:

 for d in ~/Videos/tutorials/*; do
   if [ -d "$d" ]; then
     exiftool -n -q -p '${Duration;our $sum;$_=ConvertDuration($sum+=$_)}' ./*.mp4| tail -n1
   fi
 done

The above code, when executed, gives an error File not found: ./*.mp4 for each of the subfolders present inside tutorials. However, when the line

exiftool -n -q -p '${Duration;our $sum;$_=ConvertDuration($sum+=$_)}' ./*.mp4| tail -n1

is executed individually inside each sub-folders, I am able to get the correct output.

What changes should I make in the above code to get it working?


Solution 1:

You could use this instead which finds you all your mp4's in a specific folder and all subfolders and sums their duration to a total. This takes also care if they have special characters like a space etc.

find ~/Videos/tutorials/ -name "*.mp4" -printf "\"%p\" " | xargs exiftool -n -q -p '${Duration;our $sum;$_=ConvertDuration($sum+=$_)}' | tail -n 1

To see the totals of every subfolder aswell

#!/bin/bash

SEARCHPATH=~/Videos/tutorials

echo "## TOTAL OF EVERY MP4"
find  $SEARCHPATH -name "*.mp4" -printf "\"%p\" " | xargs exiftool -n -q -p '${Duration;our $sum;$_=ConvertDuration($sum+=$_)}' | tail -n 1

find $SEARCHPATH -type d  > /tmp/allFolders.txt

while read -r; do
echo "## TOTAL OF $REPLY"
find "$REPLY" -maxdepth 1 -name "*.mp4" -printf "\"%p\" " | xargs exiftool -n -q -p '${Duration;our $sum;$_=ConvertDuration($sum+=$_)}' | tail -n 1
done < /tmp/allFolders.txt

#cleanup
rm /tmp/allFolders.txt

Solution 2:

You should replace ./*.mp4 with "$d"/*.mp4:

 for d in ~/Videos/tutorials/*; do
   if [ -d "$d" ]; then
     exiftool -n -q -p '${Duration;our $sum;$_=ConvertDuration($sum+=$_)}' "$d"/*.mp4| tail -n1
   fi
 done

Your for loop is finding any directories in ~/Videos/tutorials/ and assigning its path to the value of the d variable. That's the directory you want to check for mp4 files with exiftool, whereas you're telling exiftool to use the current directory with ./.

Another approach would be to cd to the directory each time, so your code would become:

 for d in ~/Videos/tutorials/*; do
   if [ -d "$d" ]; then
     cd "$d"
     exiftool -n -q -p '${Duration;our $sum;$_=ConvertDuration($sum+=$_)}' ./*.mp4| tail -n1
   fi
 done

The following approach can be used if you have troublesome filenames for some of the mp4 files, such as containing spaces. It saves the filenames into an array, and expands them quoted in the exiftool command.

 for d in ~/Videos/tutorials/*; do
       if [ -d "$d" ]; then
         cd "$d"
         files=(./*.mp4)         
         exiftool -n -q -p '${Duration;our $sum;$_=ConvertDuration($sum+=$_)}' "${files[@]}"| tail -n1
       fi
     done