diff --git a/packages/shared/gpu_encoder.py b/packages/shared/gpu_encoder.py index db311d9fe..e1b805070 100644 --- a/packages/shared/gpu_encoder.py +++ b/packages/shared/gpu_encoder.py @@ -183,11 +183,14 @@ class GpuEncoderClient: input_url = mezz_get_for_p4000 logger.info( "[gpu-encoder] mezzanine uploaded to relay: key=%s size=%d took=%.2fs", - mezz_key, mezz_size, time.time() - t_up, + mezz_key, + mezz_size, + time.time() - t_up, ) except (GpuEncodeError, OSError, urllib.error.URLError) as e: logger.warning( - "[gpu-encoder] relay mezz upload failed (%s), fallback to OSS", e, + "[gpu-encoder] relay mezz upload failed (%s), fallback to OSS", + e, ) used_transport = "oss" # relay 上传失败的部分文件 best-effort 清理 @@ -369,7 +372,7 @@ class GpuEncoderClient: def _upload_file_put(self, url: str, path: Path, content_type: str) -> None: """HTTP PUT 流式上传文件到指定 URL(mezzanine 上传到 relay 用,Tailscale/Docker 内网)。""" - import io + file_size = path.stat().st_size # 使用生成器/文件对象流式上传,避免一次性载入大 mezzanine 文件到内存 with open(path, "rb") as f: diff --git a/tests/unit/test_gpu_encoder.py b/tests/unit/test_gpu_encoder.py index b4d01dc9e..1ed7985f4 100644 --- a/tests/unit/test_gpu_encoder.py +++ b/tests/unit/test_gpu_encoder.py @@ -129,9 +129,7 @@ class TestPostSync: body = {"status": "failed", "ffmpeg_rc": 1, "message": "Invalid data"} with mock.patch("urllib.request.urlopen", return_value=_fake_response(body=body)): with pytest.raises(GpuEncodeError, match="rc=1"): - client._post_sync( - {"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10} - ) + client._post_sync({"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10}) def test_http_4xx_raises(self, client): err = urllib.error.HTTPError( @@ -139,37 +137,27 @@ class TestPostSync: ) with mock.patch("urllib.request.urlopen", side_effect=err): with pytest.raises(GpuEncodeError, match="HTTP 422"): - client._post_sync( - {"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10} - ) + client._post_sync({"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10}) def test_connection_error_raises(self, client): with mock.patch("urllib.request.urlopen", side_effect=urllib.error.URLError("conn refused")): with pytest.raises(GpuEncodeError, match="connection error"): - client._post_sync( - {"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10} - ) + client._post_sync({"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10}) def test_timeout_error_raises(self, client): with mock.patch("urllib.request.urlopen", side_effect=socket.timeout("timed out")): with pytest.raises(GpuEncodeError, match="connection error"): - client._post_sync( - {"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10} - ) + client._post_sync({"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10}) def test_bad_json_raises(self, client): with mock.patch("urllib.request.urlopen", return_value=_fake_response(body=b"not-json")): with pytest.raises(GpuEncodeError, match="bad JSON"): - client._post_sync( - {"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10} - ) + client._post_sync({"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10}) def test_uploaded_false_logs_warning_but_succeeds(self, client, caplog): body = {"status": "completed", "ffmpeg_rc": 0, "uploaded": False, "job_id": "j"} with mock.patch("urllib.request.urlopen", return_value=_fake_response(body=body)), caplog.at_level("WARNING"): - res = client._post_sync( - {"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10} - ) + res = client._post_sync({"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10}) assert res["status"] == "completed" assert "uploaded=false" in caplog.text @@ -192,8 +180,10 @@ class TestRelayUrl: def test_result_get_url_falls_back_to_external_when_not_set(self): c = GpuEncoderClient( - endpoint="http://gpu", relay_base_url="http://api.example.com", - relay_secret="s", mezzanine_transport="oss", + endpoint="http://gpu", + relay_base_url="http://api.example.com", + relay_secret="s", + mezzanine_transport="oss", ) put = c._result_put_url("k", "s") get = c._result_get_url("k", "s") @@ -516,7 +506,6 @@ class TestOssHelpers: svc.delete_file.assert_called_once_with("k") - # ── encode_mezzanine_to_output: relay transport (default path) ────── class TestEncodeMezzanineRelay: def test_relay_happy_path_uploads_to_relay(self, tmp_path): @@ -535,9 +524,16 @@ class TestEncodeMezzanineRelay: with ( mock.patch.object(c, "_upload_file_put") as m_put, mock.patch.object( - c, "_post_sync", - return_value={"job_id": "j", "status": "completed", "ffmpeg_rc": 0, - "uploaded": True, "size": 100, "duration": 1.0}, + c, + "_post_sync", + return_value={ + "job_id": "j", + "status": "completed", + "ffmpeg_rc": 0, + "uploaded": True, + "size": 100, + "duration": 1.0, + }, ) as m_post, mock.patch.object(c, "_download_to_file", return_value=100), mock.patch.object(c, "_relay_delete") as m_del, @@ -575,7 +571,8 @@ class TestEncodeMezzanineRelay: mock.patch.object(c, "_upload_file_put", side_effect=GpuEncodeError("relay 500")), mock.patch.object(c, "_upload_mezzanine_to_oss", return_value=("https://oss/signed", "ossk")), mock.patch.object( - c, "_post_sync", + c, + "_post_sync", return_value={"job_id": "j", "status": "completed", "ffmpeg_rc": 0, "uploaded": True}, ) as m_post, mock.patch.object(c, "_download_to_file", return_value=50), @@ -615,6 +612,7 @@ class TestEncodeMezzanineRelay: with pytest.raises(GpuEncodeError, match="relay PUT failed"): client._upload_file_put("http://relay/m", f, "video/mp4") + # ── Singleton / factory ──────────────────────────────────────────── class TestSingletonFactory: def test_build_client_import_error_returns_none(self):