quasarr 2.4.8__py3-none-any.whl → 2.4.9__py3-none-any.whl
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.
- quasarr/__init__.py +134 -70
- quasarr/api/__init__.py +40 -31
- quasarr/api/arr/__init__.py +116 -108
- quasarr/api/captcha/__init__.py +262 -137
- quasarr/api/config/__init__.py +76 -46
- quasarr/api/packages/__init__.py +138 -102
- quasarr/api/sponsors_helper/__init__.py +29 -16
- quasarr/api/statistics/__init__.py +19 -19
- quasarr/downloads/__init__.py +165 -72
- quasarr/downloads/linkcrypters/al.py +35 -18
- quasarr/downloads/linkcrypters/filecrypt.py +107 -52
- quasarr/downloads/linkcrypters/hide.py +5 -6
- quasarr/downloads/packages/__init__.py +342 -177
- quasarr/downloads/sources/al.py +191 -100
- quasarr/downloads/sources/by.py +31 -13
- quasarr/downloads/sources/dd.py +27 -14
- quasarr/downloads/sources/dj.py +1 -3
- quasarr/downloads/sources/dl.py +126 -71
- quasarr/downloads/sources/dt.py +11 -5
- quasarr/downloads/sources/dw.py +28 -14
- quasarr/downloads/sources/he.py +32 -24
- quasarr/downloads/sources/mb.py +19 -9
- quasarr/downloads/sources/nk.py +14 -10
- quasarr/downloads/sources/nx.py +8 -18
- quasarr/downloads/sources/sf.py +45 -20
- quasarr/downloads/sources/sj.py +1 -3
- quasarr/downloads/sources/sl.py +9 -5
- quasarr/downloads/sources/wd.py +32 -12
- quasarr/downloads/sources/wx.py +35 -21
- quasarr/providers/auth.py +42 -37
- quasarr/providers/cloudflare.py +28 -30
- quasarr/providers/hostname_issues.py +2 -1
- quasarr/providers/html_images.py +2 -2
- quasarr/providers/html_templates.py +22 -14
- quasarr/providers/imdb_metadata.py +149 -80
- quasarr/providers/jd_cache.py +131 -39
- quasarr/providers/log.py +1 -1
- quasarr/providers/myjd_api.py +260 -196
- quasarr/providers/notifications.py +53 -41
- quasarr/providers/obfuscated.py +9 -4
- quasarr/providers/sessions/al.py +71 -55
- quasarr/providers/sessions/dd.py +21 -14
- quasarr/providers/sessions/dl.py +30 -19
- quasarr/providers/sessions/nx.py +23 -14
- quasarr/providers/shared_state.py +292 -141
- quasarr/providers/statistics.py +75 -43
- quasarr/providers/utils.py +33 -27
- quasarr/providers/version.py +45 -14
- quasarr/providers/web_server.py +10 -5
- quasarr/search/__init__.py +30 -18
- quasarr/search/sources/al.py +124 -73
- quasarr/search/sources/by.py +110 -59
- quasarr/search/sources/dd.py +57 -35
- quasarr/search/sources/dj.py +69 -48
- quasarr/search/sources/dl.py +159 -100
- quasarr/search/sources/dt.py +110 -74
- quasarr/search/sources/dw.py +121 -61
- quasarr/search/sources/fx.py +108 -62
- quasarr/search/sources/he.py +78 -49
- quasarr/search/sources/mb.py +96 -48
- quasarr/search/sources/nk.py +80 -50
- quasarr/search/sources/nx.py +91 -62
- quasarr/search/sources/sf.py +171 -106
- quasarr/search/sources/sj.py +69 -48
- quasarr/search/sources/sl.py +115 -71
- quasarr/search/sources/wd.py +67 -44
- quasarr/search/sources/wx.py +188 -123
- quasarr/storage/config.py +65 -52
- quasarr/storage/setup.py +238 -140
- quasarr/storage/sqlite_database.py +10 -4
- {quasarr-2.4.8.dist-info → quasarr-2.4.9.dist-info}/METADATA +2 -2
- quasarr-2.4.9.dist-info/RECORD +81 -0
- quasarr-2.4.8.dist-info/RECORD +0 -81
- {quasarr-2.4.8.dist-info → quasarr-2.4.9.dist-info}/WHEEL +0 -0
- {quasarr-2.4.8.dist-info → quasarr-2.4.9.dist-info}/entry_points.txt +0 -0
- {quasarr-2.4.8.dist-info → quasarr-2.4.9.dist-info}/licenses/LICENSE +0 -0
quasarr/providers/jd_cache.py
CHANGED
|
@@ -3,16 +3,49 @@
|
|
|
3
3
|
# Project by https://github.com/rix1337
|
|
4
4
|
|
|
5
5
|
from quasarr.providers.log import debug
|
|
6
|
-
from quasarr.providers.myjd_api import
|
|
6
|
+
from quasarr.providers.myjd_api import (
|
|
7
|
+
MYJDException,
|
|
8
|
+
RequestTimeoutException,
|
|
9
|
+
TokenExpiredException,
|
|
10
|
+
)
|
|
7
11
|
|
|
8
12
|
# Known archive extensions for fallback detection
|
|
9
|
-
ARCHIVE_EXTENSIONS = frozenset(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
ARCHIVE_EXTENSIONS = frozenset(
|
|
14
|
+
[
|
|
15
|
+
".rar",
|
|
16
|
+
".zip",
|
|
17
|
+
".7z",
|
|
18
|
+
".tar",
|
|
19
|
+
".gz",
|
|
20
|
+
".bz2",
|
|
21
|
+
".xz",
|
|
22
|
+
".001",
|
|
23
|
+
".002",
|
|
24
|
+
".003",
|
|
25
|
+
".004",
|
|
26
|
+
".005",
|
|
27
|
+
".006",
|
|
28
|
+
".007",
|
|
29
|
+
".008",
|
|
30
|
+
".009",
|
|
31
|
+
".r00",
|
|
32
|
+
".r01",
|
|
33
|
+
".r02",
|
|
34
|
+
".r03",
|
|
35
|
+
".r04",
|
|
36
|
+
".r05",
|
|
37
|
+
".r06",
|
|
38
|
+
".r07",
|
|
39
|
+
".r08",
|
|
40
|
+
".r09",
|
|
41
|
+
".part1.rar",
|
|
42
|
+
".part01.rar",
|
|
43
|
+
".part001.rar",
|
|
44
|
+
".part2.rar",
|
|
45
|
+
".part02.rar",
|
|
46
|
+
".part002.rar",
|
|
47
|
+
]
|
|
48
|
+
)
|
|
16
49
|
|
|
17
50
|
|
|
18
51
|
class JDPackageCache:
|
|
@@ -43,8 +76,12 @@ class JDPackageCache:
|
|
|
43
76
|
|
|
44
77
|
def get_stats(self):
|
|
45
78
|
"""Return cache statistics string."""
|
|
46
|
-
pkg_count = len(self._downloader_packages or []) + len(
|
|
47
|
-
|
|
79
|
+
pkg_count = len(self._downloader_packages or []) + len(
|
|
80
|
+
self._linkgrabber_packages or []
|
|
81
|
+
)
|
|
82
|
+
link_count = len(self._downloader_links or []) + len(
|
|
83
|
+
self._linkgrabber_links or []
|
|
84
|
+
)
|
|
48
85
|
return f"{self._api_calls} API calls | {pkg_count} packages, {link_count} links cached"
|
|
49
86
|
|
|
50
87
|
@property
|
|
@@ -54,13 +91,17 @@ class JDPackageCache:
|
|
|
54
91
|
self._api_calls += 1
|
|
55
92
|
try:
|
|
56
93
|
self._linkgrabber_packages = self._device.linkgrabber.query_packages()
|
|
57
|
-
debug(
|
|
94
|
+
debug(
|
|
95
|
+
f"JDPackageCache: Retrieved {len(self._linkgrabber_packages)} linkgrabber packages"
|
|
96
|
+
)
|
|
58
97
|
except (TokenExpiredException, RequestTimeoutException, MYJDException) as e:
|
|
59
98
|
debug(f"JDPackageCache: Failed to fetch linkgrabber_packages: {e}")
|
|
60
99
|
self._linkgrabber_packages = []
|
|
61
100
|
else:
|
|
62
101
|
self._cache_hits += 1
|
|
63
|
-
debug(
|
|
102
|
+
debug(
|
|
103
|
+
f"JDPackageCache: Using cached linkgrabber_packages ({len(self._linkgrabber_packages)} packages)"
|
|
104
|
+
)
|
|
64
105
|
return self._linkgrabber_packages
|
|
65
106
|
|
|
66
107
|
@property
|
|
@@ -70,13 +111,17 @@ class JDPackageCache:
|
|
|
70
111
|
self._api_calls += 1
|
|
71
112
|
try:
|
|
72
113
|
self._linkgrabber_links = self._device.linkgrabber.query_links()
|
|
73
|
-
debug(
|
|
114
|
+
debug(
|
|
115
|
+
f"JDPackageCache: Retrieved {len(self._linkgrabber_links)} linkgrabber links"
|
|
116
|
+
)
|
|
74
117
|
except (TokenExpiredException, RequestTimeoutException, MYJDException) as e:
|
|
75
118
|
debug(f"JDPackageCache: Failed to fetch linkgrabber_links: {e}")
|
|
76
119
|
self._linkgrabber_links = []
|
|
77
120
|
else:
|
|
78
121
|
self._cache_hits += 1
|
|
79
|
-
debug(
|
|
122
|
+
debug(
|
|
123
|
+
f"JDPackageCache: Using cached linkgrabber_links ({len(self._linkgrabber_links)} links)"
|
|
124
|
+
)
|
|
80
125
|
return self._linkgrabber_links
|
|
81
126
|
|
|
82
127
|
@property
|
|
@@ -86,13 +131,17 @@ class JDPackageCache:
|
|
|
86
131
|
self._api_calls += 1
|
|
87
132
|
try:
|
|
88
133
|
self._downloader_packages = self._device.downloads.query_packages()
|
|
89
|
-
debug(
|
|
134
|
+
debug(
|
|
135
|
+
f"JDPackageCache: Retrieved {len(self._downloader_packages)} downloader packages"
|
|
136
|
+
)
|
|
90
137
|
except (TokenExpiredException, RequestTimeoutException, MYJDException) as e:
|
|
91
138
|
debug(f"JDPackageCache: Failed to fetch downloader_packages: {e}")
|
|
92
139
|
self._downloader_packages = []
|
|
93
140
|
else:
|
|
94
141
|
self._cache_hits += 1
|
|
95
|
-
debug(
|
|
142
|
+
debug(
|
|
143
|
+
f"JDPackageCache: Using cached downloader_packages ({len(self._downloader_packages)} packages)"
|
|
144
|
+
)
|
|
96
145
|
return self._downloader_packages
|
|
97
146
|
|
|
98
147
|
@property
|
|
@@ -102,13 +151,17 @@ class JDPackageCache:
|
|
|
102
151
|
self._api_calls += 1
|
|
103
152
|
try:
|
|
104
153
|
self._downloader_links = self._device.downloads.query_links()
|
|
105
|
-
debug(
|
|
154
|
+
debug(
|
|
155
|
+
f"JDPackageCache: Retrieved {len(self._downloader_links)} downloader links"
|
|
156
|
+
)
|
|
106
157
|
except (TokenExpiredException, RequestTimeoutException, MYJDException) as e:
|
|
107
158
|
debug(f"JDPackageCache: Failed to fetch downloader_links: {e}")
|
|
108
159
|
self._downloader_links = []
|
|
109
160
|
else:
|
|
110
161
|
self._cache_hits += 1
|
|
111
|
-
debug(
|
|
162
|
+
debug(
|
|
163
|
+
f"JDPackageCache: Using cached downloader_links ({len(self._downloader_links)} links)"
|
|
164
|
+
)
|
|
112
165
|
return self._downloader_links
|
|
113
166
|
|
|
114
167
|
@property
|
|
@@ -137,7 +190,8 @@ class JDPackageCache:
|
|
|
137
190
|
for ext in ARCHIVE_EXTENSIONS:
|
|
138
191
|
if name_lower.endswith(ext):
|
|
139
192
|
debug(
|
|
140
|
-
f"JDPackageCache: Found archive extension '{ext}' in file '{name}' for package {package_uuid}"
|
|
193
|
+
f"JDPackageCache: Found archive extension '{ext}' in file '{name}' for package {package_uuid}"
|
|
194
|
+
)
|
|
141
195
|
return True
|
|
142
196
|
return False
|
|
143
197
|
|
|
@@ -153,16 +207,22 @@ class JDPackageCache:
|
|
|
153
207
|
confirmed_archives = set()
|
|
154
208
|
|
|
155
209
|
if not package_uuids:
|
|
156
|
-
debug(
|
|
210
|
+
debug(
|
|
211
|
+
"JDPackageCache: _bulk_detect_archives called with empty package_uuids"
|
|
212
|
+
)
|
|
157
213
|
return confirmed_archives, True
|
|
158
214
|
|
|
159
215
|
package_list = list(package_uuids)
|
|
160
|
-
debug(
|
|
216
|
+
debug(
|
|
217
|
+
f"JDPackageCache: Bulk archive detection for {len(package_list)} packages"
|
|
218
|
+
)
|
|
161
219
|
|
|
162
220
|
try:
|
|
163
221
|
self._api_calls += 1
|
|
164
222
|
archive_infos = self._device.extraction.get_archive_info([], package_list)
|
|
165
|
-
debug(
|
|
223
|
+
debug(
|
|
224
|
+
f"JDPackageCache: get_archive_info returned {len(archive_infos) if archive_infos else 0} results"
|
|
225
|
+
)
|
|
166
226
|
|
|
167
227
|
if archive_infos:
|
|
168
228
|
for i, archive_info in enumerate(archive_infos):
|
|
@@ -171,20 +231,27 @@ class JDPackageCache:
|
|
|
171
231
|
# Try to get packageUUID from response
|
|
172
232
|
pkg_uuid = archive_info.get("packageUUID")
|
|
173
233
|
if pkg_uuid:
|
|
174
|
-
debug(
|
|
234
|
+
debug(
|
|
235
|
+
f"JDPackageCache: Confirmed archive via packageUUID: {pkg_uuid}"
|
|
236
|
+
)
|
|
175
237
|
confirmed_archives.add(pkg_uuid)
|
|
176
238
|
else:
|
|
177
239
|
# Log what fields ARE available for debugging
|
|
178
240
|
debug(
|
|
179
|
-
f"JDPackageCache: archive_info has no packageUUID, available keys: {list(archive_info.keys())}"
|
|
241
|
+
f"JDPackageCache: archive_info has no packageUUID, available keys: {list(archive_info.keys())}"
|
|
242
|
+
)
|
|
180
243
|
else:
|
|
181
244
|
debug(f"JDPackageCache: archive_info[{i}] is empty/None")
|
|
182
245
|
|
|
183
|
-
debug(
|
|
246
|
+
debug(
|
|
247
|
+
f"JDPackageCache: Bulk detection confirmed {len(confirmed_archives)} archives: {confirmed_archives}"
|
|
248
|
+
)
|
|
184
249
|
return confirmed_archives, True
|
|
185
250
|
|
|
186
251
|
except Exception as e:
|
|
187
|
-
debug(
|
|
252
|
+
debug(
|
|
253
|
+
f"JDPackageCache: Bulk archive detection API FAILED: {type(e).__name__}: {e}"
|
|
254
|
+
)
|
|
188
255
|
return confirmed_archives, False
|
|
189
256
|
|
|
190
257
|
def detect_all_archives(self, packages, links):
|
|
@@ -206,11 +273,17 @@ class JDPackageCache:
|
|
|
206
273
|
return set()
|
|
207
274
|
|
|
208
275
|
all_package_uuids = {p.get("uuid") for p in packages if p.get("uuid")}
|
|
209
|
-
debug(
|
|
276
|
+
debug(
|
|
277
|
+
f"JDPackageCache: detect_all_archives for {len(all_package_uuids)} packages"
|
|
278
|
+
)
|
|
210
279
|
|
|
211
280
|
# ONE bulk API call for all packages
|
|
212
|
-
confirmed_archives, api_succeeded = self._bulk_detect_archives(
|
|
213
|
-
|
|
281
|
+
confirmed_archives, api_succeeded = self._bulk_detect_archives(
|
|
282
|
+
all_package_uuids
|
|
283
|
+
)
|
|
284
|
+
debug(
|
|
285
|
+
f"JDPackageCache: Bulk API succeeded={api_succeeded}, confirmed={len(confirmed_archives)} archives"
|
|
286
|
+
)
|
|
214
287
|
|
|
215
288
|
# For packages NOT confirmed as archives, apply safety fallbacks
|
|
216
289
|
unconfirmed = all_package_uuids - confirmed_archives
|
|
@@ -219,21 +292,28 @@ class JDPackageCache:
|
|
|
219
292
|
for pkg_uuid in unconfirmed:
|
|
220
293
|
# Fallback 1: Check file extensions
|
|
221
294
|
if self._has_archive_extension(pkg_uuid, links):
|
|
222
|
-
debug(
|
|
295
|
+
debug(
|
|
296
|
+
f"JDPackageCache: Package {pkg_uuid} confirmed as archive via extension fallback"
|
|
297
|
+
)
|
|
223
298
|
confirmed_archives.add(pkg_uuid)
|
|
224
299
|
# Fallback 2: If bulk API failed completely, assume archive (safe)
|
|
225
300
|
elif not api_succeeded:
|
|
226
|
-
debug(
|
|
301
|
+
debug(
|
|
302
|
+
f"JDPackageCache: SAFETY - Bulk API failed, assuming package {pkg_uuid} is archive"
|
|
303
|
+
)
|
|
227
304
|
confirmed_archives.add(pkg_uuid)
|
|
228
305
|
else:
|
|
229
|
-
debug(
|
|
306
|
+
debug(
|
|
307
|
+
f"JDPackageCache: Package {pkg_uuid} confirmed as NON-archive (API worked, no extension match)"
|
|
308
|
+
)
|
|
230
309
|
|
|
231
310
|
# Cache results for is_package_archive() lookups
|
|
232
311
|
for pkg_uuid in all_package_uuids:
|
|
233
312
|
self._archive_cache[pkg_uuid] = pkg_uuid in confirmed_archives
|
|
234
313
|
|
|
235
314
|
debug(
|
|
236
|
-
f"JDPackageCache: Final archive detection: {len(confirmed_archives)}/{len(all_package_uuids)} packages are archives"
|
|
315
|
+
f"JDPackageCache: Final archive detection: {len(confirmed_archives)}/{len(all_package_uuids)} packages are archives"
|
|
316
|
+
)
|
|
237
317
|
return confirmed_archives
|
|
238
318
|
|
|
239
319
|
def is_package_archive(self, package_uuid, links=None):
|
|
@@ -253,10 +333,14 @@ class JDPackageCache:
|
|
|
253
333
|
if package_uuid in self._archive_cache:
|
|
254
334
|
self._cache_hits += 1
|
|
255
335
|
cached = self._archive_cache[package_uuid]
|
|
256
|
-
debug(
|
|
336
|
+
debug(
|
|
337
|
+
f"JDPackageCache: is_package_archive({package_uuid}) = {cached} (cached)"
|
|
338
|
+
)
|
|
257
339
|
return cached
|
|
258
340
|
|
|
259
|
-
debug(
|
|
341
|
+
debug(
|
|
342
|
+
f"JDPackageCache: is_package_archive({package_uuid}) - cache miss, querying API"
|
|
343
|
+
)
|
|
260
344
|
|
|
261
345
|
# Single package lookup (fallback if detect_all_archives wasn't called)
|
|
262
346
|
is_archive = None
|
|
@@ -271,19 +355,27 @@ class JDPackageCache:
|
|
|
271
355
|
debug(f"JDPackageCache: API says is_archive = {is_archive}")
|
|
272
356
|
except Exception as e:
|
|
273
357
|
api_failed = True
|
|
274
|
-
debug(
|
|
358
|
+
debug(
|
|
359
|
+
f"JDPackageCache: Single archive detection API FAILED for {package_uuid}: {type(e).__name__}: {e}"
|
|
360
|
+
)
|
|
275
361
|
|
|
276
362
|
# Fallback: check file extensions if API failed or returned False
|
|
277
363
|
if (api_failed or not is_archive) and links:
|
|
278
364
|
if self._has_archive_extension(package_uuid, links):
|
|
279
|
-
debug(
|
|
365
|
+
debug(
|
|
366
|
+
f"JDPackageCache: Package {package_uuid} confirmed as archive via extension fallback"
|
|
367
|
+
)
|
|
280
368
|
is_archive = True
|
|
281
369
|
|
|
282
370
|
# SAFETY: If API failed and no extension detected, assume archive (conservative)
|
|
283
371
|
if is_archive is None:
|
|
284
|
-
debug(
|
|
372
|
+
debug(
|
|
373
|
+
f"JDPackageCache: SAFETY - Detection uncertain for {package_uuid}, assuming archive"
|
|
374
|
+
)
|
|
285
375
|
is_archive = True
|
|
286
376
|
|
|
287
377
|
self._archive_cache[package_uuid] = is_archive
|
|
288
|
-
debug(
|
|
378
|
+
debug(
|
|
379
|
+
f"JDPackageCache: is_package_archive({package_uuid}) = {is_archive} (final)"
|
|
380
|
+
)
|
|
289
381
|
return is_archive
|
quasarr/providers/log.py
CHANGED