fix: resolve CI lint failures (black/isort/flake8 E741)
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Successful in 1m24s
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 1m25s
CI/CD Pipeline / Frontend Lint (push) Successful in 1m57s
CI/CD Pipeline / Build Production Runtime Images (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 2m8s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Build Production Runtime Images (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (push) Successful in 3m17s
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped

- Fix flake8 E741 ambiguous variable name 'l' in unified_render_service.py and test_unified_render_service.py
- Apply black formatting to 4 files
- Apply isort import sorting to 8 files
This commit is contained in:
用户CI Test
2026-07-09 22:44:26 +08:00
parent 9c99c9ea96
commit 015fbb6d96
9 changed files with 86 additions and 83 deletions
+3 -6
View File
@@ -2,13 +2,10 @@
视频处理模块
"""
from .processor import VideoProcessor, VideoResult
# 共享工具模块(供 editing_modes / generation / edit_plan_generation 等复用)
from . import ffmpeg_utils
from . import oss_helpers
from . import dedup_helpers
from .unified_render_service import UnifiedRenderService, RenderResult
from . import dedup_helpers, ffmpeg_utils, oss_helpers
from .processor import VideoProcessor, VideoResult
from .unified_render_service import RenderResult, UnifiedRenderService
__all__ = [
"VideoProcessor",
@@ -91,9 +91,7 @@ def create_video_record_and_dedup(
# (b) 批次内查重(仅当有 batch_id 时)
if not duplicate_result and batch_id:
duplicate_result = deduplicator.check_batch_duplicate(
fingerprint, batch_id, video_id, session
)
duplicate_result = deduplicator.check_batch_duplicate(fingerprint, batch_id, video_id, session)
if duplicate_result:
generated_video.is_duplicate = True
@@ -21,7 +21,7 @@ else:
from pathlib import Path
from typing import Optional
from video_processing.ffmpeg_utils import FFMPEG_BIN, run_ffmpeg, probe_video_info
from video_processing.ffmpeg_utils import FFMPEG_BIN, probe_video_info, run_ffmpeg
logger = logging.getLogger(__name__)
@@ -28,11 +28,11 @@ from pathlib import Path
from typing import Any
from video_processing.ffmpeg_utils import (
FFMPEG_BIN,
DEFAULT_OUTPUT_WIDTH,
DEFAULT_OUTPUT_HEIGHT,
DEFAULT_FPS,
DEFAULT_OUTPUT_HEIGHT,
DEFAULT_OUTPUT_WIDTH,
DEFAULT_TRANSITION_DURATION,
FFMPEG_BIN,
build_xfade_filter_chain,
probe_duration,
probe_video_info,
@@ -273,7 +273,7 @@ class UnifiedRenderService:
)
# 按 z_index 排序返回
layers = sorted(layer_map.values(), key=lambda l: l.z_index)
layers = sorted(layer_map.values(), key=lambda lyr: lyr.z_index)
return layers
def _build_filter_complex(self, layers: list[RenderLayer]) -> tuple[str, list[str]]:
@@ -30,13 +30,12 @@ OUTPUT_FPS = 25.0
# ── 共享工具模块导入 ──────────────────────────────────────────────────────────
from video_processing.dedup_helpers import create_video_record_and_dedup
from video_processing.oss_helpers import (
download_asset,
upload_to_oss,
)
from video_processing.unified_render_service import UnifiedRenderService
from video_processing.dedup_helpers import create_video_record_and_dedup
# ── Repository imports (延迟导入避免循环依赖) ─────────────────────────────────
+56 -45
View File
@@ -85,16 +85,15 @@ def _update_task_status(task_id: str, status_action: str, **kwargs) -> bool:
# ── 共享工具模块导入 ──────────────────────────────────────────────────────────
from video_processing.ffmpeg_utils import FFMPEG_BIN, run_ffmpeg, probe_duration
from video_processing.dedup_helpers import create_video_record_and_dedup
from video_processing.ffmpeg_utils import FFMPEG_BIN, probe_duration, run_ffmpeg
from video_processing.oss_helpers import (
download_asset,
oss_bucket,
upload_to_oss,
)
from video_processing.dedup_helpers import create_video_record_and_dedup
from video_processing.unified_render_service import UnifiedRenderService
# ── 虚拟 Plan / Clip(内存中构建,不写数据库) ────────────────────────────────
@@ -156,24 +155,28 @@ def _build_plan_and_clips_from_task(
# 1 main + N-1 overlay
for i, p in enumerate(downloaded_paths):
clip_type = "main" if i == 0 else "overlay"
clips.append(_VirtualClip(
id=f"vc_{i:03d}",
plan_id=task_id,
clip_type=clip_type,
order=i,
asset_id=path_to_asset_id[p],
))
clips.append(
_VirtualClip(
id=f"vc_{i:03d}",
plan_id=task_id,
clip_type=clip_type,
order=i,
asset_id=path_to_asset_id[p],
)
)
elif mode == "voice_over":
# N 个 main(config.role=b_roll)
for i, p in enumerate(downloaded_paths):
clips.append(_VirtualClip(
id=f"vc_{i:03d}",
plan_id=task_id,
clip_type="main",
order=i,
asset_id=path_to_asset_id[p],
config={"role": "b_roll"},
))
clips.append(
_VirtualClip(
id=f"vc_{i:03d}",
plan_id=task_id,
clip_type="main",
order=i,
asset_id=path_to_asset_id[p],
config={"role": "b_roll"},
)
)
elif mode == "voice_pip":
# 1 background + 1 corner_voice + N-2 b_roll
for i, p in enumerate(downloaded_paths):
@@ -183,23 +186,27 @@ def _build_plan_and_clips_from_task(
clip_type = "corner_voice"
else:
clip_type = "b_roll"
clips.append(_VirtualClip(
id=f"vc_{i:03d}",
plan_id=task_id,
clip_type=clip_type,
order=i,
asset_id=path_to_asset_id[p],
))
clips.append(
_VirtualClip(
id=f"vc_{i:03d}",
plan_id=task_id,
clip_type=clip_type,
order=i,
asset_id=path_to_asset_id[p],
)
)
else:
# ONE_TAKE (default): N 个 main clips
for i, p in enumerate(downloaded_paths):
clips.append(_VirtualClip(
id=f"vc_{i:03d}",
plan_id=task_id,
clip_type="main",
order=i,
asset_id=path_to_asset_id[p],
))
clips.append(
_VirtualClip(
id=f"vc_{i:03d}",
plan_id=task_id,
clip_type="main",
order=i,
asset_id=path_to_asset_id[p],
)
)
return plan, clips, asset_path_map
@@ -237,15 +244,23 @@ def _mux_audio_track(video_path: Path, audio_path: str, output_path: Path) -> No
command = [
FFMPEG_BIN,
"-y",
"-i", str(video_path),
"-i", audio_path,
"-c:v", "copy",
"-c:a", "aac",
"-b:a", "192k",
"-i",
str(video_path),
"-i",
audio_path,
"-c:v",
"copy",
"-c:a",
"aac",
"-b:a",
"192k",
"-shortest",
"-map", "0:v:0",
"-map", "1:a:0",
"-movflags", "+faststart",
"-map",
"0:v:0",
"-map",
"1:a:0",
"-movflags",
"+faststart",
str(output_path),
]
run_ffmpeg(command)
@@ -380,9 +395,7 @@ def generate_video(self, task_id: str) -> dict:
output_path = temp_path / output_name
# 1. 从素材库下载视频素材
downloaded_videos = _download_library_assets(
asset_library_id, temp_path, asset_ids=task_asset_ids or None
)
downloaded_videos = _download_library_assets(asset_library_id, temp_path, asset_ids=task_asset_ids or None)
# 2. 下载配音(如有)
audio_path: str | None = None
@@ -485,5 +498,3 @@ def generate_video(self, task_id: str) -> dict:
"task_id": task_id,
"error": str(error),
}
@@ -14,13 +14,12 @@ import tempfile
from pathlib import Path
import pytest
from worker_app.tasks.generation import _build_plan_and_clips_from_task
from video_processing.unified_render_service import (
RenderResult,
UnifiedRenderService,
_resolve_layer_role,
)
from worker_app.tasks.generation import _build_plan_and_clips_from_task
pytestmark = pytest.mark.skipif(
not shutil.which("ffmpeg"),
+7 -8
View File
@@ -9,20 +9,19 @@ import shutil
import subprocess
import tempfile
from pathlib import Path
from unittest.mock import patch, MagicMock
from unittest.mock import MagicMock, patch
import pytest
from video_processing.unified_render_service import (
RenderResult,
UnifiedRenderService,
)
from worker_app.tasks.generation import (
_build_plan_and_clips_from_task,
_mux_audio_track,
_create_fallback_clip,
OUTPUT_WIDTH,
OUTPUT_HEIGHT,
OUTPUT_WIDTH,
_build_plan_and_clips_from_task,
_create_fallback_clip,
_mux_audio_track,
)
pytestmark = pytest.mark.skipif(
@@ -215,11 +214,11 @@ class TestFullPipeline:
def test_dedup_helper_integration(self):
"""验证 dedup_helpers.create_video_record_and_dedup 的导入和签名。"""
from video_processing.dedup_helpers import create_video_record_and_dedup
# 只验证函数存在且签名正确(不实际调用,需要数据库)
import inspect
from video_processing.dedup_helpers import create_video_record_and_dedup
sig = inspect.signature(create_video_record_and_dedup)
params = set(sig.parameters.keys())
expected = {
+12 -12
View File
@@ -8,19 +8,17 @@ from __future__ import annotations
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any
from unittest.mock import patch, MagicMock
from unittest.mock import MagicMock, patch
import pytest
from video_processing.unified_render_service import (
ResolvedClip,
RenderLayer,
RenderResult,
ResolvedClip,
UnifiedRenderService,
_resolve_layer_role,
)
# ── Fixtures ──────────────────────────────────────────────────────────────────
@@ -167,12 +165,12 @@ class TestGroupClipsIntoLayers:
resolved = svc._resolve_clips()
layers = svc._group_clips_into_layers(resolved)
roles = {l.role for l in layers}
roles = {lyr.role for lyr in layers}
assert "main" in roles
assert "overlay" in roles
main_layer = next(l for l in layers if l.role == "main")
overlay_layer = next(l for l in layers if l.role == "overlay")
main_layer = next(lyr for lyr in layers if lyr.role == "main")
overlay_layer = next(lyr for lyr in layers if lyr.role == "overlay")
assert len(main_layer.clips) == 1
assert len(overlay_layer.clips) == 2
assert overlay_layer.z_index > main_layer.z_index
@@ -206,7 +204,7 @@ class TestGroupClipsIntoLayers:
resolved = svc._resolve_clips()
layers = svc._group_clips_into_layers(resolved)
roles = {l.role for l in layers}
roles = {lyr.role for lyr in layers}
assert roles == {"background", "corner_voice", "broll"}
assert len(layers) == 3
@@ -390,10 +388,12 @@ class TestRender:
asset_paths = {"asset_c1.mp4": Path("/tmp/asset_c1.mp4")}
svc = _make_service(clips, asset_paths)
with _patch_path_exists(), \
patch("video_processing.unified_render_service.probe_duration", return_value=5.0), \
patch.object(svc, "_execute_ffmpeg") as mock_exec, \
patch.object(svc, "_probe_output", return_value=(5.0, 1024, 1280, 720)):
with (
_patch_path_exists(),
patch("video_processing.unified_render_service.probe_duration", return_value=5.0),
patch.object(svc, "_execute_ffmpeg") as mock_exec,
patch.object(svc, "_probe_output", return_value=(5.0, 1024, 1280, 720)),
):
result = svc.render()
assert isinstance(result, RenderResult)