#!/usr/bin/env python3 import os import argparse import urllib.parse import shutil from multiprocessing import Pool from string import Template import numpy as np from tqdm.auto import tqdm """ root and webroot must point to the same folder, one on filesystem and one on the webserver. Use absolut paths, e.g. /data/pictures/ and https://pictures.example.com/ """ _ROOT = "/data/pictures/" _WEBROOT = "https://pictures.example.com/" _FOLDERICON = "https://www.svgrepo.com/show/400249/folder.svg" _ROOTTITLE = "Pictures" imgext = [".jpg", ".jpeg"] rawext = [".3fr", ".ari", ".arw", ".bay", ".braw", ".crw", ".cr2", ".cr3", ".cap", ".data", ".dcs", ".dcr", ".dng", ".drf", ".eip", ".erf", ".fff", ".gpr", ".iiq", ".k25", ".kdc", ".mdc", ".mef", ".mos", ".mrw", ".nef", ".nrw", ".obm", ".orf", ".pef", ".ptx", ".pxn", ".r3d", ".raf", ".raw", ".rwl", ".rw2", ".rwz", ".sr2", ".srf", ".srw", ".tif", ".tiff", ".x3f"] excludes = ["Galleries", ".previews", "Archives", "Wallpaper"] thumbnails: list[tuple[str, str]] = [] HTMLHEADER = """ $title """ def thumbnail_convert(arguments: tuple[str, str]): folder, item = arguments if not os.path.exists(os.path.join(args.root, ".previews", folder.removeprefix(args.root), os.path.splitext(item)[0]) + ".jpg") or args.regenerate: if shutil.which("magick"): os.system(f'magick "{os.path.join(folder, item)}" -quality 75% -define jpeg:size=1024x1024 -define jpeg:extent=100kb -thumbnail 512x512 -auto-orient "{os.path.join(args.root, ".previews", folder.removeprefix(args.root), os.path.splitext(item)[0])}.jpg"') else: os.system(f'convert "{os.path.join(folder, item)}" -quality 75% -define jpeg:size=1024x1024 -define jpeg:extent=100kb -thumbnail 512x512 -auto-orient "{os.path.join(args.root, ".previews", folder.removeprefix(args.root), os.path.splitext(item)[0])}.jpg"') def listfolder(folder: str, title: str): pbar.desc = f"Generating html files - {folder}" pbar.update(0) items: list[str] = os.listdir(folder) items.sort() images: list[str] = [] subfolders: list[str] = [] if not os.path.exists(os.path.join(args.root, ".previews", folder.removeprefix(args.root))): os.mkdir(os.path.join(args.root, ".previews", folder.removeprefix(args.root))) temp_obj = Template(HTMLHEADER) contains_files = False for item in items: if item not in excludes: if os.path.isdir(os.path.join(folder, item)): subfolders.extend([f'
Folder icon
{item}
']) listfolder(os.path.join(folder, item), item) else: pbar.desc = f"Generating html files - {folder}" pbar.update(0) contains_files = True if os.path.splitext(item)[1].lower() in imgext: image = f'
{item}
{item}' if not os.path.exists(os.path.join(args.root, ".previews", folder.removeprefix(args.root), item)): thumbnails.append((folder, item)) for raw in rawext: if os.path.exists(os.path.join(folder, os.path.splitext(item)[0] + raw)): if raw in (".tif", ".tiff"): image += f': TIFF' else: image += f': RAW' elif os.path.exists(os.path.join(folder, os.path.splitext(item)[0] + raw.upper())): if raw in (".tif", ".tiff"): image += f': TIFF' else: image += f': RAW' image += "
" images.extend([image]) pbar.desc = f"Generating html files - {folder}" pbar.update(0) if len(images) > 0 or (args.fancyfolders and not contains_files): with open(os.path.join(folder, "index.html"), "w", encoding="utf-8") as f: f.write(temp_obj.substitute(title=title)) f.write('
\n') f.write(f"

{title}

\n") f.write('
\n') for subfolder in subfolders: f.write(subfolder) f.write("\n") f.write("
\n") f.write("
\n") if len(images) > 0: f.write('
\n') for chunk in np.array_split(images, 8): f.write('
\n') for image in chunk: f.write(f" {image}\n") f.write("
\n") f.write("
\n") f.write(" \n") f.close() else: if os.path.exists(os.path.join(folder, "index.html")): os.remove(os.path.join(folder, "index.html")) pbar.update(1) def gettotal(folder): global total pbar.desc = f"Traversing filesystem - {folder}" pbar.update(0) items: list[str] = os.listdir(folder) items.sort() for item in items: if item not in excludes: if os.path.isdir(os.path.join(folder, item)): total += 1 pbar.update(1) gettotal(os.path.join(folder, item)) def main(): global total global args global pbar total = 0 # Parse command-line arguments parser = argparse.ArgumentParser(description="Generate html files for static image host.") parser.add_argument("-p", "--root", help="Root folder", default=_ROOT, required=False, type=str, dest="root") parser.add_argument("-w", "--webroot", help="Webroot url", default=_WEBROOT, required=False, type=str, dest="webroot") parser.add_argument("-i", "--foldericon", help="Foldericon url", default=_FOLDERICON, required=False, type=str, dest="foldericon", metavar="ICON") parser.add_argument("-r", "--regenerate", help="Regenerate thumbnails", action="store_true", default=False, required=False, dest="regenerate") parser.add_argument("-n", "--non-interactive", help="Disable interactive mode", action="store_true", default=False, required=False, dest="non_interactive") parser.add_argument("--fancyfolders", help="Use fancy folders instead of default apache ones", action="store_true", default=False, required=False, dest="fancyfolders") args = parser.parse_args() if not args.root.endswith("/"): args.root += "/" if not args.webroot.endswith("/"): args.webroot += "/" if not os.path.exists(os.path.join(args.root, ".previews")): os.mkdir(os.path.join(args.root, ".previews")) if args.non_interactive: print("Generating html files...") listfolder(args.root, _ROOTTITLE) with Pool(os.cpu_count()) as p: print("Generating thumbnails...") p.map(thumbnail_convert, thumbnails) else: pbar = tqdm(desc="Traversing filesystem", unit=" folders", ascii=True, dynamic_ncols=True) gettotal(args.root) pbar.close() pbar = tqdm(total=total + 1, desc="Generating html files", unit=" files", ascii=True, dynamic_ncols=True) listfolder(args.root, _ROOTTITLE) pbar.close() with Pool(os.cpu_count()) as p: for r in tqdm(p.imap_unordered(thumbnail_convert, thumbnails), total=len(thumbnails), desc="Generating thumbnails", unit=" files", ascii=True, dynamic_ncols=True): ... if __name__ == "__main__": main()