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>
This commit is contained in:
Frank Schwenk
2026-06-12 18:14:40 +02:00
parent f6e901bad8
commit 8abd7ddb59
15 changed files with 668 additions and 10 deletions
+38 -8
View File
@@ -15,6 +15,7 @@ import requests
from jinja2 import Environment, FileSystemLoader
import topics
from images import create_responsive_variants, load_image_descriptor
GERMAN_MONTHS = [
"Januar",
@@ -205,7 +206,7 @@ def generate_hero_image(
date_iso: str,
prompt: str,
negative_prompt: str,
) -> str | None:
) -> dict[str, str] | None:
try:
response = requests.post(
"https://gateway.pixazo.ai/getImage/v1/getSDXLImage",
@@ -233,12 +234,18 @@ def generate_hero_image(
img_response = requests.get(image_url, timeout=120)
img_response.raise_for_status()
if len(img_response.content) < 50_000:
log(
f"warning: hero image for {date_iso} too small "
f"({len(img_response.content)} bytes), discarding"
)
return None
img_dir = site_dir / "img"
img_dir.mkdir(parents=True, exist_ok=True)
img_path = img_dir / f"{date_iso}.jpg"
img_path.write_bytes(img_response.content)
return f"/img/{date_iso}.jpg"
return create_responsive_variants(img_dir, date_iso)
except (requests.RequestException, OSError, KeyError, TypeError, ValueError) as exc:
log(f"warning: hero image generation failed: {exc}")
return None
@@ -252,7 +259,8 @@ def render_template(name: str, context: dict) -> str:
return env.get_template(name).render(**context)
def build_archiv_entries(archive: list[dict]) -> list[dict]:
def build_archiv_entries(archive: list[dict], site_dir: Path) -> list[dict]:
img_dir = site_dir / "img"
entries = []
for item in sorted(archive, key=lambda e: e["date"], reverse=True):
prompts = item.get("prompts")
@@ -263,7 +271,9 @@ def build_archiv_entries(archive: list[dict]) -> list[dict]:
"backronym": item["backronym"],
"tagline": item["tagline"],
"url": f"/archiv/{item['date']}.html",
"image": f"/img/{item['date']}.jpg" if item.get("has_image") else None,
"image": load_image_descriptor(img_dir, item["date"])
if item.get("has_image")
else None,
"topics": item.get("topics", []),
"prompts": prompts if isinstance(prompts, dict) else None,
}
@@ -323,18 +333,26 @@ def main() -> int:
log("PIXAZO_API_KEY not set, skipping hero image")
human_date = date_human(today)
startup_context = {
startup_base = {
"date": date_iso,
"date_human": human_date,
"theme": theme,
"hero_image": hero_image,
"s": llm_json,
"site_url": os.environ.get("SITE_URL", "https://mobea.de"),
}
startup_html = render_template("startup.html.j2", startup_context)
startup_html = render_template(
"startup.html.j2",
{**startup_base, "canonical_path": "/"},
)
archiv_day_html = render_template(
"startup.html.j2",
{**startup_base, "canonical_path": f"/archiv/{date_iso}.html"},
)
archiv_day_path = site_dir / "archiv" / f"{date_iso}.html"
write_text_atomic(index_path, startup_html)
write_text_atomic(archiv_day_path, startup_html)
write_text_atomic(archiv_day_path, archiv_day_html)
log(f"wrote {index_path} and {archiv_day_path}")
archive_entry = {
@@ -357,9 +375,21 @@ def main() -> int:
save_json_atomic(data_dir / "archive.json", archive)
log(f"updated {data_dir / 'archive.json'}")
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",
)
archiv_html = render_template(
"archiv.html.j2",
{"entries": build_archiv_entries(archive)},
{
"entries": build_archiv_entries(archive, site_dir),
"site_url": os.environ.get("SITE_URL", "https://mobea.de"),
"og_image": latest_image,
},
)
write_text_atomic(site_dir / "archiv" / "index.html", archiv_html)
log(f"wrote {site_dir / 'archiv' / 'index.html'}")