"""
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 ] [-e ]
OPTIONS:
-h, --help This help message
-v, --version Program version
-m Make gallery page
-t Title [default: Unknown]
-f First (top-level) gallery name [default: Unknown]
-e 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 = """
%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 = """
%s
"""
FLEXTEMPLATE_PHOTO = """
%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.")