From 3b93fb87194bcbf22221a86fed0e73de15a62ee9 Mon Sep 17 00:00:00 2001 From: Flo Greistorfer Date: Mon, 16 Dec 2024 12:40:49 +0100 Subject: [PATCH] updated imports and added info file documentation --- .version | 2 +- README.md | 1 + StaticGalleryBuilder.code-workspace | 2 - builder.py | 21 +++++----- modules/argumentparser.py | 10 ++--- modules/css_color.py | 13 +++--- modules/generate_html.py | 62 ++++++++++++++--------------- modules/svg_handling.py | 35 ++++++++-------- 8 files changed, 71 insertions(+), 75 deletions(-) diff --git a/.version b/.version index acdc3f1..6550da6 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.4.2 \ No newline at end of file +2.4.3 \ No newline at end of file diff --git a/README.md b/README.md index 605a43e..a2e4897 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ To generate a web manifest file: - The root and web root paths must point to the same folder, one on the filesystem and one on the web server. Use absolute paths. - The script generates the preview thumbnails in a `.thumbnails` subdirectory within the root folder. - The `.lock` file prevents multiple instances of the script from running simultaneously. Make sure to remove it if the script terminates unexpectedly. +- Add a `info` file into any directory containing pictures and it will be read and displayed as a tooltip on the website. ## License diff --git a/StaticGalleryBuilder.code-workspace b/StaticGalleryBuilder.code-workspace index 638ac02..d580e83 100644 --- a/StaticGalleryBuilder.code-workspace +++ b/StaticGalleryBuilder.code-workspace @@ -4,14 +4,12 @@ "charliermarsh.ruff", "esbenp.prettier-vscode", "ms-edgedevtools.vscode-edge-devtools", - "ms-python.black-formatter", "ms-python.debugpy", "ms-python.python", "ms-python.vscode-pylance", "samuelcolvin.jinjahtml", "vscode.css-language-features", "vscode.html-language-features", - "waderyan.gitblame", ], }, "folders": [ diff --git a/builder.py b/builder.py index 19da464..cb6d13f 100755 --- a/builder.py +++ b/builder.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 import os import re +import sys import shutil import fnmatch import urllib.parse from multiprocessing import Pool, freeze_support from pathlib import Path -from typing import Dict, List, Tuple from tqdm.auto import tqdm from PIL import Image, ImageOps @@ -36,10 +36,10 @@ IMG_EXTENSIONS = [".jpg", ".jpeg", ".png"] NOT_LIST = ["*/Galleries/*", "Archives"] # fmt: on -pbardict: Dict[str, tqdm] = {} +pbardict: dict[str, tqdm] = {} -def init_globals(_args: Args, raw: List[str]) -> Tuple[Args, List[str]]: +def init_globals(_args: Args, raw: list[str]) -> tuple[Args, list[str]]: """ Initialize global variables and set default values for arguments. @@ -47,12 +47,12 @@ def init_globals(_args: Args, raw: List[str]) -> Tuple[Args, List[str]]: ----------- _args : Args Parsed command-line arguments. - raw : List[str] - List of raw file extensions. + raw : list[str] + list of raw file extensions. Returns: -------- - Tuple[Args, List[str]] + tuple[Args, list[str]] Updated arguments and raw file extensions. """ if not _args.file_extensions: @@ -118,13 +118,13 @@ def copy_static_files(_args: Args) -> None: f.write(themehead + '\n.foldericon {\n content: url("data:image/svg+xml,' + svg + '");\n}\n' + themetail) -def generate_thumbnail(arguments: Tuple[str, str, str]) -> None: +def generate_thumbnail(arguments: tuple[str, str, str]) -> None: """ Generate a thumbnail for a given image. Parameters: ----------- - arguments : Tuple[str, str, str, bool] + arguments : tuple[str, str, str, bool] A tuple containing the folder, item, root directory, and regenerate thumbnails flag. """ folder, item, root_directory = arguments @@ -187,7 +187,7 @@ def main() -> None: """ Main function to process images and generate a static image hosting website. """ - thumbnails: List[Tuple[str, str, str, bool]] = [] + thumbnails: list[tuple[str, str, str, bool]] = [] args = parse_arguments(VERSION) args, raw = init_globals(args, RAW_EXTENSIONS) @@ -195,7 +195,7 @@ def main() -> None: lock_file = os.path.join(args.root_directory, ".lock") if os.path.exists(lock_file): print("Another instance of this program is running.") - exit() + sys.exit() try: Path(lock_file).touch() @@ -248,7 +248,6 @@ def main() -> None: finally: os.remove(lock_file) logger.info("finished builder", extra={"version": VERSION}) - return if __name__ == "__main__": diff --git a/modules/argumentparser.py b/modules/argumentparser.py index ef15404..a4dbfee 100644 --- a/modules/argumentparser.py +++ b/modules/argumentparser.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import List, Optional +from typing import Optional import os import argparse @@ -29,9 +29,9 @@ class Args: ----------- author_name : str The name of the author of the images. - exclude_folders : List[str] + exclude_folders : list[str] A list of folders to exclude from processing. - file_extensions : List[str] + file_extensions : list[str] A list of file extensions to include. generate_webmanifest : bool Whether to generate a web manifest file. @@ -56,8 +56,8 @@ class Args: """ author_name: str - exclude_folders: List[str] - file_extensions: List[str] + exclude_folders: list[str] + file_extensions: list[str] generate_webmanifest: bool ignore_other_files: bool license_type: Optional[str] diff --git a/modules/css_color.py b/modules/css_color.py index fb23bee..6cee8fc 100644 --- a/modules/css_color.py +++ b/modules/css_color.py @@ -1,11 +1,10 @@ import re import colorsys -from typing import Dict -from modules.logger import logger +from modules import logger -def extract_colorscheme(theme_path: str) -> Dict[str, str]: +def extract_colorscheme(theme_path: str) -> dict[str, str]: """ Extract color scheme from a CSS theme file. @@ -16,8 +15,8 @@ def extract_colorscheme(theme_path: str) -> Dict[str, str]: Returns: -------- - Dict[str, str] - Dictionary containing color scheme variables and their hexadecimal values. + dict[str, str] + dictionary containing color scheme variables and their hexadecimal values. """ logger.info("extracting color scheme from theme file", extra={"theme_path": theme_path}) pattern = r"--(color[1-4]|bcolor1):\s*(#[0-9a-fA-F]+|rgba?\([^)]*\)|hsla?\([^)]*\)|[a-zA-Z]+);" @@ -127,7 +126,7 @@ def css_color_to_hex(css_color: str) -> str: a = float(groups["a"]) if groups["a"] else 1.0 if a < 1.0: logger.debug("converting rgba color to hex", extra={"color": css_color, "r": r, "g": g, "b": b, "a": a}) - return rgb_to_hex((r, g, b)) + "{:02x}".format(round(a * 255)) + return rgb_to_hex((r, g, b)) + f"{round(a * 255):02x}" else: logger.debug("converting rgb color to hex", extra={"color": css_color, "r": r, "g": g, "b": b}) return rgb_to_hex((r, g, b)) @@ -140,7 +139,7 @@ def css_color_to_hex(css_color: str) -> str: rgb_color = hsl_to_rgb((h, s, l)) if a < 1.0: logger.debug("converting hsla color to hex", extra={"color": css_color, "hsl": (h, s, l), "a": a}) - return rgb_to_hex(rgb_color) + "{:02x}".format(round(a * 255)) + return rgb_to_hex(rgb_color) + f"{round(a * 255):02x}" else: logger.debug("converting hsl color to hex", extra={"color": css_color, "hsl": (h, s, l)}) return rgb_to_hex(rgb_color) diff --git a/modules/generate_html.py b/modules/generate_html.py index 537254a..6026ecb 100644 --- a/modules/generate_html.py +++ b/modules/generate_html.py @@ -3,15 +3,15 @@ import re import urllib.parse import fnmatch import json -from typing import Any, Dict, List, Tuple +from typing import Any from datetime import datetime from tqdm.auto import tqdm from PIL import Image, ExifTags, TiffImagePlugin from jinja2 import Environment, FileSystemLoader -from modules.logger import logger -import modules.cclicense as cclicense +from modules import logger +from modules import cclicense from modules.argumentparser import Args # Constants for file paths and exclusions @@ -29,12 +29,12 @@ Image.MAX_IMAGE_PIXELS = 933120000 # Initialize Jinja2 environment for template rendering env = Environment(loader=FileSystemLoader(os.path.join(SCRIPTDIR, "templates"))) -thumbnails: List[Tuple[str, str]] = [] -info: Dict[str, str] = {} -pbardict: Dict[str, tqdm] = {} +thumbnails: list[tuple[str, str]] = [] +info: dict[str, str] = {} +pbardict: dict[str, tqdm] = {} -def initialize_sizelist(folder: str) -> Dict[str, Dict[str, int]]: +def initialize_sizelist(folder: str) -> dict[str, dict[str, int]]: """ Initializes the size list JSON file if it doesn't exist. @@ -42,7 +42,7 @@ def initialize_sizelist(folder: str) -> Dict[str, Dict[str, int]]: folder (str): The folder in which the size list file is located. Returns: - Dict[str, Dict[str, int]]: The size list dictionary. + dict[str, dict[str, int]]: The size list dictionary. """ sizelist = {} sizelist_path = os.path.join(folder, ".sizelist.json") @@ -60,12 +60,12 @@ def initialize_sizelist(folder: str) -> Dict[str, Dict[str, int]]: return sizelist -def update_sizelist(sizelist: Dict[str, Dict[str, Any]], folder: str) -> None: +def update_sizelist(sizelist: dict[str, dict[str, Any]], folder: str) -> None: """ Updates the size list JSON file. Args: - sizelist (Dict[str, Dict[str, int]]): The size list dictionary to be written to the file. + sizelist (dict[str, dict[str, int]]): The size list dictionary to be written to the file. folder (str): The folder in which the size list file is located. """ sizelist_path = os.path.join(folder, ".sizelist.json") @@ -79,7 +79,7 @@ def update_sizelist(sizelist: Dict[str, Dict[str, Any]], folder: str) -> None: os.remove(sizelist_path) -def get_image_info(item: str, folder: str) -> Dict[str, Any]: +def get_image_info(item: str, folder: str) -> dict[str, Any]: """ Extracts image information and EXIF data. @@ -88,7 +88,7 @@ def get_image_info(item: str, folder: str) -> Dict[str, Any]: folder (str): The folder containing the image. Returns: - Dict[str, Any]: A dictionary containing image width, height, and EXIF data. + dict[str, Any]: A dictionary containing image width, height, and EXIF data. """ file = os.path.join(folder, item) with Image.open(file) as img: @@ -111,7 +111,7 @@ def get_image_info(item: str, folder: str) -> Dict[str, Any]: newtuple = () for i in content: if isinstance(i, TiffImagePlugin.IFDRational): - newtuple = newtuple + (i.limit_rational(1000000),) + newtuple = (*newtuple, i.limit_rational(1000000)) if newtuple: content = newtuple if tag in ["DateTime", "DateTimeOriginal", "DateTimeDigitized"]: @@ -135,7 +135,7 @@ def get_image_info(item: str, folder: str) -> Dict[str, Any]: 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]: """ Processes an image and prepares its data for the HTML template. @@ -144,11 +144,11 @@ def process_image(item: str, folder: str, _args: Args, baseurl: str, sizelist: D folder (str): The folder containing the image. _args (Args): Parsed command line arguments. baseurl (str): Base URL for the web root. - sizelist (Dict[str, Dict[str, int]]): Dictionary containing size information for images. - raw (List[str]): List of raw image file extensions. + sizelist (dict[str, dict[str, int]]): dictionary containing size information for images. + raw (list[str]): list of raw image file extensions. Returns: - 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) if item not in sizelist or _args.reread_metadata: @@ -181,7 +181,7 @@ def process_image(item: str, folder: str, _args: Args, baseurl: str, sizelist: D return image -def generate_html(folder: str, title: str, _args: Args, raw: List[str], version: str) -> None: +def generate_html(folder: str, title: str, _args: Args, raw: list[str], version: str) -> None: """ Generates HTML content for a folder of images. @@ -189,7 +189,7 @@ def generate_html(folder: str, title: str, _args: Args, raw: List[str], version: folder (str): The folder to generate HTML for. title (str): The title of the HTML page. _args (Args): Parsed command line arguments. - raw (List[str]): Raw image file names. + raw (list[str]): Raw image file names. """ logger.info("processing folder", extra={"folder": folder}) if _args.regenerate_thumbnails: @@ -256,7 +256,7 @@ def create_thumbnail_folder(foldername: str, root_directory: str) -> None: os.mkdir(thumbnails_path) -def process_subfolder(item: str, folder: str, baseurl: str, subfolders: List[Dict[str, str]], _args: Args, raw: List[str], version: str) -> None: +def process_subfolder(item: str, folder: str, baseurl: str, subfolders: list[dict[str, str]], _args: Args, raw: list[str], version: str) -> None: """ Processes a subfolder. @@ -264,9 +264,9 @@ def process_subfolder(item: str, folder: str, baseurl: str, subfolders: List[Dic item (str): The name of the subfolder. folder (str): The parent folder containing the subfolder. baseurl (str): Base URL for the web root. - subfolders (List[Dict[str, str]]): List to store subfolder details. + subfolders (list[dict[str, str]]): list to store subfolder details. _args (Args): Parsed command line arguments. - raw (List[str]): Raw image file extensions. + raw (list[str]): Raw image file extensions. """ subfolder_url = f"{_args.web_root_url}{baseurl}{urllib.parse.quote(item)}/index.html" if _args.web_root_url.startswith("file://") else f"{_args.web_root_url}{baseurl}{urllib.parse.quote(item)}" subfolders.append({"url": subfolder_url, "name": item}) @@ -288,12 +288,12 @@ def process_info_file(folder: str, item: str) -> None: info[urllib.parse.quote(folder)] = f.read() -def should_generate_html(images: List[Dict[str, Any]], contains_files, _args: Args) -> bool: +def should_generate_html(images: list[dict[str, Any]], contains_files, _args: Args) -> bool: """ Determines if HTML should be generated. Args: - images (List[Dict[str, Any]]): List of images. + images (list[dict[str, Any]]): list of images. _args (Args): Parsed command line arguments. Returns: @@ -302,7 +302,7 @@ def should_generate_html(images: List[Dict[str, Any]], contains_files, _args: Ar return images or (_args.use_fancy_folders and not contains_files) or (_args.use_fancy_folders and _args.ignore_other_files) -def create_html_file(folder: str, title: str, foldername: str, images: List[Dict[str, Any]], subfolders: List[Dict[str, str]], _args: Args, version: str) -> None: +def create_html_file(folder: str, title: str, foldername: str, images: list[dict[str, Any]], subfolders: list[dict[str, str]], _args: Args, version: str) -> None: """ Creates the HTML file using the template. @@ -310,8 +310,8 @@ def create_html_file(folder: str, title: str, foldername: str, images: List[Dict folder (str): The folder to create the HTML file in. title (str): The title of the HTML page. foldername (str): The name of the folder. - images (List[Dict[str, Any]]): A list of images to include in the HTML. - subfolders (List[Dict[str, str]]): A list of subfolders to include in the HTML. + images (list[dict[str, Any]]): A list of images to include in the HTML. + subfolders (list[dict[str, str]]): A list of subfolders to include in the HTML. _args (Args): Parsed command line arguments. """ html_file = os.path.join(folder, "index.html") @@ -360,19 +360,19 @@ def create_html_file(folder: str, title: str, foldername: str, images: List[Dict f.write(content) -def list_folder(total: int, folder: str, title: str, _args: Args, raw: List[str], version: str) -> List[Tuple[str, str]]: +def list_folder(total: int, folder: str, title: str, _args: Args, raw: list[str], version: str) -> list[tuple[str, str]]: """ - Lists and processes a folder, generating HTML files. + lists and processes a folder, generating HTML files. Args: total (int): Total number of folders to process. folder (str): The folder to process. title (str): The title of the HTML page. _args (Args): Parsed command line arguments. - raw (List[str]): Raw image file names. + raw (list[str]): Raw image file names. Returns: - List[Tuple[str, str]]: List of thumbnails generated. + list[tuple[str, str]]: list of thumbnails generated. """ if not _args.non_interactive_mode: pbardict["htmlbar"] = tqdm(total=total, desc="Generating HTML files", unit="folders", ascii=True, dynamic_ncols=True) diff --git a/modules/svg_handling.py b/modules/svg_handling.py index 3aa289a..4dd3355 100644 --- a/modules/svg_handling.py +++ b/modules/svg_handling.py @@ -1,6 +1,5 @@ import os import shutil -from typing import List, Dict from subprocess import Popen, PIPE from PIL import Image from jinja2 import Environment, FileSystemLoader @@ -14,7 +13,7 @@ try: except ImportError: SVGSUPPORT = False -from modules.logger import logger +from modules import logger from modules.argumentparser import Args from modules.css_color import extract_theme_color, extract_colorscheme @@ -38,14 +37,14 @@ class Icon: purpose: str -def render_svg_icon(colorscheme: Dict[str, str], iconspath: str) -> str: +def render_svg_icon(colorscheme: dict[str, str], iconspath: str) -> str: """ Render an SVG icon using the provided color scheme. Parameters: ----------- - colorscheme : Dict[str, str] - Dictionary containing color scheme variables and their values. + colorscheme : dict[str, str] + dictionary containing color scheme variables and their values. iconspath : str Path to the directory where the icon will be saved. @@ -134,7 +133,7 @@ def icons(_args: Args) -> None: generate_favicon(iconspath, _args.root_directory) -def render_manifest_json(_args: Args, icon_list: List[Icon], colors: Dict[str, str]) -> None: +def render_manifest_json(_args: Args, icon_list: list[Icon], colors: dict[str, str]) -> None: """ Render the manifest.json file for the web application. @@ -142,10 +141,10 @@ def render_manifest_json(_args: Args, icon_list: List[Icon], colors: Dict[str, s ----------- _args : Args Parsed command-line arguments. - icon_list : List[Icon] - List of icons to be included in the manifest. - colors : Dict[str, str] - Dictionary containing color scheme and theme color. + icon_list : list[Icon] + list of icons to be included in the manifest. + colors : dict[str, str] + dictionary containing color scheme and theme color. """ manifest = env.get_template("manifest.json.j2") content = manifest.render( @@ -160,14 +159,14 @@ def render_manifest_json(_args: Args, icon_list: List[Icon], colors: Dict[str, s f.write(content) -def create_icons_from_svg(files: List[str], iconspath: str, _args: Args) -> List[Icon]: +def create_icons_from_svg(files: list[str], iconspath: str, _args: Args) -> list[Icon]: """ Create icons from an SVG file. Parameters: ----------- - files : List[str] - List of files in the icons directory. + files : list[str] + list of files in the icons directory. iconspath : str Path to the directory where the icons will be saved. _args : Args @@ -175,8 +174,8 @@ def create_icons_from_svg(files: List[str], iconspath: str, _args: Args) -> List Returns: -------- - List[Icon] - List of icons created from the SVG file. + list[Icon] + list of icons created from the SVG file. """ svg = [file for file in files if file.endswith(".svg")][0] logger.info("creating icons for web application", extra={"iconspath": iconspath, "svg": svg}) @@ -218,7 +217,7 @@ def create_icons_from_svg(files: List[str], iconspath: str, _args: Args) -> List return icon_list -def create_icons_from_png(iconspath: str, web_root_url: str) -> List[Icon]: +def create_icons_from_png(iconspath: str, web_root_url: str) -> list[Icon]: """ Create icons from PNG files. @@ -231,8 +230,8 @@ def create_icons_from_png(iconspath: str, web_root_url: str) -> List[Icon]: Returns: -------- - List[Icon] - List of icons created from PNG files. + list[Icon] + list of icons created from PNG files. """ icon_list = [] for icon in os.listdir(iconspath):