#!/usr/bin/env python3 """Mirror the app corpus's photographs into both concepts. Reads afrix-web/lib/api/fixtures.json (the wired corpus) and img/credits-scenes.json, then for every post carrying a photograph: build.py the matching main-feed entry becomes k="image" with the real tone and byte count (the canvas draws the tone well) concept-web/afrix-web.html the matching post gets media {ar, tone, bytes, art} with the 25 KB thumb embedded, behind its tap-to-load chip Posts are matched by the first 40 characters of their text. Run build.py and reseed.py afterwards for the canvas; the web concept is written directly. python3 mirror_photos.py """ import base64, io, json, os, re HERE = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.dirname(HERE) FIX = os.path.join(ROOT, "afrix-web", "lib", "api", "fixtures.json") MEDIA = os.path.join(ROOT, "afrix-web", "public", "media") WEB = os.path.join(ROOT, "concept-web", "afrix-web.html") BUILD = os.path.join(HERE, "build.py") def key(text: str) -> str: """Letters and digits only: "#TemaPort at 06:40" and "Tema port at 06:40" are one post.""" return re.sub(r"[^a-z0-9]", "", (text or "").lower())[:32] def main() -> None: fx = json.load(io.open(FIX, encoding="utf-8")) photos = {} for p in fx["posts"]: m = (p.get("media") or [None])[0] if m and m.get("src") and os.path.exists(os.path.join(MEDIA, os.path.basename(m["src"]))): photos[key(p["body"])] = (p["id"], m) kb = lambda m: f"{round(m['thumb_bytes'] / 1024)} KB" # ---- canvas source ---- src = io.open(BUILD, encoding="utf-8").read() hit = 0 def fix_entry(mm): nonlocal hit body = mm.group(0) text = re.search(r'x="([^"]*)"', body).group(1) k = key(text.encode().decode("unicode_escape") if "\\u" in text else text) if k not in photos: return body pid, m = photos[k] hit += 1 body = re.sub(r'k="[a-z]+"', 'k="image"', body, count=1) body = re.sub(r',\s*tone="#[0-9A-Fa-f]{6}",\s*bytes="[^"]*"', "", body) return body.replace(f'x="{text}",', f'x="{text}",\n tone="{m["tone"]}",bytes="{kb(m)}",', 1) src = re.sub(r'dict\(i="[^"]*",n="[^"]*",s="[^"]*",h="[^"]*",t="[^"]*",k="[a-z]+",\s*\n\s*x="[^"]*",(?:\s*\n\s*tone="#[0-9A-Fa-f]{6}",bytes="[^"]*",)?', fix_entry, src) io.open(BUILD, "w", encoding="utf-8").write(src) print(f"build.py: {hit} image entries") # ---- web concept ---- html = io.open(WEB, encoding="utf-8").read() mm = re.search(r"var DATA = (\{.*?\});\n", html, re.S) D = json.loads(mm.group(1)) credits = {c["post"]: c for c in json.load(open(os.path.join(HERE, "img", "credits-scenes.json")))} n = 0 for posts in D["posts"].values(): for po in posts: k = key(po.get("text", "")) if k in photos: pid, m = photos[k] b64 = base64.b64encode(open(os.path.join(MEDIA, os.path.basename(m["src"])), "rb").read()).decode() alt = credits.get(pid, {}).get("alt") or m.get("alt") or "" po["media"] = {"ar": f"{m['width']} / {m['height']}", "tone": m["tone"], "bytes": kb(m), "art": f'{alt}'} n += 1 html = html[:mm.start(1)] + json.dumps(D, ensure_ascii=False, separators=(",", ":")) + html[mm.end(1):] io.open(WEB, "w", encoding="utf-8").write(html) print(f"web concept: {n} posts with photographs") if __name__ == "__main__": main()