返回 douyin-downloader
test_media_quality.py
根目录 / tests / test_media_quality.py
1 """回归测试:视频与实况图的最高清源选择逻辑。"""
2
3 from core.downloader_base import BaseDownloader
4
5
6 def _build_video_downloader(tmp_path):
7 from auth import CookieManager
8 from config import ConfigLoader
9 from control import QueueManager, RateLimiter, RetryHandler
10 from core.api_client import DouyinAPIClient
11 from core.video_downloader import VideoDownloader
12 from storage import FileManager
13
14 config = ConfigLoader()
15 config.update(path=str(tmp_path))
16 return VideoDownloader(
17 config,
18 DouyinAPIClient({}),
19 FileManager(str(tmp_path)),
20 CookieManager(str(tmp_path / ".cookies.json")),
21 database=None,
22 rate_limiter=RateLimiter(max_per_second=5),
23 retry_handler=RetryHandler(max_retries=1),
24 queue_manager=QueueManager(max_workers=1),
25 )
26
27
28 class TestPickHighestQualityPlayAddr:
29 def test_empty_input_returns_none(self):
30 assert BaseDownloader._pick_highest_quality_play_addr({}) is None
31 assert BaseDownloader._pick_highest_quality_play_addr({"bit_rate": []}) is None
32 assert BaseDownloader._pick_highest_quality_play_addr({"bit_rate": None}) is None
33
34 def test_selects_highest_bit_rate(self):
35 video = {
36 "bit_rate": [
37 {
38 "bit_rate": 500000,
39 "play_addr": {"url_list": ["https://low.example/video"]},
40 },
41 {
42 "bit_rate": 1500000,
43 "play_addr": {"url_list": ["https://high.example/video"]},
44 },
45 {
46 "bit_rate": 900000,
47 "play_addr": {"url_list": ["https://mid.example/video"]},
48 },
49 ]
50 }
51 best = BaseDownloader._pick_highest_quality_play_addr(video)
52 assert best is not None
53 assert best["url_list"] == ["https://high.example/video"]
54
55 def test_tie_breaks_by_width(self):
56 video = {
57 "bit_rate": [
58 {
59 "bit_rate": 1000000,
60 "play_addr": {
61 "url_list": ["https://narrow.example/video"],
62 "width": 1080,
63 },
64 },
65 {
66 "bit_rate": 1000000,
67 "play_addr": {
68 "url_list": ["https://wider.example/video"],
69 "width": 1440,
70 },
71 },
72 ]
73 }
74 best = BaseDownloader._pick_highest_quality_play_addr(video)
75 assert best is not None
76 assert best["url_list"] == ["https://wider.example/video"]
77
78 def test_ignores_malformed_entries(self):
79 video = {
80 "bit_rate": [
81 "not a dict",
82 {"bit_rate": "invalid"},
83 {"bit_rate": 800000}, # no play_addr
84 {
85 "bit_rate": 600000,
86 "play_addr": {"url_list": ["https://valid.example/v"]},
87 },
88 ]
89 }
90 best = BaseDownloader._pick_highest_quality_play_addr(video)
91 assert best is not None
92 assert best["url_list"] == ["https://valid.example/v"]
93
94
95 def test_collect_image_live_urls_prefers_high_bitrate(tmp_path):
96 """实况图解析应优先采用 bit_rate 最高的 play_addr。"""
97 # 用真实的 VideoDownloader 实例化来验证(_collect_image_live_urls 是实例方法)。
98 downloader = _build_video_downloader(tmp_path)
99
100 aweme_data = {
101 "image_post_info": {
102 "images": [
103 {
104 "video": {
105 "bit_rate": [
106 {
107 "bit_rate": 500000,
108 "play_addr": {"url_list": ["https://low.example/live"]},
109 },
110 {
111 "bit_rate": 2_000_000,
112 "play_addr": {"url_list": ["https://high.example/live"]},
113 },
114 ],
115 "play_addr": {"url_list": ["https://fallback.example/live"]},
116 }
117 }
118 ]
119 }
120 }
121
122 urls = downloader._collect_image_live_urls(aweme_data)
123 assert urls == ["https://high.example/live"]
124
125
126 def test_note_aweme_without_gallery_assets_falls_back_to_video(tmp_path):
127 """部分 /note/ 作品返回 aweme_type=68 但媒体落在 video 字段。"""
128 downloader = _build_video_downloader(tmp_path)
129
130 aweme_data = {
131 "aweme_id": "7646971177114611826",
132 "aweme_type": 68,
133 "video": {"play_addr": {"url_list": ["https://example.com/note-video.mp4"]}},
134 }
135
136 assert downloader._detect_media_type(aweme_data) == "video"
137
138
139 def test_note_aweme_with_gallery_assets_stays_gallery(tmp_path):
140 downloader = _build_video_downloader(tmp_path)
141
142 aweme_data = {
143 "aweme_id": "7646971177114611826",
144 "aweme_type": 68,
145 "images": [{"url_list": ["https://example.com/1.jpg"]}],
146 "video": {"play_addr": {"url_list": ["https://example.com/audio-ish.mp4"]}},
147 }
148
149 assert downloader._detect_media_type(aweme_data) == "gallery"
150
151
152 def test_build_no_watermark_url_uses_h264_play_addr_variant(tmp_path):
153 downloader = _build_video_downloader(tmp_path)
154
155 aweme_data = {
156 "aweme_id": "7646971177114611826",
157 "video": {
158 "play_addr_h264": {
159 "url_list": ["https://v3-web.douyinvod.com/note-h264.mp4"]
160 }
161 },
162 }
163
164 video_info = downloader._build_no_watermark_url(aweme_data)
165 assert video_info is not None
166 assert video_info[0] == "https://v3-web.douyinvod.com/note-h264.mp4"
167
168
169 def test_collect_image_live_urls_uses_h264_variant_when_play_addr_missing(tmp_path):
170 downloader = _build_video_downloader(tmp_path)
171
172 aweme_data = {
173 "image_post_info": {
174 "images": [
175 {
176 "video": {
177 "play_addr_h264": {
178 "url_list": ["https://v3-web.douyinvod.com/live-h264.mp4"]
179 }
180 }
181 }
182 ]
183 }
184 }
185
186 urls = downloader._collect_image_live_urls(aweme_data)
187 assert urls == ["https://v3-web.douyinvod.com/live-h264.mp4"]
188
188 lines PYTHON