test_video_extract.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. """原生整段视频提取离线测试:注入假 http_post 返回 segments,断言段卡 + 内容。"""
  2. from __future__ import annotations
  3. import json
  4. from core.config import PgConfig, Settings
  5. from creation_knowledge.integrations.video_extract import extract_video
  6. from core.models import Post
  7. SEGMENTS = json.dumps({
  8. "video_title": "AI视频教程", "overall": "教 AI 出片",
  9. "segments": [
  10. {"start": "00:00", "end": "00:18", "title": "问题与简介",
  11. "knowledge_types": ["what"], "what": "常见翻车", "why": None, "how": None},
  12. {"start": "00:19", "end": "00:48", "title": "去噪原理",
  13. "knowledge_types": ["why"], "what": None, "why": "噪声去噪类似雕刻", "how": None},
  14. ]}, ensure_ascii=False)
  15. class _Resp:
  16. def __init__(self, content): self._c = content
  17. def raise_for_status(self): return None
  18. def json(self): return {"choices": [{"message": {"content": self._c}}]}
  19. def _settings() -> Settings:
  20. return Settings(
  21. pg=PgConfig(host="h", port=5432, user="u", password="p", database="d"),
  22. aiddit_crawler_base_url="x", crawler_timeout=30,
  23. openrouter_timeout_seconds=90,
  24. openrouter_model="google/gemini-3-flash-preview",
  25. openrouter_base_url="https://openrouter.ai/api/v1", openrouter_api_key="k",
  26. llm_model="m", max_cards=12,
  27. frames_dir="runtime/frames", douyin_ratio="540p", data_dir="")
  28. def _fake_post():
  29. captured = {}
  30. def post(url, headers=None, json=None, timeout=None):
  31. captured["url"] = url
  32. captured["json"] = json
  33. return _Resp(SEGMENTS)
  34. return post, captured
  35. def test_video_extract_makes_segment_cards(tmp_path):
  36. vf = tmp_path / "v.mp4"
  37. vf.write_bytes(b"\x00\x00\x00\x18ftypmp42fakevideobytes")
  38. fake, captured = _fake_post()
  39. post = Post(id="dy_1", platform="douyin", url="u", content_id="1",
  40. content_type="video", video_urls=["http://x/play?ratio=default"])
  41. out = extract_video(post, settings=_settings(), http_post=fake, video_path=str(vf))
  42. # ExtractedContent.cards = 每段一条内容
  43. assert len(out.cards) == 2
  44. assert out.cards[0].index == 1 and "问题" in out.cards[0].content
  45. assert out.is_empty is False and out.text # overall 进 text
  46. # post.cards = 段卡,带 start/end(秒)
  47. assert len(post.cards) == 2
  48. assert post.cards[0].kind == "segment"
  49. assert post.cards[0].start == 0.0 and post.cards[1].start == 19.0
  50. assert post.cards[1].end == 48.0
  51. assert post.cards[0].url is None # 段卡默认无图
  52. # 请求体含 base64 video_url
  53. parts = captured["json"]["messages"][0]["content"]
  54. assert any(p["type"] == "video_url"
  55. and p["video_url"]["url"].startswith("data:video/mp4;base64,")
  56. for p in parts)
  57. def test_video_extract_saves_and_sets_url(tmp_path):
  58. """传 save_path/public_url:整段视频落盘 + 段卡 url 指向整段。"""
  59. vf = tmp_path / "src.mp4"
  60. vf.write_bytes(b"\x00\x00\x00\x18ftypmp42fakevideobytes")
  61. save_path = tmp_path / "out" / "video.mp4"
  62. fake, _ = _fake_post()
  63. post = Post(id="dy_1", platform="douyin", url="u", content_id="1",
  64. content_type="video", video_urls=["http://x/play"])
  65. extract_video(post, settings=_settings(), http_post=fake, video_path=str(vf),
  66. save_path=save_path, public_url="/data/B/douyin/dy_1/video.mp4")
  67. assert save_path.exists() and save_path.read_bytes() == vf.read_bytes()
  68. assert all(c.url == "/data/B/douyin/dy_1/video.mp4" for c in post.cards)
  69. def test_extract_video_oss_direct_feed():
  70. """传 oss_video_url:不下载、不 base64,请求体直接用该 http 直链;段卡 url 指向它。"""
  71. fake, captured = _fake_post()
  72. downloads = []
  73. def spy_downloader(url, platform):
  74. downloads.append(url)
  75. return b"should-not-be-called"
  76. post = Post(id="dy_2", platform="douyin", url="u", content_id="2",
  77. content_type="video", video_urls=["http://cdn/raw.mp4?sig=tmp"])
  78. out = extract_video(post, settings=_settings(), http_post=fake,
  79. downloader=spy_downloader,
  80. oss_video_url="https://res.cybertogether.net/crawler/video/x.mp4")
  81. assert downloads == [] # 未触发任何下载
  82. parts = captured["json"]["messages"][0]["content"]
  83. vparts = [p for p in parts if p["type"] == "video_url"]
  84. assert vparts and vparts[0]["video_url"]["url"] == "https://res.cybertogether.net/crawler/video/x.mp4"
  85. assert not vparts[0]["video_url"]["url"].startswith("data:") # 不是 base64
  86. assert out.cards and len(post.cards) == 2
  87. assert all(c.url == "https://res.cybertogether.net/crawler/video/x.mp4" for c in post.cards)