#!/usr/bin/python3 import datetime import os import pyexiv2 import random import re import subprocess import sys import traceback from PIL import Image, ImageOps from configparser import ConfigParser from optparse import OptionParser from os.path import isdir, isfile, join, getmtime from pathlib import Path from string import Template __all__ = [] __version__ = 0.1 __date__ = '2021-12-27' __updated__ = '2021-12-27' DEBUG = 0 TESTRUN = 0 PROFILE = 0 thumbsize = (202, 202) thumbsize = (250, 250) html = Template(""" $title
$images
""") def find_file_upwards(filename, max_levels=4): current_dir = os.getcwd() for level in range(max_levels + 1): # +1 to include current directory file_path = os.path.join(current_dir, "res", "js", filename) if os.path.isfile(file_path): return current_dir, level parent_dir = os.path.dirname(current_dir) if parent_dir == current_dir: # Reached root directory break current_dir = parent_dir return None, None def get_updirs_to_webroot(filename): try: result_dir, levels = find_file_upwards(filename) path_components = [] current = os.getcwd() for _ in range(levels): path_components.insert(0, os.path.basename(current)) current = os.path.dirname(current) return "/"+"/".join(path_components) except: print(f"could not upwards find {filename}") def write_command_to_history(max_entries=20): # Define the history file path history_file = os.path.join(os.getcwd(), '.history') # Get the current timestamp timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") # Function to check if a string contains special characters def contains_special_chars(s): special_chars = r'[^a-zA-Z0-9_\-\.\/]' return bool(re.search(special_chars, s)) # Quote each argument if it contains special characters quoted_args = [] for arg in sys.argv[1:]: if contains_special_chars(arg): quoted_args.append(f'"{arg}"') else: quoted_args.append(arg) # Construct the full command with script name and quoted arguments full_command = f"{sys.argv[0]} {' '.join(quoted_args)}" # Read existing entries try: with open(history_file, 'r') as f: lines = f.readlines() except FileNotFoundError: lines = [] # Append the new entry new_entry = f"{full_command} # {timestamp}\n" lines.append(new_entry) # Truncate to max_entries lines = lines[-max_entries:] # Write the updated entries back to the file with open(history_file, 'w') as f: f.writelines(lines) # Priorität der Auswertung # 1. Exifdaten def get_caption(jpg): metadata = pyexiv2.ImageMetadata(jpg) metadata.read() tags = '' indextags = '' year = '' try: year = re.sub(':\d\d:\d\d\s\d\d:\d\d:\d\d', '', metadata['Exif.Image.DateTimeOriginal'].raw_value) tags = str(year) indextags = 'year|' + year except: pass try: tags = tags + ', ' + metadata['Xmp.dc.title'].value["x-default"] indextags = indextags + ',' + 'title|' + metadata['Xmp.dc.title'].value["x-default"].replace(',','') except: pass try: tags = tags + ', ' + metadata['Xmp.dc.description'].value["x-default"] indextags = indextags + ',' + 'description|' + metadata['Xmp.dc.description'].value["x-default"].replace(',','') except: pass try: for l in metadata['Xmp.lr.hierarchicalSubject'].raw_value: tags = tags + ', ' + l.split('|')[-1] # last element of list indextags = indextags + ',' + l.split('|')[0] + '|' + l.split('|')[-1] # last element of list except: pass return(re.sub('^,\s', '', tags), re.sub('^,\s*', '', indextags), ) def create_header(jpeglist): header = random.choice(jpeglist) if not os.path.exists('header'): os.makedirs('header') xsize = '2000' cmd = ['convert', '-quiet', header, '-thumbnail', xsize + 'x400^', '-gravity', 'center', '-extent', xsize + 'x400', "header/header.jpg"] subprocess.call(cmd, shell=False) def getimagesize(jpg): jpg = str(jpg) im = Image.open(jpg) width, height = im.size return(f"data-lg-size=\"{width}-{height}\"") def create_nav_link(link, text): if link == None: return('') link = str(link) if link.find(' ') > -1: link, text = link.split(' ', 1) return(f"{text}") def create_forward_link(link, text): if link == None: return('') link = str(link) if link.find(' ') > -1: link, text = link.split(' ', 1) return(f">> {text}") def create_backward_link(link, text): if link == None: return('') link = str(link) if link.find(' ') > -1: link, text = link.split(' ', 1) return(f"{text} <<") def read_title_file(): """ Wenn im aktuellen Verzeichnis eine Datei _title existiert, gib ihren Inhalt zurück""" try: with open('_title', 'r') as titlefile: return(titlefile.read()) except: return None def makethumb(jpg): if not os.path.isdir('_thumbnails'): os.mkdir('_thumbnails'); if not os.path.isfile(join('_thumbnails', jpg)): image = Image.open(jpg) # thumb = ImageOps.fit(image, thumbsize, Image.ANTIALIAS) # thumb.save(join('_thumbnails', jpg)) image.thumbnail(thumbsize, Image.Resampling.LANCZOS) image.save(join('_thumbnails', jpg)) else: if getmtime(jpg) > getmtime(join('_thumbnails', jpg)): image = Image.open(jpg) image.thumbnail(thumbsize, Image.Resampling.LANCZOS) image.save(join('_thumbnails', jpg)) def read_config(opts): try: config_object = ConfigParser() config_object.read( "." + os.path.splitext(os.path.basename(__file__))[0] ) ini = dict(config_object.items('default')) for key in ini: if not opts.__dict__[key]: opts.__dict__[key] = ini[key] except: pass def write_config(opts): ini = {} for key in opts.__dict__: value = opts.__dict__[key] if value is None: pass elif key == 'verbose': pass else: ini[key] = str(value) config_object = ConfigParser() config_object.read_dict({'default' : ini}) with open("." + os.path.splitext(os.path.basename(__file__))[0], 'w') as conf: config_object.write(conf) def create_index_html(indexdir, opts): startdir = os.getcwd() os.chdir(indexdir) write_command_to_history() read_config(opts) jpegs = [] jpeglist = [] valid_images = [".jpg",".JPG"] dirlist = os.listdir('.') dirlist.sort() for jpg in dirlist: ext = os.path.splitext(jpg)[1] if ext.lower() not in valid_images: continue makethumb(jpg); jpg = str(jpg) jpeglist.append(jpg) thumb = jpg caption, indextags = get_caption(jpg) sizestring = getimagesize(jpg) jpegs.append(f""" """) images = "\n".join(jpegs) create_header(jpeglist) forward = create_forward_link(opts.forward, 'weiter') backward = create_backward_link(opts.backward, 'zurück') up = create_nav_link('..', 'Index') title = read_title_file() or opts.title or os.path.basename(os.path.normpath(os.getcwd())) with open('index.html', 'w') as f: f.write(html.substitute(images=images, title=title, navigation='    '.join([backward, up, forward]), dir_to_zip=get_updirs_to_webroot("lightgallery.min.js") )) write_config(opts) os.chdir(startdir) def main(argv=None): '''Command line options.''' program_name = os.path.basename(sys.argv[0]) program_version = "v0.1" program_build_date = "%s" % __updated__ program_version_string = '%%prog %s (%s)' % (program_version, program_build_date) program_longdesc = '''create index.html for lightgallery in a directory of jpegs''' program_license = "Copyright 2021 Markus Spring (markus-spring.info). \ Licensed under the Apache License 2.0\nhttp://www.apache.org/licenses/LICENSE-2.0" if argv is None: argv = sys.argv[1:] try: # setup option parser parser = OptionParser(version=program_version_string, epilog=program_longdesc, description=program_license) # parser.add_option("-i", "--in", dest="infile", help="set input path [default: %default]", metavar="FILE") # parser.add_option("-o", "--out", dest="outfile", help="set output path [default: %default]", metavar="FILE") parser.add_option("-v", "--verbose", dest="verbose", action="count", help="set verbosity level [default: %default]") parser.add_option("-t", "--title", action="store", dest="title", help="set gallery title [default: %default]") parser.add_option("-f", "--forward", action="store", dest="forward", help="forward url") parser.add_option("-b", "--backward", action="store", dest="backward", help="backward url") parser.add_option("-u", "--up", action="store", dest="up", help="up url") parser.set_defaults( verbose=0 ) (opts, args) = parser.parse_args(argv) if opts.verbose > 0: print(("verbosity level = %d" % opts.verbose)) # if opts.title: # print(("title = %s" % opts.title)) if len(args) < 1: parser.error("directory argument missing") # MAIN BODY # create_index_html(args[0], opts) except BaseException as ex: ex_type, ex_value, ex_traceback = sys.exc_info() # Get current system exception trace_back = traceback.extract_tb(ex_traceback) # Extract unformatter stack traces as tuples stack_trace = list() # Format stacktrace for trace in trace_back: stack_trace.append("File : %s , Line : %d, Func.Name : %s, Message : %s" % (trace[0], trace[1], trace[2], trace[3])) print("Exception type : %s " % ex_type.__name__) print("Exception message : %s" %ex_value) print("Stack trace : %s" %stack_trace) if __name__ == "__main__": if DEBUG: sys.argv.append("-h") if TESTRUN: import doctest doctest.testmod() if PROFILE: import cProfile import pstats profile_filename = 'config_reader.config_reader_profile.txt' cProfile.run('main()', profile_filename) statsfile = open("profile_stats.txt", "wb") p = pstats.Stats(profile_filename, stream=statsfile) stats = p.strip_dirs().sort_stats('cumulative') stats.print_stats() statsfile.close() sys.exit(0) sys.exit(main()) # Local Variables: # compile-command: "python3 /home/springm/projekte/python/lightgallery/create_lightgallery.py -t '1958-1963' -f '../002_1960-1962 1960-1962' -u.. /media/vaters_dias/001_1958-1963" # End: