diff --git a/.gitignore b/.gitignore index 6c2e6cf..49a40c4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ result # Vim swap files *.swp +# Generated website files +build +dist diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cadbc6e --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +.PHONY: all, clean + +ifeq ($(origin IMAGE_DIR),undefined) +$(error $$IMAGE_DIR is not set) +endif + +# Files for distribution +DIST_DIR ?= dist +# Temporary build artifacts +BUILD_DIR ?= build + +images := $(shell find $(IMAGE_DIR) -iname '*.jpg') +image_names_cs := $(notdir $(images)) +image_names := $(shell echo $(image_names_cs) | tr A-Z a-z) +dist_images := $(addprefix $(DIST_DIR)/,$(image_names)) + +extra_files := $(wildcard extra/*) +dist_extra_files := $(extra_files:%=dist/%) + +define copy_image_rule = +$(DIST_DIR)/$(1): $(shell find $(IMAGE_DIR) -iname $(1)) + @mkdir -p $(DIST_DIR) + cp --preserve $$< $$@ +endef + +build_metas := $(dist_images:$(DIST_DIR)/%.jpg=$(BUILD_DIR)/%.meta.json) +dist_html := $(dist_images:$(DIST_DIR)/%.jpg=$(DIST_DIR)/%.html) + +### RULES ### + +all: $(dist_html) $(dist_extra_files) + +clean: + rm -rf $(DIST_DIR) $(BUILD_DIR) + +$(foreach f,$(image_names),$(eval $(call copy_image_rule,$(f)))) + +$(BUILD_DIR)/%.meta.json: $(DIST_DIR)/%.jpg extract-metadata.py + @mkdir -p $(BUILD_DIR) + ./extract-metadata.py --root $(DIST_DIR) $< > $@ + +$(DIST_DIR)/%.html: $(BUILD_DIR)/%.meta.json photo.hbs + @mkdir -p $(DIST_DIR) + handlebars-cli photo.hbs "$$(cat $<)" > $@ + +$(DIST_DIR)/extra/%: extra/% + @mkdir -p $(DIST_DIR)/extra + cp -r $< $@ diff --git a/extra/photo.css b/extra/photo.css new file mode 100644 index 0000000..ab63818 --- /dev/null +++ b/extra/photo.css @@ -0,0 +1,38 @@ +html { + font-size: 16px; +} + +body { + margin: 0; + font-family: 'Source Sans Pro', sans-serif; +} + +.my-epic-layout { + display: grid; + grid-template-columns: 1fr 20em; + grid-template-rows: 1fr; + width: 100vw; + height: 100vh; +} + +.picture-frame { + padding: 1em; + background-color: #555; +} + +.picture-frame img { + max-width: 100%; + max-height: 100%; + object-fit: contain; +} + +.text-frame { + padding: 1em; + background-color: #f0f0f0; +} + +h1 { + font-weight: normal; + font-size: 1.5em; + margin-top: 0; +} diff --git a/extract-metadata.py b/extract-metadata.py new file mode 100755 index 0000000..3b0f6ac --- /dev/null +++ b/extract-metadata.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 + +import clize +import exif +import json + + +from datetime import datetime +from enum import Enum +from pathlib import Path +from typing import Any + + +def exif_content_to_json(tag, content): + match content: + case Enum() as e: + return e.name + case int() as i: + return i + case float() as f: + return f + case str() as s: + if tag.startswith("datetime"): + dt = datetime.strptime(s, "%Y:%m:%d %H:%M:%S") + return dt.strftime("%Y-%m-%d %H:%M") + return s + case _: + return {"pyType": str(type(content)), "stringified": str(content)} + + +def extract_exif_data(image_path: Path) -> dict[str, Any]: + with image_path.open("rb") as image_file: + image = exif.Image(image_file) + + exif_data = {} + for tag in image.list_all(): + try: + content = image[tag] + except: + # Some keys are in list_all() but can't be accessed for my camera :( + # Skip them. + continue + exif_data[tag] = exif_content_to_json(tag, content) + + return exif_data + + +def extract_stat_data(image_path: Path): + FIELDS = ["st_size"] + + s = image_path.stat() + stat_data = {} + for field in FIELDS: + stat_data[field] = getattr(s, field) + + return stat_data + + + +def extract_metadata(image_path: Path, *, root: Path): + exif_data = extract_exif_data(image_path) + stat_data = extract_stat_data(image_path) + + metadata = { + "exif": exif_data, + "stat": stat_data, + "path": { + "name": image_path.name, + "relativeToRoot": str(image_path.relative_to(root)), + }, + } + + print(json.dumps(metadata, indent=2)) + + +if __name__ == "__main__": + clize.run(extract_metadata) diff --git a/flake.nix b/flake.nix index 9da35f4..0b2c4a1 100644 --- a/flake.nix +++ b/flake.nix @@ -23,6 +23,7 @@ packages.handlebars-rust pkgs.imagemagick pkgs.gnumake + (pkgs.python3.withPackages (ps: [ps.clize ps.exif])) ]; }; } diff --git a/photo.hbs b/photo.hbs new file mode 100644 index 0000000..99b3acf --- /dev/null +++ b/photo.hbs @@ -0,0 +1,24 @@ + + +
+ +{{ exif.datetime }}
+
+ Exposure Time {{ exif.exposure_time }}s
+ Aperture f{{ exif.aperture_value }}
+ ISO {{ exif.photographic_sensitivity }}
+