Files
xiaoxia-saas/packages/domain/asset_atom_clip.py
T
xiaoxia f1621ace9f
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (push) Successful in 5s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 2m17s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m26s
CI/CD Pipeline / Integration Tests (push) Successful in 3m48s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 4m13s
CI/CD Pipeline / Build Staging API Image (push) Successful in 4m44s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 5m19s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 59s
CI/CD Pipeline / Validate - Style (push) Successful in 7m51s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 2m54s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m44s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 3m58s
CI/CD Pipeline / Unit Tests (push) Successful in 10m42s
CI/CD Pipeline / Validate - Security (push) Successful in 12m25s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
feat(#1970): 素材原子化切片 P1 - 数据层/切片逻辑/原子片段级选片 (#1974)
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-18 03:57:07 +08:00

82 lines
2.9 KiB
Python

"""素材原子片段(Atom Clip)领域实体 — #1970 智能剪辑流程重构。
原子片段是素材的逻辑切分单元,不物理切割视频文件。
每条记录指向某条素材的一段 [start_time, end_time] 区间。
"""
from __future__ import annotations
import uuid
from dataclasses import dataclass, field
from datetime import UTC, datetime
@dataclass
class AssetAtomClip:
"""素材原子片段。
Attributes:
id: 唯一标识。
asset_id: 所属素材 ID。
start_time: 片段起始时间(秒,浮点)。
end_time: 片段结束时间(秒,浮点)。
duration: 片段时长 = end_time - start_time(秒)。
clip_index: 在同一素材内的顺序编号(从 0 开始)。
tags: 继承自素材的标签,JSONB 存储,可为空列表。
scene_change_at: 片段尾部是否对齐了 scdet 镜头切换点(存储该切点的精确时间),
未对齐时为 None。
is_fallback: 是否为兜底逻辑在内存中生成的临时片段(不入库)。
created_at: 创建时间。
"""
id: str
asset_id: str
start_time: float
end_time: float
duration: float
clip_index: int
tags: list[str] = field(default_factory=list)
scene_change_at: float | None = None
is_fallback: bool = False
created_at: datetime | None = None
def __post_init__(self):
if not self.id:
self.id = str(uuid.uuid4())
if self.duration <= 0:
self.duration = round(self.end_time - self.start_time, 3)
if self.duration < 0:
raise ValueError(f"duration must be >= 0, got start={self.start_time}, end={self.end_time}")
if self.start_time < 0:
raise ValueError(f"start_time must be >= 0, got {self.start_time}")
if self.end_time <= self.start_time:
raise ValueError(f"end_time must be > start_time, got start={self.start_time}, end={self.end_time}")
if self.clip_index < 0:
raise ValueError(f"clip_index must be >= 0, got {self.clip_index}")
if self.created_at is None:
self.created_at = datetime.now(UTC)
@classmethod
def create(
cls,
asset_id: str,
start_time: float,
end_time: float,
clip_index: int,
tags: list[str] | None = None,
scene_change_at: float | None = None,
is_fallback: bool = False,
) -> AssetAtomClip:
"""工厂方法:创建一个新的原子片段。"""
return cls(
id="", # __post_init__ 会自动生成
asset_id=asset_id,
start_time=round(start_time, 3),
end_time=round(end_time, 3),
duration=round(end_time - start_time, 3),
clip_index=clip_index,
tags=tags or [],
scene_change_at=scene_change_at,
is_fallback=is_fallback,
)