#!/usr/bin/env python3 """Inject favicon and social meta tags into existing static HTML pages.""" from __future__ import annotations import json import os import re import sys from pathlib import Path from generate import build_archiv_entries, log, render_template, write_text_atomic def extract_meta(html: str, prop: str) -> str | None: match = re.search(rf'property="{re.escape(prop)}" content="([^"]*)"', html) return match.group(1) if match else None def extract_name_meta(html: str, name: str) -> str | None: match = re.search(rf'name="{re.escape(name)}" content="([^"]*)"', html) return match.group(1) if match else None def extract_hero_image(html: str) -> str | None: match = re.search(r' str: return render_template( "_head_common.html.j2", { "site_url": site_url, "canonical_path": canonical_path, "og_title": og_title, "og_description": og_description, "og_image": og_image, }, ).rstrip() def inject_head_block(html: str, block: str) -> str: if 'rel="icon"' in html: return html marker = ' bool: html = path.read_text(encoding="utf-8") og_title = extract_meta(html, "og:title") or extract_name_meta(html, "twitter:title") og_description = extract_meta(html, "og:description") or extract_name_meta(html, "description") if not og_title or not og_description: log(f"warning: skipping {path}, missing title/description") return False block = render_head_block( site_url=site_url, canonical_path=canonical_path, og_title=og_title, og_description=og_description, og_image=extract_hero_image(html), ) updated = inject_head_block(html, block) if updated != html: write_text_atomic(path, updated) log(f"patched {path}") return True return False def patch_archiv_index(path: Path, site_url: str, og_image: str) -> bool: html = path.read_text(encoding="utf-8") og_title = "MOBEA Archiv – Unsere bisherigen Pivots" og_description = "Jeden Tag ein neues Startup. Hier ruhen die bisherigen." if 'property="og:title"' not in html: html = html.replace( '', '\n' f' \n' f' ', 1, ) block = render_head_block( site_url=site_url, canonical_path="/archiv/", og_title=og_title, og_description=og_description, og_image=og_image, ) updated = inject_head_block(html, block) if updated != html: write_text_atomic(path, updated) log(f"patched {path}") return True return False def main() -> int: repo_root = Path(__file__).resolve().parent.parent site_dir = Path(os.environ.get("SITE_DIR", repo_root / "public")) data_dir = Path(os.environ.get("DATA_DIR", repo_root / "data")) site_url = os.environ.get("SITE_URL", "https://mobea.de") archive = json.loads((data_dir / "archive.json").read_text(encoding="utf-8")) latest_image = next( ( f"/img/{entry['date']}.jpg" for entry in sorted(archive, key=lambda e: e["date"], reverse=True) if entry.get("has_image") ), "/og-default.jpg", ) patched = 0 index_path = site_dir / "index.html" if index_path.exists(): patched += patch_startup_page(index_path, "/", site_url) archiv_dir = site_dir / "archiv" for path in sorted(archiv_dir.glob("2026-*.html")): patched += patch_startup_page(path, f"/archiv/{path.name}", site_url) archiv_index = archiv_dir / "index.html" if archiv_index.exists(): patched += patch_archiv_index(archiv_index, site_url, latest_image) log(f"done, patched {patched} file(s)") return 0 if __name__ == "__main__": sys.exit(main())