quasarr 1.16.11__tar.gz → 1.17.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 (70) hide show
  1. {quasarr-1.16.11 → quasarr-1.17.0}/PKG-INFO +1 -1
  2. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/api/captcha/__init__.py +153 -5
  3. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/version.py +1 -1
  4. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr.egg-info/PKG-INFO +1 -1
  5. {quasarr-1.16.11 → quasarr-1.17.0}/LICENSE +0 -0
  6. {quasarr-1.16.11 → quasarr-1.17.0}/README.md +0 -0
  7. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/__init__.py +0 -0
  8. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/api/__init__.py +0 -0
  9. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/api/arr/__init__.py +0 -0
  10. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/api/config/__init__.py +0 -0
  11. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/api/sponsors_helper/__init__.py +0 -0
  12. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/api/statistics/__init__.py +0 -0
  13. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/__init__.py +0 -0
  14. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/linkcrypters/__init__.py +0 -0
  15. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/linkcrypters/al.py +0 -0
  16. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/linkcrypters/filecrypt.py +0 -0
  17. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/linkcrypters/hide.py +0 -0
  18. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/packages/__init__.py +0 -0
  19. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/sources/__init__.py +0 -0
  20. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/sources/al.py +0 -0
  21. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/sources/by.py +0 -0
  22. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/sources/dd.py +0 -0
  23. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/sources/dt.py +0 -0
  24. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/sources/dw.py +0 -0
  25. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/sources/mb.py +0 -0
  26. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/sources/nx.py +0 -0
  27. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/sources/sf.py +0 -0
  28. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/sources/sl.py +0 -0
  29. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/downloads/sources/wd.py +0 -0
  30. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/__init__.py +0 -0
  31. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/cloudflare.py +0 -0
  32. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/html_images.py +0 -0
  33. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/html_templates.py +0 -0
  34. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/imdb_metadata.py +0 -0
  35. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/log.py +0 -0
  36. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/myjd_api.py +0 -0
  37. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/notifications.py +0 -0
  38. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/obfuscated.py +0 -0
  39. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/sessions/__init__.py +0 -0
  40. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/sessions/al.py +0 -0
  41. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/sessions/dd.py +0 -0
  42. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/sessions/nx.py +0 -0
  43. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/shared_state.py +0 -0
  44. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/statistics.py +0 -0
  45. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/providers/web_server.py +0 -0
  46. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/search/__init__.py +0 -0
  47. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/search/sources/__init__.py +0 -0
  48. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/search/sources/al.py +0 -0
  49. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/search/sources/by.py +0 -0
  50. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/search/sources/dd.py +0 -0
  51. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/search/sources/dt.py +0 -0
  52. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/search/sources/dw.py +0 -0
  53. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/search/sources/fx.py +0 -0
  54. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/search/sources/mb.py +0 -0
  55. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/search/sources/nx.py +0 -0
  56. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/search/sources/sf.py +0 -0
  57. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/search/sources/sl.py +0 -0
  58. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/search/sources/wd.py +0 -0
  59. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/storage/__init__.py +0 -0
  60. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/storage/config.py +0 -0
  61. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/storage/setup.py +0 -0
  62. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr/storage/sqlite_database.py +0 -0
  63. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr.egg-info/SOURCES.txt +0 -0
  64. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr.egg-info/dependency_links.txt +0 -0
  65. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr.egg-info/entry_points.txt +0 -0
  66. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr.egg-info/not-zip-safe +0 -0
  67. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr.egg-info/requires.txt +0 -0
  68. {quasarr-1.16.11 → quasarr-1.17.0}/quasarr.egg-info/top_level.txt +0 -0
  69. {quasarr-1.16.11 → quasarr-1.17.0}/setup.cfg +0 -0
  70. {quasarr-1.16.11 → quasarr-1.17.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quasarr
3
- Version: 1.16.11
3
+ Version: 1.17.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
@@ -11,7 +11,7 @@ import requests
11
11
  from bottle import request, response, redirect
12
12
 
13
13
  import quasarr.providers.html_images as images
14
- from quasarr.downloads.linkcrypters.filecrypt import get_filecrypt_links
14
+ from quasarr.downloads.linkcrypters.filecrypt import get_filecrypt_links, DLC
15
15
  from quasarr.downloads.packages import delete_package
16
16
  from quasarr.providers import shared_state
17
17
  from quasarr.providers.html_templates import render_button, render_centered_html
@@ -65,13 +65,17 @@ def setup_captcha_routes(app):
65
65
  others = [ln for ln in links if "rapidgator" not in ln[1].lower()]
66
66
  prioritized_links = rapid + others
67
67
 
68
+ # This is required for bypass on circlecaptcha
69
+ original_url = data.get("original_url", "")
70
+
68
71
  payload = {
69
72
  "package_id": package_id,
70
73
  "title": title,
71
74
  "password": password,
72
75
  "mirror": desired_mirror,
73
76
  "session": session,
74
- "links": prioritized_links
77
+ "links": prioritized_links,
78
+ "original_url": original_url
75
79
  }
76
80
 
77
81
  encoded_payload = urlsafe_b64encode(json.dumps(payload).encode()).decode()
@@ -97,6 +101,34 @@ def setup_captcha_routes(app):
97
101
  except Exception as e:
98
102
  return {"error": f"Failed to decode payload: {str(e)}"}
99
103
 
104
+ def render_bypass_section(url, package_id, title, password):
105
+ """Render the bypass UI section for both cutcaptcha and circle captcha pages"""
106
+ return f'''
107
+ <div style="margin-top: 40px; padding-top: 20px; border-top: 2px solid #ccc;">
108
+ <h3>Bypass CAPTCHA</h3>
109
+ <a href="{url}" target="_blank">Protected Link</a>
110
+ <form id="bypass-form" action="/captcha/bypass-submit" method="post" enctype="multipart/form-data">
111
+ <input type="hidden" name="package_id" value="{package_id}" />
112
+ <input type="hidden" name="title" value="{title}" />
113
+ <input type="hidden" name="password" value="{password}" />
114
+
115
+ <div style="margin-bottom: 15px;">
116
+ <label for="links-input"><b>Paste direct download links (one per line):</b></label><br>
117
+ <textarea id="links-input" name="links" rows="5" style="width: 100%; padding: 8px; font-family: monospace; resize: vertical;"></textarea>
118
+ </div>
119
+
120
+ <div style="margin-bottom: 15px;">
121
+ <label for="dlc-file"><b>Or upload DLC file:</b></label><br>
122
+ <input type="file" id="dlc-file" name="dlc_file" accept=".dlc" />
123
+ </div>
124
+
125
+ <div>
126
+ {render_button("Submit Bypass", "primary", {"type": "submit"})}
127
+ </div>
128
+ </form>
129
+ </div>
130
+ '''
131
+
100
132
  @app.get('/captcha/delete/<package_id>')
101
133
  def delete_captcha_package(package_id):
102
134
  success = delete_package(shared_state, package_id)
@@ -188,6 +220,11 @@ def setup_captcha_routes(app):
188
220
  solve_another_html = render_button("Solve another CAPTCHA", "primary", {"onclick": "location.href='/captcha'"})
189
221
  back_button_html = render_button("Back", "secondary", {"onclick": "location.href='/'"})
190
222
 
223
+ url = prioritized_links[0][0]
224
+
225
+ # Add bypass section
226
+ bypass_section = render_bypass_section(url, package_id, title, password)
227
+
191
228
  content = render_centered_html(r'''
192
229
  <script type="text/javascript">
193
230
  var api_key = "''' + captcha_values()["api_key"] + r'''";
@@ -200,6 +237,7 @@ def setup_captcha_routes(app):
200
237
  document.getElementById("mirrors-select").remove();
201
238
  document.getElementById("delete-package-section").style.display = "none";
202
239
  document.getElementById("back-button-section").style.display = "none";
240
+ document.getElementById("bypass-section").style.display = "none";
203
241
 
204
242
  // Remove width limit on result screen
205
243
  var packageTitle = document.getElementById("package-title");
@@ -247,6 +285,7 @@ def setup_captcha_routes(app):
247
285
  <div>
248
286
  <h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
249
287
  <p id="package-title" style="max-width: 370px; word-wrap: break-word; overflow-wrap: break-word;"><b>Package:</b> {title}</p>
288
+ <h3>Solve CAPTCHA</h3>
250
289
  <div id="captcha-key"></div>
251
290
  {link_select}<br><br>
252
291
  <input type="hidden" id="link-hidden" value="{prioritized_links[0][0]}" />
@@ -267,6 +306,9 @@ def setup_captcha_routes(app):
267
306
  <p>
268
307
  {render_button("Back", "secondary", {"onclick": "location.href='/'"})}
269
308
  </p>
309
+ </div>
310
+ <div id="bypass-section">
311
+ {bypass_section}
270
312
  </div>
271
313
  </div>
272
314
  </html>''')
@@ -346,6 +388,104 @@ def setup_captcha_routes(app):
346
388
  response.set_header(header, resp.headers[header])
347
389
  return resp.content
348
390
 
391
+ @app.post('/captcha/bypass-submit')
392
+ def handle_bypass_submit():
393
+ """Handle bypass submission with either links or DLC file"""
394
+ try:
395
+ package_id = request.forms.get('package_id')
396
+ title = request.forms.get('title')
397
+ password = request.forms.get('password', '')
398
+ links_input = request.forms.get('links', '').strip()
399
+ dlc_upload = request.files.get('dlc_file')
400
+
401
+ if not package_id or not title:
402
+ return render_centered_html(f'''<h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
403
+ <p><b>Error:</b> Missing package information.</p>
404
+ <p>
405
+ {render_button("Back", "secondary", {"onclick": "location.href='/captcha'"})}
406
+ </p>''')
407
+
408
+ # Process links input
409
+ if links_input:
410
+ info(f"Processing direct links bypass for {title}")
411
+ links = [link.strip() for link in links_input.split('\n') if link.strip()]
412
+ info(f"Received {len(links)} direct download links")
413
+
414
+ # Process DLC file
415
+ elif dlc_upload:
416
+ info(f"Processing DLC file bypass for {title}")
417
+ dlc_content = dlc_upload.file.read()
418
+ try:
419
+ decrypted_links = DLC(shared_state, dlc_content).decrypt()
420
+ if decrypted_links:
421
+ links = decrypted_links
422
+ info(f"Decrypted {len(links)} links from DLC file")
423
+ else:
424
+ raise ValueError("DLC decryption returned no links")
425
+ except Exception as e:
426
+ info(f"DLC decryption failed: {e}")
427
+ return render_centered_html(f'''<h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
428
+ <p><b>Error:</b> Failed to decrypt DLC file: {str(e)}</p>
429
+ <p>
430
+ {render_button("Back", "secondary", {"onclick": "location.href='/captcha'"})}
431
+ </p>''')
432
+ else:
433
+ return render_centered_html(f'''<h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
434
+ <p><b>Error:</b> Please provide either links or a DLC file.</p>
435
+ <p>
436
+ {render_button("Back", "secondary", {"onclick": "location.href='/captcha'"})}
437
+ </p>''')
438
+
439
+ # Download the package
440
+ if links:
441
+ downloaded = shared_state.download_package(links, title, password, package_id)
442
+ if downloaded:
443
+ StatsHelper(shared_state).increment_package_with_links(links)
444
+ StatsHelper(shared_state).increment_captcha_decryptions_manual()
445
+ shared_state.get_db("protected").delete(package_id)
446
+
447
+ # Check if there are more CAPTCHAs to solve
448
+ remaining_protected = shared_state.get_db("protected").retrieve_all_titles()
449
+ has_more_captchas = bool(remaining_protected)
450
+
451
+ if has_more_captchas:
452
+ solve_button = render_button("Solve another CAPTCHA", "primary", {
453
+ "onclick": "location.href='/captcha'",
454
+ })
455
+ else:
456
+ solve_button = "<b>No more CAPTCHAs</b>"
457
+
458
+ return render_centered_html(f'''<h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
459
+ <p><b>Success!</b> Package "{title}" bypassed and submitted to JDownloader.</p>
460
+ <p>{len(links)} link(s) processed.</p>
461
+ <p>
462
+ {solve_button}
463
+ </p>
464
+ <p>
465
+ {render_button("Back", "secondary", {"onclick": "location.href='/'"})}
466
+ </p>''')
467
+ else:
468
+ StatsHelper(shared_state).increment_failed_decryptions_manual()
469
+ return render_centered_html(f'''<h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
470
+ <p><b>Error:</b> Failed to submit package to JDownloader.</p>
471
+ <p>
472
+ {render_button("Try Again", "secondary", {"onclick": "location.href='/captcha'"})}
473
+ </p>''')
474
+ else:
475
+ return render_centered_html(f'''<h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
476
+ <p><b>Error:</b> No valid links found.</p>
477
+ <p>
478
+ {render_button("Back", "secondary", {"onclick": "location.href='/captcha'"})}
479
+ </p>''')
480
+
481
+ except Exception as e:
482
+ info(f"Bypass submission error: {e}")
483
+ return render_centered_html(f'''<h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
484
+ <p><b>Error:</b> {str(e)}</p>
485
+ <p>
486
+ {render_button("Back", "secondary", {"onclick": "location.href='/captcha'"})}
487
+ </p>''')
488
+
349
489
  @app.post('/captcha/decrypt-filecrypt')
350
490
  def submit_token():
351
491
  protected = shared_state.get_db("protected").retrieve_all_titles()
@@ -382,7 +522,8 @@ def setup_captcha_routes(app):
382
522
  "size_mb": 0,
383
523
  "password": password,
384
524
  "mirror": mirror,
385
- "session": session
525
+ "session": session,
526
+ "original_url": link
386
527
  })
387
528
  shared_state.get_db("protected").update_store(package_id, blob)
388
529
  info(f"Another CAPTCHA solution is required for {mirror} link: {replace_url}")
@@ -432,20 +573,26 @@ def setup_captcha_routes(app):
432
573
  package_id = payload.get("package_id")
433
574
  session_id = payload.get("session")
434
575
  title = payload.get("title", "Unknown Package")
576
+ password = payload.get("password", "")
577
+ original_url = payload.get("original_url", "")
435
578
  url = payload.get("links")[0] if payload.get("links") else None
436
579
 
437
580
  if not url or not session_id or not package_id:
438
581
  response.status = 400
439
582
  return "Missing required parameters"
440
583
 
584
+ # Add bypass section
585
+ bypass_section = render_bypass_section(original_url, package_id, title, password)
586
+
441
587
  return render_centered_html(f"""
442
588
  <!DOCTYPE html>
443
589
  <html>
444
590
  <body>
445
591
  <h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
446
592
  <p><b>Package:</b> {title}</p>
447
- <form action="/captcha/decrypt-filecrypt-circle?url={url}&session_id={session_id}&&package_id={package_id}" method="post">
448
- <input type="image" src="/captcha/circle.php?url={url}&session_id={session_id}" name="button" alt="Captcha">
593
+ <h3>Solve CAPTCHA</h3>
594
+ <form action="/captcha/decrypt-filecrypt-circle?url={url}&session_id={session_id}&package_id={package_id}" method="post">
595
+ <input type="image" src="/captcha/circle.php?url={url}&session_id={session_id}" name="button" alt="Circle CAPTCHA">
449
596
  </form>
450
597
  <p>
451
598
  {render_button("Delete Package", "secondary", {"onclick": f"location.href='/captcha/delete/{package_id}'"})}
@@ -453,6 +600,7 @@ def setup_captcha_routes(app):
453
600
  <p>
454
601
  {render_button("Back", "secondary", {"onclick": "location.href='/'"})}
455
602
  </p>
603
+ {bypass_section}
456
604
  </body>
457
605
  </html>""")
458
606
 
@@ -8,7 +8,7 @@ import requests
8
8
 
9
9
 
10
10
  def get_version():
11
- return "1.16.11"
11
+ return "1.17.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: 1.16.11
3
+ Version: 1.17.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