6aec0ec6f4
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Successful in 38s
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 2m44s
CI/CD Pipeline / Unit Tests (push) Successful in 2m48s
CI/CD Pipeline / Integration Tests (push) Successful in 1m22s
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m32s
CI Build & Deploy Pipeline / Build Staging API Image (push) Successful in 5m57s
CI Build & Deploy Pipeline / Build Staging Web Image (push) Successful in 29s
CI Build & Deploy Pipeline / Build Staging Worker Image (push) Successful in 7m41s
CI Build & Deploy Pipeline / Build Production API Image (push) Has been skipped
CI Build & Deploy Pipeline / Build Production Web Image (push) Has been skipped
CI Build & Deploy Pipeline / Build Production Worker Image (push) Has been skipped
CI Build & Deploy Pipeline / Deploy Production (push) Has been skipped
CI Build & Deploy Pipeline / Production Browser E2E (push) Has been skipped
CI Build & Deploy Pipeline / Staging E2E Tests (push) Successful in 2m3s
CI Build & Deploy Pipeline / Staging API Integration Tests (push) Successful in 3m13s
feat: 转场特效 - 预设库 + 单片段设置 + 批量应用 (#398)
307 lines
9.3 KiB
Python
Executable File
307 lines
9.3 KiB
Python
Executable File
"""转场特效预设库.
|
|
|
|
视频片段之间的转场效果,基于 FFmpeg xfade 滤镜实现。
|
|
所有转场预设包含时长范围和默认参数。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import List, Optional
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class TransitionPreset:
|
|
"""转场特效预设"""
|
|
|
|
id: str
|
|
name: str
|
|
category: str # 分类:basic / fade / slide / zoom / warp / special
|
|
description: str = ""
|
|
tags: List[str] = field(default_factory=list)
|
|
# FFmpeg xfade transition 名称
|
|
transition: str = "fade"
|
|
# 默认时长(秒)
|
|
default_duration: float = 0.5
|
|
# 支持的时长范围
|
|
min_duration: float = 0.1
|
|
max_duration: float = 3.0
|
|
# 是否需要额外参数
|
|
has_custom_params: bool = False
|
|
|
|
|
|
# ── 预设库清单 ────────────────────────────────────────────────────────────────
|
|
|
|
TRANSITION_PRESET_LIBRARY: List[TransitionPreset] = [
|
|
# ── 基础 basic ────────────────────────────────────────────────────
|
|
TransitionPreset(
|
|
id="transition_none",
|
|
name="无转场",
|
|
category="basic",
|
|
description="硬切,无过渡效果",
|
|
tags=["硬切", "无"],
|
|
transition="none",
|
|
default_duration=0.0,
|
|
min_duration=0.0,
|
|
max_duration=0.0,
|
|
),
|
|
TransitionPreset(
|
|
id="transition_random",
|
|
name="随机",
|
|
category="basic",
|
|
description="随机选择一个转场效果",
|
|
tags=["随机", "惊喜"],
|
|
transition="random",
|
|
default_duration=0.5,
|
|
),
|
|
# ── 淡入淡出 fade ────────────────────────────────────────────────
|
|
TransitionPreset(
|
|
id="transition_fade",
|
|
name="淡入淡出",
|
|
category="fade",
|
|
description="经典交叉淡入淡出",
|
|
tags=["经典", "柔和"],
|
|
transition="fade",
|
|
default_duration=0.5,
|
|
min_duration=0.2,
|
|
max_duration=2.0,
|
|
),
|
|
TransitionPreset(
|
|
id="transition_fadeblack",
|
|
name="黑场过渡",
|
|
category="fade",
|
|
description="经过黑色画面过渡",
|
|
tags=["黑场", "电影感"],
|
|
transition="fadeblack",
|
|
default_duration=0.6,
|
|
min_duration=0.2,
|
|
max_duration=2.0,
|
|
),
|
|
TransitionPreset(
|
|
id="transition_fadewhite",
|
|
name="白场过渡",
|
|
category="fade",
|
|
description="经过白色画面过渡",
|
|
tags=["白场", "梦幻"],
|
|
transition="fadewhite",
|
|
default_duration=0.6,
|
|
min_duration=0.2,
|
|
max_duration=2.0,
|
|
),
|
|
TransitionPreset(
|
|
id="transition_fadegrays",
|
|
name="灰度过渡",
|
|
category="fade",
|
|
description="经过灰度画面过渡",
|
|
tags=["灰度", "文艺"],
|
|
transition="fadegrays",
|
|
default_duration=0.6,
|
|
min_duration=0.2,
|
|
max_duration=2.0,
|
|
),
|
|
# ── 滑动 slide ──────────────────────────────────────────────────
|
|
TransitionPreset(
|
|
id="transition_slideleft",
|
|
name="左滑",
|
|
category="slide",
|
|
description="画面向左滑动",
|
|
tags=["滑动", "左"],
|
|
transition="slideleft",
|
|
default_duration=0.5,
|
|
min_duration=0.2,
|
|
max_duration=1.5,
|
|
),
|
|
TransitionPreset(
|
|
id="transition_slideright",
|
|
name="右滑",
|
|
category="slide",
|
|
description="画面向右滑动",
|
|
tags=["滑动", "右"],
|
|
transition="slideright",
|
|
default_duration=0.5,
|
|
min_duration=0.2,
|
|
max_duration=1.5,
|
|
),
|
|
TransitionPreset(
|
|
id="transition_slideup",
|
|
name="上滑",
|
|
category="slide",
|
|
description="画面向上滑动",
|
|
tags=["滑动", "上"],
|
|
transition="slideup",
|
|
default_duration=0.5,
|
|
min_duration=0.2,
|
|
max_duration=1.5,
|
|
),
|
|
TransitionPreset(
|
|
id="transition_slidedown",
|
|
name="下滑",
|
|
category="slide",
|
|
description="画面向下滑动",
|
|
tags=["滑动", "下"],
|
|
transition="slidedown",
|
|
default_duration=0.5,
|
|
min_duration=0.2,
|
|
max_duration=1.5,
|
|
),
|
|
# ── 缩放 zoom ───────────────────────────────────────────────────
|
|
TransitionPreset(
|
|
id="transition_zoomin",
|
|
name="放大进入",
|
|
category="zoom",
|
|
description="下一段画面从中心放大出现",
|
|
tags=["放大", "冲击"],
|
|
transition="zoomin",
|
|
default_duration=0.5,
|
|
min_duration=0.2,
|
|
max_duration=1.5,
|
|
),
|
|
TransitionPreset(
|
|
id="transition_zoomout",
|
|
name="缩小退出",
|
|
category="zoom",
|
|
description="当前画面缩小退出",
|
|
tags=["缩小", "拉远"],
|
|
transition="zoomout",
|
|
default_duration=0.5,
|
|
min_duration=0.2,
|
|
max_duration=1.5,
|
|
),
|
|
# ── 擦除 warp ───────────────────────────────────────────────────
|
|
TransitionPreset(
|
|
id="transition_dissolve",
|
|
name="溶解",
|
|
category="warp",
|
|
description="像素溶解效果",
|
|
tags=["溶解", "像素"],
|
|
transition="dissolve",
|
|
default_duration=0.8,
|
|
min_duration=0.3,
|
|
max_duration=2.0,
|
|
),
|
|
TransitionPreset(
|
|
id="transition_wipeleft",
|
|
name="左擦除",
|
|
category="warp",
|
|
description="从右向左擦除",
|
|
tags=["擦除", "左"],
|
|
transition="wipeleft",
|
|
default_duration=0.5,
|
|
min_duration=0.2,
|
|
max_duration=1.5,
|
|
),
|
|
TransitionPreset(
|
|
id="transition_wiperight",
|
|
name="右擦除",
|
|
category="warp",
|
|
description="从左向右擦除",
|
|
tags=["擦除", "右"],
|
|
transition="wiperight",
|
|
default_duration=0.5,
|
|
min_duration=0.2,
|
|
max_duration=1.5,
|
|
),
|
|
TransitionPreset(
|
|
id="transition_wipeup",
|
|
name="上擦除",
|
|
category="warp",
|
|
description="从下向上擦除",
|
|
tags=["擦除", "上"],
|
|
transition="wipeup",
|
|
default_duration=0.5,
|
|
min_duration=0.2,
|
|
max_duration=1.5,
|
|
),
|
|
TransitionPreset(
|
|
id="transition_wipedown",
|
|
name="下擦除",
|
|
category="warp",
|
|
description="从上向下擦除",
|
|
tags=["擦除", "下"],
|
|
transition="wipedown",
|
|
default_duration=0.5,
|
|
min_duration=0.2,
|
|
max_duration=1.5,
|
|
),
|
|
TransitionPreset(
|
|
id="transition_circlecrop",
|
|
name="圆形展开",
|
|
category="warp",
|
|
description="圆形从中心展开",
|
|
tags=["圆形", "展开"],
|
|
transition="circlecrop",
|
|
default_duration=0.6,
|
|
min_duration=0.2,
|
|
max_duration=1.5,
|
|
),
|
|
# ── 特效 special ───────────────────────────────────────────────
|
|
TransitionPreset(
|
|
id="transition_hblur",
|
|
name="水平模糊",
|
|
category="special",
|
|
description="水平方向模糊过渡",
|
|
tags=["模糊", "水平"],
|
|
transition="hblur",
|
|
default_duration=0.5,
|
|
min_duration=0.2,
|
|
max_duration=1.5,
|
|
),
|
|
TransitionPreset(
|
|
id="transition_wipeblur",
|
|
name="模糊擦除",
|
|
category="special",
|
|
description="带模糊效果的擦除",
|
|
tags=["模糊", "擦除"],
|
|
transition="wipeblur",
|
|
default_duration=0.6,
|
|
min_duration=0.2,
|
|
max_duration=1.5,
|
|
),
|
|
]
|
|
|
|
|
|
# ── 工具函数 ──────────────────────────────────────────────────────────────────
|
|
|
|
|
|
def get_transition_preset(preset_id: str) -> Optional[TransitionPreset]:
|
|
"""根据 ID 获取转场预设"""
|
|
for p in TRANSITION_PRESET_LIBRARY:
|
|
if p.id == preset_id:
|
|
return p
|
|
return None
|
|
|
|
|
|
def list_transition_presets(
|
|
*,
|
|
category: Optional[str] = None,
|
|
keyword: Optional[str] = None,
|
|
) -> List[TransitionPreset]:
|
|
"""筛选转场预设列表
|
|
|
|
Args:
|
|
category: 按分类筛选
|
|
keyword: 关键词搜索
|
|
|
|
Returns:
|
|
筛选后的预设列表
|
|
"""
|
|
results = TRANSITION_PRESET_LIBRARY
|
|
|
|
if category:
|
|
results = [p for p in results if p.category == category]
|
|
|
|
if keyword:
|
|
kw = keyword.lower()
|
|
results = [
|
|
p
|
|
for p in results
|
|
if kw in p.name.lower() or kw in p.description.lower() or any(kw in t.lower() for t in p.tags)
|
|
]
|
|
|
|
return results
|
|
|
|
|
|
def get_default_transition() -> TransitionPreset:
|
|
"""获取默认转场(无转场/硬切)"""
|
|
return get_transition_preset("transition_none") # type: ignore
|