完成视频复刻和优化
This commit is contained in:
@@ -31,6 +31,7 @@ from apps.billing.services.ledger import charge_reserved_credit, release_credit,
|
||||
from .models import AITask, ModelConfig
|
||||
from .providers.volcano import VolcanoArkProvider
|
||||
from .generation_errors import classify_generation_error, public_error_for_task
|
||||
from .media_probe import REF_DURATION_MAX
|
||||
from .video_errors import parse_provider_error
|
||||
from .video_pricing import get_resolution
|
||||
|
||||
@@ -319,7 +320,7 @@ def build_content_items(*, team, prompt: str, mode: str, references: list) -> di
|
||||
raise ValueError(f"参考音频最多 3 条,当前 {audio_n} 条,请减少后重试")
|
||||
if audio_n > 0 and image_n + video_n == 0:
|
||||
raise ValueError("音频不能单独作为参考素材,请同时提供参考图片或视频")
|
||||
if video_duration_total > 15:
|
||||
if video_duration_total > REF_DURATION_MAX:
|
||||
raise ValueError("参考视频总时长不能超过 15 秒,请缩短后重试")
|
||||
|
||||
# @label 替换:按 label 长度降序,防子串吞噬
|
||||
@@ -409,6 +410,7 @@ def submit_free_video(*, team, user, params: dict) -> AITask:
|
||||
"""提交一条自由创作视频。校验/估价/预留在前(失败不留半套),火山调用在事务外。
|
||||
创建失败不抛:返回 FAILED 任务(带友好中文错误),前端渲染失败卡。校验类错误抛 ValueError → 400。"""
|
||||
prompt = str(params.get("prompt") or "").strip()
|
||||
feature = str(params.get("feature") or "free_video").strip() or "free_video"
|
||||
mode = str(params.get("mode") or "universal")
|
||||
model_name = str(params.get("model") or HIGH_RES_MODEL)
|
||||
aspect_ratio = str(params.get("aspect_ratio") or "16:9")
|
||||
@@ -486,7 +488,7 @@ def submit_free_video(*, team, user, params: dict) -> AITask:
|
||||
reserve_amount = video_reserve_amount(quote.points)
|
||||
|
||||
request_payload = {
|
||||
"feature": "free_video",
|
||||
"feature": feature,
|
||||
"mode": mode,
|
||||
"model": model_name,
|
||||
"endpoint": model_config.endpoint,
|
||||
@@ -505,6 +507,13 @@ def submit_free_video(*, team, user, params: dict) -> AITask:
|
||||
"references": built["snapshots"],
|
||||
"model_routing_v1": True,
|
||||
}
|
||||
extra = params.get("extra_payload")
|
||||
if isinstance(extra, dict):
|
||||
protected = set(request_payload)
|
||||
for key, value in extra.items():
|
||||
if key in protected or value in (None, ""):
|
||||
continue
|
||||
request_payload[key] = value
|
||||
|
||||
# 建任务 + 预留同一事务:余额不足/限额拦截时回滚任务行,不留半套
|
||||
with transaction.atomic():
|
||||
@@ -546,7 +555,7 @@ def submit_free_video(*, team, user, params: dict) -> AITask:
|
||||
generate_audio=generate_audio,
|
||||
seed=seed if seed != -1 else None,
|
||||
search_mode=search_mode,
|
||||
request_summary={"feature": "free_video", "mode": mode},
|
||||
request_summary={"feature": feature, "mode": mode},
|
||||
)
|
||||
response, provider_task_id = routed.value
|
||||
# execute_model_call 以原子 F 表达式累计实际尝试的平台成本;刷新内存对象,
|
||||
@@ -625,7 +634,7 @@ def _notify_failure(task: AITask, *, raw: str, hint: str) -> None:
|
||||
task=task,
|
||||
project=None,
|
||||
recipient=task.created_by,
|
||||
stage_label="自由创作视频",
|
||||
stage_label="视频复刻" if (task.request_payload or {}).get("feature") == "video_replace" else "自由创作视频",
|
||||
raw=raw,
|
||||
hint=hint,
|
||||
)
|
||||
@@ -643,19 +652,27 @@ def _store_free_video_media(*, task: AITask, media: str) -> Asset:
|
||||
asset_id = uuid.uuid4()
|
||||
object_key = f"teams/{task.team_id}/free-create/{asset_id}.mp4"
|
||||
stored = TosStorage().upload_fileobj(fileobj=fileobj, object_key=object_key, content_type=content_type)
|
||||
prompt = (task.request_payload or {}).get("prompt") or ""
|
||||
payload = task.request_payload or {}
|
||||
prompt = payload.get("prompt") or ""
|
||||
feature = payload.get("feature") or "free_video"
|
||||
subject = str(payload.get("subject_name") or "").strip()
|
||||
if feature == "video_replace":
|
||||
mode_label = "角色复刻" if payload.get("replace_mode") == "character" else "商品复刻"
|
||||
asset_name = f"{subject}{mode_label}" if subject else mode_label
|
||||
else:
|
||||
asset_name = prompt[:255] or "自由创作视频"
|
||||
asset = Asset.objects.create(
|
||||
id=asset_id,
|
||||
team=task.team,
|
||||
created_by=task.created_by,
|
||||
# Asset.name 是通用资产字段,受 255 字符上限约束;完整显示标题由资产接口从
|
||||
# 关联任务的 request_payload.prompt 派生,避免再出现 50 字符业务截断。
|
||||
name=(prompt[:255] or "自由创作视频"),
|
||||
name=asset_name[:255],
|
||||
asset_type=Asset.Type.VIDEO,
|
||||
source=Asset.Source.AI_GENERATED,
|
||||
category=Asset.Category.FREE_CREATE,
|
||||
origin_task=task,
|
||||
metadata={"feature": "free_video"},
|
||||
metadata={"feature": feature},
|
||||
)
|
||||
AssetFile.objects.create(
|
||||
asset=asset,
|
||||
@@ -892,6 +909,7 @@ def serialize_free_video_task(task: AITask, *, include_deleted_assets: bool = Fa
|
||||
"status": task.status,
|
||||
"mode": payload.get("mode") or "universal",
|
||||
"model": payload.get("model") or "",
|
||||
"feature": payload.get("feature") or "free_video",
|
||||
"prompt": payload.get("prompt") or "",
|
||||
"aspect_ratio": payload.get("aspect_ratio") or "16:9",
|
||||
"resolution": payload.get("resolution") or "720p",
|
||||
|
||||
Reference in New Issue
Block a user