This commit is contained in:
2025-09-24 11:28:42 +08:00
parent ec1705769c
commit 873c89c778
9 changed files with 100 additions and 39 deletions

View File

@@ -21,11 +21,11 @@ class FFmpegConfig:
# 新增配置选项,消除硬编码 # 新增配置选项,消除硬编码
max_download_workers: int = 8 max_download_workers: int = 8
progress_args: List[str] = None progress_args: Optional[List[str]] = None
loglevel_args: List[str] = None loglevel_args: Optional[List[str]] = None
null_audio_args: List[str] = None null_audio_args: Optional[List[str]] = None
overlay_scale_mode: str = "scale2ref" # 新版本使用scale2ref,旧版本使用scale overlay_scale_mode: str = "scale2ref" # 新版本使用scale2ref,旧版本使用scale
amix_args: List[str] = None amix_args: Optional[List[str]] = None
@classmethod @classmethod
def from_env(cls) -> "FFmpegConfig": def from_env(cls) -> "FFmpegConfig":
@@ -35,12 +35,14 @@ class FFmpegConfig:
default_args = ["-shortest"] default_args = ["-shortest"]
re_encode_video_args = None re_encode_video_args = None
if os.getenv("RE_ENCODE_VIDEO_ARGS"): re_encode_video_env = os.getenv("RE_ENCODE_VIDEO_ARGS")
re_encode_video_args = os.getenv("RE_ENCODE_VIDEO_ARGS").split(" ") if re_encode_video_env:
re_encode_video_args = re_encode_video_env.split(" ")
re_encode_encoder_args = None re_encode_encoder_args = None
if os.getenv("RE_ENCODE_ENCODER_ARGS"): re_encode_encoder_env = os.getenv("RE_ENCODE_ENCODER_ARGS")
re_encode_encoder_args = os.getenv("RE_ENCODE_ENCODER_ARGS").split(" ") if re_encode_encoder_env:
re_encode_encoder_args = re_encode_encoder_env.split(" ")
# 新增配置项的默认值 # 新增配置项的默认值
progress_args = ["-progress", "-"] progress_args = ["-progress", "-"]

View File

@@ -21,7 +21,7 @@ template_service = get_template_service()
if "redownload" in sys.argv: if "redownload" in sys.argv:
print("Redownloading all templates...") print("Redownloading all templates...")
try: try:
for template_name in template_service.templates.keys(): for template_name in template_service.get_all_templates().keys():
print(f"Redownloading template: {template_name}") print(f"Redownloading template: {template_name}")
if not template_service.download_template(template_name): if not template_service.download_template(template_name):
print(f"Failed to download template: {template_name}") print(f"Failed to download template: {template_name}")

38
mypy.ini Normal file
View File

@@ -0,0 +1,38 @@
[mypy]
python_version = 3.9
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = False
disallow_incomplete_defs = False
check_untyped_defs = False
disallow_untyped_decorators = False
no_implicit_optional = False
warn_redundant_casts = False
warn_unused_ignores = False
warn_no_return = False
warn_unreachable = False
strict_equality = False
namespace_packages = True
explicit_package_bases = True
# Exclude duplicate modules and legacy code
exclude = biz/ffmpeg\.py
# Ignore missing type annotations for third-party libraries
[mypy-requests.*]
ignore_missing_imports = True
[mypy-flask.*]
ignore_missing_imports = True
[mypy-pytest.*]
ignore_missing_imports = True
[mypy-PIL.*]
ignore_missing_imports = True
[mypy-psutil.*]
ignore_missing_imports = True
[mypy-opentelemetry.*]
ignore_missing_imports = True

View File

@@ -19,7 +19,7 @@ def _get_template_service():
return _template_service return _template_service
# 向后兼容的全局变量和函数 # 向后兼容的全局变量和函数
TEMPLATES = {} TEMPLATES: dict = {}
def _update_templates_dict(): def _update_templates_dict():
"""更新全局TEMPLATES字典以保持向后兼容""" """更新全局TEMPLATES字典以保持向后兼容"""

View File

@@ -23,8 +23,10 @@ def temp_dir():
def test_ffmpeg_config(): def test_ffmpeg_config():
"""测试用FFmpeg配置""" """测试用FFmpeg配置"""
return FFmpegConfig( return FFmpegConfig(
encoder_args="-c:v h264", encoder_args=["-c:v", "h264"],
video_args="", video_args=["-profile:v", "high"],
audio_args=["-c:a", "aac"],
default_args=["-shortest"],
) )
@@ -49,9 +51,8 @@ def test_storage_config():
def sample_render_task(): def sample_render_task():
"""示例渲染任务""" """示例渲染任务"""
return RenderTask( return RenderTask(
task_id="test_task_001", input_files=["test_input.mp4"],
template_id="test_template", output_file="test_output.mp4",
output_path="test_output.mp4",
frame_rate=25, frame_rate=25,
effects=["zoom:0,2.0,3.0", "ospeed:1.5"], effects=["zoom:0,2.0,3.0", "ospeed:1.5"],
ext_data={ ext_data={

View File

@@ -96,7 +96,7 @@ class TestFFmpegCommandBuilder:
task.ext_data = {"posJson": "{}"} task.ext_data = {"posJson": "{}"}
builder = FFmpegCommandBuilder(task) builder = FFmpegCommandBuilder(task)
filter_args = [] filter_args: list[str] = []
result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1) result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1)
@@ -112,7 +112,7 @@ class TestFFmpegCommandBuilder:
task.ext_data = {"posJson": "{}"} task.ext_data = {"posJson": "{}"}
builder = FFmpegCommandBuilder(task) builder = FFmpegCommandBuilder(task)
filter_args = [] filter_args: list[str] = []
result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1) result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1)
@@ -127,7 +127,7 @@ class TestFFmpegCommandBuilder:
task.effects = ["invalid_effect:params"] task.effects = ["invalid_effect:params"]
builder = FFmpegCommandBuilder(task) builder = FFmpegCommandBuilder(task)
filter_args = [] filter_args: list[str] = []
result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1) result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1)
@@ -141,7 +141,7 @@ class TestFFmpegCommandBuilder:
task.effects = [] task.effects = []
builder = FFmpegCommandBuilder(task) builder = FFmpegCommandBuilder(task)
filter_args = [] filter_args: list[str] = []
result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1) result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1)
@@ -210,7 +210,7 @@ class TestFFmpegCommandBuilder:
task.frame_rate = 30 task.frame_rate = 30
builder = FFmpegCommandBuilder(task) builder = FFmpegCommandBuilder(task)
filter_args = [] filter_args: list[str] = []
builder._add_effects(filter_args, "[0:v]", 1) builder._add_effects(filter_args, "[0:v]", 1)

View File

@@ -16,21 +16,41 @@ class MockRenderTask:
def __init__( def __init__(
self, self,
task_id: str = "test_task", input_files: Optional[List[str]] = None,
template_id: str = "test_template", output_file: str = "test_output.mp4",
effects: List[str] = None, effects: Optional[List[str]] = None,
ext_data: Dict[str, Any] = None, ext_data: Optional[Dict[str, Any]] = None,
frame_rate: int = 25, frame_rate: int = 25,
output_path: str = "test_output.mp4",
): ):
self.task_id = task_id # RenderTask required fields
self.template_id = template_id self.input_files = input_files or []
self.effects = effects or [] self.output_file = output_file
self.ext_data = ext_data or {} self.task_type = "copy" # TaskType.COPY equivalent
# Optional fields that match RenderTask
self.resolution = None
self.frame_rate = frame_rate self.frame_rate = frame_rate
self.output_path = output_path self.speed = 1.0
self.input_files = [] self.mute = True
self.overlays = [] self.annexb = False
# Cut parameters
self.zoom_cut = None
self.center_cut = None
# Resource lists
self.subtitles: List[str] = []
self.luts: List[str] = []
self.audios: List[str] = []
self.overlays: List[str] = []
self.effects = effects or []
# Extension data
self.ext_data = ext_data or {}
# Legacy compatibility
self.task_id = "test_task"
self.template_id = "test_template"
self.use_center_cut = False self.use_center_cut = False
self.use_zoom_cut = False self.use_zoom_cut = False
self.audio_file = None self.audio_file = None

View File

@@ -1,7 +1,7 @@
class RenderWorkerError(Exception): class RenderWorkerError(Exception):
"""RenderWorker基础异常类""" """RenderWorker基础异常类"""
def __init__(self, message: str, error_code: str = None): def __init__(self, message: str, error_code: Optional[str] = None):
super().__init__(message) super().__init__(message)
self.message = message self.message = message
self.error_code = error_code or self.__class__.__name__ self.error_code = error_code or self.__class__.__name__
@@ -55,9 +55,9 @@ class FFmpegError(RenderError):
def __init__( def __init__(
self, self,
message: str, message: str,
command: list = None, command: Optional[list] = None,
return_code: int = None, return_code: Optional[int] = None,
stderr: str = None, stderr: Optional[str] = None,
): ):
super().__init__(message) super().__init__(message)
self.command = command self.command = command
@@ -69,7 +69,7 @@ class EffectError(RenderError):
"""效果处理错误""" """效果处理错误"""
def __init__( def __init__(
self, message: str, effect_name: str = None, effect_params: str = None self, message: str, effect_name: Optional[str] = None, effect_params: Optional[str] = None
): ):
super().__init__(message) super().__init__(message)
self.effect_name = effect_name self.effect_name = effect_name
@@ -86,7 +86,7 @@ class APIError(RenderWorkerError):
"""API调用错误""" """API调用错误"""
def __init__( def __init__(
self, message: str, status_code: int = None, response_body: str = None self, message: str, status_code: Optional[int] = None, response_body: Optional[str] = None
): ):
super().__init__(message) super().__init__(message)
self.status_code = status_code self.status_code = status_code

View File

@@ -26,7 +26,7 @@ def safe_json_loads(json_str: Union[str, bytes], default: Any = None) -> Any:
try: try:
return json.loads(json_str) return json.loads(json_str)
except (json.JSONDecodeError, TypeError) as e: except (json.JSONDecodeError, TypeError) as e:
logger.warning(f"Failed to parse JSON: {e}, input: {json_str}") logger.warning(f"Failed to parse JSON: {e}, input: {json_str!r}")
return default or {} return default or {}