Home Updates Messages SandBox

Thumbnails for Archives with Images

I often keep keep collections of images in ZIP archives with no compression – this allows me to group them conveniently and save them on CD and DVD disks without running out of TOC space. I also allows me to browse them conveniently with a comic book reader such as Comix. There was only one problem with this approach: no thumbnails. In the old, old Windows days I was using an image viewer called AcdSee (an old, simple version, not what it has become now). Among other things, it allowed me to browse inside archives and to display thumbnails for them. This was very convenient. This viewer greatly influenced many open source image viewers, but I never found one with both the features I needed and sane user interface. When a new version of XFCE came out with its excellent file manager Thunar, I not only learned to use a graphical file manager, but I also began to use it for browsing images, together with a lightweight and fast image viewer such as Mirage. Still I had no thumbnails for the archives or directories.

So I lost hope for a convenient solution. Then I noticed a strange thing about some of the thumbnails: when I created an XCF image with GIMP, Thunar displayed it nicely with its thumbnail. But if I renamed or moved the file, the thumbnail was no longer available until I opened the file in GIMP again (or just browsed the directory with GIMP and used preview on that file). The explanation is simple: Thunar doesn't know how to make thumbnails for XCF files, but it will use an existing thumbnail if it was generated with some other application. This means that I can generate thumbnails for my archive files, and Thunar will display them. A little searching and I found out that Thunar uses Freedesktop's Thumbnail Managing Standard for storing and retrieving thumbnails. That's why it can share them with many other applications that use the same standard.

The following Python script will generate thumbnail images for ZIP archives containing images. The script is using PIL to generate the thumbnails, so you need to have it installed.

#!/usr/bin/python

import zipfile
import sys
import re
import os
import hashlib
import StringIO
import urllib
import Image
import PngImagePlugin

image_re = re.compile('\.jpe?g$|\.png$|\.gif$|\.tiff?$', re.I)

def find_first_image(archive):
    """Find the first image file in a zip archive."""

    for image_name in archive.namelist():
        if image_re.search(image_name):
            return image_name
    return None

def create_thumbnail(file_name):
    """Create a thumbnail image for a zip file."""

    archive = zipfile.ZipFile(file_name)
    image_name = find_first_image(archive)
    if not image_name:
        return None
    image_data = archive.read(image_name)
    image_file = StringIO.StringIO(image_data)
    image = Image.open(image_file)
    image.thumbnail((128,128), Image.ANTIALIAS)
    return image

def get_file_uri(file_name):
    """Return URI of the file."""

    path = os.path.abspath(file_name)
    return 'file://' + urllib.pathname2url(path)

def get_thumb_info(file_name):
    """Create meta information for the thumbnail."""

    info = PngImagePlugin.PngInfo()
    info.add_text("Thumb::URI", get_file_uri(file_name))
    mtime = str(os.path.getmtime(file_name))
    info.add_text("Thumb::MTime", mtime)
    return info

def save_thumbnail(file_name, image):
    """Save the image as a thumbnail for specified file."""

    if not image:
        return
    file_uri = get_file_uri(file_name)
    file_uri_hash = hashlib.md5(file_uri).hexdigest()
    temp_name = "zipthumbgen_temp.png"
    thumb_name = ".".join((file_uri_hash, "png"))
    thumb_path = os.path.join(os.getenv('HOME', ''), '.thumbnails/normal/')
    temp_file = os.path.join(thumb_path, temp_name)
    thumb_file = os.path.join(thumb_path, thumb_name)
    info = get_thumb_info(file_name)
    image.save(temp_file, "PNG", pnginfo=info)
    os.chmod(temp_file, 0600)
    os.rename(temp_file, thumb_file)
    return thumb_file

for file_name in sys.argv[1:]:
    print file_name, "-->", save_thumbnail(
        file_name,
        create_thumbnail(file_name)
    )

The script is a little hacky, but it does its job. You need to run it with a list of files as argument – then it will generate thumbnails for these files and save them in your home directory. Some day I must also write a script to generate thumbnails for removable devices – the standard allows that. Of course, it's also possible to tell Thunar to attempt to generate the thumbnails automatically for all archives, using custom thumbnailers, but I decided I will pass – just in case I had some huge archives without actual images in them.