Files
xiaoxia-saas/apps/worker/video_processing/speed_engine.py
T
CI Bot 107f2264a7
ACR Cleanup / ACR Image Cleanup (pull_request_target) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 15s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 39s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m35s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m27s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 2m56s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m30s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 7s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m0s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 3m21s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m2s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 3m40s
AI Code Review / AI Code Review (pull_request) Successful in 3m48s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 46m0s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 6m19s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 0s
CI/CD Pipeline / CI Gate (pull_request) Failing after 1s
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1218h24m43s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 1218h28m2s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1218h28m6s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1218h29m59s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1219h34m24s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1219h43m28s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1219h43m32s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 1219h43m34s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1221h1m19s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1221h1m21s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1221h1m23s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1219h0m52s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1220h16m17s
test(wave117): 抽离speed_config领域模型 + 57单测
- 抽离SpeedConfig数据类、atempo多级拆分、视频/音频滤镜构建、时长计算到packages/domain/speed_config.py
- speed_engine.py: 167→74行 (-56%),保留SpeedEngine薄包装类+模块级向后兼容导入
- 57个纯逻辑单测全绿,原有100个测试无回归
- 合计157 passed
2026-07-27 18:06:38 +08:00

75 lines
2.4 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""视频调速引擎 — 基于 FFmpeg setpts + atempo 的速度调整能力.
支持:
- 0.25x ~ 4x 变速范围
- 视频调速(setpts)
- 音频调速(atempo,多级串联处理超范围值)
- 音调修正(pitch_correct,默认开启)
- 边界自动钳制,不阻断渲染
注:核心领域模型已抽离到 packages/domain/speed_config.py,
本模块保留薄包装层,确保向后兼容。
"""
from __future__ import annotations
from typing import Optional
from packages.domain.speed_config import ( # noqa: F401 — 向后兼容
DEFAULT_SPEED,
MAX_SPEED,
MIN_SPEED,
SpeedConfig,
_split_atempo_stages,
adjust_duration as _adjust_duration_base,
build_audio_filter as _build_audio_filter_base,
build_video_filter as _build_video_filter_base,
resolve_clip_speed as _resolve_clip_speed_base,
)
class SpeedEngine:
"""调速引擎 — 生成 FFmpeg 调速滤镜链.
薄包装层,实际逻辑委托给 packages.domain.speed_config。
"""
def build_video_filter(self, config: SpeedConfig) -> str:
"""生成视频调速滤镜字符串."""
return _build_video_filter_base(config)
def build_audio_filter(self, config: SpeedConfig) -> str:
"""生成音频调速滤镜字符串."""
return _build_audio_filter_base(config)
@staticmethod
def _split_atempo_stages(speed: float) -> list[float]:
"""将速度拆分为多级 atempo 串联(内部方法,向后兼容)."""
return _split_atempo_stages(speed)
def adjust_duration(self, original_duration: float, config: SpeedConfig) -> float:
"""计算调速后的时长."""
return _adjust_duration_base(original_duration, config)
def build_clip_speed_filter(
self,
speed: float,
pitch_correct: bool = True,
) -> tuple[str, str, SpeedConfig]:
"""便捷方法:从单一 speed 值生成视频+音频滤镜."""
config = SpeedConfig(speed=speed, pitch_correct=pitch_correct)
config.clamp()
return (
self.build_video_filter(config),
self.build_audio_filter(config),
config,
)
@staticmethod
def resolve_clip_speed(
clip_config: dict,
global_speed: float = DEFAULT_SPEED,
) -> float:
"""从 clip config 中解析 playback_speed,0 或缺失则使用全局速度."""
return _resolve_clip_speed_base(clip_config, global_speed)