You've already forked FrameTour-RenderWorker
u
This commit is contained in:
@@ -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", "-"]
|
||||||
|
|||||||
2
index.py
2
index.py
@@ -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
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
|
return _template_service
|
||||||
|
|
||||||
# 向后兼容的全局变量和函数
|
# 向后兼容的全局变量和函数
|
||||||
TEMPLATES = {}
|
TEMPLATES: dict = {}
|
||||||
|
|
||||||
def _update_templates_dict():
|
def _update_templates_dict():
|
||||||
"""更新全局TEMPLATES字典以保持向后兼容"""
|
"""更新全局TEMPLATES字典以保持向后兼容"""
|
||||||
|
|||||||
@@ -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={
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user