| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- #!/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("""<html>
- <head>
- <title>$title</title>
- <link type="text/css" rel="stylesheet" href="/res/css/lightgallery-bundle.css" />
- <link type="text/css" rel="stylesheet" href="/res/css/lg-zoom.css" />
- <link type="text/css" rel="stylesheet" href="/res/css/lg-thumbnail.css" />
- <link type="text/css" rel="stylesheet" href="/res/css/bootstrap.min.css" />
- <link type="text/css" rel="stylesheet" href="/res/user.css" />
- <script src="/res/js/lightgallery.min.js"></script>
- <script src="/res/plugins/hash/lg-hash.min.js"></script>
- <script src="/res/plugins/fullscreen/lg-fullscreen.min.js"></script>
- <script src="/res/plugins/autoplay/lg-autoplay.min.js"></script>
- <script src="/res/plugins/thumbnail/lg-thumbnail.min.js"></script>
- <script src="/res/plugins/zoom/lg-zoom.min.js"></script>
- <style>
- #header {
- background-image: url("header/header.jpg");
- }
- </style>
- </head>
- <body>
- <div id="header">
- <h1>$title</h1>
- <div id="nav">$navigation</div>
- </div>
- <div id="outer_lightgallery">
- <div id="lightgallery">
- $images
- </div>
- </div>
- <script type="text/javascript">
- lightGallery(document.getElementById('lightgallery'), {
- plugins: [lgZoom, lgThumbnail, lgAutoplay, lgFullscreen ],
- //speed: 500,
- //mode: 'fade',
- licenseKey: '0000-0000-000-0000'
- });
- </script>
- </body>
- </html>
- """)
- 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"<a href='{link}'>{text}</a>")
- def create_forward_link(link, text):
- if link == None:
- return('')
- link = str(link)
- if link.find(' ') > -1:
- link, text = link.split(' ', 1)
- return(f"<a href='{link}'>>> {text}</a>")
- def create_backward_link(link, text):
- if link == None:
- return('')
- link = str(link)
- if link.find(' ') > -1:
- link, text = link.split(' ', 1)
- return(f"<a href='{link}'>{text} <<</a>")
- 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""" <a href="{jpg}" {sizestring} data-sub-html="{caption}" data_index="{indextags}">
- <img src="_thumbnails/{thumb}" />
- </a>""")
- 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]) ))
- 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:
|