Merge pull request 'feat: 跨视频片段避让 — 生成前注入已用区间 #1670' (#1671) from feat/cross-video-avoidance-1670 into develop
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 4s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 4s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Check push changed paths (push) Successful in 6s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 17s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 8s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 7s
CI/CD Pipeline / Integration Tests (push) Successful in 1m39s
CI/CD Pipeline / Build Staging API Image (push) Successful in 22s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 16s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 18s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 2s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m57s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 3m28s
CI/CD Pipeline / Validate - Style (push) Failing after 4m10s
AI Code Review / AI Code Review (pull_request) Failing after 4m7s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 4m28s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 2m15s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 5m43s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m31s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 2m56s
CI/CD Pipeline / Unit Tests (push) Successful in 8m3s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 6m25s
CI/CD Pipeline / Validate - Security (push) Successful in 29m7s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Failing after 304h12m42s
CI/CD Pipeline / Deploy Production (push) Failing after 304h12m42s
CI/CD Pipeline / CI Gate (push) Failing after 304h12m47s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 304h12m47s
CI/CD Pipeline / Build Production Web Image (push) Failing after 304h12m47s
CI/CD Pipeline / Build Production API Image (push) Failing after 304h12m47s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Failing after 304h38m51s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 304h39m48s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 304h39m56s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 304h39m56s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 304h39m56s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 304h39m56s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 304h39m57s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 304h39m57s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 304h39m58s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 304h39m59s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 304h39m59s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 304h39m59s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 304h41m54s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 304h39m59s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 304h41m54s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 304h41m54s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 304h42m15s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 304h42m16s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 304h42m16s
CI/CD Pipeline / Frontend Lint (push) Failing after 304h42m18s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Failing after 304h42m16s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 304h42m22s
CI/CD Pipeline / PR Build API Image (push) Failing after 304h42m24s
CI/CD Pipeline / Validate - Security (pull_request) Failing after 304h42m16s
CI/CD Pipeline / Validate - Style (pull_request) Failing after 304h42m17s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 304h42m24s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 304h42m29s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 305h14m18s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 305h14m22s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 305h14m23s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 305h16m38s
CI/CD Pipeline / PR Build Web Image (push) Failing after 305h16m46s

This commit was merged in pull request #1671.
This commit is contained in:
2026-09-03 22:42:03 +08:00
4 changed files with 431 additions and 9 deletions
@@ -131,3 +131,65 @@ class SQLAlchemyEditPlanClipRepository:
created_at=model.created_at,
updated_at=model.updated_at,
)
def list_used_segments_by_user(
self,
user_id: str,
*,
limit_recent: int = 50,
) -> dict[str, list[tuple[float, float]]]:
"""查询用户已有视频中已使用的素材区间(跨视频避让).
JOIN edit_plans 表,按 created_by_user_id 过滤,只查 status='completed'
的 plan 下 status='rendered' 且 asset_id 非空的 clips。按 plan 的
created_at DESC 取最近 limit_recent 个 plan。
Returns:
{asset_id: [(start_time, start_time + duration), ...]}
空结果返回空 dict。
"""
from packages.adapters.sqlalchemy_impl.models import EditPlanModel
if not user_id:
return {}
# 1. 查出最近 limit_recent 个已完成 plan 的 ID
recent_plan_ids = [
row[0]
for row in self.session.query(EditPlanModel.id)
.filter(
EditPlanModel.created_by_user_id == user_id,
EditPlanModel.status == "completed",
)
.order_by(EditPlanModel.created_at.desc())
.limit(limit_recent)
.all()
]
if not recent_plan_ids:
return {}
# 2. 查这些 plan 下已渲染、有素材的 clips
clips = (
self.session.query(
EditPlanClipModel.asset_id,
EditPlanClipModel.start_time,
EditPlanClipModel.duration,
)
.filter(
EditPlanClipModel.plan_id.in_(recent_plan_ids),
EditPlanClipModel.status == "rendered",
EditPlanClipModel.asset_id != "",
EditPlanClipModel.asset_id.isnot(None),
)
.all()
)
# 3. 聚合为 {asset_id: [(start, start+duration), ...]}
result: dict[str, list[tuple[float, float]]] = {}
for asset_id, start_time, duration in clips:
if asset_id not in result:
result[asset_id] = []
result[asset_id].append((start_time or 0.0, (start_time or 0.0) + (duration or 0.0)))
return result