fix(lipsync): video_url扩展名校验放宽,支持MOV/M4V/WebM等容器 #1878

Merged
xiaoxia merged 4 commits from fix/lipsync-video-ext-0913 into develop 2026-09-13 22:51:10 +08:00
4 changed files with 22 additions and 10 deletions
+1 -1
View File
@@ -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
+3 -2
View File
@@ -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
+14 -3
View File
@@ -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