diff --git a/apps/worker/worker_app/tasks/generation.py b/apps/worker/worker_app/tasks/generation.py index 1ba072f71..94e492205 100644 --- a/apps/worker/worker_app/tasks/generation.py +++ b/apps/worker/worker_app/tasks/generation.py @@ -1051,20 +1051,15 @@ def generate_video(self, task_id: str) -> dict: ) if _meta_model: meta = dict(_meta_model.extra_meta or {}) + # #2024/P0 finalize-400: 直接展开 _precompute_render_metadata 返回的 + # 完整 dict(含 file_url/fingerprint_dict/fingerprint_chunks/is_duplicate + # /duplicate_of/...),避免手写字段白名单漏传字段导致 finalize 读不到数据。 meta["rendered_output"] = { + **dict(rendered_output or {}), + # file_url/duration 由外层调用方拿到的实际上传结果,优先覆盖预计算值 "file_url": file_url, "file_size": file_size, "duration": duration, - "width": rendered_output.get("width", 1280), - "height": rendered_output.get("height", 720), - "fps": rendered_output.get("fps", 25.0), - "name": rendered_output.get("name", ""), - "thumbnail_url": rendered_output.get("thumbnail_url", ""), - "mode": rendered_output.get("mode", editing_mode.value), - "fingerprint_dict": rendered_output.get("fingerprint_dict"), - "batch_id": batch_id, - "project_id": project_id, - "user_id": user_id, } _meta_model.extra_meta = meta _finalize_meta_session.commit() diff --git a/packages/adapters/sqlalchemy_impl/generation_task_repository.py b/packages/adapters/sqlalchemy_impl/generation_task_repository.py index 433aaa829..1c3a65ba8 100755 --- a/packages/adapters/sqlalchemy_impl/generation_task_repository.py +++ b/packages/adapters/sqlalchemy_impl/generation_task_repository.py @@ -43,6 +43,7 @@ def _to_domain(model: GenerationTaskModel) -> GenerationTask: output_height=getattr(model, "output_height", 720) or 720, cover_url=getattr(model, "cover_url", "") or "", title_config=dict(getattr(model, "title_config", {}) or {}), + extra_meta=dict(getattr(model, "extra_meta", {}) or {}), logs=model.logs or "[]", created_at=model.created_at, updated_at=model.updated_at, @@ -88,6 +89,7 @@ class SQLAlchemyGenerationTaskRepository: output_height=task.output_height, cover_url=task.cover_url or "", title_config=dict(task.title_config) if task.title_config else {}, + extra_meta=dict(task.extra_meta) if task.extra_meta else {}, logs=task.logs, created_at=task.created_at, updated_at=task.updated_at, @@ -322,6 +324,7 @@ class SQLAlchemyGenerationTaskRepository: model.output_height = task.output_height model.cover_url = task.cover_url or "" model.title_config = dict(task.title_config) if task.title_config else {} + model.extra_meta = dict(task.extra_meta) if task.extra_meta else {} model.logs = task.logs self.session.commit() return task diff --git a/packages/application/generated_video_finalize.py b/packages/application/generated_video_finalize.py index 2ab79e99b..6398d2614 100644 --- a/packages/application/generated_video_finalize.py +++ b/packages/application/generated_video_finalize.py @@ -52,6 +52,18 @@ class RenderedOutput: def from_dict(cls, data: dict[str, Any]) -> "RenderedOutput": if not isinstance(data, dict): raise ValueError("rendered_output must be a dict") + # fingerprint_chunks 历史上有两种位置: + # 1) 顶层 ``fingerprint_chunks``(由 compute_render_fingerprint_and_dedup 直接返回) + # 2) 嵌套在 ``fingerprint_dict["chunks"]``(VideoFingerprint.to_dict() 序列化的结构) + # 顶层优先;顶层为空时回退到嵌套位置,兼容旧数据。 + fp_dict = data.get("fingerprint_dict") or {} + chunks_raw = data.get("fingerprint_chunks") + if not chunks_raw and isinstance(fp_dict, dict): + chunks_raw = fp_dict.get("chunks") + # md5 同样可能在顶层或嵌套在 fingerprint_dict 内(历史数据兼容) + md5_value = data.get("video_fingerprint_md5") + if not md5_value and isinstance(fp_dict, dict): + md5_value = fp_dict.get("md5") return cls( file_url=str(data.get("file_url") or ""), file_size=int(data.get("file_size") or 0), @@ -65,14 +77,14 @@ class RenderedOutput: batch_id=str(data.get("batch_id") or ""), project_id=str(data.get("project_id") or ""), user_id=str(data.get("user_id") or ""), - fingerprint_dict=data.get("fingerprint_dict"), - fingerprint_chunks=data.get("fingerprint_chunks"), + fingerprint_dict=fp_dict or None, + fingerprint_chunks=chunks_raw if isinstance(chunks_raw, list) else None, is_duplicate=bool(data.get("is_duplicate", False)), duplicate_of=data.get("duplicate_of"), duplicate_rate=_safe_float(data.get("duplicate_rate")), match_count=_safe_int(data.get("match_count")), visual_similarity=_safe_float(data.get("visual_similarity")), - video_fingerprint_md5=str(data.get("video_fingerprint_md5") or ""), + video_fingerprint_md5=str(md5_value or ""), )