Get total duration of video files in a directory

I have no .ts here but this works for .mp4. Use ffprobe (part of ffmpeg) to get the time in seconds, e.g:

ffprobe -v quiet -of csv=p=0 -show_entries format=duration Inception.mp4 
275.690000

so for all .mp4 files in the current dir:

find . -maxdepth 1 -iname '*.mp4' -exec ffprobe -v quiet -of csv=p=0 -show_entries format=duration {} \;
149.233333
130.146667
275.690000

then use paste to pass the output to bc and get the total time in seconds:

find . -maxdepth 1 -iname '*.mp4' -exec ffprobe -v quiet -of csv=p=0 -show_entries format=duration {} \; | paste -sd+ -| bc
555.070000

So, for .ts files you could try:

find . -maxdepth 1 -iname '*.ts' -exec ffprobe -v quiet -of csv=p=0 -show_entries format=duration {} \; | paste -sd+ -| bc

Another tool that works for the video files I have here is exiftool, e.g.:

exiftool -S -n Inception.mp4 | grep ^Duration
Duration: 275.69
exiftool -q -p '$Duration#' Inception.mp4
275.69

Total length for all .mp4 files in the current directory:

exiftool -S -n ./*.mp4 | awk '/^Duration/ {print $2}' | paste -sd+ -| bc
555.070000000000
exiftool -q -p '$Duration#' ./*.mp4 | awk '{sum += $0}; END{print sum}'
555.070000000000

You could also pipe the output to another command to convert the total to DD:HH:MM:SS, see the answers here.

Or use exiftool's internal ConvertDuration for that (you need a relatively recent version though):

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

This uses ffmpeg and prints the time out in total seconds:

times=()
for f in *.ts; do
    _t=$(ffmpeg -i "$f" 2>&1 | grep "Duration" | grep -o " [0-9:.]*, " | head -n1 | tr ',' ' ' | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')
    times+=("$_t")
done
echo "${times[@]}" | sed 's/ /+/g' | bc

Explanation:

for f in *.ts; do iterates each of the files that ends in ".ts"

ffmpeg -i "$f" 2>&1 redirects output to stderr

grep "Duration" | grep -o " [0-9:.]*, " | head -n1 | tr ',' ' ' isolates the time

awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }' Converts time to seconds

times+=("$_t") adds the seconds to an array

echo "${times[@]}" | sed 's/ /+/g' | bc expands each of the arguments and replaces the spaces and pipes it to bc a common linux calculator


Streamlining @jmunsch's answer, and using the paste I just learned from @slm's answer, you could end up with something like this:

for i in *.ts; do LC_ALL=C ffmpeg -i "$i" 2>&1 | \
awk -F: '/Duration:/{print $2*3600+$3*60+$4}'; done | paste -sd+ | bc

Just like jmunsch did, I'm using ffmpeg to print the duration, ignoring the error about a missing output file and instead searching the error output for the duration line. I invoke ffmpeg with all aspects of the locale forced to the standard C locale, so that I won't have to worry about localized output messages.

Next I'm using a single awk instead of his grep | grep | head | tr | awk. That awk invocation looks for the (hopefully unique) line containing Duration:. Using colon as a separator, that label is field 1, the hours are field 2, the minutes filed 3 and the seconds field 4. The trailing comma after the seconds doesn't seem to bother my awk, but if someone has problems there, he could include a tr -d , in the pipeline between ffmpeg and awk.

Now comes the part from slm: I'm using paste to replace newlines with plus signs, but without affecting the trailing newline (contrary to the tr \\n + I had in a previous version of this answer). That gives the sum expression which can be fed to bc.

Inspired by slm's idea of using date to handle time-like formats, here is a version which does use it to format the resulting seconds as days, hours, minutes and seconds with fractional part:

TZ=UTC+0 date +'%j %T.%N' --date=@$(for i in *.ts; do LC_ALL=C \
ffmpeg -i "$i" 2>&1 | awk -F: '/Duration:/{print $2*3600+$3*60+$4}'; done \
| paste -sd+ | bc) | awk '{print $1-1 "d",$2}' | sed 's/[.0]*$//'

The part inside $(…) is exactly as before. Using the @ character as an indication, we use this as the number of seconds since the 1 January 1970. The resulting “date” gets formatted as day of the year, time and nanoseconds. From that day of the year we subtract one, since an input of zero seconds already leads to day 1 of that year 1970. I don't think there is a way to get day of the year counts starting at zero.

The final sed gets rid of extra trailing zeros. The TZ setting should hopefully force the use of UTC, so that daylight saving time won't interfere with really large video collections. If you have more than one year worth of video, this approach still won't work, though.