完成极速成片

This commit is contained in:
Azmat@qq.com
2026-08-25 18:46:20 +08:00
parent df6784b90c
commit 2f70d3e8a0
15 changed files with 495 additions and 146 deletions
+73 -26
View File
@@ -2217,10 +2217,20 @@ def generate_person_triview(*, project, user, portrait_asset) -> AITask:
ref_url = _asset_preview_url(portrait_asset)
# 人物三视图提示词:正文可在 admin「提示词」页改(无占位符)
tri_prompt = render_prompt("person_triview", THREE_VIEW_PROMPT)
portrait_label = ""
for group in project.base_asset_groups.filter(kind=BaseAssetGroup.Kind.PERSON):
meta = group.metadata or {}
if meta.get("triview_of"):
continue
candidates = [str(value) for value in (group.candidate_assets or [])]
if str(group.adopted_asset_id or "") == asset_key or asset_key in candidates:
portrait_label = str(meta.get("label") or "")
break
payload = {
"model": model_config.name,
"prompt": tri_prompt,
"kind": "person",
"label": portrait_label or str(portrait_asset.name or ""),
"triview_of": asset_key,
"reference_image": ref_url,
"model_routing_v1": True,
@@ -3234,6 +3244,26 @@ def collect_video_review_blockers(project, only_segment: "VideoSegment | None" =
return blockers
_VIDEO_INFLIGHT_STATUSES = {
AITask.Status.CREATED,
AITask.Status.RESERVED,
AITask.Status.SUBMITTED,
AITask.Status.POLLING,
AITask.Status.POSTPROCESSING,
}
def video_segment_has_inflight_task(video_segment: VideoSegment) -> bool:
"""同一镜头是否已有在途视频任务。防止极速成片并发推进把同一段提交两次。"""
segment_id = str(video_segment.id)
tasks = AITask.objects.filter(
project_id=video_segment.project_id,
task_type=AITask.Type.VIDEO_SEGMENT,
status__in=_VIDEO_INFLIGHT_STATUSES,
).only("request_payload")
return any(str((task.request_payload or {}).get("video_segment_id") or "") == segment_id for task in tasks)
def submit_video_segment(
*,
video_segment: VideoSegment,
@@ -3261,7 +3291,17 @@ def submit_video_segment(
model_config = get_default_model(ModelConfig.Capability.VIDEO)
if model_config is None:
raise ValueError("no active video model configured")
project = video_segment.project
with transaction.atomic():
video_segment = VideoSegment.objects.select_for_update().select_related("project").get(pk=video_segment.pk)
project = video_segment.project
if video_segment.status in {VideoSegment.Status.RUNNING, VideoSegment.Status.SUCCEEDED}:
return None
if video_segment_has_inflight_task(video_segment):
return None
if video_segment.status != VideoSegment.Status.QUEUED:
video_segment.status = VideoSegment.Status.QUEUED
video_segment.save(update_fields=["status", "updated_at"])
# 衔接:按 sort_order 把视频段绑到对应脚本镜,并织出跟住该镜的提示词。
scene = None
@@ -3289,31 +3329,38 @@ def submit_video_segment(
references=[],
team=project.team,
)
task = create_ai_task(
project=project,
user=user,
task_type=AITask.Type.VIDEO_SEGMENT,
model_config=model_config,
quote=quote,
reserve_amount=video_reserve_amount(quote.points),
request_payload={
"model": model_config.name,
"endpoint": model_config.endpoint,
"prompt": final_prompt,
"duration": video_segment.target_duration_seconds,
"ratio": aspect_ratio,
"resolution": resolution,
"estimated_tokens": est_tokens,
# 团队价格系数快照:按实结算用它,中途改价不影响在途任务(jimeng 同款纪律)
"price_multiplier": quote.meta.get("price_multiplier", "1"),
"video_segment_id": str(video_segment.id),
"reference_images": reference_images,
"model_routing_v1": True,
},
)
# 提交尝试的实际平台成本由 AIModelAttempt 累加;成片后再用实际模型的 usage true-up 覆盖。
task.base_cost = Decimal("0")
task.save(update_fields=["base_cost", "updated_at"])
with transaction.atomic():
video_segment = VideoSegment.objects.select_for_update().select_related("project").get(pk=video_segment.pk)
project = video_segment.project
if video_segment.status in {VideoSegment.Status.RUNNING, VideoSegment.Status.SUCCEEDED}:
return None
if video_segment_has_inflight_task(video_segment):
return None
task = create_ai_task(
project=project,
user=user,
task_type=AITask.Type.VIDEO_SEGMENT,
model_config=model_config,
quote=quote,
reserve_amount=video_reserve_amount(quote.points),
request_payload={
"model": model_config.name,
"endpoint": model_config.endpoint,
"prompt": final_prompt,
"duration": video_segment.target_duration_seconds,
"ratio": aspect_ratio,
"resolution": resolution,
"estimated_tokens": est_tokens,
# 团队价格系数快照:按实结算用它,中途改价不影响在途任务(jimeng 同款纪律)
"price_multiplier": quote.meta.get("price_multiplier", "1"),
"video_segment_id": str(video_segment.id),
"reference_images": reference_images,
"model_routing_v1": True,
},
)
# 提交尝试的实际平台成本由 AIModelAttempt 累加;成片后再用实际模型的 usage true-up 覆盖。
task.base_cost = Decimal("0")
task.save(update_fields=["base_cost", "updated_at"])
try:
routed = execute_routed_video_submit(
task=task,