test_platform_video_url.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. from __future__ import annotations
  2. from content_agent.integrations.platform_video_url import (
  3. classify_url_candidate,
  4. find_url_candidates,
  5. select_video_url,
  6. )
  7. def _probe(status: str = "verified"):
  8. def probe(url: str, platform: str):
  9. return {
  10. "http_status": 206 if status == "verified" else None,
  11. "content_type": "video/mp4" if status == "verified" else "",
  12. "content_range": "bytes 0-99/100" if status == "verified" else "",
  13. "range_bytes": 100 if status == "verified" else 0,
  14. "looks_like_video": status == "verified",
  15. "probe_status": status,
  16. }
  17. return probe
  18. def test_classifies_urls_without_ad_substring_false_positive():
  19. payload = {
  20. "content_link": "https://www.example.com/item",
  21. "image_url_list": [{"image_url": "https://img.example/a.jpg"}],
  22. "avatar": "https://wx.qlogo.cn/avatar",
  23. "bgm_data": {"play_url": "https://audio.example/a.m4a"},
  24. "ads": {"video_url": "https://ad.example/ad.mp4"},
  25. "video_url_list": [
  26. {
  27. "video_url": (
  28. "https://findermp.video.qq.com/251/20302/stodownload?"
  29. "head=1&token=abc"
  30. )
  31. }
  32. ],
  33. }
  34. by_path = {item.path: item for item in find_url_candidates(payload)}
  35. assert by_path["$.content_link"].kind == "page"
  36. assert by_path["$.image_url_list[0].image_url"].kind == "image"
  37. assert by_path["$.avatar"].kind == "avatar"
  38. assert by_path["$.bgm_data.play_url"].kind == "audio"
  39. assert by_path["$.ads.video_url"].kind == "ad_or_material"
  40. assert by_path["$.video_url_list[0].video_url"].kind == "video"
  41. assert classify_url_candidate("$.video_url", "https://x/stodownload?head=1")[0] == "video"
  42. def test_kuaishou_prefers_verified_detail_video_over_search():
  43. result = select_video_url(
  44. "kuaishou",
  45. [
  46. ("search", {"video_url_list": [{"video_url": "https://v23-3.kwaicdn.com/search.mp4"}]}),
  47. ("detail", {"video_url_list": [{"video_url": "https://tymov2.a.kwimgs.com/detail.mp4"}]}),
  48. ],
  49. probe_fn=_probe(),
  50. )
  51. assert result["play_url"] == "https://tymov2.a.kwimgs.com/detail.mp4"
  52. assert result["platform_raw_payload"]["selected_video_url_path"] == "$.detail.video_url_list[0].video_url"
  53. assert result["platform_raw_payload"]["selected_video_url_probe_status"] == "verified"
  54. assert [candidate["url"] for candidate in result["video_url_candidates"]] == [
  55. "https://tymov2.a.kwimgs.com/detail.mp4",
  56. "https://v23-3.kwaicdn.com/search.mp4",
  57. ]
  58. assert "video_url_candidates" not in result["platform_raw_payload"]
  59. def test_shipinhao_prefers_findermp_video_and_filters_non_video():
  60. result = select_video_url(
  61. "shipinhao",
  62. [
  63. (
  64. "search",
  65. {
  66. "image_url_list": [{"image_url": "https://findermp.video.qq.com/cover.jpg"}],
  67. "video_url_list": [{"video_url": "https://findermp.video.qq.com/video.mp4"}],
  68. },
  69. )
  70. ],
  71. probe_fn=_probe(),
  72. )
  73. assert result["play_url"] == "https://findermp.video.qq.com/video.mp4"
  74. assert result["platform_raw_payload"]["video_url_candidate_counts"] == {
  75. "image": 1,
  76. "video": 1,
  77. }
  78. def test_probe_failure_falls_back_to_strong_video_candidate():
  79. result = select_video_url(
  80. "kuaishou",
  81. [("search", {"video_url_list": [{"video_url": "https://v23-3.kwaicdn.com/search.mp4"}]})],
  82. probe_fn=_probe("failed"),
  83. )
  84. assert result["play_url"] == "https://v23-3.kwaicdn.com/search.mp4"
  85. assert result["platform_raw_payload"]["selected_video_url_probe_status"] == "failed_fallback"
  86. assert result["media_failure_reason"] is None
  87. def test_douyin_demotes_v11_weba_when_better_candidate_exists():
  88. result = select_video_url(
  89. "douyin",
  90. [
  91. (
  92. "search",
  93. {
  94. "video": {
  95. "play_addr": {
  96. "url_list": [
  97. "https://v11-weba.douyinvod.com/video.mp4",
  98. "https://v26-web.douyinvod.com/video.mp4",
  99. ]
  100. }
  101. }
  102. },
  103. )
  104. ],
  105. probe_fn=_probe(),
  106. )
  107. assert result["play_url"] == "https://v26-web.douyinvod.com/video.mp4"
  108. assert result["platform_raw_payload"]["selected_video_url_host"] == "v26-web.douyinvod.com"
  109. assert [candidate["host"] for candidate in result["video_url_candidates"]] == [
  110. "v26-web.douyinvod.com",
  111. "v11-weba.douyinvod.com",
  112. ]
  113. def test_video_url_candidates_are_capped_to_selected_plus_two():
  114. result = select_video_url(
  115. "kuaishou",
  116. [
  117. (
  118. "search",
  119. {
  120. "video_url_list": [
  121. {"video_url": f"https://v{index}.kwaicdn.com/{index}.mp4"}
  122. for index in range(5)
  123. ]
  124. },
  125. )
  126. ],
  127. probe_fn=_probe(),
  128. )
  129. assert len(result["video_url_candidates"]) == 3
  130. assert result["video_url_candidates"][0]["url"] == result["play_url"]
  131. def test_no_video_candidate_returns_no_valid_play_url():
  132. result = select_video_url(
  133. "kuaishou",
  134. [("search", {"image_url_list": [{"image_url": "https://img.example/a.jpg"}]})],
  135. probe_fn=_probe(),
  136. )
  137. assert result["play_url"] is None
  138. assert result["media_failure_reason"] == "no_valid_play_url"
  139. assert result["platform_raw_payload"]["no_valid_play_url"] is True
  140. assert result["video_url_candidates"] == []