test_video_fetch.py 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. """V3-M2A: video_fetch download + compress + data URL (all mocked)."""
  2. from __future__ import annotations
  3. import base64
  4. import httpx
  5. import pytest
  6. from content_agent.integrations import video_fetch
  7. from content_agent.integrations.video_fetch import VideoFetchError, fetch_and_compress
  8. class FakeHttpClient:
  9. def __init__(self, *, content=b"rawvideo", status=200, error=None):
  10. self.content = content
  11. self.status = status
  12. self.error = error
  13. self.calls = []
  14. def get(self, url, *, headers, follow_redirects, timeout):
  15. self.calls.append({"url": url, "headers": headers})
  16. if self.error:
  17. raise self.error
  18. return httpx.Response(
  19. self.status, content=self.content, request=httpx.Request("GET", url)
  20. )
  21. def _patch_compress(monkeypatch, out=b"\x00\x00\x00\x18ftypmp42compressed"):
  22. monkeypatch.setattr(video_fetch, "_compress", lambda raw, exe: out)
  23. def test_fetch_builds_data_url(monkeypatch):
  24. _patch_compress(monkeypatch)
  25. out = fetch_and_compress("http://v/x", "douyin", http_client=FakeHttpClient(), ffmpeg_exe="ffmpeg")
  26. assert out.startswith("data:video/mp4;base64,")
  27. decoded = base64.b64decode(out.split(",", 1)[1])
  28. assert decoded.startswith(b"\x00\x00\x00\x18ftyp")
  29. def test_fetch_uses_platform_headers(monkeypatch):
  30. _patch_compress(monkeypatch)
  31. for platform, ua_token, referer in [
  32. ("douyin", "iPhone", "https://www.douyin.com/"),
  33. ("shipinhao", "Windows", "https://channels.weixin.qq.com/"),
  34. ]:
  35. client = FakeHttpClient()
  36. fetch_and_compress("http://v/x", platform, http_client=client, ffmpeg_exe="ffmpeg")
  37. headers = client.calls[0]["headers"]
  38. assert ua_token in headers["User-Agent"]
  39. assert headers["Referer"] == referer
  40. def test_fetch_missing_play_url_raises():
  41. with pytest.raises(VideoFetchError, match="missing play_url"):
  42. fetch_and_compress("", "douyin", http_client=FakeHttpClient())
  43. def test_fetch_download_failure_raises():
  44. client = FakeHttpClient(error=httpx.ConnectError("boom"))
  45. with pytest.raises(VideoFetchError, match="download failed"):
  46. fetch_and_compress("http://v/x", "douyin", http_client=client, ffmpeg_exe="ffmpeg")
  47. def test_fetch_oversize_raises(monkeypatch):
  48. _patch_compress(monkeypatch, out=b"x" * (video_fetch.MAX_INLINE_BYTES + 1))
  49. with pytest.raises(VideoFetchError, match="oversize"):
  50. fetch_and_compress("http://v/x", "douyin", http_client=FakeHttpClient(), ffmpeg_exe="ffmpeg")
  51. def test_fetch_saves_raw_video_when_requested(monkeypatch, tmp_path):
  52. # 2026-06-12 拍板: save_raw_to 指定时原片落盘(自动建父目录);默认不落盘。
  53. _patch_compress(monkeypatch)
  54. save_path = tmp_path / "run_1" / "c1.mp4"
  55. fetch_and_compress(
  56. "http://v/x", "douyin",
  57. http_client=FakeHttpClient(content=b"rawvideo"),
  58. ffmpeg_exe="ffmpeg",
  59. save_raw_to=str(save_path),
  60. )
  61. assert save_path.read_bytes() == b"rawvideo"
  62. fetch_and_compress("http://v/x", "douyin", http_client=FakeHttpClient(), ffmpeg_exe="ffmpeg")
  63. assert list(tmp_path.iterdir()) == [save_path.parent]