68 lines
3.2 KiB
Python
68 lines
3.2 KiB
Python
import requests
|
|
import json
|
|
import urllib.parse
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
|
|
BASE_URL = "http://localhost:7890"
|
|
|
|
def process_char(char):
|
|
name = char.get("name")
|
|
if name in ["Marcian", "Nyrilla", "Arthag", "Lysandra", "Darrag", "Himgi"]:
|
|
return # Skip ones we already fixed perfectly
|
|
|
|
char_id = urllib.parse.quote(char["id"])
|
|
sheet = char.get("sheet") or {}
|
|
if isinstance(sheet, str):
|
|
try:
|
|
sheet = json.loads(sheet)
|
|
except:
|
|
sheet = {}
|
|
|
|
phys = sheet.get('physical', '')
|
|
cloth = sheet.get('clothing', '')
|
|
|
|
# Portrait
|
|
portrait_prompt = f"Task: Generate a game NPC portrait. Subject: {name}. Physical traits: {phys}. Clothing: {cloth}. Context: dialogue portrait for a stylized exploration game set in an original world. Style: painterly game concept art with clear shape language, grounded materials, and slightly exaggerated but believable proportions. Composition: a simple square headshot/bust on a plain neutral muted parchment background, facing forward with a subtle expression. Lighting: neutral studio key with soft rim separation and no cinematic color."
|
|
|
|
# Concept
|
|
concept_prompt = f"Task: Generate a game NPC design sheet. Subject: {name}. Physical traits: {phys}. Clothing: {cloth}. Context: handoff reference for a stylized exploration game set in an original world. Style: painterly game concept art with clear shape language, grounded materials, and slightly exaggerated but believable proportions. Composition: full-body front, side, and back views across the top; below them, one dialogue pose and a single clean callout row containing exactly 3 items on a muted parchment landscape board. Lighting: neutral studio key with soft rim separation and no cinematic color."
|
|
|
|
print(f"[{name}] Starting generation...")
|
|
# Generate Portrait
|
|
try:
|
|
resp_img = requests.post(f"{BASE_URL}/api/character-generate-image", json={"prompt": portrait_prompt})
|
|
if resp_img.ok:
|
|
char["image"] = resp_img.json().get("image")
|
|
except Exception as e:
|
|
print(f"Error portrait {name}: {e}")
|
|
|
|
# Generate Concept
|
|
try:
|
|
resp_concept = requests.post(f"{BASE_URL}/api/character-generate-image", json={"prompt": concept_prompt})
|
|
if resp_concept.ok:
|
|
sheet["concept_art_image"] = resp_concept.json().get("image")
|
|
except Exception as e:
|
|
print(f"Error concept {name}: {e}")
|
|
|
|
sheet["image_prompt"] = portrait_prompt
|
|
sheet["concept_art_prompt"] = concept_prompt
|
|
char["sheet"] = sheet
|
|
|
|
resp_put = requests.put(f"{BASE_URL}/api/characters/{char_id}", json=char)
|
|
print(f"[{name}] Finished. Status: {resp_put.status_code}")
|
|
|
|
def main():
|
|
resp = requests.get(f"{BASE_URL}/api/characters")
|
|
characters = resp.json().get("characters", [])
|
|
|
|
to_process = [c for c in characters if c.get("name") not in ["Marcian", "Nyrilla", "Arthag", "Lysandra", "Darrag", "Himgi"]]
|
|
|
|
print(f"Processing {len(to_process)} characters...")
|
|
|
|
# Use 10 workers for speed! Pollinations can handle it.
|
|
with ThreadPoolExecutor(max_workers=10) as executor:
|
|
executor.map(process_char, to_process)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|