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)
@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