""" PyGallery.py Wesley R. Elsberry Minimal automatic gallery construction from a directory tree of images. Create a page for a gallery based on the current directory tree of images. Uses CSS flexbox for arrangement. Scans for JPG, PNG images in the directory tree. Creates thumbnails in a pygal.thumbs directory. Strips out EXIF data from thumbnails, mostly for size consideration. """ import sys import os import platform import docopt import PIL import re import glob import pathlib PROGNAME = "PyGallery.py" # Docstring for docopt use PROGDOCOPT = f"""{PROGNAME} USAGE: {PROGNAME} [-h|--help] {PROGNAME} [-v|--version] {PROGNAME} -m [-t ] [-f <firstgalleryname>] [-e <excludedirslist>] OPTIONS: -h, --help This help message -v, --version Program version -m Make gallery page -t <title> Title [default: Unknown] -f <firstgalleryname> First (top-level) gallery name [default: Unknown] -e <excludedirslist> List of directories to exclude [default: ['pygal.thumbs']] {PROGNAME} aims to make it simple to set up an online gallery page using a directory tree of images in JPEG or PNG format. By default, {PROGNAME} will produce an 'index.html' file in the current directory. It will create thumbnail images and a 'pygal.thumbs' directory for them. Example invocation: python {PROGNAME} -m -t 'Elsberry Family Photos' -f 'Elsberry Family' """ import sys import os import platform import docopt import PIL import re import glob import pathlib PROGNAME = "PyGallery.py" GALLERYTEMPLATE_TOP = """<html> <head> <title>%s

%s

Navigation: Click on a thumbnail to view the image. <Esc> exits the view. Left arrow for previous image, right arrow for next image.

""" JAVASCRIPT_MODAL = """ """ GALLERYTEMPLATE_BOTTOM = """ """ GALLERYTEMPLATE_PHOTO = """ """ FLEXTEMPLATE_PHOTO = """
%s
%s
""" def fill_photo_element(fullresname, thumbname, alttext="", caption="", extra=""): filepath, filename = os.path.split(fullresname) filebase, fileext = os.path.splitext(filename) if alttext in [None, ""]: alttext = filebase if caption in [None, ""]: caption = filebase #gtp = GALLERYTEMPLATE_PHOTO % (fullresname, thumbname, alttext, extra, caption) gtp = FLEXTEMPLATE_PHOTO % (fullresname, thumbname, alttext, extra, caption) return gtp def endswithany(mystr, endlist=[], usecase=True): matched = False for eli in endlist: matched = matched or mystr.endswith(eli) return matched def get_file_list(scandir=".", myset={".jpg", ".jpeg", ".jfif", ".png", ".gif"}, excludedirs=['pygal.thumbs']): # print("get_file_list excludedirs", excludedirs) files = [p for p in (p.resolve() for p in pathlib.Path(scandir).glob("**/*") if p.suffix.lower() in myset and not any(exdir in p.parts for exdir in excludedirs))] files = [x for x in files if is_not_excluded(x, excludedirs)] return files def is_not_excluded(filepath, excludedirslist): """ Check if the given filepath does not have any directory component that matches an entry in the excludedirslist. Args: - filepath (str): The path to the file. - excludedirslist (list): List of directory names to exclude. Returns: - bool: True if the file is not in an excluded directory, False otherwise. """ path_parts = pathlib.Path(filepath).parts passed = True for ppi in path_parts: if ppi in excludedirslist: passed = False #passed = not any(exdir in path_parts for exdir in excludedirslist) # print("is_not_excluded", filepath, path_parts, excludedirslist, passed) return passed # Example usage: #filepath = "/home/user/pygal.thumbs/image.jpg" #excludedirslist = ['pygal.thumbs'] #print(is_not_excluded(filepath, excludedirslist)) # This should print False def add_to_dict_of_lists(dol, key, value): if key in dol: dol[key].append(value) else: dol[key] = [value] return dol def list_to_groups(files): grouped = {} for fi in files: filepath, filename = os.path.split(fi) grouped = add_to_dict_of_lists(grouped, filepath, fi) grouplist = sorted([x for x in grouped.keys()]) return grouplist, grouped def strip_exif(files, stripper="jpegoptim -s "): for fi in files: cmd = f"{stripper} {fi}" print(cmd) os.system(cmd) def prep_thumbs(files, proc='convert', thumbsdir="pygal.thumbs", thumbwidth=128, thumbheight=86): if not os.path.exists(thumbsdir): os.mkdir(thumbsdir) thumbdict = {} thumbfiles = [] for fi in files: filepath, filename = os.path.split(fi) oldfn = fi newfn = os.path.join(thumbsdir, "thumb_" + filepath.replace("/", "_") + filename) thumbdict[fi] = newfn thumbfiles.append(newfn) cmd = f"{proc} {oldfn} -resize {thumbwidth}x{thumbheight} -channel RGB -contrast-stretch 0.02x0.02% {newfn}" print(cmd) os.system(cmd) strip_exif(thumbfiles) return thumbdict def make_index(docargs, maxlineimages=4, excludedirs=['pygal.thumbs']): # print("make_index excludedirs", excludedirs) filesraw = [str(x) for x in get_file_list(excludedirs=excludedirs)] filesraw = [x for x in filesraw if is_not_excluded(x, excludedirs)] # print(excludedirs, filesraw) curdir = os.path.abspath(".") filesrel = sorted([x[len(curdir)+1:] for x in filesraw]) #for fri in filesrel: # print(fri) grouplist, grouped = list_to_groups(filesrel) #print(grouplist) #print(grouped) thumbdict = prep_thumbs(filesrel) #print(thumbdict) #print(docargs) # We have the components. Make the page. pgstr = GALLERYTEMPLATE_TOP % (docargs["-t"], maxlineimages, docargs["-t"]) #pgstr += "\n\n" pgstr += JAVASCRIPT_MODAL pgstr += GALLERYTEMPLATE_BOTTOM # Put the string out to file fh = open("index.html", "w") fh.write(pgstr) fh.close() print("done.") def dispatch(docargs, excludedirs): #print("dispatch excludedirs", excludedirs) if docargs["-m"]: make_index(docargs, maxlineimages=4, excludedirs=excludedirs) if __name__ == "__main__": """ filepath = "/home/user/pygal.thumbs/image.jpg" filepath = "/home/user/tmp1/image.jpg" excludedirslist = ['pygal.thumbs', 'alt', 'tmp'] print(filepath, excludedirslist, is_not_excluded(filepath, excludedirslist)) # This should print False """ docargs = docopt.docopt(PROGDOCOPT, version="1.01") print(docargs) # Extract excludedirs from docargs if specified, otherwise use the default excludedirs = docargs.get('-e', ['pygal.thumbs']) #print('main excludedirs', excludedirs) dispatch(docargs, excludedirs=excludedirs) print("pygal.py done.")