quasarr 1.30.0__tar.gz → 1.32.0__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.

Potentially problematic release.


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

Files changed (89) hide show
  1. {quasarr-1.30.0 → quasarr-1.32.0}/PKG-INFO +1 -1
  2. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/api/captcha/__init__.py +29 -22
  3. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/__init__.py +7 -0
  4. quasarr-1.32.0/quasarr/downloads/packages/__init__.py +753 -0
  5. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/dl.py +24 -122
  6. quasarr-1.32.0/quasarr/downloads/sources/wx.py +168 -0
  7. quasarr-1.32.0/quasarr/providers/jd_cache.py +289 -0
  8. quasarr-1.32.0/quasarr/providers/utils.py +366 -0
  9. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/version.py +1 -1
  10. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/wx.py +40 -24
  11. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/storage/setup.py +3 -1
  12. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr.egg-info/PKG-INFO +1 -1
  13. quasarr-1.30.0/quasarr/downloads/packages/__init__.py +0 -490
  14. quasarr-1.30.0/quasarr/downloads/sources/wx.py +0 -126
  15. quasarr-1.30.0/quasarr/providers/jd_cache.py +0 -131
  16. quasarr-1.30.0/quasarr/providers/utils.py +0 -189
  17. {quasarr-1.30.0 → quasarr-1.32.0}/LICENSE +0 -0
  18. {quasarr-1.30.0 → quasarr-1.32.0}/README.md +0 -0
  19. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/__init__.py +0 -0
  20. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/api/__init__.py +0 -0
  21. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/api/arr/__init__.py +0 -0
  22. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/api/config/__init__.py +0 -0
  23. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/api/sponsors_helper/__init__.py +0 -0
  24. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/api/statistics/__init__.py +0 -0
  25. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/linkcrypters/__init__.py +0 -0
  26. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/linkcrypters/al.py +0 -0
  27. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/linkcrypters/filecrypt.py +0 -0
  28. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/linkcrypters/hide.py +0 -0
  29. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/__init__.py +0 -0
  30. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/al.py +0 -0
  31. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/by.py +0 -0
  32. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/dd.py +0 -0
  33. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/dj.py +0 -0
  34. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/dt.py +0 -0
  35. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/dw.py +0 -0
  36. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/he.py +0 -0
  37. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/mb.py +0 -0
  38. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/nk.py +0 -0
  39. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/nx.py +0 -0
  40. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/sf.py +0 -0
  41. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/sj.py +0 -0
  42. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/sl.py +0 -0
  43. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/downloads/sources/wd.py +0 -0
  44. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/__init__.py +0 -0
  45. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/cloudflare.py +0 -0
  46. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/html_images.py +0 -0
  47. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/html_templates.py +0 -0
  48. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/imdb_metadata.py +0 -0
  49. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/log.py +0 -0
  50. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/myjd_api.py +0 -0
  51. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/notifications.py +0 -0
  52. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/obfuscated.py +0 -0
  53. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/sessions/__init__.py +0 -0
  54. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/sessions/al.py +0 -0
  55. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/sessions/dd.py +0 -0
  56. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/sessions/dl.py +0 -0
  57. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/sessions/nx.py +0 -0
  58. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/shared_state.py +0 -0
  59. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/statistics.py +0 -0
  60. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/providers/web_server.py +0 -0
  61. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/__init__.py +0 -0
  62. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/__init__.py +0 -0
  63. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/al.py +0 -0
  64. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/by.py +0 -0
  65. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/dd.py +0 -0
  66. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/dj.py +0 -0
  67. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/dl.py +0 -0
  68. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/dt.py +0 -0
  69. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/dw.py +0 -0
  70. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/fx.py +0 -0
  71. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/he.py +0 -0
  72. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/mb.py +0 -0
  73. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/nk.py +0 -0
  74. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/nx.py +0 -0
  75. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/sf.py +0 -0
  76. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/sj.py +0 -0
  77. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/sl.py +0 -0
  78. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/search/sources/wd.py +0 -0
  79. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/storage/__init__.py +0 -0
  80. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/storage/config.py +0 -0
  81. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr/storage/sqlite_database.py +0 -0
  82. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr.egg-info/SOURCES.txt +0 -0
  83. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr.egg-info/dependency_links.txt +0 -0
  84. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr.egg-info/entry_points.txt +0 -0
  85. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr.egg-info/not-zip-safe +0 -0
  86. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr.egg-info/requires.txt +0 -0
  87. {quasarr-1.30.0 → quasarr-1.32.0}/quasarr.egg-info/top_level.txt +0 -0
  88. {quasarr-1.30.0 → quasarr-1.32.0}/setup.cfg +0 -0
  89. {quasarr-1.30.0 → quasarr-1.32.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quasarr
3
- Version: 1.30.0
3
+ Version: 1.32.0
4
4
  Summary: Quasarr connects JDownloader with Radarr, Sonarr and LazyLibrarian. It also decrypts links protected by CAPTCHAs.
5
5
  Home-page: https://github.com/rix1337/Quasarr
6
6
  Author: rix1337
@@ -5,7 +5,7 @@
5
5
  import json
6
6
  import re
7
7
  from base64 import urlsafe_b64encode, urlsafe_b64decode
8
- from urllib.parse import quote, unquote, urljoin
8
+ from urllib.parse import quote, unquote
9
9
 
10
10
  import requests
11
11
  from bottle import request, response, redirect, HTTPResponse
@@ -308,7 +308,7 @@ def setup_captcha_routes(app):
308
308
 
309
309
  check_package_exists(package_id)
310
310
 
311
- package_selector = render_package_selector(package_id)
311
+ package_selector = render_package_selector(package_id, title)
312
312
  failed_warning = render_failed_attempts_warning(package_id)
313
313
 
314
314
  return render_centered_html(f"""
@@ -318,7 +318,6 @@ def setup_captcha_routes(app):
318
318
  <h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
319
319
  {package_selector}
320
320
  {failed_warning}
321
- <p><b>Package:</b> {title}</p>
322
321
  {render_userscript_section(url, package_id, title, password, "hide")}
323
322
  <p>
324
323
  {render_button("Delete Package", "secondary", {"onclick": f"location.href='/captcha/delete/{package_id}'"})}
@@ -349,7 +348,7 @@ def setup_captcha_routes(app):
349
348
 
350
349
  check_package_exists(package_id)
351
350
 
352
- package_selector = render_package_selector(package_id)
351
+ package_selector = render_package_selector(package_id, title)
353
352
  failed_warning = render_failed_attempts_warning(package_id)
354
353
 
355
354
  return render_centered_html(f"""
@@ -359,7 +358,6 @@ def setup_captcha_routes(app):
359
358
  <h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
360
359
  {package_selector}
361
360
  {failed_warning}
362
- <p><b>Package:</b> {title}</p>
363
361
  {render_userscript_section(url, package_id, title, password, "junkies")}
364
362
  <p>
365
363
  {render_button("Delete Package", "secondary", {"onclick": f"location.href='/captcha/delete/{package_id}'"})}
@@ -391,7 +389,7 @@ def setup_captcha_routes(app):
391
389
 
392
390
  url = urls[0][0] if isinstance(urls[0], (list, tuple)) else urls[0]
393
391
 
394
- package_selector = render_package_selector(package_id)
392
+ package_selector = render_package_selector(package_id, title)
395
393
  failed_warning = render_failed_attempts_warning(package_id)
396
394
 
397
395
  return render_centered_html(f"""
@@ -401,7 +399,6 @@ def setup_captcha_routes(app):
401
399
  <h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
402
400
  {package_selector}
403
401
  {failed_warning}
404
- <p><b>Package:</b> {title}</p>
405
402
  {render_userscript_section(url, package_id, title, password, "keeplinks")}
406
403
  <p>
407
404
  {render_button("Delete Package", "secondary", {"onclick": f"location.href='/captcha/delete/{package_id}'"})}
@@ -433,7 +430,7 @@ def setup_captcha_routes(app):
433
430
 
434
431
  url = urls[0][0] if isinstance(urls[0], (list, tuple)) else urls[0]
435
432
 
436
- package_selector = render_package_selector(package_id)
433
+ package_selector = render_package_selector(package_id, title)
437
434
  failed_warning = render_failed_attempts_warning(package_id)
438
435
 
439
436
  return render_centered_html(f"""
@@ -443,7 +440,6 @@ def setup_captcha_routes(app):
443
440
  <h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
444
441
  {package_selector}
445
442
  {failed_warning}
446
- <p><b>Package:</b> {title}</p>
447
443
  {render_userscript_section(url, package_id, title, password, "tolink")}
448
444
  <p>
449
445
  {render_button("Delete Package", "secondary", {"onclick": f"location.href='/captcha/delete/{package_id}'"})}
@@ -619,12 +615,22 @@ def setup_captcha_routes(app):
619
615
  </script>
620
616
  '''
621
617
 
622
- def render_package_selector(current_package_id):
623
- """Render a dropdown selector for all available packages at the top of captcha UIs"""
618
+ def render_package_selector(current_package_id, current_title=None):
619
+ """Render package title, with dropdown selector if multiple packages available"""
624
620
  protected = shared_state.get_db("protected").retrieve_all_titles()
625
621
 
626
- if not protected or len(protected) <= 1:
627
- return "" # Don't show selector if only one or no packages
622
+ if not protected:
623
+ return ""
624
+
625
+ # Single package - just show the title without dropdown
626
+ if len(protected) <= 1:
627
+ if current_title:
628
+ return f'''
629
+ <div class="package-selector" style="margin-bottom: 20px; padding: 12px; background: rgba(128, 128, 128, 0.1); border: 1px solid rgba(128, 128, 128, 0.3); border-radius: 8px;">
630
+ <p style="margin: 0; word-break: break-all;"><b>📦 Package:</b> {current_title}</p>
631
+ </div>
632
+ '''
633
+ return ""
628
634
 
629
635
  sj = shared_state.values["config"]("Hostnames").get("sj")
630
636
  dj = shared_state.values["config"]("Hostnames").get("dj")
@@ -815,7 +821,7 @@ def setup_captcha_routes(app):
815
821
  f"pkg_pass={quote(password)}"
816
822
  )
817
823
 
818
- package_selector = render_package_selector(package_id)
824
+ package_selector = render_package_selector(package_id, title)
819
825
  failed_warning = render_failed_attempts_warning(package_id)
820
826
 
821
827
  return render_centered_html(f"""
@@ -825,7 +831,6 @@ def setup_captcha_routes(app):
825
831
  <h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
826
832
  {package_selector}
827
833
  {failed_warning}
828
- <p style="max-width: 370px; word-wrap: break-word; overflow-wrap: break-word;"><b>Package:</b> {title}</p>
829
834
 
830
835
  <div>
831
836
  <!-- Info section explaining the process -->
@@ -1171,7 +1176,7 @@ def setup_captcha_routes(app):
1171
1176
  bypass_section = render_filecrypt_bypass_section(url, package_id, title, password)
1172
1177
 
1173
1178
  # Add package selector and failed attempts warning
1174
- package_selector = render_package_selector(package_id)
1179
+ package_selector = render_package_selector(package_id, title)
1175
1180
 
1176
1181
  # Create fallback URL for the manual FileCrypt page
1177
1182
  fallback_payload = {
@@ -1187,6 +1192,9 @@ def setup_captcha_routes(app):
1187
1192
  failed_warning = render_failed_attempts_warning(package_id, include_delete_button=False,
1188
1193
  fallback_url=filecrypt_fallback_url) # Delete button is already below
1189
1194
 
1195
+ # Escape title for safe use in JavaScript string
1196
+ escaped_title_js = title.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r')
1197
+
1190
1198
  content = render_centered_html(r'''
1191
1199
  <style>
1192
1200
  @media (max-width: 600px) {
@@ -1203,6 +1211,9 @@ def setup_captcha_routes(app):
1203
1211
  }
1204
1212
  </style>
1205
1213
  <script type="text/javascript">
1214
+ // Package title for result display
1215
+ var packageTitleText = "''' + escaped_title_js + r'''";
1216
+
1206
1217
  // Check if we should redirect to fallback due to failed attempts
1207
1218
  (function() {
1208
1219
  const storageKey = 'captcha_attempts_''' + package_id + r'''';
@@ -1231,11 +1242,8 @@ def setup_captcha_routes(app):
1231
1242
  var warnBox = document.getElementById("failed-attempts-warning");
1232
1243
  if (warnBox) warnBox.style.display = "none";
1233
1244
 
1234
- // Remove width limit on result screen
1235
- var packageTitle = document.getElementById("package-title");
1236
- packageTitle.style.maxWidth = "none";
1237
-
1238
- document.getElementById("captcha-key").innerText = 'Using result "' + token + '" to decrypt links...';
1245
+ // Add package title to result area
1246
+ document.getElementById("captcha-key").innerHTML = '<p style="word-break: break-all;"><b>Package:</b> ' + packageTitleText + '</p><p style="word-break: break-all;">Using result "' + token + '" to decrypt links...</p>';
1239
1247
  var link = document.getElementById("link-hidden").value;
1240
1248
  const fullPath = '/captcha/decrypt-filecrypt';
1241
1249
 
@@ -1288,7 +1296,6 @@ def setup_captcha_routes(app):
1288
1296
  {package_selector}
1289
1297
  </div>
1290
1298
  {failed_warning}
1291
- <p id="package-title" style="max-width: 370px; word-wrap: break-word; overflow-wrap: break-word;"><b>Package:</b> {title}</p>
1292
1299
  <div id="captcha-key"></div>
1293
1300
  {link_select}<br><br>
1294
1301
  <input type="hidden" id="link-hidden" value="{prioritized_links[0][0]}" />
@@ -25,6 +25,7 @@ from quasarr.downloads.sources.wx import get_wx_download_links
25
25
  from quasarr.providers.log import info
26
26
  from quasarr.providers.notifications import send_discord_message
27
27
  from quasarr.providers.statistics import StatsHelper
28
+ from quasarr.providers.utils import filter_offline_links
28
29
 
29
30
  # =============================================================================
30
31
  # CRYPTER CONFIGURATION
@@ -184,6 +185,12 @@ def process_links(shared_state, source_result, title, password, package_id, imdb
184
185
  reason=f'All links are offline or IP is banned for "{title}" on {label} - "{source_url}"')
185
186
  links = valid_links
186
187
 
188
+ # Filter out verifiably offline links
189
+ links = filter_offline_links(links, shared_state=shared_state, log_func=info)
190
+ if not links:
191
+ return fail(title, package_id, shared_state,
192
+ reason=f'All verifiable links are offline for "{title}" on {label} - "{source_url}"')
193
+
187
194
  classified = classify_links(links, shared_state)
188
195
 
189
196
  # PRIORITY 1: Direct hoster links