Spaces:
Configuration error
Configuration error
| import os, uuid, shutil | |
| from pathlib import Path | |
| from typing import Dict, Tuple, Any | |
| from fastapi import FastAPI, UploadFile, File, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.staticfiles import StaticFiles | |
| from pydantic import BaseModel | |
| from PIL import Image | |
| CWD = Path(__file__).resolve().parent.parent | |
| ASSETS_DIR = Path(os.getenv("ASSETS_DIR", CWD / "mock_assets")) | |
| TMP_DIR = Path(os.getenv("TMP_DIR", CWD / "tmp")) | |
| FRONT_DIR_ENV = os.getenv("FRONT_DIR", str(CWD / "frontend")) | |
| for d in (TMP_DIR / "uploads", TMP_DIR / "previews", TMP_DIR / "orders"): | |
| d.mkdir(parents=True, exist_ok=True) | |
| UPLOADS_DIR = TMP_DIR / "uploads" | |
| PREVIEWS_DIR = TMP_DIR / "previews" | |
| ORDERS_DIR = TMP_DIR / "orders" | |
| app = FastAPI(title="Clodesigner API (restored)") | |
| app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]) | |
| front_dir = Path(FRONT_DIR_ENV); front_dir.mkdir(parents=True, exist_ok=True) | |
| app.mount("/", StaticFiles(directory=str(front_dir), html=True), name="app") | |
| app.mount("/files", StaticFiles(directory=str(CWD)), name="files") | |
| class PreviewInput(BaseModel): | |
| model: str = "MT" | |
| view: str = "front" | |
| src: str | |
| tile: bool = False | |
| offset_x: int = 0 | |
| offset_y: int = 0 | |
| scale: float = 1.0 | |
| class OrderInput(PreviewInput): | |
| details: Dict[str, Any] = {} | |
| def _ensure_rgba(img: Image.Image) -> Image.Image: | |
| return img.convert("RGBA") if img.mode != "RGBA" else img | |
| def _resize_to(img: Image.Image, size: Tuple[int, int]) -> Image.Image: | |
| return img.resize(size, Image.BICUBIC) | |
| def _load_view_assets(model: str, view: str): | |
| req = ["background.png", "mask.png", "overlay.png"] | |
| opt = ["mask_s1.png", "mask_s2.png"] | |
| candidates = [ASSETS_DIR / model / view, ASSETS_DIR / view] | |
| base = next((p for p in candidates if p.exists()), None) | |
| if not base: | |
| tried = " | ".join(map(str, candidates)) | |
| raise HTTPException(400, f"Assets dir not found for view='{view}'. Tried: {tried}") | |
| missing = [n for n in req if not (base / n).exists()] | |
| if missing: | |
| raise HTTPException(400, f"Missing assets in {base}: {', '.join(missing)}") | |
| files = {n: base/n for n in req} | |
| for n in opt: | |
| p = base / n | |
| if p.exists(): files[n] = p | |
| return files | |
| def _compose_preview(src_img_path: Path, assets): | |
| bg = _ensure_rgba(Image.open(assets["background.png"])) | |
| canvas = Image.new("RGBA", bg.size, (0,0,0,0)) | |
| canvas.alpha_composite(bg) | |
| user = _ensure_rgba(Image.open(src_img_path)).resize((1200,900), Image.BICUBIC) | |
| x = (canvas.width - user.width)//2 | |
| y = (canvas.height - user.height)//2 | |
| base_mask = Image.open(assets["mask.png"]).convert("L") | |
| user_on = Image.new("RGBA", canvas.size, (0,0,0,0)) | |
| user_on.paste(user, (x,y), user) | |
| canvas.paste(user_on, (0,0), base_mask) | |
| for s in ("mask_s1.png","mask_s2.png"): | |
| if s in assets: | |
| m = Image.open(assets[s]).convert("L") | |
| tmp = Image.new("RGBA", canvas.size, (0,0,0,0)) | |
| tmp.paste(user, (x,y), user) | |
| canvas.paste(tmp, (0,0), m) | |
| overlay = _ensure_rgba(Image.open(assets["overlay.png"])) | |
| canvas.alpha_composite(overlay) | |
| return canvas | |
| async def upload(file: UploadFile = File(...)): | |
| name = file.filename or "upload.bin" | |
| data = await file.read() | |
| if not name.lower().endswith((".png",".jpg",".jpeg")): | |
| raise HTTPException(400, "Only PNG/JPG allowed") | |
| if len(data) > 50*1024*1024: | |
| raise HTTPException(400, "File too large (>50MB)") | |
| up = UPLOADS_DIR / f"{uuid.uuid4().hex}_{name}" | |
| up.write_bytes(data) | |
| rel = up.relative_to(CWD) | |
| return {"path": str(rel).replace("\\","/"), "url": f"/files/{rel}".replace("\\","/")} | |
| async def preview(p: PreviewInput): | |
| src = (CWD / p.src) if not Path(p.src).is_absolute() else Path(p.src) | |
| if not src.exists(): | |
| raise HTTPException(400, f"Uploaded file not found: {src}") | |
| img = _compose_preview(src, _load_view_assets(p.model, p.view)) | |
| out = PREVIEWS_DIR / f"{uuid.uuid4().hex[:8]}_{p.model}_{p.view}.png" | |
| img.save(out, "PNG") | |
| rel = out.relative_to(CWD) | |
| return {"ok": True, "url": f"/files/{rel}".replace("\\","/")} | |
| async def order(p: OrderInput): | |
| src = (CWD / p.src) if not Path(p.src).is_absolute() else Path(p.src) | |
| if not src.exists(): | |
| raise HTTPException(400, f"Uploaded file not found: {src}") | |
| oid = uuid.uuid4().hex[:8] | |
| base = (ORDERS_DIR / oid); (base/"mockups").mkdir(parents=True, exist_ok=True); (base/"sources").mkdir(parents=True, exist_ok=True) | |
| shutil.copy2(src, base/"sources"/Path(src.name)) | |
| img = _compose_preview(src, _load_view_assets(p.model, p.view)) | |
| out = base/"mockups"/f"{p.model}_{p.view}.png"; img.save(out, "PNG") | |
| return {"ok": True, "order": oid, | |
| "mockups": [f"/files/{out.relative_to(CWD)}".replace("\\","/")], | |
| "mockups_dir": f"/files/{(base/'mockups').relative_to(CWD)}".replace("\\","/")} | |