style: auto-format with black + isort + prettier [skip ci-format-check]
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 23s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 43s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 17s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 1m12s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m37s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m38s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m3s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 2m8s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m12s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 4m17s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m23s
AI Code Review / AI Code Review (pull_request) Successful in 6m26s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 16m23s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Failing after 3s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped

This commit is contained in:
CI Bot
2026-09-07 11:49:16 +00:00
parent add93bb9de
commit 07aa03da24
2 changed files with 27 additions and 19 deletions
+23 -16
View File
@@ -1155,7 +1155,6 @@ def compute_structure_similarity(clips1: list[dict], clips2: list[dict]) -> floa
# 三维度加权:数量 0.3 + 类型 0.4 + 时长 0.3
return count_sim * 0.3 + type_sim * 0.4 + duration_sim * 0.3
def compute_duplicate_rate(
self,
fingerprint: VideoFingerprint,
@@ -1205,12 +1204,16 @@ def compute_structure_similarity(clips1: list[dict], clips2: list[dict]) -> floa
# Issue #P2-后端3: 加载当前视频的文案+结构数据
from packages.adapters.sqlalchemy_impl.models import EditPlanClipModel, GeneratedVideoModel
current_video_obj = session.query(GeneratedVideoModel).filter(GeneratedVideoModel.id == current_video_id).first() if current_video_id else None
current_video_obj = (
session.query(GeneratedVideoModel).filter(GeneratedVideoModel.id == current_video_id).first()
if current_video_id
else None
)
current_plan_id = getattr(current_video_obj, "edit_plan_id", "") or ""
current_clips_data = []
current_text_content = ""
if current_plan_id:
current_clips = (
session.query(EditPlanClipModel)
@@ -1218,10 +1221,7 @@ def compute_structure_similarity(clips1: list[dict], clips2: list[dict]) -> floa
.order_by(EditPlanClipModel.order)
.all()
)
current_clips_data = [
{"clip_type": c.clip_type, "duration": c.duration}
for c in current_clips
]
current_clips_data = [{"clip_type": c.clip_type, "duration": c.duration} for c in current_clips]
# 拼接所有片段的文本内容
current_text_content = " ".join(c.text_content for c in current_clips if c.text_content)
@@ -1299,7 +1299,7 @@ def compute_structure_similarity(clips1: list[dict], clips2: list[dict]) -> floa
existing_plan_id = getattr(existing, "edit_plan_id", "") or ""
existing_clips_data = []
existing_text_content = ""
if existing_plan_id:
existing_clips = (
session.query(EditPlanClipModel)
@@ -1307,22 +1307,29 @@ def compute_structure_similarity(clips1: list[dict], clips2: list[dict]) -> floa
.order_by(EditPlanClipModel.order)
.all()
)
existing_clips_data = [
{"clip_type": c.clip_type, "duration": c.duration}
for c in existing_clips
]
existing_clips_data = [{"clip_type": c.clip_type, "duration": c.duration} for c in existing_clips]
existing_text_content = " ".join(c.text_content for c in existing_clips if c.text_content)
# 计算文案相似度(有文案才算)
text_sim = compute_text_similarity(current_text_content, existing_text_content) if (current_text_content and existing_text_content) else 0.0
text_sim = (
compute_text_similarity(current_text_content, existing_text_content)
if (current_text_content and existing_text_content)
else 0.0
)
# 计算结构相似度(有片段才算)
structure_sim = compute_structure_similarity(current_clips_data, existing_clips_data) if (current_clips_data and existing_clips_data) else 0.0
structure_sim = (
compute_structure_similarity(current_clips_data, existing_clips_data)
if (current_clips_data and existing_clips_data)
else 0.0
)
# 多维度融合:visual*0.5 + text*0.25 + structure*0.25
# 如果文案/结构数据缺失,只用视觉维度(visual 权重提升到 1.0)
if current_text_content and existing_text_content and current_clips_data and existing_clips_data:
dup_rate = (visual_sim * VISUAL_WEIGHT + text_sim * TEXT_WEIGHT + structure_sim * STRUCTURE_WEIGHT) * 100
dup_rate = (
visual_sim * VISUAL_WEIGHT + text_sim * TEXT_WEIGHT + structure_sim * STRUCTURE_WEIGHT
) * 100
else:
# 降级:只有视觉维度
dup_rate = visual_sim * 100
+4 -3
View File
@@ -31,6 +31,7 @@ _mock_if_absent("packages.shared.storage")
# Mock cv2 and numpy if not available
try:
import cv2 as _cv2
if not isinstance(_cv2, MagicMock):
_HAS_CV2 = True
else:
@@ -154,11 +155,11 @@ class TestStructureSimilarity:
"""时长分布不同,时长相似度低。"""
clips1 = [
{"clip_type": "video", "duration": 10.0}, # 占比 80%
{"clip_type": "title", "duration": 2.5}, # 占比 20%
{"clip_type": "title", "duration": 2.5}, # 占比 20%
]
clips2 = [
{"clip_type": "video", "duration": 2.0}, # 占比 20%
{"clip_type": "title", "duration": 8.0}, # 占比 80%
{"clip_type": "video", "duration": 2.0}, # 占比 20%
{"clip_type": "title", "duration": 8.0}, # 占比 80%
]
sim = compute_structure_similarity(clips1, clips2)
assert 0.7 < sim < 0.9 # 类型相同但时长分布不同,sim=0.82