diff --git a/.memory/worklog.json b/.memory/worklog.json index 0d8a2f5..82ff4ce 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1,30 +1,5 @@ { "entries": [ - { - "files_changed": 1, - "message": "Claude 会话结束 · 持续 0 秒 · 最近命令:claude · 分支 main · 1 项未提交变更 · 最近提交:auto-save 2026-05-20 14:23 (+1, ~1)", - "ts": "2026-05-20T06:37:09Z", - "type": "session-end" - }, - { - "files_changed": 1, - "message": "Claude 会话结束 · 持续 0 秒 · 最近命令:claude · 分支 main · 1 项未提交变更 · 最近提交:auto-save 2026-05-20 14:23 (+1, ~1)", - "ts": "2026-05-20T06:37:09Z", - "type": "session-end" - }, - { - "files_changed": 2, - "hash": "16f78ba", - "message": "auto-save 2026-05-20 14:39 (+1, ~1)", - "ts": "2026-05-20T14:39:42+08:00", - "type": "commit" - }, - { - "files_changed": 2, - "message": "Codex 会话活跃 · 最近命令:codex · 分支 main · 2 项未提交变更 · 最近提交:auto-save 2026-05-20 14:39 (+1, ~1)", - "ts": "2026-05-20T06:43:58Z", - "type": "session-heartbeat" - }, { "files_changed": 3, "hash": "d6bba9d", @@ -3206,6 +3181,31 @@ "type": "session-heartbeat", "message": "Codex 会话活跃 · 最近命令:codex · 分支 main · 1 项未提交变更 · 最近提交:chore: disable password login in production", "files_changed": 1 + }, + { + "ts": "2026-05-26T09:08:30+08:00", + "type": "commit", + "message": "chore: migrate legacy password data to Feishu owner", + "hash": "e0330bf", + "files_changed": 3 + }, + { + "ts": "2026-05-26T01:09:34Z", + "type": "session-heartbeat", + "message": "Codex 会话活跃 · 最近命令:codex · 分支 main · 1 项未提交变更 · 最近提交:chore: migrate legacy password data to Feishu owner", + "files_changed": 1 + }, + { + "ts": "2026-05-26T01:19:34Z", + "type": "session-heartbeat", + "message": "Codex 会话活跃 · 最近命令:codex · 分支 main · 1 项未提交变更 · 最近提交:chore: migrate legacy password data to Feishu owner", + "files_changed": 1 + }, + { + "ts": "2026-05-26T01:29:34Z", + "type": "session-heartbeat", + "message": "Codex 会话活跃 · 最近命令:codex · 分支 main · 1 项未提交变更 · 最近提交:chore: migrate legacy password data to Feishu owner", + "files_changed": 1 } ] } diff --git a/api/main.py b/api/main.py index cf02bf6..1cf8d0f 100644 --- a/api/main.py +++ b/api/main.py @@ -8035,6 +8035,92 @@ def video_url_from_response(data: dict) -> str: return "" +def _video_public_error(raw: object) -> str: + text = str(raw or "").strip() + lower = text.lower() + + if any(token.lower() in lower for token in ( + "InputImageSensitiveContentDetected.PrivacyInformation".lower(), + "privacyinformation", + "privacy information", + "real person", + "input image may contain real person", + "human face", + "face detected", + "肖像", + "隐私", + "真人", + "人脸", + )): + return ( + "视频生成失败:参考图里有清晰人物或疑似真实人脸,视频模型出于肖像/隐私风控拒绝生成。" + "请换成无可识别人脸的首帧,或先裁掉/模糊人物脸,再重新生成视频。" + ) + + if any(token in lower for token in ( + "sensitivecontent", + "sensitive content", + "content policy", + "violate", + "violation", + "not allowed", + "risk control", + "moderation", + "敏感", + "安全审核", + "风控", + "违规", + )): + return ( + "视频生成失败:参考图或提示词触发了视频模型的内容安全审核。" + "请换一张更中性的参考图,避免真实人物、暴露、医疗夸大、危险动作或敏感文字后重试。" + ) + + if any(token in lower for token in ("unauthorized", "invalid api key", "permission denied", "forbidden", "http 401", "http 403")): + return "视频生成失败:视频通道认证或权限异常,请联系管理员检查服务器上的视频 API Key 和模型权限。" + + if any(token in lower for token in ("http 429", "rate limit", "too many requests", "quota", "insufficient", "balance", "限流", "额度", "余额")): + return "视频生成失败:视频模型当前限流或额度不足,请稍后重试;如果持续出现,请联系管理员检查视频通道额度。" + + if any(token in lower for token in ("timeout", "timed out", "readtimeout", "connecttimeout", "超时")): + return "视频生成失败:视频模型响应超时,可能是上游繁忙或网络不稳定。请稍后重试,或缩短时长后再生成。" + + if any(token in lower for token in ( + "name or service not known", + "temporary failure in name resolution", + "nodename nor servname", + "connection refused", + "network is unreachable", + "connecterror", + "ssl:", + "网络", + "dns", + )): + return "视频生成失败:服务器连接视频模型网关异常,请稍后重试;如果连续失败,请联系管理员检查视频网关网络。" + + if any(token in lower for token in ("http 404", "http 405", "unsupported", "not found", "method not allowed")): + return "视频生成失败:当前视频模型接口路径不可用,请联系管理员检查视频网关配置。" + + if lower.startswith("video status: failed") or "video status: failed" in lower: + return "视频生成失败:视频模型返回生成失败。请换一张更清晰、主体更稳定的参考图,或简化提示词后重试。" + + if text.startswith("视频生成失败:"): + return text[:500] + if text: + return f"视频生成失败:{text[:460]}" + return "视频生成失败:未知错误,请换一张参考图或稍后重试。" + + +def _video_create_failure_message(create_errors: list[str]) -> str: + raw = " | ".join(create_errors) + public = _video_public_error(raw) + if public.startswith("视频生成失败:当前视频模型接口路径不可用"): + return public + if public.startswith("视频生成失败:") and public != f"视频生成失败:{raw[:460]}": + return public + return "视频生成失败:视频模型没有接受本次请求。请换一张参考图或简化提示词后重试;如果持续失败,请联系管理员。" + + def download_generated_video(client, base: str, headers: dict, provider_id: str, direct_url: str, out_mp4: Path) -> None: if direct_url: url = direct_url if direct_url.startswith("http") else f"{base}{direct_url if direct_url.startswith('/') else '/' + direct_url}"