quasarr 2.4.8__py3-none-any.whl → 2.4.10__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.

Potentially problematic release.


This version of quasarr might be problematic. Click here for more details.

Files changed (76) hide show
  1. quasarr/__init__.py +134 -70
  2. quasarr/api/__init__.py +40 -31
  3. quasarr/api/arr/__init__.py +116 -108
  4. quasarr/api/captcha/__init__.py +262 -137
  5. quasarr/api/config/__init__.py +76 -46
  6. quasarr/api/packages/__init__.py +138 -102
  7. quasarr/api/sponsors_helper/__init__.py +29 -16
  8. quasarr/api/statistics/__init__.py +19 -19
  9. quasarr/downloads/__init__.py +165 -72
  10. quasarr/downloads/linkcrypters/al.py +35 -18
  11. quasarr/downloads/linkcrypters/filecrypt.py +107 -52
  12. quasarr/downloads/linkcrypters/hide.py +5 -6
  13. quasarr/downloads/packages/__init__.py +342 -177
  14. quasarr/downloads/sources/al.py +191 -100
  15. quasarr/downloads/sources/by.py +31 -13
  16. quasarr/downloads/sources/dd.py +27 -14
  17. quasarr/downloads/sources/dj.py +1 -3
  18. quasarr/downloads/sources/dl.py +126 -71
  19. quasarr/downloads/sources/dt.py +11 -5
  20. quasarr/downloads/sources/dw.py +28 -14
  21. quasarr/downloads/sources/he.py +32 -24
  22. quasarr/downloads/sources/mb.py +19 -9
  23. quasarr/downloads/sources/nk.py +14 -10
  24. quasarr/downloads/sources/nx.py +8 -18
  25. quasarr/downloads/sources/sf.py +45 -20
  26. quasarr/downloads/sources/sj.py +1 -3
  27. quasarr/downloads/sources/sl.py +9 -5
  28. quasarr/downloads/sources/wd.py +32 -12
  29. quasarr/downloads/sources/wx.py +35 -21
  30. quasarr/providers/auth.py +42 -37
  31. quasarr/providers/cloudflare.py +28 -30
  32. quasarr/providers/hostname_issues.py +2 -1
  33. quasarr/providers/html_images.py +2 -2
  34. quasarr/providers/html_templates.py +22 -14
  35. quasarr/providers/imdb_metadata.py +149 -80
  36. quasarr/providers/jd_cache.py +131 -39
  37. quasarr/providers/log.py +1 -1
  38. quasarr/providers/myjd_api.py +260 -196
  39. quasarr/providers/notifications.py +53 -41
  40. quasarr/providers/obfuscated.py +9 -4
  41. quasarr/providers/sessions/al.py +71 -55
  42. quasarr/providers/sessions/dd.py +21 -14
  43. quasarr/providers/sessions/dl.py +30 -19
  44. quasarr/providers/sessions/nx.py +23 -14
  45. quasarr/providers/shared_state.py +292 -141
  46. quasarr/providers/statistics.py +75 -43
  47. quasarr/providers/utils.py +33 -27
  48. quasarr/providers/version.py +45 -14
  49. quasarr/providers/web_server.py +10 -5
  50. quasarr/search/__init__.py +30 -18
  51. quasarr/search/sources/al.py +124 -73
  52. quasarr/search/sources/by.py +110 -59
  53. quasarr/search/sources/dd.py +57 -35
  54. quasarr/search/sources/dj.py +69 -48
  55. quasarr/search/sources/dl.py +159 -100
  56. quasarr/search/sources/dt.py +110 -74
  57. quasarr/search/sources/dw.py +121 -61
  58. quasarr/search/sources/fx.py +108 -62
  59. quasarr/search/sources/he.py +78 -49
  60. quasarr/search/sources/mb.py +96 -48
  61. quasarr/search/sources/nk.py +80 -50
  62. quasarr/search/sources/nx.py +91 -62
  63. quasarr/search/sources/sf.py +171 -106
  64. quasarr/search/sources/sj.py +69 -48
  65. quasarr/search/sources/sl.py +115 -71
  66. quasarr/search/sources/wd.py +67 -44
  67. quasarr/search/sources/wx.py +188 -123
  68. quasarr/storage/config.py +65 -52
  69. quasarr/storage/setup.py +238 -140
  70. quasarr/storage/sqlite_database.py +10 -4
  71. {quasarr-2.4.8.dist-info → quasarr-2.4.10.dist-info}/METADATA +4 -3
  72. quasarr-2.4.10.dist-info/RECORD +81 -0
  73. quasarr-2.4.8.dist-info/RECORD +0 -81
  74. {quasarr-2.4.8.dist-info → quasarr-2.4.10.dist-info}/WHEEL +0 -0
  75. {quasarr-2.4.8.dist-info → quasarr-2.4.10.dist-info}/entry_points.txt +0 -0
  76. {quasarr-2.4.8.dist-info → quasarr-2.4.10.dist-info}/licenses/LICENSE +0 -0
@@ -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 TokenExpiredException, RequestTimeoutException, MYJDException
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
- '.rar', '.zip', '.7z', '.tar', '.gz', '.bz2', '.xz',
11
- '.001', '.002', '.003', '.004', '.005', '.006', '.007', '.008', '.009',
12
- '.r00', '.r01', '.r02', '.r03', '.r04', '.r05', '.r06', '.r07', '.r08', '.r09',
13
- '.part1.rar', '.part01.rar', '.part001.rar',
14
- '.part2.rar', '.part02.rar', '.part002.rar',
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(self._linkgrabber_packages or [])
47
- link_count = len(self._downloader_links or []) + len(self._linkgrabber_links or [])
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(f"JDPackageCache: Retrieved {len(self._linkgrabber_packages)} linkgrabber packages")
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(f"JDPackageCache: Using cached linkgrabber_packages ({len(self._linkgrabber_packages)} packages)")
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(f"JDPackageCache: Retrieved {len(self._linkgrabber_links)} linkgrabber links")
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(f"JDPackageCache: Using cached linkgrabber_links ({len(self._linkgrabber_links)} links)")
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(f"JDPackageCache: Retrieved {len(self._downloader_packages)} downloader packages")
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(f"JDPackageCache: Using cached downloader_packages ({len(self._downloader_packages)} packages)")
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(f"JDPackageCache: Retrieved {len(self._downloader_links)} downloader links")
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(f"JDPackageCache: Using cached downloader_links ({len(self._downloader_links)} links)")
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("JDPackageCache: _bulk_detect_archives called with empty package_uuids")
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(f"JDPackageCache: Bulk archive detection for {len(package_list)} packages")
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(f"JDPackageCache: get_archive_info returned {len(archive_infos) if archive_infos else 0} results")
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(f"JDPackageCache: Confirmed archive via packageUUID: {pkg_uuid}")
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(f"JDPackageCache: Bulk detection confirmed {len(confirmed_archives)} archives: {confirmed_archives}")
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(f"JDPackageCache: Bulk archive detection API FAILED: {type(e).__name__}: {e}")
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(f"JDPackageCache: detect_all_archives for {len(all_package_uuids)} packages")
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(all_package_uuids)
213
- debug(f"JDPackageCache: Bulk API succeeded={api_succeeded}, confirmed={len(confirmed_archives)} archives")
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(f"JDPackageCache: Package {pkg_uuid} confirmed as archive via extension fallback")
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(f"JDPackageCache: SAFETY - Bulk API failed, assuming package {pkg_uuid} is archive")
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(f"JDPackageCache: Package {pkg_uuid} confirmed as NON-archive (API worked, no extension match)")
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(f"JDPackageCache: is_package_archive({package_uuid}) = {cached} (cached)")
336
+ debug(
337
+ f"JDPackageCache: is_package_archive({package_uuid}) = {cached} (cached)"
338
+ )
257
339
  return cached
258
340
 
259
- debug(f"JDPackageCache: is_package_archive({package_uuid}) - cache miss, querying API")
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(f"JDPackageCache: Single archive detection API FAILED for {package_uuid}: {type(e).__name__}: {e}")
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(f"JDPackageCache: Package {package_uuid} confirmed as archive via extension fallback")
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(f"JDPackageCache: SAFETY - Detection uncertain for {package_uuid}, assuming archive")
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(f"JDPackageCache: is_package_archive({package_uuid}) = {is_archive} (final)")
378
+ debug(
379
+ f"JDPackageCache: is_package_archive({package_uuid}) = {is_archive} (final)"
380
+ )
289
381
  return is_archive
quasarr/providers/log.py CHANGED
@@ -15,5 +15,5 @@ def info(string):
15
15
 
16
16
 
17
17
  def debug(string):
18
- if os.getenv('DEBUG'):
18
+ if os.getenv("DEBUG"):
19
19
  info(string)