File size: 4,197 Bytes
bb94a34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import io
import json
import os
import sys
import time
from pathlib import Path

from PIL import Image

from backend.providers.black_forest_image import generate_image

ROOT = Path(__file__).resolve().parent.parent
IMAGE_DIR = ROOT / "frontend" / "public" / "fallbacks"
ASCII_DIR = ROOT / "backend" / "fallback_ascii"
MANIFEST = ROOT / "assets" / "fallback-manifest.json"

SUBJECTS = [
    ("antique-key", "an oversized antique key with a circular bow and three distinct teeth"),
    ("lighthouse", "a solitary lighthouse projecting two sharp triangular beams"),
    ("watchful-eye", "a single watchful human eye with a bold iris and clean eyelid contour"),
    ("open-door", "an open doorway with one bright geometric ray emerging through it"),
    ("hourglass", "a large hourglass with clearly separated falling grains"),
    ("broken-chain", "two heavy broken chain links pulling apart at the center"),
    ("broadcast-microphone", "a vintage broadcast microphone with a simple circular signal halo"),
    ("burning-match", "one upright match with a large graphic flame"),
    ("theatrical-mask", "a single cracked theatrical mask with one strong diagonal fracture"),
    ("compass", "a large compass face with one bold needle pointing northeast"),
    ("raised-hand", "one open human hand reaching upward with clearly separated fingers"),
    ("spiral-shell", "a single nautilus shell with a bold geometric spiral"),
    ("alarm-clock", "a classic twin-bell alarm clock with clear hands near midnight"),
    ("paper-boat", "one folded paper boat riding on a single curved wave"),
    ("mountain-flag", "one steep triangular mountain peak with a flag at the summit"),
    ("sealed-letter", "a single closed envelope with a large circular wax seal"),
    ("labyrinth", "a simple circular labyrinth with one visible route toward the center"),
    ("fallen-crown", "one tilted crown with three tall points and a single missing jewel"),
    ("sprouting-seed", "one seed split open with a strong sprout and two leaves"),
    ("satellite-dish", "one satellite dish angled upward with three concentric signal arcs"),
]

STYLE = (
    "Create a terminal-screen pseudo-ASCII illustration. Faithfully preserve the "
    "object's defining geometry before adding stylistic detail. The image itself "
    "must visibly be made only from evenly spaced glowing green monospace terminal "
    "glyphs on pure black: Unicode Braille dots, block characters, box-drawing "
    "lines, slashes and punctuation. Preserve circles, ovals, straight rails, "
    "symmetry, gaps and radial lines where required. Use large readable glyph cells "
    "with visible black gaps between characters. Center one complete object. Crisp "
    "bitmap terminal screenshot, no photorealism, no smooth painted surfaces, no "
    "solid vector fills, no caption, no labels, no border, no interface chrome, "
    "no watermark."
)


def main() -> None:
    if not os.getenv("BFL_API_KEY", "").strip():
        raise SystemExit("BFL_API_KEY is not configured")

    IMAGE_DIR.mkdir(parents=True, exist_ok=True)
    MANIFEST.parent.mkdir(parents=True, exist_ok=True)
    records = []
    force = "--force" in sys.argv

    for index, (slug, description) in enumerate(SUBJECTS, start=1):
        image_path = IMAGE_DIR / f"fallback-{index:02d}.webp"
        prompt = f"{description}. {STYLE}"

        if not force and image_path.exists():
            print(f"[{index:02d}/20] skip {slug}")
        else:
            print(f"[{index:02d}/20] generate {slug}", flush=True)
            result = generate_image(prompt, width=768, height=512)
            with Image.open(io.BytesIO(result)) as image:
                image.convert("RGB").save(image_path, "WEBP", quality=88, method=6)
            time.sleep(0.35)

        records.append(
            {
                "index": index,
                "slug": slug,
                "description": description,
                "image": f"/fallbacks/fallback-{index:02d}.webp",
            }
        )

    MANIFEST.write_text(json.dumps(records, indent=2) + "\n", encoding="utf-8")
    print(f"Wrote {len(records)} BFL fallback objects and manifest")


if __name__ == "__main__":
    main()