Files
mobea/generator/patch_head_meta.py
Frank Schwenk 8abd7ddb59 feat: Archiv-Backfill, responsive Bilder und Site-Meta
Ergänzt Backfill- und Hilfsskripte, erzeugt nach der Pixazo-Generierung
komprimierte srcset-Varianten, validiert leere Bilder und fügt Favicon,
Web Manifest sowie Open-Graph-/Twitter-Tags in die Templates ein.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-12 18:14:40 +02:00

150 lines
4.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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'<img src="/img/(\d{4}-\d{2}-\d{2})(?:-\d+w)?\.jpg"', html)
return f"/img/{match.group(1)}.jpg" if match else None
def render_head_block(
*,
site_url: str,
canonical_path: str,
og_title: str,
og_description: str,
og_image: str | None,
) -> 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 = '<meta property="og:description"'
idx = html.find(marker)
if idx == -1:
marker = '<meta name="description"'
idx = html.find(marker)
if idx == -1:
raise ValueError("could not find description meta tag")
line_end = html.find("\n", idx)
if line_end == -1:
raise ValueError("unexpected HTML structure")
return html[: line_end + 1] + block + "\n" + html[line_end + 1 :]
def patch_startup_page(path: Path, canonical_path: str, site_url: str) -> 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(
'<meta name="description" content="Jeden Tag ein neues Startup. Hier ruhen die bisherigen.">',
'<meta name="description" content="Jeden Tag ein neues Startup. Hier ruhen die bisherigen.">\n'
f' <meta property="og:title" content="{og_title}">\n'
f' <meta property="og:description" content="{og_description}">',
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())