storyboard-tui / scripts /generate_bfl_fallbacks.py
drdavidtang's picture
Update professor voice and storyboard flow
bb94a34 verified
Raw
History Blame Contribute Delete
4.2 kB
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()