You've already forked FrameTour-RenderWorker
u
This commit is contained in:
@@ -21,11 +21,11 @@ class FFmpegConfig:
|
||||
|
||||
# 新增配置选项,消除硬编码
|
||||
max_download_workers: int = 8
|
||||
progress_args: List[str] = None
|
||||
loglevel_args: List[str] = None
|
||||
null_audio_args: List[str] = None
|
||||
progress_args: Optional[List[str]] = None
|
||||
loglevel_args: Optional[List[str]] = None
|
||||
null_audio_args: Optional[List[str]] = None
|
||||
overlay_scale_mode: str = "scale2ref" # 新版本使用scale2ref,旧版本使用scale
|
||||
amix_args: List[str] = None
|
||||
amix_args: Optional[List[str]] = None
|
||||
|
||||
@classmethod
|
||||
def from_env(cls) -> "FFmpegConfig":
|
||||
@@ -35,12 +35,14 @@ class FFmpegConfig:
|
||||
default_args = ["-shortest"]
|
||||
|
||||
re_encode_video_args = None
|
||||
if os.getenv("RE_ENCODE_VIDEO_ARGS"):
|
||||
re_encode_video_args = os.getenv("RE_ENCODE_VIDEO_ARGS").split(" ")
|
||||
re_encode_video_env = os.getenv("RE_ENCODE_VIDEO_ARGS")
|
||||
if re_encode_video_env:
|
||||
re_encode_video_args = re_encode_video_env.split(" ")
|
||||
|
||||
re_encode_encoder_args = None
|
||||
if os.getenv("RE_ENCODE_ENCODER_ARGS"):
|
||||
re_encode_encoder_args = os.getenv("RE_ENCODE_ENCODER_ARGS").split(" ")
|
||||
re_encode_encoder_env = os.getenv("RE_ENCODE_ENCODER_ARGS")
|
||||
if re_encode_encoder_env:
|
||||
re_encode_encoder_args = re_encode_encoder_env.split(" ")
|
||||
|
||||
# 新增配置项的默认值
|
||||
progress_args = ["-progress", "-"]
|
||||
|
||||
2
index.py
2
index.py
@@ -21,7 +21,7 @@ template_service = get_template_service()
|
||||
if "redownload" in sys.argv:
|
||||
print("Redownloading all templates...")
|
||||
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}")
|
||||
if not template_service.download_template(template_name):
|
||||
print(f"Failed to download template: {template_name}")
|
||||
|
||||
38
mypy.ini
Normal file
38
mypy.ini
Normal 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
|
||||
@@ -19,7 +19,7 @@ def _get_template_service():
|
||||
return _template_service
|
||||
|
||||
# 向后兼容的全局变量和函数
|
||||
TEMPLATES = {}
|
||||
TEMPLATES: dict = {}
|
||||
|
||||
def _update_templates_dict():
|
||||
"""更新全局TEMPLATES字典以保持向后兼容"""
|
||||
|
||||
@@ -23,8 +23,10 @@ def temp_dir():
|
||||
def test_ffmpeg_config():
|
||||
"""测试用FFmpeg配置"""
|
||||
return FFmpegConfig(
|
||||
encoder_args="-c:v h264",
|
||||
video_args="",
|
||||
encoder_args=["-c:v", "h264"],
|
||||
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():
|
||||
"""示例渲染任务"""
|
||||
return RenderTask(
|
||||
task_id="test_task_001",
|
||||
template_id="test_template",
|
||||
output_path="test_output.mp4",
|
||||
input_files=["test_input.mp4"],
|
||||
output_file="test_output.mp4",
|
||||
frame_rate=25,
|
||||
effects=["zoom:0,2.0,3.0", "ospeed:1.5"],
|
||||
ext_data={
|
||||
|
||||
@@ -96,7 +96,7 @@ class TestFFmpegCommandBuilder:
|
||||
task.ext_data = {"posJson": "{}"}
|
||||
|
||||
builder = FFmpegCommandBuilder(task)
|
||||
filter_args = []
|
||||
filter_args: list[str] = []
|
||||
|
||||
result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1)
|
||||
|
||||
@@ -112,7 +112,7 @@ class TestFFmpegCommandBuilder:
|
||||
task.ext_data = {"posJson": "{}"}
|
||||
|
||||
builder = FFmpegCommandBuilder(task)
|
||||
filter_args = []
|
||||
filter_args: list[str] = []
|
||||
|
||||
result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1)
|
||||
|
||||
@@ -127,7 +127,7 @@ class TestFFmpegCommandBuilder:
|
||||
task.effects = ["invalid_effect:params"]
|
||||
|
||||
builder = FFmpegCommandBuilder(task)
|
||||
filter_args = []
|
||||
filter_args: list[str] = []
|
||||
|
||||
result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1)
|
||||
|
||||
@@ -141,7 +141,7 @@ class TestFFmpegCommandBuilder:
|
||||
task.effects = []
|
||||
|
||||
builder = FFmpegCommandBuilder(task)
|
||||
filter_args = []
|
||||
filter_args: list[str] = []
|
||||
|
||||
result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1)
|
||||
|
||||
@@ -210,7 +210,7 @@ class TestFFmpegCommandBuilder:
|
||||
task.frame_rate = 30
|
||||
|
||||
builder = FFmpegCommandBuilder(task)
|
||||
filter_args = []
|
||||
filter_args: list[str] = []
|
||||
|
||||
builder._add_effects(filter_args, "[0:v]", 1)
|
||||
|
||||
|
||||
@@ -16,21 +16,41 @@ class MockRenderTask:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
task_id: str = "test_task",
|
||||
template_id: str = "test_template",
|
||||
effects: List[str] = None,
|
||||
ext_data: Dict[str, Any] = None,
|
||||
input_files: Optional[List[str]] = None,
|
||||
output_file: str = "test_output.mp4",
|
||||
effects: Optional[List[str]] = None,
|
||||
ext_data: Optional[Dict[str, Any]] = None,
|
||||
frame_rate: int = 25,
|
||||
output_path: str = "test_output.mp4",
|
||||
):
|
||||
self.task_id = task_id
|
||||
self.template_id = template_id
|
||||
self.effects = effects or []
|
||||
self.ext_data = ext_data or {}
|
||||
# RenderTask required fields
|
||||
self.input_files = input_files or []
|
||||
self.output_file = output_file
|
||||
self.task_type = "copy" # TaskType.COPY equivalent
|
||||
|
||||
# Optional fields that match RenderTask
|
||||
self.resolution = None
|
||||
self.frame_rate = frame_rate
|
||||
self.output_path = output_path
|
||||
self.input_files = []
|
||||
self.overlays = []
|
||||
self.speed = 1.0
|
||||
self.mute = True
|
||||
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_zoom_cut = False
|
||||
self.audio_file = None
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class RenderWorkerError(Exception):
|
||||
"""RenderWorker基础异常类"""
|
||||
|
||||
def __init__(self, message: str, error_code: str = None):
|
||||
def __init__(self, message: str, error_code: Optional[str] = None):
|
||||
super().__init__(message)
|
||||
self.message = message
|
||||
self.error_code = error_code or self.__class__.__name__
|
||||
@@ -55,9 +55,9 @@ class FFmpegError(RenderError):
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
command: list = None,
|
||||
return_code: int = None,
|
||||
stderr: str = None,
|
||||
command: Optional[list] = None,
|
||||
return_code: Optional[int] = None,
|
||||
stderr: Optional[str] = None,
|
||||
):
|
||||
super().__init__(message)
|
||||
self.command = command
|
||||
@@ -69,7 +69,7 @@ class EffectError(RenderError):
|
||||
"""效果处理错误"""
|
||||
|
||||
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)
|
||||
self.effect_name = effect_name
|
||||
@@ -86,7 +86,7 @@ class APIError(RenderWorkerError):
|
||||
"""API调用错误"""
|
||||
|
||||
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)
|
||||
self.status_code = status_code
|
||||
|
||||
@@ -26,7 +26,7 @@ def safe_json_loads(json_str: Union[str, bytes], default: Any = None) -> Any:
|
||||
try:
|
||||
return json.loads(json_str)
|
||||
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 {}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user