优化视频复刻
This commit is contained in:
@@ -1344,3 +1344,85 @@ def _fail_digest_task(task, reservation, message: str) -> None:
|
||||
release_credit(reservation=reservation, reason=message[:200])
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 复用入口:视频复刻·商品 拿分镜稿
|
||||
# --------------------------------------------------------------------------- #
|
||||
def digest_asset_video(*, asset, task, model_config_id=None) -> tuple[str, dict]:
|
||||
"""把一条已入库的参考视频拆成中文分镜稿,供「视频复刻·商品」当提示词骨架。
|
||||
|
||||
与「提炼提示词」页共用同一份 SKILL.md、同一个 Gemini 3.1 Pro 和同一套质检,
|
||||
保证两处产出一致;区别只在于这里不另建 VIDEO_DIGEST 任务、也不单独扣积分——
|
||||
复刻本来就是一次收费,拆解是它的内部工序。模型调用的审计仍记在传入的复刻
|
||||
task 上(AIModelAttempt),出问题查得到。
|
||||
"""
|
||||
from apps.ai.services import execute_routed_text_request
|
||||
|
||||
primary = asset.files.filter(is_primary=True).first() or asset.files.first()
|
||||
if primary is None or not primary.object_key:
|
||||
raise VideoDigestError("参考视频没有可用文件,请重新上传")
|
||||
suffix = Path(primary.object_key).suffix.lower()
|
||||
if suffix not in ALLOWED_SUFFIXES:
|
||||
suffix = ".mp4"
|
||||
|
||||
model_config = resolve_digest_model_config(preferred_id=model_config_id)
|
||||
if model_config is None:
|
||||
raise VideoDigestError("视频复刻需要 Gemini 3.1 Pro(会看图),当前没有启用,请联系管理员")
|
||||
|
||||
local_path = ""
|
||||
try:
|
||||
local_path = _download_source_to_temp(primary.object_key, suffix)
|
||||
base_name = (asset.name or "参考视频").rsplit(".", 1)[0]
|
||||
upload = _FileFromPath(local_path, f"{base_name}{suffix}")
|
||||
video, frames, duration, extras = digest_input_from_upload(upload)
|
||||
extras = extras or {}
|
||||
logger.info(
|
||||
"replace digest using %s:%s input=%s duration=%.1fs frames=%s",
|
||||
model_config.provider.name,
|
||||
model_config.name,
|
||||
"native_video" if video is not None else "frames",
|
||||
duration,
|
||||
len(frames),
|
||||
)
|
||||
messages = build_digest_messages(
|
||||
frames,
|
||||
duration,
|
||||
video=video,
|
||||
aspect_ratio=ratio_label(int(extras.get("width") or 0), int(extras.get("height") or 0)),
|
||||
file_title=title_from_filename(str(extras.get("file_name") or "")),
|
||||
)
|
||||
routed = execute_routed_text_request(
|
||||
task=task,
|
||||
primary_model=model_config,
|
||||
messages=messages,
|
||||
streaming=True,
|
||||
structured_output=False,
|
||||
business_operation="video_digest",
|
||||
temperature=0.4,
|
||||
validate_text=lambda text: validate_digest_text(
|
||||
text, duration=duration, frame_count=len(frames) or (24 if video else 0)
|
||||
),
|
||||
extra_body={"max_tokens": DIGEST_MAX_TOKENS},
|
||||
request_summary={
|
||||
"duration_seconds": round(duration, 2),
|
||||
"frame_count": len(frames),
|
||||
"input": "native_video" if video is not None else "frames",
|
||||
"for": "video_replace",
|
||||
},
|
||||
allow_retry=False,
|
||||
allow_fallback=False,
|
||||
)
|
||||
_text, _response, digest = routed.value
|
||||
meta = {
|
||||
"digest_model": model_config.name,
|
||||
"digest_input": "native_video" if video is not None else "frames",
|
||||
"digest_frames": len(frames),
|
||||
"digest_duration": round(duration, 2),
|
||||
"digest_shots": shot_count(digest),
|
||||
"digest_ratio": ratio_label(int(extras.get("width") or 0), int(extras.get("height") or 0)),
|
||||
}
|
||||
return digest, meta
|
||||
finally:
|
||||
if local_path:
|
||||
Path(local_path).unlink(missing_ok=True)
|
||||
|
||||
Reference in New Issue
Block a user