feat: improve subject conversion composer

This commit is contained in:
2026-05-20 16:52:31 +08:00
parent 5ac48749df
commit b9c5511128
7 changed files with 273 additions and 52 deletions

View File

@@ -4706,6 +4706,60 @@ def add_manual_frame(job_id: str, t: float) -> Job:
return job
@app.post("/jobs/{job_id}/frames/upload", response_model=Job)
async def upload_reference_frame(job_id: str, file: UploadFile = File(...)) -> Job:
"""把用户拖入的图片保存为一张参考帧,供转换层和主体生成复用。"""
job = JOBS.get(job_id)
if not job:
raise HTTPException(404, "job not found")
content_type = (file.content_type or "").lower()
suffix = Path(file.filename or "").suffix.lower()
if content_type and not content_type.startswith("image/"):
raise HTTPException(400, "only image uploads are supported")
if not content_type and suffix not in {".jpg", ".jpeg", ".png", ".webp", ".bmp"}:
raise HTTPException(400, "only image uploads are supported")
d = job_dir(job_id)
frames_dir = d / "frames"
frames_dir.mkdir(parents=True, exist_ok=True)
next_idx = max((f.index for f in job.frames), default=-1) + 1
tmp = frames_dir / f"{next_idx:03d}.upload"
out = frames_dir / f"{next_idx:03d}.jpg"
try:
await _save_upload_to_path(file, tmp)
with Image.open(tmp) as raw:
img = ImageOps.exif_transpose(raw).convert("RGB")
img.thumbnail((2400, 2400), Image.LANCZOS)
img.save(out, "JPEG", quality=92, optimize=True)
except Exception as e:
try:
out.unlink()
except OSError:
pass
raise HTTPException(400, f"reference image upload failed: {e}")
finally:
try:
tmp.unlink()
except OSError:
pass
next_timestamp = max((float(f.timestamp) for f in job.frames), default=float(job.duration or 0)) + 0.01
new_frame = KeyFrame(
index=next_idx,
timestamp=round(next_timestamp, 2),
url=f"/jobs/{job_id}/frames/{next_idx}.jpg",
description={
"scene": "用户拖入的转换层参考图",
"objects": [],
"style": "uploaded reference image",
"suggested_prompt": "",
},
)
merged = sorted(list(job.frames) + [new_frame], key=lambda f: f.timestamp)
update(job, frames=merged, message=f"已加入上传参考图,共 {len(merged)}")
return job
@app.get("/jobs/{job_id}", response_model=Job)
def get_job(job_id: str) -> Job:
job = JOBS.get(job_id)