fix(lipsync): video_url扩展名校验放宽,支持MOV/M4V/WebM等容器 #1878
@@ -321,4 +321,4 @@ def finalize_render_job(
|
||||
) from exc
|
||||
except Exception as exc:
|
||||
logger.error("渲染任务finalize失败: job_id=%s err=%s", job_id, exc, exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"保存到成片库失败: {str(exc)}")
|
||||
raise HTTPException(status_code=500, detail=f"保存到成片库失败: {str(exc)}") from exc
|
||||
|
||||
@@ -82,8 +82,9 @@ class CreateLipsyncJobRequest(BaseModel):
|
||||
if not video.startswith(("http://", "https://")):
|
||||
raise ValueError("video_url 必须是 HTTP/HTTPS URL")
|
||||
lower = video.lower().split("?")[0]
|
||||
if not lower.endswith(".mp4"):
|
||||
raise ValueError("video_url 仅支持 MP4 格式")
|
||||
allowed_video_exts = (".mp4", ".mov", ".m4v", ".webm", ".avi", ".mkv", ".3gp")
|
||||
if not any(lower.endswith(ext) for ext in allowed_video_exts):
|
||||
raise ValueError("video_url 格式不支持,仅支持: " + ", ".join(allowed_video_exts))
|
||||
|
||||
has_audio = bool((self.audio_url or "").strip())
|
||||
has_tts = bool((self.voice_id or "").strip()) and bool((self.script_text or "").strip())
|
||||
|
||||
@@ -262,8 +262,8 @@ def test_smart_cover_selects_best_frame_and_persists():
|
||||
score_patch.assert_called_once()
|
||||
# 验证使用了增大的轮询参数
|
||||
call_kwargs = mk.extract_frames.call_args
|
||||
assert call_kwargs.kwargs.get("poll_interval") == 1.0 or call_kwargs[1].get("poll_interval") == 1.0
|
||||
assert call_kwargs.kwargs.get("max_poll_attempts") == 15 or call_kwargs[1].get("max_poll_attempts") == 15
|
||||
assert call_kwargs.kwargs.get("poll_interval") == 2.0 or call_kwargs[1].get("poll_interval") == 2.0
|
||||
assert call_kwargs.kwargs.get("max_poll_attempts") == 30 or call_kwargs[1].get("max_poll_attempts") == 30
|
||||
|
||||
|
||||
def test_smart_cover_returns_empty_when_mediakit_unavailable():
|
||||
@@ -334,8 +334,8 @@ def test_extract_frames_uses_extended_poll_params():
|
||||
cov.select_best_cover_frame("https://other/avatar.mp4", max_frames=3)
|
||||
|
||||
call_kwargs = mk.extract_frames.call_args
|
||||
assert call_kwargs.kwargs.get("poll_interval") == 1.0 or call_kwargs[1].get("poll_interval") == 1.0
|
||||
assert call_kwargs.kwargs.get("max_poll_attempts") == 15 or call_kwargs[1].get("max_poll_attempts") == 15
|
||||
assert call_kwargs.kwargs.get("poll_interval") == 2.0 or call_kwargs[1].get("poll_interval") == 2.0
|
||||
assert call_kwargs.kwargs.get("max_poll_attempts") == 30 or call_kwargs[1].get("max_poll_attempts") == 30
|
||||
assert call_kwargs.kwargs.get("max_retries") == 1 or call_kwargs[1].get("max_retries") == 1
|
||||
|
||||
|
||||
|
||||
@@ -91,16 +91,27 @@ class TestSchemaValidation:
|
||||
assert req.voice_id == "longxiaochun_v3"
|
||||
assert req.script_text == "大家好,欢迎来到直播间"
|
||||
|
||||
def test_invalid_video_url_not_mp4(self):
|
||||
def test_invalid_video_url_unsupported_ext(self):
|
||||
from app.schemas.lipsync import CreateLipsyncJobRequest
|
||||
|
||||
with pytest.raises(ValueError, match="MP4"):
|
||||
# 不支持的扩展名(.txt)应报错
|
||||
with pytest.raises(ValueError, match="格式不支持"):
|
||||
CreateLipsyncJobRequest(
|
||||
video_url="https://example.com/video.mov",
|
||||
video_url="https://example.com/video.txt",
|
||||
voice_id="longxiaochun_v3",
|
||||
script_text="测试文本",
|
||||
)
|
||||
|
||||
def test_mov_video_url_accepted(self):
|
||||
from app.schemas.lipsync import CreateLipsyncJobRequest
|
||||
|
||||
# .MOV 是 iPhone 拍摄的常见容器,h264 编码可直接被 MediaKit 处理
|
||||
req = CreateLipsyncJobRequest(
|
||||
video_url="https://example.com/video.mov",
|
||||
audio_url="https://example.com/audio.mp3",
|
||||
)
|
||||
assert req.video_url.endswith(".mov")
|
||||
|
||||
def test_invalid_video_url_empty(self):
|
||||
from app.schemas.lipsync import CreateLipsyncJobRequest
|
||||
|
||||
|
||||
Reference in New Issue
Block a user