From 4cbc2eae6335222b74129c9eaedab39250c3b7b9 Mon Sep 17 00:00:00 2001 From: Julian T Date: Sat, 3 Apr 2021 23:12:04 +0200 Subject: Moved to python based builder --- build.py | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100755 build.py (limited to 'build.py') diff --git a/build.py b/build.py new file mode 100755 index 0000000..05b05bd --- /dev/null +++ b/build.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +import jinja2 +from PIL import Image +import argparse +from pathlib import Path +import yaml +import os +import re +import hashlib + +imagereg = re.compile("([a-z0-9]*)\\..*") +resreg = re.compile("([0-9]*)x([0-9]*)") + +def hashfile(fname): + md5 = hashlib.md5() + print(fname) + with open(fname, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + md5.update(chunk) + + return md5 + +def parse_res(res): + m = resreg.match(res) + return (int(m.group(1)), int(m.group(2))) + +class FileLoader(jinja2.BaseLoader): + def get_source(self, environment, template): + if not os.path.exists(template): + raise TemplateNotFound(template) + mtime = os.path.getmtime(template) + with open(template, "r") as f: + source = f.read() + return source, template, lambda: mtime == getmtime(template) + +class ImageLocation: + def __init__(self, folder): + self.folder = folder + self.stuff = {} + + self.unused = set() + self.existing = {} + + if not os.path.isdir(folder): + os.mkdir(folder) + + self.look() + + self.tmpname = os.path.join(folder, "tmp") + + def look(self): + for name in os.listdir(self.folder): + m = imagereg.match(name) + if m is not None: + fhash = m.group(1) + + self.unused.add(fhash) + self.existing[fhash] = name + + def convert(self, imgname, settings): + # First determine the hash from settings + thash = hashlib.md5(bytes( + f"{settings['res']}+{settings['ext']}" + imgname + , encoding="utf8")).hexdigest() + + # Now check if it exists + if thash in self.existing: + print(f"Skipping file {imgname}[{thash}]") + return self.existing[thash], thash + + # Okay convert it + fname = f"{thash}.{settings['ext']}" + print(f"Converting file {imgname} to {fname}") + + im = Image.open(imgname) + im = im.resize(settings["res"], Image.ANTIALIAS) + tmpname = f"{self.tmpname}.{settings['ext']}" + im.save(tmpname) + + os.rename(tmpname, os.path.join(self.folder, fname)) + + self.existing[thash] = fname + self.unused.add(thash) + + return fname, thash + + def fetch(self, imgname, settings, relto=None): + # Check if we already have it + fname, hash = self.convert(imgname, settings) + + # Mark the hash as used + self.unused.remove(hash) + + if relto is not None: + full = os.path.join(self.folder, fname) + return os.path.relpath(full, start=relto) + + return fname + + def clean(self): + print("Running cleanup") + for uhash in self.unused: + fname = os.path.join(self.folder, self.existing[uhash]) + print(f"Removing file {fname}") + os.remove(fname) + +class Renderer: + def __init__(self, config_file, loadpath, resolution, extension): + self.config = Renderer.__load_config(config_file) + self.loadpath = loadpath + self.scaled = None + + self.settings = { + "res": parse_res(resolution), + "ext": extension, + } + + def __load_config(config_file): + config = {} + with open(config_file, "r") as f: + config = yaml.safe_load(f) + + print(f"Loaded config: {config}") + return config + + def imgfetch_gen(self, loc, dest): + def fn(img): + return loc.fetch(img, self.settings, relto=dest) + + return fn + + def build_to(self, dest, template, context, loc=None, clean=False): + if loc is None: + loc = ImageLocation(os.path.join(dest, "imgs")) + + jenv = jinja2.Environment( + loader = FileLoader(), + autoescape=jinja2.select_autoescape(['html', 'xml']) + ) + jenv.globals.update( + imgfetch=self.imgfetch_gen(loc, dest) + ) + + tmpl = jenv.get_template(template) + + with open(os.path.join(dest, "index.html"), "w") as f: + f.write(tmpl.render({**self.config, **context})) + + if clean: + loc.clean() + +parser = argparse.ArgumentParser() +parser.add_argument("--dest", "-d", default="build", help="where to put resulting files") +parser.add_argument("--size", "-s", default="1920x1080", help="size to scale web images to") +parser.add_argument("--commit", "-g", help="git commit hash to announce") +parser.add_argument("--clean", help="clean unused image files", action="store_true") +parser.add_argument("--config", "-c", default="imginfo.yml", help="where to load image definitions from") +parser.add_argument("--template", "-t", default="index.html.j2", help="html template to use") +parser.add_argument("--cgit", "-w", help="cgit repo base url") +parser.add_argument("--load", "-l", default=".", help="where to load full size images from") +parser.add_argument("--ext", "-e", default="png", help="image extension to use") + +args = parser.parse_args() + +context = { + "cgit": args.cgit, + "git": args.commit + } + +rend = Renderer(args.config, args.load, args.size, args.ext) +rend.build_to(args.dest, args.template, context, clean=args.clean) + + -- cgit v1.2.3