Home Blog CV Projects Patterns Notes Book Colophon Search

Gnome Thumbnailers

16 Aug, 2023

I have a hard disk full of old files, some of which are photos.

I've written a script to take find the photos and store a de-duplicated copy (based on sha256) into a drive.

I then re-generate the original folder structure so that I can browse them as before, using a hard link to the sha256 copy.

Now I'd like to be able to browse that folder structure quickly using Gnome's built in file manager. To do that I need to generate all the thumbnails in advance.

WARNING: This is quickly jotted down as a note to myself rather than a full, tested tutorial, your mileage may vary.

Before I go into some code, here are some related links:

It turns out there is an entire infrastructure for thumbnailing.

I amend my /usr/share/thumbnailers/gdk-pixbuf-thumbnailer.thumbnailer file to this to support more file types:

[Thumbnailer Entry]
TryExec=/usr/bin/gdk-pixbuf-thumbnailer
Exec=/usr/bin/gdk-pixbuf-thumbnailer -s %s %u %o
MimeType=image/png;image/jpeg;image/bmp;image/x-bmp;image/x-MS-bmp;image/gif;image/x-icon;image/x-ico;image/x-win-bitmap;image/vnd.microsoft.icon;application/ico;image/ico;image/icon;text/ico;application/x-navi-animation;image/x-portable-anymap;image/x-portable-bitmap;image/x-portable-graymap;image/x-portable-pixmap;image/tiff;image/x-xpixmap;image/x-xbitmap;image/x-tga;image/x-icns;image/x-quicktime;image/qtif;image/x-canon-cr2;image/x-sony-arw;image/x-canon-crw;image/x-kodak-dcr;image/x-adobe-dng;image/x-epson-erf;image/x-kodak-k25;image/x-kodak-kdc;image/x-minolta-mrw;image/x-nikon-nef;image/x-olympus-orf;image/x-pentax-pef;image/x-sony-sr2;image/x-sony-srf;image/x-sigma-x3f;

This doesn't seem to work for Fuji or Lumix cameras so I create a new /usr/share/thumbnailers/raf.thumbnailer like this:

[Thumbnailer Entry]
TryExec=/usr/local/bin/raf-thumbnailer
Exec=/usr/local/bin/raf-thumbnailer %i %o %s
MimeType=image/x-fuji-raf;image/x-raf;image/x-panasonic-raw;

Where the /usr/local/bin/raf-thumbnailer file looks like this:

#!/bin/bash

# Thumbnailer for Fujifilm RAF files

# This script depends on the following packages:
# - exiv2 for exiv2
# - imagemagick for convert

# Check for correct number of command-line arguments
if [ $# -eq 3 ]
then

  # First argument is path to RAW file (convert from URI to normal path)
  RAW="$1"

  # Second argument is output path for thumbnail file
  OUT="$2"

  # Third argument is image size
  SIZE="$3"

  # Remove directory from path
  NAME="${RAW##*/}"

  # Remove extension from file name
  BASE="${NAME%.*}"

  # Compose path to thumbnail as composed by exiv2
  PREV="/tmp/${BASE}-preview2.jpg"

  # Extract preview image from RAW file
  exiv2 -l /tmp -ep2 -f "${RAW}"
  
  # Convert preview image into thumbnail
  convert -auto-orient -scale "${SIZE}x${SIZE}" "${PREV}" "${OUT}"

  # Delete preview image
  rm -f "${PREV}"
fi

Checking the permissions:

ls -la /usr/share/thumbnailers/raf.thumbnailer /usr/local/bin/raf-thumbnailer
-rw-r--r-- 1 james james 1828 Aug 16 09:21 /home/james/Desktop/thumbs/thumbs.py
-rwxr-xr-x 1 root  root   901 Aug 16 09:35 /usr/local/bin/raf-thumbnailer
-rw-r--r-- 1 root  root   143 Aug 16 09:25 /usr/share/thumbnailers/raf.thumbnailer

Now, thumbnails should be correctly generated as I browse, but I want to convert them all upfront.

Here's a script to run the thumbnailer infrastructure (adjust the paths for bits you want to skip):

import os
import sys


from gi.repository import Gio, GnomeDesktop

def make_thumbnail(factory, filename):
    mtime = os.path.getmtime(filename)
    # Use Gio to determine the URI and mime type
    f = Gio.file_new_for_path(filename)
    uri = f.get_uri()
    if uri.startswith('file:///media/james/src') or uri.startswith('file:///media/james/target/'):
        # print("SKIP        %s" % uri)
        return False

    info = f.query_info(
        'standard::content-type', Gio.FileQueryInfoFlags.NONE, None)
    mime_type = info.get_content_type()

    if factory.lookup(uri, mtime) is not None:
        print("FRESH       %s" % uri)
        return False

    if not factory.can_thumbnail(uri, mime_type, mtime):
        print("UNSUPPORTED %s" % uri)
        return False

    try:
        thumbnail = factory.generate_thumbnail(uri, mime_type)
        if thumbnail is None:
            print("ERROR       %s" % uri)
            return False
    except BaseException as e:
        print("ERROR       %s" % uri)
        print(e)
        return False
    time.sleep(0.05)

    print("OK          %s" % uri)
    factory.save_thumbnail(thumbnail, uri, mtime)
    return True

def thumbnail_folder(factory, folder):
    for dirpath, dirnames, filenames in os.walk(folder):
        for filename in filenames:
            make_thumbnail(factory, os.path.join(dirpath, filename))

def main(argv):
    factory = GnomeDesktop.DesktopThumbnailFactory.new(GnomeDesktop.DesktopThumbnailSize.LARGE)
    for filename in argv[1:]:
        if os.path.isdir(filename):
            thumbnail_folder(factory, filename)
        else:
            make_thumbnail(factory, filename)

if __name__ == '__main__':
    sys.exit(main(sys.argv))

When I run this all thumbnails are generated.

Gnome is quite keen at purging them all though, so I back them all up and restore them as needed.

mkdir -p /home/james/Desktop/thumbs/cache/large
rsync -aHv /home/james/.cache/thumbnails/large/ /home/james/Desktop/thumbs/cache/large/
du -hs /home/james/Desktop/thumbs/cache/large

I can find any misgenerated ones, or work their types with commands similar to this:

find ~/.cache/thumbnails/large/ -type f -size -4096c -exec ls -la {} \;
xdg-mime query filetype INPUT_FILE

Comments

Be the first to comment.

Add Comment





Copyright James Gardner 1996-2020 All Rights Reserved. Admin.