Script day – manually find a thumbnail for a video

In a vain effort to fuel my blog with some content ( 😉 ) I’ll try to get into the habit of posting some throw-away scripts I’m writing for my personal use (at work and at home). Hopefully some people would find them interesting, or maybe – god forbid – useful.

Today we have a script that will help you pick out a nice thumbnail for a movie file – this is useful when setting up movie galleries and stuff. Of course it can be done automatically (most gallery software that support video files has automatic thumbnail extraction) but they rarely choose the best shot. The script here below would extract 100 frames from the first 30 seconds of a movie file and display them using an image browser. The script would also open a file browser window – the user will then pick the desired thumbnail image and drop it into the file browser, and then close the image browser. The script would then complete the process by naming the selected thumbnail image to be the same name as the original movie (with a different extension of course).

Requirements:

  • ffmpeg – for frame extraction
  • Image Magick’s convert – for thumbnail post processing
  • gthumb – as the image browser
  • nautilus – as the file browser

This script was written to run on my GNOME desktop, so its a bit GNOME centric, but gthumb and nautilus can be easily swapped for gwenview and dolphin, so no worries there. mencoder can possibly be used instead of ffmpeg, but I don’t know the command line for that. Image Magick is of course indispensible. The script below generates PNG thumbnails, but generating other formats – for example JPEG simply requires changing every instance of “png” to “jpg”.

Running the script is simple – supposedly its called “findthumbnail”, so I run it by providing on the command line all the video files I wish to execute it on, for example if I want to generate thumbnails for all my flv (Flash Video) files, then I’ll run

findthumbnail *.flv

The script would run on each file separately and would skip files for which there’s already an existing thumbnail. Currently the main problem is that post-processing the frames into thumbnails takes a long time (maybe 30 seconds on a fast computer) all the time the user has no feedback and is just siting there twiddling thumbs.

Anyway, here’s the code:

#!/bin/sh

while [ -n "$1" ]; do
    vid="$1"
    shift

    if ! [ -f "$vid" ]; then
        echo "File not found: $1" >&2
        continue
    fi

    vidname="$(echo $vid | sed -e 's/\.[^\.]*$//')"
    if [ -f "${vidname}.png" ]; then 
        echo "File $vid already has a thumbnail"
        continue
    fi

    if [ -z "$browser" ]; then
        nautilus -n .
        browser=yes
    fi

    mkdir -p $HOME/.local/tmp/thumbs.$$
    ffmpeg -i "$vid" -r 3 -vframes 100  -y $HOME/.local/tmp/thumbs.$$/vid_frame_%03d.png
    (
        cd $HOME/.local/tmp/thumbs.$$
        for file in *.png; do 
            convert "$file" -resize x300 -gravity center -crop 400x300+0+0 +repage thumb_"$file"
        done
        rm vid_frame*.png -f
    )

    gthumb $HOME/.local/tmp/thumbs.$$
    rm -rf $HOME/.local/tmp/thumbs.$$

    mv thumb_vid_frame_*.png "${vidname}.png"
done

See ? pretty simple.

Installation is simple creating a text file somewhere and putting that code into it. Its pretty white space insensitive, except for the first line – in order for the kernel to be able to run it there mustn’t be any white space before the first character. I usually put such scripts under ~/.local/bin which is in my local execution path1, so this script would go into ~/.local/bin/findthumbnail. After you create the file, you just need to give it execute permissions – your favorite file browser should have this option in the file properties dialog, but from the command line you’d do something like

chmod a+x ~/.local/bin/findthumbnail

A simple explanation of the various stages is in order, I think: we loop as long as we have some value from the command line – and for each loop we get the (current) first parameter from the command line and remove it. We then check if the file exists – if not we log an error and check the next parameter. If there is a file by that name, we strip the extension from the filename and check if there is already a thumbnail for that movie – that way we can rerun the script on a directory and it will only generate thumbnails that are missing. If there isn’t an old thumbnail we can go on to create a temporary directory, extract some frames from the video file into said temp directory (using ffmpeg), create thumbnails from the frames (using convert) and remove the used frames. Lastly we launch the image browser and wait for it to be closed by the user. when the image browser is done we clean up the temporary directory and if the user has chosen a thumbnail then we make sure its named like the original file.

I skipped the part about opening the file browser because this is slightly more complicated – I want to open only one file browser, regardless how many video files I’m processing, but I don’t want to open the browser if I’m not going to process any files. So I only open the file browser after I decide that I am going to do some work, and then I use a variable to flag that I already opened the file browser so I don’t need to do it again.

Thats it. Have fun.

  1. ~/.local is part of the xdg user directories specifications from freedesktop.org, and I’m not really sure what was the original purpose (the xdg specs are really terse), but I figured its a good place to put local installations and stuff, so all my user specific things are using –prefix=$HOME/.local. It also keeps the home directory relatively mess-free. []

Leave a Reply