quasarr 2.3.2__tar.gz → 2.4.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 (88) hide show
  1. {quasarr-2.3.2 → quasarr-2.4.0}/PKG-INFO +1 -1
  2. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/api/captcha/__init__.py +57 -48
  3. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/api/sponsors_helper/__init__.py +25 -37
  4. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/version.py +1 -1
  5. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr.egg-info/PKG-INFO +1 -1
  6. {quasarr-2.3.2 → quasarr-2.4.0}/LICENSE +0 -0
  7. {quasarr-2.3.2 → quasarr-2.4.0}/README.md +0 -0
  8. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/__init__.py +0 -0
  9. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/api/__init__.py +0 -0
  10. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/api/arr/__init__.py +0 -0
  11. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/api/config/__init__.py +0 -0
  12. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/api/packages/__init__.py +0 -0
  13. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/api/statistics/__init__.py +0 -0
  14. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/__init__.py +0 -0
  15. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/linkcrypters/__init__.py +0 -0
  16. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/linkcrypters/al.py +0 -0
  17. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/linkcrypters/filecrypt.py +0 -0
  18. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/linkcrypters/hide.py +0 -0
  19. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/packages/__init__.py +0 -0
  20. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/__init__.py +0 -0
  21. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/al.py +0 -0
  22. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/by.py +0 -0
  23. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/dd.py +0 -0
  24. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/dj.py +0 -0
  25. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/dl.py +0 -0
  26. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/dt.py +0 -0
  27. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/dw.py +0 -0
  28. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/he.py +0 -0
  29. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/mb.py +0 -0
  30. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/nk.py +0 -0
  31. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/nx.py +0 -0
  32. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/sf.py +0 -0
  33. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/sj.py +0 -0
  34. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/sl.py +0 -0
  35. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/wd.py +0 -0
  36. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/downloads/sources/wx.py +0 -0
  37. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/__init__.py +0 -0
  38. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/auth.py +0 -0
  39. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/cloudflare.py +0 -0
  40. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/hostname_issues.py +0 -0
  41. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/html_images.py +0 -0
  42. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/html_templates.py +0 -0
  43. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/imdb_metadata.py +0 -0
  44. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/jd_cache.py +0 -0
  45. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/log.py +0 -0
  46. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/myjd_api.py +0 -0
  47. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/notifications.py +0 -0
  48. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/obfuscated.py +0 -0
  49. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/sessions/__init__.py +0 -0
  50. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/sessions/al.py +0 -0
  51. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/sessions/dd.py +0 -0
  52. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/sessions/dl.py +0 -0
  53. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/sessions/nx.py +0 -0
  54. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/shared_state.py +0 -0
  55. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/statistics.py +0 -0
  56. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/utils.py +0 -0
  57. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/providers/web_server.py +0 -0
  58. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/__init__.py +0 -0
  59. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/__init__.py +0 -0
  60. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/al.py +0 -0
  61. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/by.py +0 -0
  62. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/dd.py +0 -0
  63. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/dj.py +0 -0
  64. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/dl.py +0 -0
  65. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/dt.py +0 -0
  66. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/dw.py +0 -0
  67. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/fx.py +0 -0
  68. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/he.py +0 -0
  69. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/mb.py +0 -0
  70. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/nk.py +0 -0
  71. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/nx.py +0 -0
  72. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/sf.py +0 -0
  73. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/sj.py +0 -0
  74. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/sl.py +0 -0
  75. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/wd.py +0 -0
  76. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/search/sources/wx.py +0 -0
  77. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/storage/__init__.py +0 -0
  78. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/storage/config.py +0 -0
  79. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/storage/setup.py +0 -0
  80. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr/storage/sqlite_database.py +0 -0
  81. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr.egg-info/SOURCES.txt +0 -0
  82. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr.egg-info/dependency_links.txt +0 -0
  83. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr.egg-info/entry_points.txt +0 -0
  84. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr.egg-info/not-zip-safe +0 -0
  85. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr.egg-info/requires.txt +0 -0
  86. {quasarr-2.3.2 → quasarr-2.4.0}/quasarr.egg-info/top_level.txt +0 -0
  87. {quasarr-2.3.2 → quasarr-2.4.0}/setup.cfg +0 -0
  88. {quasarr-2.3.2 → quasarr-2.4.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quasarr
3
- Version: 2.3.2
3
+ Version: 2.4.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
@@ -86,6 +86,8 @@ def setup_captcha_routes(app):
86
86
  except KeyError:
87
87
  desired_mirror = None
88
88
 
89
+ original_url = data.get("original_url")
90
+
89
91
  # This is required for cutcaptcha
90
92
  rapid = [ln for ln in links if "rapidgator" in ln[1].lower()]
91
93
  others = [ln for ln in links if "rapidgator" not in ln[1].lower()]
@@ -97,6 +99,7 @@ def setup_captcha_routes(app):
97
99
  "password": password,
98
100
  "mirror": desired_mirror,
99
101
  "links": prioritized_links,
102
+ "original_url": original_url,
100
103
  }
101
104
 
102
105
  encoded_payload = urlsafe_b64encode(json.dumps(payload).encode()).decode()
@@ -194,29 +197,18 @@ def setup_captcha_routes(app):
194
197
 
195
198
  return f'''
196
199
  <div>
197
- <!-- Info section explaining the process -->
198
- <div class="info-box">
199
- <h3>ℹ️ How This Works:</h3>
200
- <p style="margin-bottom: 8px;">
201
- 1. Click the link below to open {provider_name}
202
- </p>
203
- <p style="margin-top: 0; margin-bottom: 8px;">
204
- 2. Solve any CAPTCHAs on their site to reveal the download links
205
- </p>
206
- <p style="margin-top: 0; margin-bottom: 0;">
207
- 3. <b>With the userscript installed</b>, links are automatically sent back to Quasarr!
208
- </p>
209
- </div>
210
-
211
200
  <!-- One-time setup section - visually separated -->
212
201
  <div id="setup-instructions" class="setup-box">
213
202
  <h3>📦 First Time Setup:</h3>
214
203
  <p style="margin-bottom: 8px;">
215
- <a href="https://www.tampermonkey.net/" target="_blank" rel="noopener noreferrer">1. Install Tampermonkey</a>
204
+ <a href="https://www.tampermonkey.net/" target="_blank" rel="noopener noreferrer">1. On mobile Safari/Firefox or any Desktop Browser install Tampermonkey</a>
216
205
  </p>
217
- <p style="margin-top: 0; margin-bottom: 12px;">
206
+ <p style="margin-top: 0; margin-bottom: 8px;">
218
207
  <a href="{userscript_url}" target="_blank">2. Install the {provider_name} userscript</a>
219
208
  </p>
209
+ <p style="margin-top: 0; margin-bottom: 12px;">
210
+ 3. Open link, solve CAPTCHAs, and links are automatically sent back to Quasarr!
211
+ </p>
220
212
  <p style="margin-top: 0;">
221
213
  <button id="hide-setup-btn" type="button" class="btn-subtle">
222
214
  ✅ Don't show this again
@@ -318,6 +310,7 @@ def setup_captcha_routes(app):
318
310
  title = payload.get("title")
319
311
  password = payload.get("password")
320
312
  urls = payload.get("links")
313
+ original_url = payload.get("original_url")
321
314
  url = urls[0][0] if isinstance(urls[0], (list, tuple)) else urls[0]
322
315
 
323
316
  check_package_exists(package_id)
@@ -325,6 +318,10 @@ def setup_captcha_routes(app):
325
318
  package_selector = render_package_selector(package_id, title)
326
319
  failed_warning = render_failed_attempts_warning(package_id)
327
320
 
321
+ source_button = ""
322
+ if original_url:
323
+ source_button = f'<p>{render_button("Source", "secondary", {"onclick": f"window.open(\'{js_single_quoted_string_safe(original_url)}\', \'_blank\')" })}</p>'
324
+
328
325
  return render_centered_html(f"""
329
326
  <!DOCTYPE html>
330
327
  <html>
@@ -333,6 +330,7 @@ def setup_captcha_routes(app):
333
330
  {package_selector}
334
331
  {failed_warning}
335
332
  {render_userscript_section(url, package_id, title, password, "hide")}
333
+ {source_button}
336
334
  <p>
337
335
  {render_button("Delete Package", "secondary", {"onclick": f"location.href='/captcha/delete/{package_id}'"})}
338
336
  </p>
@@ -358,6 +356,7 @@ def setup_captcha_routes(app):
358
356
  title = payload.get("title")
359
357
  password = payload.get("password")
360
358
  urls = payload.get("links")
359
+ original_url = payload.get("original_url")
361
360
  url = urls[0][0] if isinstance(urls[0], (list, tuple)) else urls[0]
362
361
 
363
362
  check_package_exists(package_id)
@@ -365,6 +364,10 @@ def setup_captcha_routes(app):
365
364
  package_selector = render_package_selector(package_id, title)
366
365
  failed_warning = render_failed_attempts_warning(package_id)
367
366
 
367
+ source_button = ""
368
+ if original_url:
369
+ source_button = f'<p>{render_button("Source", "secondary", {"onclick": f"window.open(\'{js_single_quoted_string_safe(original_url)}\', \'_blank\')" })}</p>'
370
+
368
371
  return render_centered_html(f"""
369
372
  <!DOCTYPE html>
370
373
  <html>
@@ -373,6 +376,7 @@ def setup_captcha_routes(app):
373
376
  {package_selector}
374
377
  {failed_warning}
375
378
  {render_userscript_section(url, package_id, title, password, "junkies")}
379
+ {source_button}
376
380
  <p>
377
381
  {render_button("Delete Package", "secondary", {"onclick": f"location.href='/captcha/delete/{package_id}'"})}
378
382
  </p>
@@ -398,6 +402,7 @@ def setup_captcha_routes(app):
398
402
  title = payload.get("title")
399
403
  password = payload.get("password")
400
404
  urls = payload.get("links")
405
+ original_url = payload.get("original_url")
401
406
 
402
407
  check_package_exists(package_id)
403
408
 
@@ -406,6 +411,10 @@ def setup_captcha_routes(app):
406
411
  package_selector = render_package_selector(package_id, title)
407
412
  failed_warning = render_failed_attempts_warning(package_id)
408
413
 
414
+ source_button = ""
415
+ if original_url:
416
+ source_button = f'<p>{render_button("Source", "secondary", {"onclick": f"window.open(\'{js_single_quoted_string_safe(original_url)}\', \'_blank\')" })}</p>'
417
+
409
418
  return render_centered_html(f"""
410
419
  <!DOCTYPE html>
411
420
  <html>
@@ -414,6 +423,7 @@ def setup_captcha_routes(app):
414
423
  {package_selector}
415
424
  {failed_warning}
416
425
  {render_userscript_section(url, package_id, title, password, "keeplinks")}
426
+ {source_button}
417
427
  <p>
418
428
  {render_button("Delete Package", "secondary", {"onclick": f"location.href='/captcha/delete/{package_id}'"})}
419
429
  </p>
@@ -439,6 +449,7 @@ def setup_captcha_routes(app):
439
449
  title = payload.get("title")
440
450
  password = payload.get("password")
441
451
  urls = payload.get("links")
452
+ original_url = payload.get("original_url")
442
453
 
443
454
  check_package_exists(package_id)
444
455
 
@@ -447,6 +458,10 @@ def setup_captcha_routes(app):
447
458
  package_selector = render_package_selector(package_id, title)
448
459
  failed_warning = render_failed_attempts_warning(package_id)
449
460
 
461
+ source_button = ""
462
+ if original_url:
463
+ source_button = f'<p>{render_button("Source", "secondary", {"onclick": f"window.open(\'{js_single_quoted_string_safe(original_url)}\', \'_blank\')" })}</p>'
464
+
450
465
  return render_centered_html(f"""
451
466
  <!DOCTYPE html>
452
467
  <html>
@@ -455,6 +470,7 @@ def setup_captcha_routes(app):
455
470
  {package_selector}
456
471
  {failed_warning}
457
472
  {render_userscript_section(url, package_id, title, password, "tolink")}
473
+ {source_button}
458
474
  <p>
459
475
  {render_button("Delete Package", "secondary", {"onclick": f"location.href='/captcha/delete/{package_id}'"})}
460
476
  </p>
@@ -519,29 +535,18 @@ def setup_captcha_routes(app):
519
535
  <details id="bypassDetails">
520
536
  <summary id="bypassSummary">Show CAPTCHA Bypass</summary><br>
521
537
 
522
- <!-- Info section explaining the process -->
523
- <div class="info-box">
524
- <h3>ℹ️ How This Works:</h3>
525
- <p style="margin-bottom: 8px;">
526
- 1. Click the button below to open FileCrypt directly
527
- </p>
528
- <p style="margin-top: 0; margin-bottom: 8px;">
529
- 2. Solve any CAPTCHAs on their site to reveal the download links
530
- </p>
531
- <p style="margin-top: 0; margin-bottom: 0;">
532
- 3. <b>With the userscript installed</b>, links are automatically sent back to Quasarr!
533
- </p>
534
- </div>
535
-
536
538
  <!-- One-time setup section - visually separated -->
537
539
  <div id="setup-instructions" class="setup-box">
538
540
  <h3>📦 First Time Setup:</h3>
539
541
  <p style="margin-bottom: 8px;">
540
- <a href="https://www.tampermonkey.net/" target="_blank" rel="noopener noreferrer">1. Install Tampermonkey</a>
542
+ <a href="https://www.tampermonkey.net/" target="_blank" rel="noopener noreferrer">1. On mobile Safari/Firefox or any Desktop Browser install Tampermonkey</a>
541
543
  </p>
542
- <p style="margin-top: 0; margin-bottom: 12px;">
544
+ <p style="margin-top: 0; margin-bottom: 8px;">
543
545
  <a href="/captcha/filecrypt.user.js" target="_blank">2. Install the FileCrypt userscript</a>
544
546
  </p>
547
+ <p style="margin-top: 0; margin-bottom: 12px;">
548
+ 3. Open link, solve CAPTCHAs, and links are automatically sent back to Quasarr!
549
+ </p>
545
550
  <p style="margin-top: 0;">
546
551
  <button id="hide-setup-btn" type="button" class="btn-subtle">
547
552
  ✅ Don't show this again
@@ -682,6 +687,7 @@ def setup_captcha_routes(app):
682
687
  links = data.get("links", [])
683
688
  password = data.get("password", "")
684
689
  mirror = data.get("mirror")
690
+ original_url = data.get("original_url")
685
691
 
686
692
  # Prioritize rapidgator links for cutcaptcha
687
693
  rapid = [ln for ln in links if "rapidgator" in ln[1].lower()]
@@ -694,6 +700,7 @@ def setup_captcha_routes(app):
694
700
  "password": password,
695
701
  "mirror": mirror,
696
702
  "links": prioritized,
703
+ "original_url": original_url,
697
704
  }
698
705
  encoded = urlsafe_b64encode(json.dumps(payload).encode()).decode()
699
706
  captcha_type = get_captcha_type_for_links(prioritized)
@@ -818,6 +825,7 @@ def setup_captcha_routes(app):
818
825
  title = payload.get("title")
819
826
  password = payload.get("password")
820
827
  urls = payload.get("links")
828
+ original_url = payload.get("original_url")
821
829
 
822
830
  check_package_exists(package_id)
823
831
 
@@ -838,6 +846,10 @@ def setup_captcha_routes(app):
838
846
  package_selector = render_package_selector(package_id, title)
839
847
  failed_warning = render_failed_attempts_warning(package_id)
840
848
 
849
+ source_button = ""
850
+ if original_url:
851
+ source_button = f'<p>{render_button("Source", "secondary", {"onclick": f"window.open(\'{js_single_quoted_string_safe(original_url)}\', \'_blank\')" })}</p>'
852
+
841
853
  return render_centered_html(f"""
842
854
  <!DOCTYPE html>
843
855
  <html>
@@ -847,29 +859,18 @@ def setup_captcha_routes(app):
847
859
  {failed_warning}
848
860
 
849
861
  <div>
850
- <!-- Info section explaining the process -->
851
- <div class="info-box">
852
- <h3>ℹ️ How This Works:</h3>
853
- <p style="margin-bottom: 8px;">
854
- 1. Click the button below to open FileCrypt directly
855
- </p>
856
- <p style="margin-top: 0; margin-bottom: 8px;">
857
- 2. Solve any CAPTCHAs on their site to reveal the download links
858
- </p>
859
- <p style="margin-top: 0; margin-bottom: 0;">
860
- 3. <b>With the userscript installed</b>, links are automatically sent back to Quasarr!
861
- </p>
862
- </div>
863
-
864
862
  <!-- One-time setup section - visually separated -->
865
863
  <div id="setup-instructions" class="setup-box">
866
864
  <h3>📦 First Time Setup:</h3>
867
865
  <p style="margin-bottom: 8px;">
868
- <a href="https://www.tampermonkey.net/" target="_blank" rel="noopener noreferrer">1. Install Tampermonkey</a>
866
+ <a href="https://www.tampermonkey.net/" target="_blank" rel="noopener noreferrer">1. On mobile Safari/Firefox or any Desktop Browser install Tampermonkey</a>
869
867
  </p>
870
- <p style="margin-top: 0; margin-bottom: 12px;">
868
+ <p style="margin-top: 0; margin-bottom: 8px;">
871
869
  <a href="/captcha/filecrypt.user.js" target="_blank">2. Install the FileCrypt userscript</a>
872
870
  </p>
871
+ <p style="margin-top: 0; margin-bottom: 12px;">
872
+ 3. Open link, solve CAPTCHAs, and links are automatically sent back to Quasarr!
873
+ </p>
873
874
  <p style="margin-top: 0;">
874
875
  <button id="hide-setup-btn" type="button" class="btn-subtle">
875
876
  ✅ Don't show this again
@@ -921,6 +922,7 @@ def setup_captcha_routes(app):
921
922
  </div>
922
923
  </div>
923
924
 
925
+ {source_button}
924
926
  <p>
925
927
  {render_button("Delete Package", "secondary", {"onclick": f"location.href='/captcha/delete/{package_id}'"})}
926
928
  </p>
@@ -1142,6 +1144,7 @@ def setup_captcha_routes(app):
1142
1144
  password = payload.get("password")
1143
1145
  desired_mirror = payload.get("mirror")
1144
1146
  prioritized_links = payload.get("links")
1147
+ original_url = payload.get("original_url")
1145
1148
 
1146
1149
  check_package_exists(package_id)
1147
1150
 
@@ -1199,6 +1202,7 @@ def setup_captcha_routes(app):
1199
1202
  "password": password,
1200
1203
  "mirror": desired_mirror,
1201
1204
  "links": prioritized_links,
1205
+ "original_url": original_url,
1202
1206
  }
1203
1207
  fallback_encoded = urlsafe_b64encode(json.dumps(fallback_payload).encode()).decode()
1204
1208
  filecrypt_fallback_url = f"/captcha/filecrypt?data={quote(fallback_encoded)}"
@@ -1209,6 +1213,10 @@ def setup_captcha_routes(app):
1209
1213
  # Escape title for safe use in JavaScript string
1210
1214
  escaped_title_js = title.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n').replace('\r', '\\r')
1211
1215
 
1216
+ source_button_html = ""
1217
+ if original_url:
1218
+ source_button_html = f'<p>{render_button("Source", "secondary", {"onclick": f"window.open(\'{js_single_quoted_string_safe(original_url)}\', \'_blank\')" })}</p>'
1219
+
1212
1220
  content = render_centered_html(r'''
1213
1221
  <style>
1214
1222
  /* Fix captcha container to shrink-wrap iframe on desktop */
@@ -1321,6 +1329,7 @@ def setup_captcha_routes(app):
1321
1329
  </div>
1322
1330
  <br>
1323
1331
  <div id="delete-package-section">
1332
+ ''' + source_button_html + f'''
1324
1333
  <p>
1325
1334
  {render_button("Delete Package", "secondary", {"onclick": f"location.href='/captcha/delete/{package_id}'"})}
1326
1335
  </p>
@@ -33,16 +33,16 @@ def setup_sponsors_helper_routes(app):
33
33
  if not protected:
34
34
  return abort(404, "No encrypted packages found")
35
35
 
36
- # Find the first package without a "session" key
36
+ # Find the first package that hasn't been disabled
37
37
  selected_package = None
38
38
  for package in protected:
39
39
  data = json.loads(package[1])
40
- if "session" not in data:
40
+ if "disabled" not in data:
41
41
  selected_package = (package[0], data)
42
42
  break
43
43
 
44
44
  if not selected_package:
45
- return abort(404, "No valid packages without session found")
45
+ return abort(404, "No valid packages found")
46
46
 
47
47
  package_id, data = selected_package
48
48
  title = data["title"]
@@ -67,9 +67,9 @@ def setup_sponsors_helper_routes(app):
67
67
  except Exception as e:
68
68
  return abort(500, str(e))
69
69
 
70
- @app.post("/sponsors_helper/api/to_download/")
70
+ @app.post("/sponsors_helper/api/download/")
71
71
  @require_api_key
72
- def to_download_api():
72
+ def download_api():
73
73
  try:
74
74
  data = request.json
75
75
  title = data.get('name')
@@ -97,51 +97,39 @@ def setup_sponsors_helper_routes(app):
97
97
  StatsHelper(shared_state).increment_failed_decryptions_automatic()
98
98
  return abort(500, "Failed")
99
99
 
100
- @app.post("/sponsors_helper/api/to_replace/")
100
+ @app.post("/sponsors_helper/api/disable/")
101
101
  @require_api_key
102
- def to_replace_api():
102
+ def disable_api():
103
103
  try:
104
104
  data = request.json
105
- name = data.get('name')
106
105
  package_id = data.get('package_id')
107
- password = data.get('password')
108
- replace_url = data.get('replace_url')
109
- mirror = data.get('mirror')
110
- session = data.get('session')
111
-
112
- if not all([name, package_id, replace_url, mirror, session]):
113
- info("Missing required replacement data")
114
- return {"error": "Missing required replacement data"}, 400
115
-
116
- if password is None:
117
- password = ""
118
-
119
- blob = json.dumps(
120
- {
121
- "title": name,
122
- "links": [replace_url, mirror],
123
- "size_mb": 0,
124
- "password": password,
125
- "mirror": mirror,
126
- "session": session
127
- })
128
106
 
129
- shared_state.get_db("protected").update_store(package_id, blob)
107
+ if not package_id:
108
+ return {"error": "Missing package_id"}, 400
109
+
110
+ StatsHelper(shared_state).increment_failed_decryptions_automatic()
130
111
 
131
- info(f"Another CAPTCHA solution is required for {mirror} link: {replace_url}")
112
+ blob = shared_state.get_db("protected").retrieve(package_id)
113
+ package_data = json.loads(blob)
114
+ title = package_data.get('title')
115
+
116
+ package_data["disabled"] = True
117
+
118
+ shared_state.get_db("protected").update_store(package_id, json.dumps(package_data))
119
+
120
+ info(f"Disabled package {title}")
132
121
 
133
122
  StatsHelper(shared_state).increment_captcha_decryptions_automatic()
134
123
 
135
- return f"Replacement link stored for {name}"
124
+ return f"Package {title} disabled"
136
125
 
137
126
  except Exception as e:
138
- StatsHelper(shared_state).increment_failed_decryptions_automatic()
139
- info(f"Error handling replacement: {e}")
127
+ info(f"Error handling disable: {e}")
140
128
  return {"error": str(e)}, 500
141
129
 
142
- @app.delete("/sponsors_helper/api/to_failed/")
130
+ @app.delete("/sponsors_helper/api/fail/")
143
131
  @require_api_key
144
- def move_to_failed_api():
132
+ def fail_api():
145
133
  try:
146
134
  StatsHelper(shared_state).increment_failed_decryptions_automatic()
147
135
 
@@ -165,7 +153,7 @@ def setup_sponsors_helper_routes(app):
165
153
 
166
154
  @app.put("/sponsors_helper/api/set_sponsor_status/")
167
155
  @require_api_key
168
- def activate_sponsor_status():
156
+ def set_sponsor_status_api():
169
157
  try:
170
158
  data = request.body.read().decode("utf-8")
171
159
  payload = json.loads(data)
@@ -8,7 +8,7 @@ import requests
8
8
 
9
9
 
10
10
  def get_version():
11
- return "2.3.2"
11
+ return "2.4.0"
12
12
 
13
13
 
14
14
  def get_latest_version():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quasarr
3
- Version: 2.3.2
3
+ Version: 2.4.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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes