"""TemplateClipConfig domain entity for Phase 8 模板编排引擎. 定义模板中每个片段的配置规则:片段类型、时长范围、文案模板、素材要求、转场效果等。 """ from __future__ import annotations import sys from dataclasses import dataclass, field from datetime import datetime, timezone if sys.version_info >= (3, 11): from enum import StrEnum else: from enum import Enum class StrEnum(str, Enum): pass from typing import Any from uuid import uuid4 class ClipType(StrEnum): """片段类型""" INTRO = "intro" # 片头 MAIN = "main" # 主体片段 TRANSITION = "transition" # 转场片段 OUTRO = "outro" # 片尾 TITLE = "title" # 标题卡 SUBTITLE = "subtitle" # 字幕卡 class TransitionEffect(StrEnum): """转场效果""" CUT = "cut" # 硬切 FADE = "fade" # 淡入淡出 SLIDE_LEFT = "slide_left" # 左滑 SLIDE_RIGHT = "slide_right" # 右滑 DISSOLVE = "dissolve" # 溶解 WIPE = "wipe" # 擦除 @dataclass(slots=True) class TemplateClipConfig: """模板片段配置 定义模板中单个片段的规则,包括类型、时长范围、文案模板、素材要求和转场效果。 属于 EditTemplate 的子实体。 """ id: str template_id: str clip_type: ClipType order: int min_duration: float = 0.0 max_duration: float = 0.0 text_template: str = "" material_requirements: dict[str, Any] = field(default_factory=dict) transition_effect: TransitionEffect = TransitionEffect.CUT config: dict[str, Any] = field(default_factory=dict) created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) updated_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) @classmethod def create( cls, template_id: str, clip_type: ClipType | str, order: int, *, min_duration: float = 0.0, max_duration: float = 0.0, text_template: str = "", material_requirements: dict[str, Any] | None = None, transition_effect: TransitionEffect | str = TransitionEffect.CUT, config: dict[str, Any] | None = None, ) -> TemplateClipConfig: """创建模板片段配置""" if not template_id.strip(): raise ValueError("template_id 不能为空") if min_duration < 0: raise ValueError("min_duration 不能为负数") if max_duration < 0: raise ValueError("max_duration 不能为负数") if max_duration > 0 and min_duration > max_duration: raise ValueError("min_duration 不能大于 max_duration") return cls( id=uuid4().hex, template_id=template_id.strip(), clip_type=ClipType(clip_type), order=order, min_duration=min_duration, max_duration=max_duration, text_template=text_template.strip(), material_requirements=material_requirements or {}, transition_effect=TransitionEffect(transition_effect), config=config or {}, ) @property def has_duration_range(self) -> bool: """是否设置了时长范围""" return self.min_duration > 0 or self.max_duration > 0 @property def default_duration(self) -> float: """默认时长(取中间值)""" if self.min_duration > 0 and self.max_duration > 0: return (self.min_duration + self.max_duration) / 2 return self.max_duration or self.min_duration or 0.0