added metadata regeneration option and display of date captured if avialable

This commit is contained in:
2024-09-20 10:17:13 +02:00
committed by Flo Greistorfer
parent 961d79754e
commit 9403e84d78
8 changed files with 37 additions and 20 deletions

View File

@@ -1 +1 @@
2.3.4 2.3.5

View File

@@ -44,20 +44,20 @@ The script supports several command-line options to customize its behavior. Belo
### Options ### Options
- `-h, --help`: Show the help message and exit.
- `-p ROOT, --root-directory ROOT`: Specify the root folder where the images are stored. This option is required.
- `-w URL, --web-root-url URL`: Specify the base URL for the web root of the image hosting site. This option is required.
- `-t TITLE, --site-title TITLE`: Specify the title of the image hosting site. This option is required.
- `-r, --regenerate-thumbnails`: Regenerate thumbnails even if they already exist.
- `-n, --non-interactive-mode`: Run in non-interactive mode, disabling progress bars.
- `-l LICENSE, --license-type LICENSE`: Specify the license type for the images. Choices are `cc-zero`, `cc-by`, `cc-by-sa`, `cc-by-nd`, `cc-by-nc`, `cc-by-nc-sa`, and `cc-by-nc-nd`.
- `-a AUTHOR, --author-name AUTHOR`: Specify the name of the author of the images. Default is "Author". - `-a AUTHOR, --author-name AUTHOR`: Specify the name of the author of the images. Default is "Author".
- `-e EXTENSION, --file-extensions EXTENSION`: Specify the file extensions to include. This option can be specified multiple times. - `-e EXTENSION, --file-extensions EXTENSION`: Specify the file extensions to include. This option can be specified multiple times.
- `-l LICENSE, --license-type LICENSE`: Specify the license type for the images. Choices are `cc-zero`, `cc-by`, `cc-by-sa`, `cc-by-nd`, `cc-by-nc`, `cc-by-nc-sa`, and `cc-by-nc-nd`.
- `-m, --web-manifest`: Generate a web manifest file.
- `-n, --non-interactive-mode`: Run in non-interactive mode, disabling progress bars.
- `-p ROOT, --root-directory ROOT`: Specify the root folder where the images are stored. **(This option is required)**.
- `-t TITLE, --site-title TITLE`: Specify the title of the image hosting site. **(This option is required)**.
- `-w URL, --web-root-url URL`: Specify the base URL for the web root of the image hosting site. **(This option is required)**.
- `--exclude-folder FOLDER`: Specify folders to exclude from processing. This option can be specified multiple times.
- `--ignore-other-files`: Ignore files that do not match the specified extensions.
- `--regenerate-thumbnails`: Regenerate thumbnails even if they already exist.
- `--reread-metadata`: Reread image metadata if it already exists.
- `--theme-path PATH`: Specify the path to the CSS theme file. Default is the provided default theme. - `--theme-path PATH`: Specify the path to the CSS theme file. Default is the provided default theme.
- `--use-fancy-folders`: Enable fancy folder view instead of the default Apache directory listing. - `--use-fancy-folders`: Enable fancy folder view instead of the default Apache directory listing.
- `--ignore-other-files`: Ignore files that do not match the specified extensions.
- `--exclude-folder FOLDER`: Specify folders to exclude from processing. This option can be specified multiple times.
- `-m, --web-manifest`: Generate a web manifest file.
### Examples ### Examples

View File

@@ -39,7 +39,8 @@
"cc-by-nc-sa", "cc-by-nc-sa",
"-n", "-n",
"-m", "-m",
"-r" "--regenerate-thumbnails",
"--reread-metadata",
], ],
"console": "integratedTerminal", "console": "integratedTerminal",
"name": "Testfolder", "name": "Testfolder",
@@ -62,7 +63,8 @@
"--web-manifest", "--web-manifest",
"-n", "-n",
"-m", "-m",
"-r", "--regenerate-thumbnails",
"--reread-metadata",
], ],
"console": "integratedTerminal", "console": "integratedTerminal",
"name": "woek", "name": "woek",

View File

@@ -196,11 +196,13 @@ def main() -> None:
lock_file = os.path.join(args.root_directory, ".lock") lock_file = os.path.join(args.root_directory, ".lock")
if os.path.exists(lock_file): if os.path.exists(lock_file):
print("Another instance of this program is running.") print("Another instance of this program is running.")
logger.info("nother instance of this program is running") logger.error("another instance of this program is running")
exit() exit()
try: try:
Path(lock_file).touch() Path(lock_file).touch()
if args.reread_metadata:
logger.warning("reread metadata flag is set to true, all image metadata will be reread")
if args.regenerate_thumbnails: if args.regenerate_thumbnails:
logger.warning("regenerate thumbnails flag is set to true, all thumbnails will be regenerated") logger.warning("regenerate thumbnails flag is set to true, all thumbnails will be regenerated")
if os.path.exists(os.path.join(args.root_directory, ".thumbnails")): if os.path.exists(os.path.join(args.root_directory, ".thumbnails")):

View File

@@ -272,13 +272,14 @@ a.pswp__share--download:hover {
color: #BBB; } color: #BBB; }
.pswp__caption__center { .pswp__caption__center {
text-align: left; text-align: center;
max-width: 420px; max-width: 420px;
margin: 0 auto; margin: 0 auto;
font-size: 13px; font-size: 13px;
padding: 10px; padding: 10px;
line-height: 20px; line-height: 20px;
color: #CCC; } color: #CCC;
font-weight: bold; }
.pswp__caption--empty { .pswp__caption--empty {
display: none; } display: none; }

View File

@@ -58,6 +58,7 @@ class Args:
license_type: Optional[str] license_type: Optional[str]
non_interactive_mode: bool non_interactive_mode: bool
regenerate_thumbnails: bool regenerate_thumbnails: bool
reread_metadata: bool
root_directory: str root_directory: str
site_title: str site_title: str
theme_path: str theme_path: str
@@ -75,6 +76,7 @@ class Args:
result["license_type"] = self.license_type result["license_type"] = self.license_type
result["non_interactive_mode"] = self.non_interactive_mode result["non_interactive_mode"] = self.non_interactive_mode
result["regenerate_thumbnails"] = self.regenerate_thumbnails result["regenerate_thumbnails"] = self.regenerate_thumbnails
result["reread_metadata"] = self.reread_metadata
result["root_directory"] = self.root_directory result["root_directory"] = self.root_directory
result["site_title"] = self.site_title result["site_title"] = self.site_title
result["theme_path"] = self.theme_path result["theme_path"] = self.theme_path
@@ -105,12 +107,13 @@ def parse_arguments(version: str) -> Args:
parser.add_argument("-m", "--web-manifest", help="Generate a web manifest file.", action="store_true", default=False, dest="generate_webmanifest") parser.add_argument("-m", "--web-manifest", help="Generate a web manifest file.", action="store_true", default=False, dest="generate_webmanifest")
parser.add_argument("-n", "--non-interactive-mode", help="Run in non-interactive mode, disabling progress bars.", action="store_true", default=False, dest="non_interactive_mode") parser.add_argument("-n", "--non-interactive-mode", help="Run in non-interactive mode, disabling progress bars.", action="store_true", default=False, dest="non_interactive_mode")
parser.add_argument("-p", "--root-directory", help="Root directory containing the images.", required=True, type=str, dest="root_directory", metavar="ROOT") parser.add_argument("-p", "--root-directory", help="Root directory containing the images.", required=True, type=str, dest="root_directory", metavar="ROOT")
parser.add_argument("-r", "--regenerate-thumbnails", help="Regenerate thumbnails even if they already exist.", action="store_true", default=False, dest="regenerate_thumbnails")
parser.add_argument("-t", "--site-title", help="Title of the image hosting site.", required=True, type=str, dest="site_title", metavar="TITLE") parser.add_argument("-t", "--site-title", help="Title of the image hosting site.", required=True, type=str, dest="site_title", metavar="TITLE")
parser.add_argument("-w", "--web-root-url", help="Base URL of the web root for the image hosting site.", required=True, type=str, dest="web_root_url", metavar="URL") parser.add_argument("-w", "--web-root-url", help="Base URL of the web root for the image hosting site.", required=True, type=str, dest="web_root_url", metavar="URL")
parser.add_argument("--exclude-folder", help="Folders to exclude from processing, globs supported (can be specified multiple times).", action="append", dest="exclude_folders", metavar="FOLDER") parser.add_argument("--exclude-folder", help="Folders to exclude from processing, globs supported (can be specified multiple times).", action="append", dest="exclude_folders", metavar="FOLDER")
parser.add_argument("--generate-help-preview", action=HelpPreviewAction, path="help.svg", ) parser.add_argument("--generate-help-preview", action=HelpPreviewAction, path="help.svg", )
parser.add_argument("--ignore-other-files", help="Ignore files that do not match the specified extensions.", action="store_true", default=False, dest="ignore_other_files") parser.add_argument("--ignore-other-files", help="Ignore files that do not match the specified extensions.", action="store_true", default=False, dest="ignore_other_files")
parser.add_argument("--regenerate-thumbnails", help="Regenerate thumbnails even if they already exist.", action="store_true", default=False, dest="regenerate_thumbnails")
parser.add_argument("--reread-metadata", help="Reread image metadata", action="store_true", default=False, dest="reread_metadata")
parser.add_argument("--theme-path", help="Path to the CSS theme file.", default=DEFAULT_THEME_PATH, type=str, dest="theme_path", metavar="PATH") parser.add_argument("--theme-path", help="Path to the CSS theme file.", default=DEFAULT_THEME_PATH, type=str, dest="theme_path", metavar="PATH")
parser.add_argument("--use-fancy-folders", help="Enable fancy folder view instead of the default Apache directory listing.", action="store_true", default=False, dest="use_fancy_folders") parser.add_argument("--use-fancy-folders", help="Enable fancy folder view instead of the default Apache directory listing.", action="store_true", default=False, dest="use_fancy_folders")
parser.add_argument("--version", action="version", version=f"%(prog)s {version}") parser.add_argument("--version", action="version", version=f"%(prog)s {version}")
@@ -125,6 +128,7 @@ def parse_arguments(version: str) -> Args:
license_type=parsed_args.license_type, license_type=parsed_args.license_type,
non_interactive_mode=parsed_args.non_interactive_mode, non_interactive_mode=parsed_args.non_interactive_mode,
regenerate_thumbnails=parsed_args.regenerate_thumbnails, regenerate_thumbnails=parsed_args.regenerate_thumbnails,
reread_metadata=parsed_args.reread_metadata,
root_directory=parsed_args.root_directory, root_directory=parsed_args.root_directory,
site_title=parsed_args.site_title, site_title=parsed_args.site_title,
theme_path=parsed_args.theme_path, theme_path=parsed_args.theme_path,

View File

@@ -3,6 +3,7 @@ import urllib.parse
import fnmatch import fnmatch
import json import json
from typing import Any, Dict, List, Tuple from typing import Any, Dict, List, Tuple
from datetime import datetime
import numpy as np import numpy as np
from tqdm.auto import tqdm from tqdm.auto import tqdm
@@ -103,7 +104,7 @@ def get_image_info(item: str, folder: str) -> Dict[str, Any]:
tag = ExifTags.TAGS.get(tag_id, tag_id) tag = ExifTags.TAGS.get(tag_id, tag_id)
content = exifdatas.get(tag_id) content = exifdatas.get(tag_id)
if isinstance(content, bytes): if isinstance(content, bytes):
content = content.hex(" ") content = "0x" + content.hex()
if isinstance(content, TiffImagePlugin.IFDRational): if isinstance(content, TiffImagePlugin.IFDRational):
content = content.limit_rational(1000000) content = content.limit_rational(1000000)
if isinstance(content, tuple): if isinstance(content, tuple):
@@ -113,6 +114,8 @@ def get_image_info(item: str, folder: str) -> Dict[str, Any]:
newtuple = newtuple + (i.limit_rational(1000000),) newtuple = newtuple + (i.limit_rational(1000000),)
if newtuple: if newtuple:
content = newtuple content = newtuple
if tag in ["DateTime", "DateTimeOriginal", "DateTimeDigitized"]:
content = datetime.strptime(content, "%Y:%m:%d %H:%M:%S").strftime("%Y-%m-%d %H:%M:%S")
exifdata[tag] = content exifdata[tag] = content
if "Orientation" in exifdata and exifdata["Orientation"] in [6, 8]: if "Orientation" in exifdata and exifdata["Orientation"] in [6, 8]:
logger.info("image is rotated", extra={"file": file}) logger.info("image is rotated", extra={"file": file})
@@ -122,7 +125,7 @@ def get_image_info(item: str, folder: str) -> Dict[str, Any]:
del exifdata[key] del exifdata[key]
return {"width": width, "height": height, "exifdata": exifdata} return {"width": width, "height": height, "exifdata": exifdata}
else: else:
return {"width": width, "height": height} return {"width": width, "height": height, "exifdata": None}
def process_image(item: str, folder: str, _args: Args, baseurl: str, sizelist: Dict[str, Dict[str, int]], raw: List[str]) -> Dict[str, Any]: def process_image(item: str, folder: str, _args: Args, baseurl: str, sizelist: Dict[str, Dict[str, int]], raw: List[str]) -> Dict[str, Any]:
@@ -141,7 +144,7 @@ def process_image(item: str, folder: str, _args: Args, baseurl: str, sizelist: D
Dict[str, Any]: Dictionary containing image details for HTML rendering. Dict[str, Any]: Dictionary containing image details for HTML rendering.
""" """
extsplit = os.path.splitext(item) extsplit = os.path.splitext(item)
if item not in sizelist or _args.regenerate_thumbnails: if item not in sizelist or _args.reread_metadata:
sizelist[item] = get_image_info(item, folder) sizelist[item] = get_image_info(item, folder)
image = { image = {
@@ -150,6 +153,7 @@ def process_image(item: str, folder: str, _args: Args, baseurl: str, sizelist: D
"name": item, "name": item,
"width": sizelist[item]["width"], "width": sizelist[item]["width"],
"height": sizelist[item]["height"], "height": sizelist[item]["height"],
"exifdata": sizelist[item]["exifdata"],
} }
path = os.path.join(_args.root_directory, ".thumbnails", baseurl, item + ".jpg") path = os.path.join(_args.root_directory, ".thumbnails", baseurl, item + ".jpg")
if not os.path.exists(path) or _args.regenerate_thumbnails: if not os.path.exists(path) or _args.regenerate_thumbnails:

View File

@@ -147,7 +147,11 @@
var pswpElement = document.querySelectorAll('.pswp')[0]; var pswpElement = document.querySelectorAll('.pswp')[0];
var items = [ var items = [
{%- for image in allimages %} {%- for image in allimages %}
{%- if image.exifdata.DateTime %}
{ src: "{{ image.url }}", w: {{ image.width }}, h: {{ image.height }}, msrc: "{{ image.thumbnail }}", title: "Captured: {{ image.exifdata.DateTime }}" },
{%- else %}
{ src: "{{ image.url }}", w: {{ image.width }}, h: {{ image.height }}, msrc: "{{ image.thumbnail }}" }, { src: "{{ image.url }}", w: {{ image.width }}, h: {{ image.height }}, msrc: "{{ image.thumbnail }}" },
{%- endif %}
{%- endfor %} {%- endfor %}
]; ];
var re = /pid=(\d+)/; var re = /pid=(\d+)/;