Skip to content

Instantly share code, notes, and snippets.

@meawoppl
Last active May 11, 2024 18:55
Show Gist options
  • Save meawoppl/74942f65438593e69b8747b10f53d8ac to your computer and use it in GitHub Desktop.
Save meawoppl/74942f65438593e69b8747b10f53d8ac to your computer and use it in GitHub Desktop.
Nautilus Thumbnail Generator. Python 3 scalable and fast.
#!/usr/bin/python3
import concurrent.futures
import hashlib
import os
import sys
import gi
gi.require_version('GnomeDesktop', '3.0')
from gi.repository import Gio, GnomeDesktop
def make_thumbnail(factory, filename, overwrite=False):
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()
info = f.query_info('standard::content-type', Gio.FileQueryInfoFlags.NONE, None)
mime_type = info.get_content_type()
# Already thumbnailed
path = factory.lookup(uri, mtime)
if overwrite and path is not None:
print("Removed:" + path)
os.remove(path)
if (not overwrite) and (path is not None):
return False
# Not thumbnailable
if not factory.can_thumbnail(uri, mime_type, mtime):
return False
# Error during thumb
thumbnail = factory.generate_thumbnail(uri, mime_type)
if thumbnail is None:
print("Error thumbnailing: " + str(uri))
return False
factory.save_thumbnail(thumbnail, uri, mtime)
return True
def thumbnail_targets(factory, targets: list, overwrite=False):
tcount = os.cpu_count() * 2
with concurrent.futures.ThreadPoolExecutor(max_workers=tcount) as tpe:
n_created = 0
nails = tpe.map(lambda name: make_thumbnail(factory, name, overwrite=overwrite), targets)
for n, f in enumerate(nails):
update_txt = "Thumbnailing: {:.2%}".format((n + 1) / len(targets))
print(update_txt, end="\r")
n_created += f
print("\nThumbnailed %i files." % n_created)
def gather_paths(targets: list, ignore_dot_prefixed=True):
file_names = []
for target in targets:
if os.path.isdir(target):
for dirpath, folders, filenames in os.walk(target):
# Skip recursion into dotfiles if requested
# This helps with windows/osx FS/trash/etc
if ignore_dot_prefixed:
for folder in folders:
if folder.startswith("."):
folders.remove(folder)
# Append files found to list
for filename in filenames:
file_names.append(os.path.join(dirpath, filename))
continue
if os.path.isfile(target):
file_names.append(target)
continue
raise ValueError("Don't know what to do with: " + target)
file_names.sort()
print(file_names)
return file_names
if __name__ == '__main__':
import argparse
ap = argparse.ArgumentParser(
description="A tool for generating Gnome thumbnails in parallel.")
ap.add_argument(
"paths",
help="The path(s) to thumbnail (typically one or more folders)",
nargs="+")
ap.add_argument(
"--overwrite",
help="Overwrite existing thumbnails.",
action="store_true")
ap.add_argument(
"--dotfiles",
help="Don't ignore directories prefixed with '.'",
action="store_false")
parsed = ap.parse_args()
factory = GnomeDesktop.DesktopThumbnailFactory()
targets = gather_paths(parsed.paths, ignore_dot_prefixed=parsed.dotfiles)
print("%i targets found" % len(targets))
thumbnail_targets(factory, targets, overwrite=parsed.overwrite)
@barureddy
Copy link

This is exactly what I needed for previewing thousands of large images quickly. Thanks!

@meawoppl
Copy link
Author

meawoppl commented Mar 3, 2020

I'm glad you like it. I'm surprised the default gnome config doesn't support any concurrency in this. Feel free to share, and lmk if it needs any improvements.

@jp-x-g
Copy link

jp-x-g commented Sep 14, 2023

This is a very useful script. Thank you.

Here is a minor issue: running the script with no arguments generates a strange error, like this.

[x@localhost]$ python3 thumbnailer.py 
usage: meawoppl thumbnailer.py [-h] [--overwrite] [--dotfiles] paths [paths ...]
meawoppl thumbnailer.py: error: the following arguments are required: paths

I'd recommend making argparse handle this a bit more smoothly (i.e. printing the --help output and exiting if the script's invoked with no arguments, rather than exiting without explaining what the help flag is); I may make a pull request for this later if I remember.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment