返回 douyin-downloader
test_downloader_naming_templates.py
根目录 / tests / test_downloader_naming_templates.py
1 """End-to-end check that custom `filename_template` / `folder_template` /
2 `folderstyle` settings actually change the on-disk paths a downloader writes
3 to.
4
5 This complements `test_naming.py` (unit tests for the helper) and
6 `test_server_settings_naming.py` (PATCH/GET round-trips). Here we exercise the
7 real `_download_aweme_assets` code path with a monkey-patched `_download_with_retry`
8 so no network I/O happens.
9 """
10
11 from __future__ import annotations
12
13 from datetime import datetime
14
15 import pytest
16
17 from auth import CookieManager
18 from config import ConfigLoader
19 from control import QueueManager, RateLimiter, RetryHandler
20 from core.api_client import DouyinAPIClient
21 from core.video_downloader import VideoDownloader
22 from storage import FileManager
23
24
25 def _build_downloader(tmp_path):
26 config = ConfigLoader()
27 config.update(path=str(tmp_path))
28
29 file_manager = FileManager(str(tmp_path))
30 cookie_manager = CookieManager(str(tmp_path / ".cookies.json"))
31 api_client = DouyinAPIClient({})
32
33 downloader = VideoDownloader(
34 config,
35 api_client,
36 file_manager,
37 cookie_manager,
38 database=None,
39 rate_limiter=RateLimiter(max_per_second=5),
40 retry_handler=RetryHandler(max_retries=1),
41 queue_manager=QueueManager(max_workers=1),
42 )
43
44 return downloader, api_client
45
46
47 def _make_aweme(publish_ts: int, aweme_id: str = "7600000000000000000"):
48 return {
49 "aweme_id": aweme_id,
50 "desc": "登山日记",
51 "create_time": publish_ts,
52 "author": {"nickname": "爬山佬", "sec_uid": "MS4wLjABAAA"},
53 "video": {"play_addr": {"url_list": ["https://example.com/v.mp4"]}},
54 }
55
56
57 @pytest.mark.asyncio
58 async def test_default_template_matches_legacy_filename(tmp_path, monkeypatch):
59 """Users who don't touch the new settings see zero behaviour change."""
60 downloader, api_client = _build_downloader(tmp_path)
61 downloader.config.update(music=False, cover=False, avatar=False, json=False, folderstyle=True)
62
63 saved = []
64
65 async def _fake_download(self, _url, save_path, _session, **_):
66 saved.append(save_path)
67 return True
68
69 downloader._download_with_retry = _fake_download.__get__(downloader, VideoDownloader)
70
71 async def _fake_session():
72 return object()
73
74 monkeypatch.setattr(api_client, "get_session", _fake_session)
75
76 publish_ts = int(datetime(2024, 3, 15, 18, 30).timestamp())
77 aweme = _make_aweme(publish_ts, aweme_id="7412345678901234567")
78
79 assert await downloader._download_aweme_assets(aweme, author_name="爬山佬", mode="post") is True
80
81 assert len(saved) == 1
82 save_path = saved[0]
83 assert save_path.name == "2024-03-15_登山日记_7412345678901234567.mp4"
84 assert save_path.parent.name == "2024-03-15_登山日记_7412345678901234567"
85
86 await api_client.close()
87
88
89 @pytest.mark.asyncio
90 async def test_custom_filename_template_applies(tmp_path, monkeypatch):
91 downloader, api_client = _build_downloader(tmp_path)
92 downloader.config.update(
93 music=False,
94 cover=False,
95 avatar=False,
96 json=False,
97 folderstyle=True,
98 filename_template="{author}_{date}_{id}",
99 folder_template="{year}-{month}_{id}",
100 )
101
102 saved = []
103
104 async def _fake_download(self, _url, save_path, _session, **_):
105 saved.append(save_path)
106 return True
107
108 downloader._download_with_retry = _fake_download.__get__(downloader, VideoDownloader)
109
110 async def _fake_session():
111 return object()
112
113 monkeypatch.setattr(api_client, "get_session", _fake_session)
114
115 publish_ts = int(datetime(2024, 7, 4, 9, 15).timestamp())
116 aweme = _make_aweme(publish_ts, aweme_id="7419999999999999999")
117
118 assert await downloader._download_aweme_assets(aweme, author_name="爬山佬", mode="post") is True
119
120 assert len(saved) == 1
121 save_path = saved[0]
122 assert save_path.name == "爬山佬_2024-07-04_7419999999999999999.mp4"
123 assert save_path.parent.name == "2024-07_7419999999999999999"
124
125 await api_client.close()
126
127
128 @pytest.mark.asyncio
129 async def test_folderstyle_false_skips_subdirectory(tmp_path, monkeypatch):
130 downloader, api_client = _build_downloader(tmp_path)
131 downloader.config.update(
132 music=False,
133 cover=False,
134 avatar=False,
135 json=False,
136 folderstyle=False,
137 )
138
139 saved = []
140
141 async def _fake_download(self, _url, save_path, _session, **_):
142 saved.append(save_path)
143 return True
144
145 downloader._download_with_retry = _fake_download.__get__(downloader, VideoDownloader)
146
147 async def _fake_session():
148 return object()
149
150 monkeypatch.setattr(api_client, "get_session", _fake_session)
151
152 publish_ts = int(datetime(2024, 1, 1, 0, 0).timestamp())
153 aweme = _make_aweme(publish_ts, aweme_id="7401010101010101010")
154
155 assert await downloader._download_aweme_assets(aweme, author_name="爬山佬", mode="post") is True
156
157 # With folderstyle=False the save_dir is just `{base}/{author}/{mode}`
158 # — no per-aweme subdir.
159 save_path = saved[0]
160 assert save_path.parent.name == "post"
161 assert save_path.parent.parent.name == "爬山佬"
162
163 await api_client.close()
164
164 lines PYTHON