This commit is contained in:
2026-02-06 08:00:53 +01:00
committed by Florian Greistorfer
parent 7d254f5a3e
commit 7d086a7a20
6 changed files with 44 additions and 51 deletions

View File

@@ -129,8 +129,6 @@
"[python]": { "[python]": {
"editor.defaultFormatter": "charliermarsh.ruff" "editor.defaultFormatter": "charliermarsh.ruff"
}, },
"black-formatter.args": ["-l 260"],
"black-formatter.interpreter": ["/usr/bin/python3"],
"editor.formatOnSave": false, "editor.formatOnSave": false,
"emmet.includeLanguages": { "emmet.includeLanguages": {
"jinja-css": "css", "jinja-css": "css",
@@ -157,7 +155,9 @@
"json.schemaDownload.enable": true, "json.schemaDownload.enable": true,
"json.schemas": [ "json.schemas": [
{ {
"fileMatch": ["manifest.json.j2"], "fileMatch": [
"manifest.json.j2"
],
"url": "https://json.schemastore.org/web-manifest-combined.json" "url": "https://json.schemastore.org/web-manifest-combined.json"
} }
], ],
@@ -169,9 +169,10 @@
"packageManager": "ms-python.python:pip" "packageManager": "ms-python.python:pip"
} }
], ],
"python.analysis.inlayHints.callArgumentNames": "off", "python.analysis.inlayHints.callArgumentNames": "all",
"python.analysis.inlayHints.functionReturnTypes": false, "python.analysis.inlayHints.functionReturnTypes": true,
"python.analysis.inlayHints.variableTypes": false, "python.analysis.inlayHints.variableTypes": true,
"python.analysis.typeCheckingMode": "standard",
"yaml.schemas": { "yaml.schemas": {
"https://raw.githubusercontent.com/pamburus/hl/master/schema/json/config.schema.json": "file:///home/user/git/github.com/greflm13/StaticGalleryBuilder/hl_config.yaml" "https://raw.githubusercontent.com/pamburus/hl/master/schema/json/config.schema.json": "file:///home/user/git/github.com/greflm13/StaticGalleryBuilder/hl_config.yaml"
}, },
@@ -228,7 +229,9 @@
"kind": "build", "kind": "build",
"isDefault": true "isDefault": true
}, },
"dependsOn": ["Clean"] "dependsOn": [
"Clean"
]
}, },
{ {
"command": "rm -rf build dist", "command": "rm -rf build dist",
@@ -278,4 +281,4 @@
} }
] ]
} }
} }

View File

@@ -125,7 +125,7 @@ def parse_arguments(version: str) -> Args:
""" """
# fmt: off # fmt: off
if RICH: if RICH:
parser = configargparse.ArgumentParser(default_config_files=[CONFIGPATH], add_config_file_help=False, description="generate HTML files for a static image hosting website", formatter_class=RichHelpFormatter) parser = configargparse.ArgumentParser(default_config_files=[CONFIGPATH], add_config_file_help=False, description="generate HTML files for a static image hosting website", formatter_class=RichHelpFormatter) # pyright: ignore[reportPossiblyUnboundVariable]
else: else:
parser = configargparse.ArgumentParser(default_config_files=[CONFIGPATH], add_config_file_help=False, description="generate HTML files for a static image hosting website") parser = configargparse.ArgumentParser(default_config_files=[CONFIGPATH], add_config_file_help=False, description="generate HTML files for a static image hosting website")
parser.add_argument("-a", "--author-name", help="name of the author of the images", default=DEFAULT_AUTHOR, type=str, dest="author_name", metavar="AUTHOR") parser.add_argument("-a", "--author-name", help="name of the author of the images", default=DEFAULT_AUTHOR, type=str, dest="author_name", metavar="AUTHOR")
@@ -140,7 +140,7 @@ def parse_arguments(version: str) -> Args:
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("--folderthumbnails", help="generate subfolder thumbnails (first image in folder will be shown)", action="store_true", default=False, dest="folder_thumbs") parser.add_argument("--folderthumbnails", help="generate subfolder thumbnails (first image in folder will be shown)", action="store_true", default=False, dest="folder_thumbs")
if RICH: if RICH:
parser.add_argument("--generate-help-preview", action=HelpPreviewAction, path="help.svg") parser.add_argument("--generate-help-preview", action=HelpPreviewAction, path="help.svg") # pyright: ignore[reportPossiblyUnboundVariable]
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("--ignore-extension", help="file extensions to ignore (can be specified multiple times)", action="append", default=[], dest="ignore_extensions", metavar="EXTENSION") parser.add_argument("--ignore-extension", help="file extensions to ignore (can be specified multiple times)", action="append", default=[], dest="ignore_extensions", metavar="EXTENSION")
parser.add_argument("--regenerate-thumbnails", help="regenerate thumbnails even if they already exist", action="store_true", default=False, dest="regenerate_thumbnails") parser.add_argument("--regenerate-thumbnails", help="regenerate thumbnails even if they already exist", action="store_true", default=False, dest="regenerate_thumbnails")

View File

@@ -128,4 +128,4 @@ def licensepicswitch(cclicense: str) -> list[str]:
], ],
} }
return switch.get(cclicense, "") return switch.get(cclicense, [])

View File

@@ -138,9 +138,14 @@ def update_metadata(metadata: Metadata, folder: str) -> None:
""" """
metadata_path = os.path.join(folder, ".metadata.json") metadata_path = os.path.join(folder, ".metadata.json")
if metadata: if metadata:
with open(metadata_path, "w", encoding="utf-8") as metadatafile: if os.path.exists(metadata_path):
logger.info("writing metadata file", extra={"file": metadata_path}) logger.info("updating metadata file", extra={"file": metadata_path})
metadatafile.write(json.dumps(metadata.to_dict(), indent=4)) with open(metadata_path, "w", encoding="utf-8") as metadatafile:
metadatafile.write(json.dumps(metadata.to_dict(), indent=4))
else:
logger.info("creating metadata file", extra={"file": metadata_path})
with open(metadata_path, "x", encoding="utf-8") as metadatafile:
metadatafile.write(json.dumps(metadata.to_dict(), indent=4))
else: else:
if os.path.exists(metadata_path): if os.path.exists(metadata_path):
logger.info("deleting empty metadata file", extra={"file": metadata_path}) logger.info("deleting empty metadata file", extra={"file": metadata_path})
@@ -257,11 +262,11 @@ def get_image_info(item: str, folder: str) -> ImageMetadata:
return ImageMetadata(w=width, h=height, tags=tags, exifdata=exifdata, xmp=xmp, src="", msrc="", name="", title="") return ImageMetadata(w=width, h=height, tags=tags, exifdata=exifdata, xmp=xmp, src="", msrc="", name="", title="")
def nested_dict(): def nested_dict() -> defaultdict[Any, Any]:
return defaultdict(nested_dict) return defaultdict(nested_dict)
def insert_path(d, path): def insert_path(d, path) -> None:
for part in path[:-1]: for part in path[:-1]:
d = d[part] d = d[part]
last = path[-1] last = path[-1]

View File

@@ -18,7 +18,7 @@ import gzip
import shutil import shutil
import logging import logging
from datetime import datetime from datetime import datetime
from pythonjsonlogger import json as jsonlogger from pythonjsonlogger import jsonlogger
# Constants for file paths and exclusions # Constants for file paths and exclusions
SCRIPTDIR = os.path.dirname(os.path.realpath(__file__)).removesuffix(__package__ if __package__ else "") SCRIPTDIR = os.path.dirname(os.path.realpath(__file__)).removesuffix(__package__ if __package__ else "")

View File

@@ -1,5 +1,6 @@
import os import os
import shutil import shutil
from dataclasses import dataclass
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from PIL import Image from PIL import Image
from jinja2 import Environment, FileSystemLoader from jinja2 import Environment, FileSystemLoader
@@ -26,6 +27,7 @@ ICON_SIZES = ["36x36", "48x48", "72x72", "96x96", "144x144", "192x192", "512x512
env = Environment(loader=FileSystemLoader(os.path.join(SCRIPTDIR, "templates"))) env = Environment(loader=FileSystemLoader(os.path.join(SCRIPTDIR, "templates")))
@dataclass
class Icon: class Icon:
src: str src: str
type: str type: str
@@ -68,11 +70,12 @@ def save_png_icon(content: str, iconspath: str) -> None:
iconspath : str iconspath : str
Path to the directory where the PNG icon will be saved. Path to the directory where the PNG icon will be saved.
""" """
tmpimg = BytesIO() if SVGSUPPORT:
cairosvg.svg2png(bytestring=content, write_to=tmpimg) tmpimg = BytesIO() # pyright: ignore[reportPossiblyUnboundVariable]
with Image.open(tmpimg) as iconfile: cairosvg.svg2png(bytestring=content, write_to=tmpimg) # pyright: ignore[reportPossiblyUnboundVariable]
logger.info("saving png icon", extra={"iconspath": iconspath}) with Image.open(tmpimg) as iconfile:
iconfile.save(os.path.join(iconspath, "icon.png")) logger.info("saving png icon", extra={"iconspath": iconspath})
iconfile.save(os.path.join(iconspath, "icon.png"))
def generate_favicon(iconspath: str, root_directory: str) -> None: def generate_favicon(iconspath: str, root_directory: str) -> None:
@@ -176,40 +179,20 @@ def create_icons_from_svg(files: list[str], iconspath: str, _args: Args) -> list
svg = [file for file in files if file.endswith(".svg")][0] svg = [file for file in files if file.endswith(".svg")][0]
logger.info("creating icons for web application", extra={"iconspath": iconspath, "svg": svg}) logger.info("creating icons for web application", extra={"iconspath": iconspath, "svg": svg})
icon_list = [ icon_list = [
{"src": f"{_args.web_root_url}.static/icons/{svg}", "type": "image/svg+xml", "sizes": "512x512", "purpose": "maskable"}, Icon(src=f"{_args.web_root_url}.static/icons/{svg}", type="image/svg+xml", sizes="512x512", purpose="maskable"),
{"src": f"{_args.web_root_url}.static/icons/{svg}", "type": "image/svg+xml", "sizes": "512x512", "purpose": "any"}, Icon(src=f"{_args.web_root_url}.static/icons/{svg}", type="image/svg+xml", sizes="512x512", purpose="any"),
] ]
for size in ICON_SIZES: for size in ICON_SIZES:
tmpimg = BytesIO() tmpimg = BytesIO() # pyright: ignore[reportPossiblyUnboundVariable]
sizes = size.split("x") sizes = size.split("x")
iconpath = os.path.join(iconspath, os.path.splitext(svg)[0] + "-" + size + ".png") iconpath = os.path.join(iconspath, os.path.splitext(svg)[0] + "-" + size + ".png")
logger.info("converting svg to png", extra={"svg": svg, "size": size}) logger.info("converting svg to png", extra={"svg": svg, "size": size})
cairosvg.svg2png( cairosvg.svg2png(url=os.path.join(iconspath, svg), write_to=tmpimg, output_width=int(sizes[0]), output_height=int(sizes[1]), scale=1) # pyright: ignore[reportPossiblyUnboundVariable]
url=os.path.join(iconspath, svg),
write_to=tmpimg,
output_width=int(sizes[0]),
output_height=int(sizes[1]),
scale=1,
)
with Image.open(tmpimg) as iconfile: with Image.open(tmpimg) as iconfile:
logger.info("saving png file", extra={"iconpath": iconpath}) logger.info("saving png file", extra={"iconpath": iconpath})
iconfile.save(iconpath, format="PNG") iconfile.save(iconpath, format="PNG")
icon_list.append( icon_list.append(Icon(src=f"{_args.web_root_url}.static/icons/{os.path.splitext(svg)[0]}-{size}.png", sizes=size, type="image/png", purpose="maskable"))
{ icon_list.append(Icon(src=f"{_args.web_root_url}.static/icons/{os.path.splitext(svg)[0]}-{size}.png", sizes=size, type="image/png", purpose="any"))
"src": f"{_args.web_root_url}.static/icons/{os.path.splitext(svg)[0]}-{size}.png",
"sizes": size,
"type": "image/png",
"purpose": "maskable",
}
)
icon_list.append(
{
"src": f"{_args.web_root_url}.static/icons/{os.path.splitext(svg)[0]}-{size}.png",
"sizes": size,
"type": "image/png",
"purpose": "any",
}
)
return icon_list return icon_list
@@ -236,8 +219,8 @@ def create_icons_from_png(iconspath: str, web_root_url: str) -> list[Icon]:
with Image.open(os.path.join(iconspath, icon)) as iconfile: with Image.open(os.path.join(iconspath, icon)) as iconfile:
iconsize = f"{iconfile.size[0]}x{iconfile.size[1]}" iconsize = f"{iconfile.size[0]}x{iconfile.size[1]}"
logger.info("using icon", extra={"iconspath": iconspath, "icon": icon, "size": iconsize}) logger.info("using icon", extra={"iconspath": iconspath, "icon": icon, "size": iconsize})
icon_list.append({"src": f"{web_root_url}.static/icons/{icon}", "sizes": iconsize, "type": "image/png", "purpose": "maskable"}) icon_list.append(Icon(src=f"{web_root_url}.static/icons/{icon}", sizes=iconsize, type="image/png", purpose="maskable"))
icon_list.append({"src": f"{web_root_url}.static/icons/{icon}", "sizes": iconsize, "type": "image/png", "purpose": "any"}) icon_list.append(Icon(src=f"{web_root_url}.static/icons/{icon}", sizes=iconsize, type="image/png", purpose="any"))
return icon_list return icon_list
@@ -254,7 +237,9 @@ def webmanifest(_args: Args) -> None:
iconspath = os.path.join(_args.root_directory, ".static", "icons") iconspath = os.path.join(_args.root_directory, ".static", "icons")
files = os.listdir(iconspath) files = os.listdir(iconspath)
icon_list = create_icons_from_svg(files, iconspath, _args) if SVGSUPPORT and any(file.endswith(".svg") for file in files) else create_icons_from_png(iconspath, _args.web_root_url) icon_list = (
create_icons_from_svg(files, iconspath, _args) if SVGSUPPORT and any(file.endswith(".svg") for file in files) else create_icons_from_png(iconspath, _args.web_root_url)
)
if not icon_list: if not icon_list:
print("No icons found in the static/icons folder!") print("No icons found in the static/icons folder!")