|
|
@@ -50,6 +50,7 @@ DEFAULT_BASE_URL = config.get("ANIM_STUDIO_BASE_URL", "https://x.long.bid/v1")
|
|
|
DEFAULT_API_KEY = config.get("ANIM_STUDIO_API_KEY", "")
|
|
|
DEFAULT_IMAGE_MODEL = config.get("ANIM_STUDIO_IMAGE_MODEL", "gpt-image-2")
|
|
|
DEFAULT_TEXT_MODEL = config.get("ANIM_STUDIO_TEXT_MODEL", "gpt-5.4-mini")
|
|
|
+DEFAULT_IMAGE_TIMEOUT = int(config.get("ANIM_STUDIO_IMAGE_TIMEOUT", "600") or "600")
|
|
|
JOBS = {}
|
|
|
JOBS_LOCK = threading.Lock()
|
|
|
|
|
|
@@ -472,7 +473,8 @@ def _has_alpha(img):
|
|
|
def _generate_alpha_image(creds, prompt, size, label, log):
|
|
|
img = providers.generate(creds["provider"], prompt, creds["api_key"],
|
|
|
creds.get("base_url", "https://api.openai.com/v1"),
|
|
|
- creds.get("model", "gpt-image-2"), size)
|
|
|
+ creds.get("model", "gpt-image-2"), size,
|
|
|
+ timeout=int(creds.get("timeout") or DEFAULT_IMAGE_TIMEOUT))
|
|
|
if _has_alpha(img):
|
|
|
return img
|
|
|
log(f"🧠 [{label}] 模型没有真实 Alpha,改用百度智能抠图兜底…")
|
|
|
@@ -482,6 +484,17 @@ def _generate_alpha_image(creds, prompt, size, label, log):
|
|
|
raise RuntimeError("图片没有真实 Alpha,抠图后仍不合格")
|
|
|
|
|
|
|
|
|
+def _creds_from_request(data):
|
|
|
+ return {
|
|
|
+ "provider": data.get("provider", "OpenAI 兼容接口"),
|
|
|
+ "api_key": (data.get("api_key") or DEFAULT_API_KEY).strip(),
|
|
|
+ "base_url": (data.get("base_url") or DEFAULT_BASE_URL).strip(),
|
|
|
+ "model": (data.get("model") or DEFAULT_IMAGE_MODEL).strip(),
|
|
|
+ "size": data.get("size", "1024x1024"),
|
|
|
+ "timeout": int(data.get("timeout") or DEFAULT_IMAGE_TIMEOUT),
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
def _new_asset_version():
|
|
|
return f"v{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
|
|
|
|
|
@@ -565,8 +578,14 @@ def _run_retry_boss_part_job(job_id, game, boss_id, part_id, creds):
|
|
|
if attempt > 1:
|
|
|
_append_job_log(job_id, f"🔁 [{part_id}] 拆件不合格,重新生成(第 {attempt}/3 次)…")
|
|
|
prompt = base_prompt if not correction else ", ".join([base_prompt, correction])
|
|
|
- img = _generate_alpha_image(creds, prompt, target.get("size", boss.get("size", creds.get("size", "1024x1024"))),
|
|
|
- f"{boss_id}/{part_id}", lambda m: _append_job_log(job_id, m))
|
|
|
+ try:
|
|
|
+ img = _generate_alpha_image(creds, prompt, target.get("size", boss.get("size", creds.get("size", "1024x1024"))),
|
|
|
+ f"{boss_id}/{part_id}", lambda m: _append_job_log(job_id, m))
|
|
|
+ except Exception as e:
|
|
|
+ last_reason = str(e)
|
|
|
+ _append_job_log(job_id, f"⚠️ [{part_id}] 图像接口失败/超时:{last_reason}")
|
|
|
+ correction = "接口刚才失败或超时。请继续只输出这一件干净拆件,不要完整角色,不要其他部件。"
|
|
|
+ continue
|
|
|
ok, reason, detail = asset_quality.boss_part_quality(part_id, img)
|
|
|
if ok:
|
|
|
_append_job_log(job_id, f"✅ [{part_id}] 拆件质量通过:最大主体 {detail.get('largestShare', 0):.0%}")
|
|
|
@@ -708,8 +727,14 @@ def _run_retry_boss_parts_from_preview_job(job_id, game, boss_id, creds):
|
|
|
if attempt > 1:
|
|
|
_append_job_log(job_id, f"🔁 [{pid}] 拆件不合格,重新生成(第 {attempt}/3 次)…")
|
|
|
prompt = base_prompt if not correction else ", ".join([base_prompt, correction])
|
|
|
- img = _generate_alpha_image(creds, prompt, part.get("size", boss.get("size", creds.get("size", "1024x1024"))),
|
|
|
- f"{boss_id}/{pid}", lambda m: _append_job_log(job_id, m))
|
|
|
+ try:
|
|
|
+ img = _generate_alpha_image(creds, prompt, part.get("size", boss.get("size", creds.get("size", "1024x1024"))),
|
|
|
+ f"{boss_id}/{pid}", lambda m: _append_job_log(job_id, m))
|
|
|
+ except Exception as e:
|
|
|
+ last_reason = str(e)
|
|
|
+ _append_job_log(job_id, f"⚠️ [{pid}] 图像接口失败/超时:{last_reason}")
|
|
|
+ correction = "接口刚才失败或超时。请继续只输出这一件干净拆件,不要完整角色,不要其他部件。"
|
|
|
+ continue
|
|
|
ok, reason, detail = asset_quality.boss_part_quality(pid, img)
|
|
|
if ok:
|
|
|
_append_job_log(job_id, f"✅ [{idx}/{len(parts)}] {pid} 通过:最大主体 {detail.get('largestShare', 0):.0%}")
|
|
|
@@ -1082,13 +1107,7 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
manifest = slot_workflow.complete_manifest(manifest)
|
|
|
except Exception as e:
|
|
|
return self._send(400, {"ok": False, "error": f"manifest 非法 JSON: {e}"})
|
|
|
- creds = {
|
|
|
- "provider": data.get("provider", "OpenAI 兼容接口"),
|
|
|
- "api_key": (data.get("api_key") or DEFAULT_API_KEY).strip(),
|
|
|
- "base_url": (data.get("base_url") or DEFAULT_BASE_URL).strip(),
|
|
|
- "model": (data.get("model") or DEFAULT_IMAGE_MODEL).strip(),
|
|
|
- "size": data.get("size", "1024x1024"),
|
|
|
- }
|
|
|
+ creds = _creds_from_request(data)
|
|
|
logs = []
|
|
|
if data.get("async", True):
|
|
|
job_id = uuid.uuid4().hex
|
|
|
@@ -1194,13 +1213,7 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
bad = [x for x in task_ids if x not in valid_ids]
|
|
|
if bad:
|
|
|
return self._send(400, {"ok": False, "error": f"任务不存在: {', '.join(bad)}"})
|
|
|
- creds = {
|
|
|
- "provider": data.get("provider", "OpenAI 兼容接口"),
|
|
|
- "api_key": (data.get("api_key") or DEFAULT_API_KEY).strip(),
|
|
|
- "base_url": (data.get("base_url") or DEFAULT_BASE_URL).strip(),
|
|
|
- "model": (data.get("model") or DEFAULT_IMAGE_MODEL).strip(),
|
|
|
- "size": data.get("size", "1024x1024"),
|
|
|
- }
|
|
|
+ creds = _creds_from_request(data)
|
|
|
job_id = uuid.uuid4().hex
|
|
|
with JOBS_LOCK:
|
|
|
JOBS[job_id] = {"status": "queued", "ok": None, "logs": ["补生成任务已创建,等待开始…"],
|
|
|
@@ -1220,13 +1233,7 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
return self._send(400, {"ok": False, "error": f"无效的 game: {game!r}"})
|
|
|
if not boss_id or not part_id:
|
|
|
return self._send(400, {"ok": False, "error": "缺少 bossId 或 partId"})
|
|
|
- creds = {
|
|
|
- "provider": data.get("provider", "OpenAI 兼容接口"),
|
|
|
- "api_key": (data.get("api_key") or DEFAULT_API_KEY).strip(),
|
|
|
- "base_url": (data.get("base_url") or DEFAULT_BASE_URL).strip(),
|
|
|
- "model": (data.get("model") or DEFAULT_IMAGE_MODEL).strip(),
|
|
|
- "size": data.get("size", "1024x1024"),
|
|
|
- }
|
|
|
+ creds = _creds_from_request(data)
|
|
|
job_id = uuid.uuid4().hex
|
|
|
with JOBS_LOCK:
|
|
|
JOBS[job_id] = {"status": "queued", "ok": None, "logs": ["拆件重生任务已创建,等待开始…"],
|
|
|
@@ -1246,13 +1253,7 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
return self._send(400, {"ok": False, "error": f"无效的 game: {game!r}"})
|
|
|
if not boss_id:
|
|
|
return self._send(400, {"ok": False, "error": "缺少 bossId"})
|
|
|
- creds = {
|
|
|
- "provider": data.get("provider", "OpenAI 兼容接口"),
|
|
|
- "api_key": (data.get("api_key") or DEFAULT_API_KEY).strip(),
|
|
|
- "base_url": (data.get("base_url") or DEFAULT_BASE_URL).strip(),
|
|
|
- "model": (data.get("model") or DEFAULT_IMAGE_MODEL).strip(),
|
|
|
- "size": data.get("size", "1024x1024"),
|
|
|
- }
|
|
|
+ creds = _creds_from_request(data)
|
|
|
job_id = uuid.uuid4().hex
|
|
|
with JOBS_LOCK:
|
|
|
JOBS[job_id] = {"status": "queued", "ok": None, "logs": ["主图重生任务已创建,等待开始…"],
|
|
|
@@ -1272,13 +1273,7 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
return self._send(400, {"ok": False, "error": f"无效的 game: {game!r}"})
|
|
|
if not boss_id:
|
|
|
return self._send(400, {"ok": False, "error": "缺少 bossId"})
|
|
|
- creds = {
|
|
|
- "provider": data.get("provider", "OpenAI 兼容接口"),
|
|
|
- "api_key": (data.get("api_key") or DEFAULT_API_KEY).strip(),
|
|
|
- "base_url": (data.get("base_url") or DEFAULT_BASE_URL).strip(),
|
|
|
- "model": (data.get("model") or DEFAULT_IMAGE_MODEL).strip(),
|
|
|
- "size": data.get("size", "1024x1024"),
|
|
|
- }
|
|
|
+ creds = _creds_from_request(data)
|
|
|
job_id = uuid.uuid4().hex
|
|
|
with JOBS_LOCK:
|
|
|
JOBS[job_id] = {"status": "queued", "ok": None, "logs": ["按主图重生拆件任务已创建,等待开始…"],
|
|
|
@@ -1303,13 +1298,7 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
groups = {k: v for k, v in groups.items() if v}
|
|
|
if not groups:
|
|
|
return self._send(400, {"ok": False, "error": "当前资源库没有缺失任务"})
|
|
|
- creds = {
|
|
|
- "provider": data.get("provider", "OpenAI 兼容接口"),
|
|
|
- "api_key": (data.get("api_key") or DEFAULT_API_KEY).strip(),
|
|
|
- "base_url": (data.get("base_url") or DEFAULT_BASE_URL).strip(),
|
|
|
- "model": (data.get("model") or DEFAULT_IMAGE_MODEL).strip(),
|
|
|
- "size": data.get("size", "1024x1024"),
|
|
|
- }
|
|
|
+ creds = _creds_from_request(data)
|
|
|
job_id = uuid.uuid4().hex
|
|
|
with JOBS_LOCK:
|
|
|
JOBS[job_id] = {"status": "queued", "ok": None, "logs": ["批量补生成任务已创建,等待开始…"],
|