#!/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
""")
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: