devart-dl 4.3.2__tar.gz → 4.3.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {devart_dl-4.3.2/devart_dl.egg-info → devart_dl-4.3.4}/PKG-INFO +3 -2
- {devart_dl-4.3.2 → devart_dl-4.3.4}/README.en.md +1 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/README.md +2 -1
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/models.py +15 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/official_api.py +21 -1
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/provider.py +30 -10
- {devart_dl-4.3.2 → devart_dl-4.3.4/devart_dl.egg-info}/PKG-INFO +3 -2
- {devart_dl-4.3.2 → devart_dl-4.3.4}/pyproject.toml +1 -1
- {devart_dl-4.3.2 → devart_dl-4.3.4}/tests/test_official_provider.py +27 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/tests/test_provider.py +25 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/.env.example +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/LICENSE +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/MANIFEST.in +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/bin/devart-dl +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/bin/install.sh +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/__init__.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/api.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/archive.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/auth.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/cli.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/da_cookie_login.mjs +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/download.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/errors.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/http.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/manager.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/oauth.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/path.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/da_downloader/targets.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/devart_dl.egg-info/SOURCES.txt +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/devart_dl.egg-info/dependency_links.txt +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/devart_dl.egg-info/entry_points.txt +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/devart_dl.egg-info/requires.txt +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/devart_dl.egg-info/top_level.txt +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/requirements.txt +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/setup.cfg +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/setup.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/tests/test_api.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/tests/test_archive.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/tests/test_cli.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/tests/test_conformance.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/tests/test_download.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/tests/test_http_downloader.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/tests/test_manager.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/tests/test_path.py +0 -0
- {devart_dl-4.3.2 → devart_dl-4.3.4}/tests/test_targets.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devart-dl
|
|
3
|
-
Version: 4.3.
|
|
3
|
+
Version: 4.3.4
|
|
4
4
|
Summary: 专业的 DeviantArt 作品批量下载工具 - Professional DeviantArt batch downloader with anti-ban features
|
|
5
5
|
Author: DeviantArt Downloader Team
|
|
6
6
|
License-Expression: MIT
|
|
@@ -85,7 +85,7 @@ devart-dl login browser # 打开你的 Chrome 登录,自动保存 Cookie,
|
|
|
85
85
|
|
|
86
86
|
> 普通用户无需手动下载 GitHub Release 里的 `.whl`、`.tar.gz` 或 Source code,运行上面的 `pip install` 即可。文件区别和 SHA256 校验方法见[完整文档](https://redtidev1918.github.io/deviantart-downloader/#/?id=github-release-%e9%87%8c%e7%9a%84%e6%96%87%e4%bb%b6%e6%98%af%e4%bb%80%e4%b9%88)。
|
|
87
87
|
|
|
88
|
-
##
|
|
88
|
+
## 功能
|
|
89
89
|
|
|
90
90
|
- **官方 API(OAuth)优先**:登录一次即可下载原图,无需导出 Cookie,也无需防封延时。
|
|
91
91
|
- **URL 优先**:直接粘贴作品 / 画廊 / 收藏夹 / 标签链接即可下载。
|
|
@@ -94,6 +94,7 @@ devart-dl login browser # 打开你的 Chrome 登录,自动保存 Cookie,
|
|
|
94
94
|
- **路径模板 + 元数据 sidecar**:`--directory`/`--filename` 模板与 `--write-info-json`。
|
|
95
95
|
- **代理与降级**:支持 `--proxy`;未登录 OAuth 时自动回退 Cookie。
|
|
96
96
|
- **Resolve 模式**:`devart-dl resolve URL` 输出 `ResolvedDeviation`/`MediaAsset` 的 JSON 契约,不写文件。
|
|
97
|
+
- **付费/订阅锁定识别**:官方 API 返回 `premium_folder_data` / `tier_access`,或网页端非成熟作品的模糊主图(`blur_`),都会识别为「需要订阅/购买」:网页通道跳过,官方通道明确报错,不再把打码图当原图保存。
|
|
97
98
|
|
|
98
99
|
## 文档
|
|
99
100
|
|
|
@@ -25,6 +25,7 @@ See [CHANGELOG.md](CHANGELOG.md) for release notes.
|
|
|
25
25
|
- **Path templates** — `--directory` / `--filename` support `{id}` `{title}` `{author}` `{published}` `{ext}` and more, with safe sanitization.
|
|
26
26
|
- **Metadata sidecars** — `--write-info-json` writes a `.json` next to each file.
|
|
27
27
|
- **Proxy & fallback** — `--proxy` or proxy environment variables; falls back to cookies when not logged in via OAuth.
|
|
28
|
+
- **Paid / subscription-lock detection** — official API `premium_folder_data` / `tier_access`, or a blurred (`blur_`) non-mature main on the web path, is recognised as subscription/purchase-gated: the web path skips it and the official path fails with a clear message instead of saving a censored placeholder.
|
|
28
29
|
|
|
29
30
|
---
|
|
30
31
|
|
|
@@ -51,7 +51,7 @@ devart-dl login browser # 打开你的 Chrome 登录,自动保存 Cookie,
|
|
|
51
51
|
|
|
52
52
|
> 普通用户无需手动下载 GitHub Release 里的 `.whl`、`.tar.gz` 或 Source code,运行上面的 `pip install` 即可。文件区别和 SHA256 校验方法见[完整文档](https://redtidev1918.github.io/deviantart-downloader/#/?id=github-release-%e9%87%8c%e7%9a%84%e6%96%87%e4%bb%b6%e6%98%af%e4%bb%80%e4%b9%88)。
|
|
53
53
|
|
|
54
|
-
##
|
|
54
|
+
## 功能
|
|
55
55
|
|
|
56
56
|
- **官方 API(OAuth)优先**:登录一次即可下载原图,无需导出 Cookie,也无需防封延时。
|
|
57
57
|
- **URL 优先**:直接粘贴作品 / 画廊 / 收藏夹 / 标签链接即可下载。
|
|
@@ -60,6 +60,7 @@ devart-dl login browser # 打开你的 Chrome 登录,自动保存 Cookie,
|
|
|
60
60
|
- **路径模板 + 元数据 sidecar**:`--directory`/`--filename` 模板与 `--write-info-json`。
|
|
61
61
|
- **代理与降级**:支持 `--proxy`;未登录 OAuth 时自动回退 Cookie。
|
|
62
62
|
- **Resolve 模式**:`devart-dl resolve URL` 输出 `ResolvedDeviation`/`MediaAsset` 的 JSON 契约,不写文件。
|
|
63
|
+
- **付费/订阅锁定识别**:官方 API 返回 `premium_folder_data` / `tier_access`,或网页端非成熟作品的模糊主图(`blur_`),都会识别为「需要订阅/购买」:网页通道跳过,官方通道明确报错,不再把打码图当原图保存。
|
|
63
64
|
|
|
64
65
|
## 文档
|
|
65
66
|
|
|
@@ -29,6 +29,7 @@ class Deviation:
|
|
|
29
29
|
is_downloadable: bool
|
|
30
30
|
is_mature: bool
|
|
31
31
|
deviation_type: str
|
|
32
|
+
premium: bool = False
|
|
32
33
|
|
|
33
34
|
@classmethod
|
|
34
35
|
def from_api_response(cls, data: Dict[str, Any]) -> "Deviation":
|
|
@@ -63,6 +64,7 @@ class Deviation:
|
|
|
63
64
|
),
|
|
64
65
|
is_mature=bool(data.get("isMature", data.get("is_mature", False))),
|
|
65
66
|
deviation_type=str(deviation_type),
|
|
67
|
+
premium=_is_locked(data),
|
|
66
68
|
)
|
|
67
69
|
|
|
68
70
|
def _extract_extension_from_media(self, media: Dict[str, Any]) -> str:
|
|
@@ -91,12 +93,25 @@ class Deviation:
|
|
|
91
93
|
flags = []
|
|
92
94
|
if self.is_mature:
|
|
93
95
|
flags.append("MATURE")
|
|
96
|
+
if self.premium:
|
|
97
|
+
flags.append("PREMIUM")
|
|
94
98
|
if self.is_downloadable:
|
|
95
99
|
flags.append("DOWNLOADABLE")
|
|
96
100
|
flag_str = f" [{', '.join(flags)}]" if flags else ""
|
|
97
101
|
return f"{self.title} by {self.author}{flag_str}"
|
|
98
102
|
|
|
99
103
|
|
|
104
|
+
def _is_locked(data: Dict[str, Any]) -> bool:
|
|
105
|
+
"""True for premium/subscription-locked works (official/web DTO shapes)."""
|
|
106
|
+
premium = data.get("premium_folder_data") or data.get("premiumFolderData")
|
|
107
|
+
if isinstance(premium, dict):
|
|
108
|
+
access = premium.get("has_access", premium.get("hasAccess"))
|
|
109
|
+
if access is False:
|
|
110
|
+
return True
|
|
111
|
+
tier = data.get("tier_access") or data.get("tierAccess")
|
|
112
|
+
return tier in ("locked", "locked-subscribed")
|
|
113
|
+
|
|
114
|
+
|
|
100
115
|
@dataclass(frozen=True)
|
|
101
116
|
class DownloadItem:
|
|
102
117
|
"""A single resolved media file, produced by a provider and consumed by the
|
|
@@ -316,6 +316,26 @@ def deviation_uuid(init_data: dict) -> str:
|
|
|
316
316
|
return uuid
|
|
317
317
|
|
|
318
318
|
|
|
319
|
+
def is_locked(deviation: dict) -> bool:
|
|
320
|
+
"""Premium/paid works the current account cannot access.
|
|
321
|
+
|
|
322
|
+
The official DTO marks these with ``premium_folder_data.has_access ==
|
|
323
|
+
False`` or ``tier_access`` locked states (matching DAKit); the web DTO may
|
|
324
|
+
use the camelCase pair ``premiumFolderData.hasAccess`` / ``tierAccess``.
|
|
325
|
+
"""
|
|
326
|
+
premium = deviation.get("premium_folder_data") or deviation.get(
|
|
327
|
+
"premiumFolderData"
|
|
328
|
+
)
|
|
329
|
+
if isinstance(premium, dict) and premium.get("has_access", premium.get("hasAccess")) is False:
|
|
330
|
+
return True
|
|
331
|
+
tier = deviation.get("tier_access") or deviation.get("tierAccess")
|
|
332
|
+
return tier in ("locked", "locked-subscribed")
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def is_blurred(url: str) -> bool:
|
|
336
|
+
"""True when the URL is a censored ``blur_`` Wix transform (mature/paid)."""
|
|
337
|
+
return "blur_" in url
|
|
338
|
+
|
|
319
339
|
def additional_media_urls(init_data: dict) -> list:
|
|
320
340
|
"""Original-file URLs of a multimedia deviation's extra pages
|
|
321
341
|
(``deviation.extended.additionalMedia``, each entry nests its Wix
|
|
@@ -336,7 +356,7 @@ def additional_media_urls(init_data: dict) -> list:
|
|
|
336
356
|
if not isinstance(media, dict):
|
|
337
357
|
continue
|
|
338
358
|
url = _descriptor_media_url(media)
|
|
339
|
-
if url and
|
|
359
|
+
if url and not is_blurred(url):
|
|
340
360
|
urls.append(url)
|
|
341
361
|
return urls
|
|
342
362
|
|
|
@@ -20,6 +20,8 @@ from .official_api import (
|
|
|
20
20
|
additional_media_urls,
|
|
21
21
|
deviation_init,
|
|
22
22
|
deviation_uuid,
|
|
23
|
+
is_blurred,
|
|
24
|
+
is_locked,
|
|
23
25
|
)
|
|
24
26
|
from .targets import DownloadTarget, TargetKind
|
|
25
27
|
|
|
@@ -120,6 +122,11 @@ class WebProvider:
|
|
|
120
122
|
if media_url is None:
|
|
121
123
|
logger.warning("no media URL for %r; skipping", deviation.title)
|
|
122
124
|
return None
|
|
125
|
+
# 付费/订阅锁定:非成熟作品的模糊主图 = 当前账号拿不到原图,跳过而不是把
|
|
126
|
+
# 打码图当成作品保存(成熟打码由 official provider / additional pages 处理)。
|
|
127
|
+
if deviation.premium or (not deviation.is_mature and is_blurred(media_url)):
|
|
128
|
+
logger.warning("premium/subscription-locked %r; skipping", deviation.title)
|
|
129
|
+
return None
|
|
123
130
|
return DownloadItem(
|
|
124
131
|
artwork_id=deviation.deviation_id,
|
|
125
132
|
url=deviation.url,
|
|
@@ -262,6 +269,8 @@ class OfficialProvider:
|
|
|
262
269
|
original = self.client.original_download(uuid)
|
|
263
270
|
ext = _ext_from_url(original.filename)
|
|
264
271
|
return original.url, ext
|
|
272
|
+
url: Optional[str] = None
|
|
273
|
+
ext: Optional[str] = None
|
|
265
274
|
content = deviation.get("content") or {}
|
|
266
275
|
# `content.src` is a poster for video works; only videos[].src is playable.
|
|
267
276
|
videos = deviation.get("videos") or []
|
|
@@ -269,16 +278,27 @@ class OfficialProvider:
|
|
|
269
278
|
for video in videos:
|
|
270
279
|
src = video.get("src") if isinstance(video, dict) else None
|
|
271
280
|
if src:
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
281
|
+
url, ext = str(src), _ext_from_url(str(src))
|
|
282
|
+
break
|
|
283
|
+
if url is None and content.get("src"):
|
|
284
|
+
url, ext = str(content["src"]), _ext_from_url(str(content["src"]))
|
|
285
|
+
if url is None:
|
|
286
|
+
preview = deviation.get("preview") or {}
|
|
287
|
+
if preview.get("src"):
|
|
288
|
+
url, ext = str(preview["src"]), _ext_from_url(str(preview["src"]))
|
|
289
|
+
if url is None:
|
|
290
|
+
thumbs = deviation.get("thumbs") or []
|
|
291
|
+
if thumbs and isinstance(thumbs[0], dict) and thumbs[0].get("src"):
|
|
292
|
+
url, ext = str(thumbs[0]["src"]), _ext_from_url(str(thumbs[0]["src"]))
|
|
293
|
+
if url is None:
|
|
294
|
+
raise MediaUnavailableError(f"no media available for deviation {uuid}")
|
|
295
|
+
# 付费/订阅锁定:非成熟作品的模糊主图(或显式 premium 字段)=拿不到原图,
|
|
296
|
+
# 明确报错而不是保存一张打码占位图。成熟作品由 mature_content=true 提供未打码源。
|
|
297
|
+
if is_locked(deviation) or (not deviation.get("is_mature") and is_blurred(url)):
|
|
298
|
+
raise MediaUnavailableError(
|
|
299
|
+
f"deviation {uuid} requires a subscription/purchase (premium)"
|
|
300
|
+
)
|
|
301
|
+
return url, ext
|
|
282
302
|
|
|
283
303
|
|
|
284
304
|
def _ext_from_url(url: str) -> Optional[str]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devart-dl
|
|
3
|
-
Version: 4.3.
|
|
3
|
+
Version: 4.3.4
|
|
4
4
|
Summary: 专业的 DeviantArt 作品批量下载工具 - Professional DeviantArt batch downloader with anti-ban features
|
|
5
5
|
Author: DeviantArt Downloader Team
|
|
6
6
|
License-Expression: MIT
|
|
@@ -85,7 +85,7 @@ devart-dl login browser # 打开你的 Chrome 登录,自动保存 Cookie,
|
|
|
85
85
|
|
|
86
86
|
> 普通用户无需手动下载 GitHub Release 里的 `.whl`、`.tar.gz` 或 Source code,运行上面的 `pip install` 即可。文件区别和 SHA256 校验方法见[完整文档](https://redtidev1918.github.io/deviantart-downloader/#/?id=github-release-%e9%87%8c%e7%9a%84%e6%96%87%e4%bb%b6%e6%98%af%e4%bb%80%e4%b9%88)。
|
|
87
87
|
|
|
88
|
-
##
|
|
88
|
+
## 功能
|
|
89
89
|
|
|
90
90
|
- **官方 API(OAuth)优先**:登录一次即可下载原图,无需导出 Cookie,也无需防封延时。
|
|
91
91
|
- **URL 优先**:直接粘贴作品 / 画廊 / 收藏夹 / 标签链接即可下载。
|
|
@@ -94,6 +94,7 @@ devart-dl login browser # 打开你的 Chrome 登录,自动保存 Cookie,
|
|
|
94
94
|
- **路径模板 + 元数据 sidecar**:`--directory`/`--filename` 模板与 `--write-info-json`。
|
|
95
95
|
- **代理与降级**:支持 `--proxy`;未登录 OAuth 时自动回退 Cookie。
|
|
96
96
|
- **Resolve 模式**:`devart-dl resolve URL` 输出 `ResolvedDeviation`/`MediaAsset` 的 JSON 契约,不写文件。
|
|
97
|
+
- **付费/订阅锁定识别**:官方 API 返回 `premium_folder_data` / `tier_access`,或网页端非成熟作品的模糊主图(`blur_`),都会识别为「需要订阅/购买」:网页通道跳过,官方通道明确报错,不再把打码图当原图保存。
|
|
97
98
|
|
|
98
99
|
## 文档
|
|
99
100
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "devart-dl"
|
|
7
|
-
version = "4.3.
|
|
7
|
+
version = "4.3.4"
|
|
8
8
|
description = "专业的 DeviantArt 作品批量下载工具 - Professional DeviantArt batch downloader with anti-ban features"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -274,6 +274,33 @@ def test_no_media_raises(monkeypatch) -> None:
|
|
|
274
274
|
list(provider.resolve(TargetParser.parse("123456")))
|
|
275
275
|
|
|
276
276
|
|
|
277
|
+
def test_blurred_non_mature_main_raises_premium(monkeypatch) -> None:
|
|
278
|
+
monkeypatch.setattr(provider_mod, "deviation_init", lambda identifier, username=None, **_: {"deviation": {"extended": {"deviationUuid": "uuid-1", "additionalMedia": []}}})
|
|
279
|
+
client = FakeClient()
|
|
280
|
+
client.deviations["uuid-1"] = make_deviation(
|
|
281
|
+
uuid="uuid-1",
|
|
282
|
+
content_src="https://images.test/blur_40/full.jpg",
|
|
283
|
+
is_downloadable=False,
|
|
284
|
+
mature=False,
|
|
285
|
+
)
|
|
286
|
+
provider = OfficialProvider(client, quality="f")
|
|
287
|
+
|
|
288
|
+
with pytest.raises(MediaUnavailableError, match="subscription/purchase"):
|
|
289
|
+
list(provider.resolve(TargetParser.parse("123456")))
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def test_premium_folder_without_access_raises_premium(monkeypatch) -> None:
|
|
293
|
+
monkeypatch.setattr(provider_mod, "deviation_init", lambda identifier, username=None, **_: {"deviation": {"extended": {"deviationUuid": "uuid-1", "additionalMedia": []}}})
|
|
294
|
+
client = FakeClient()
|
|
295
|
+
deviation = make_deviation(uuid="uuid-1", is_downloadable=False)
|
|
296
|
+
deviation["premium_folder_data"] = {"has_access": False}
|
|
297
|
+
client.deviations["uuid-1"] = deviation
|
|
298
|
+
provider = OfficialProvider(client, quality="f")
|
|
299
|
+
|
|
300
|
+
with pytest.raises(MediaUnavailableError, match="subscription/purchase"):
|
|
301
|
+
list(provider.resolve(TargetParser.parse("123456")))
|
|
302
|
+
|
|
303
|
+
|
|
277
304
|
def test_tag_target() -> None:
|
|
278
305
|
client = FakeClient()
|
|
279
306
|
client.pages[("browse_tags", "landscape", 0)] = {
|
|
@@ -74,6 +74,31 @@ def test_gallery_yields_items_with_media_url_and_extension() -> None:
|
|
|
74
74
|
assert items[0].extension == "png"
|
|
75
75
|
|
|
76
76
|
|
|
77
|
+
def test_blurred_non_mature_item_is_skipped() -> None:
|
|
78
|
+
dev = make_deviation()
|
|
79
|
+
provider = make_provider(
|
|
80
|
+
pages=[([dev], False, 1, "")],
|
|
81
|
+
media_urls={"123": "https://images.test/blur_40/123.jpg"},
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
items = list(provider.resolve(TargetParser.parse("https://www.deviantart.com/alice/gallery")))
|
|
85
|
+
|
|
86
|
+
assert items == []
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_premium_locked_item_is_skipped() -> None:
|
|
90
|
+
dev = make_deviation()
|
|
91
|
+
dev.premium = True
|
|
92
|
+
provider = make_provider(
|
|
93
|
+
pages=[([dev], False, 1, "")],
|
|
94
|
+
media_urls={"123": "https://images.test/123.jpg"},
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
items = list(provider.resolve(TargetParser.parse("https://www.deviantart.com/alice/gallery")))
|
|
98
|
+
|
|
99
|
+
assert items == []
|
|
100
|
+
|
|
101
|
+
|
|
77
102
|
def test_pagination_across_two_pages() -> None:
|
|
78
103
|
first = make_deviation("1")
|
|
79
104
|
second = make_deviation("2")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|