About two years ago I started hosting my own media library on a NAS. Since then I collected and digitized a lot of my DVD and Blu-ray collection. Now during the holidays I was wondering: How much playtime do I have in my library?

This consisted of a three-part Problem:

  • How can I recursively find all relevant media files in a directory, since the structure is often nested with subdirectories?
  • How do I extract the playtime of each file?
  • How do I add the playtime together and display it in a sensible way? e.g., not in total seconds but in hours.

First, we need to select the relevant media files. In my case this only includes .mkv files. So we could use the find command on Linux to walk through the directories and return all .mkv files using

find . -type f -name "*.mkv"

Onto getting the playtime for a file in an efficiently. The brilliant ffmpeg library includes a package to extract the metadata of an audio or video file.

ffprobe -v error -show_entries format=duration \
-of default=noprint_wrappers=1:nokey=1 {}

This command shows us the duration of whichever file we pass into {} and only displays error, such that we don’t see every individual file path when we run our script. Also, we want to only display the runtime without any additional information such that we can easily sum up the values in the next step.

Here we use awk to sum up the duration values and format them rounded to two decimal places. Putting it all together, our script looks like this:

find . -type f -name "*.mkv" -exec ffprobe -v error -show_entries \
format=duration -of default=noprint_wrappers=1:nokey=1 {} \; \
| awk '{s+=$1} END {h=s/3600; s=s%3600; printf "%.2d:%.2d\n", int(h), int(s/60)}' 

As a little bonus, I also tried to run the command using fd command, which is a Rust-based drop in replacement for find. Since most of the runtime seems to be spent walking through the directory tree, it can speed up the script execution quite a bit. In my case, from 4 min 23 sec to 1 min 29 sec. So this might be worth the effort depending on how large and nested your library is. You will need to adapt the syntax slightly:

fd --extension mkv --exec \
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 {} \; | awk '{s+=$1} END {h=s/3600; s=s%3600; printf "%.2d:%.2d\n", int(h), int(s/60)}'

Note: The executable of fd will be called on fdfind on Debian-based systems.