quasarr 1.24.1__tar.gz → 1.26.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 (85) hide show
  1. {quasarr-1.24.1 → quasarr-1.26.0}/PKG-INFO +3 -3
  2. {quasarr-1.24.1 → quasarr-1.26.0}/README.md +2 -2
  3. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/api/captcha/__init__.py +181 -63
  4. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/__init__.py +2 -1
  5. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/linkcrypters/hide.py +22 -8
  6. quasarr-1.26.0/quasarr/providers/html_images.py +22 -0
  7. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/html_templates.py +48 -1
  8. quasarr-1.26.0/quasarr/providers/obfuscated.py +104 -0
  9. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/version.py +1 -1
  10. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/web_server.py +8 -1
  11. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr.egg-info/PKG-INFO +3 -3
  12. quasarr-1.24.1/quasarr/providers/html_images.py +0 -22
  13. quasarr-1.24.1/quasarr/providers/obfuscated.py +0 -86
  14. {quasarr-1.24.1 → quasarr-1.26.0}/LICENSE +0 -0
  15. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/__init__.py +0 -0
  16. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/api/__init__.py +0 -0
  17. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/api/arr/__init__.py +0 -0
  18. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/api/config/__init__.py +0 -0
  19. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/api/sponsors_helper/__init__.py +0 -0
  20. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/api/statistics/__init__.py +0 -0
  21. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/linkcrypters/__init__.py +0 -0
  22. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/linkcrypters/al.py +0 -0
  23. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/linkcrypters/filecrypt.py +0 -0
  24. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/packages/__init__.py +0 -0
  25. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/__init__.py +0 -0
  26. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/al.py +0 -0
  27. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/by.py +0 -0
  28. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/dd.py +0 -0
  29. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/dj.py +0 -0
  30. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/dl.py +0 -0
  31. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/dt.py +0 -0
  32. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/dw.py +0 -0
  33. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/he.py +0 -0
  34. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/mb.py +0 -0
  35. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/nk.py +0 -0
  36. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/nx.py +0 -0
  37. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/sf.py +0 -0
  38. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/sj.py +0 -0
  39. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/sl.py +0 -0
  40. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/wd.py +0 -0
  41. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/downloads/sources/wx.py +0 -0
  42. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/__init__.py +0 -0
  43. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/cloudflare.py +0 -0
  44. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/imdb_metadata.py +0 -0
  45. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/log.py +0 -0
  46. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/myjd_api.py +0 -0
  47. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/notifications.py +0 -0
  48. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/sessions/__init__.py +0 -0
  49. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/sessions/al.py +0 -0
  50. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/sessions/dd.py +0 -0
  51. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/sessions/dl.py +0 -0
  52. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/sessions/nx.py +0 -0
  53. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/shared_state.py +0 -0
  54. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/providers/statistics.py +0 -0
  55. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/__init__.py +0 -0
  56. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/__init__.py +0 -0
  57. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/al.py +0 -0
  58. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/by.py +0 -0
  59. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/dd.py +0 -0
  60. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/dj.py +0 -0
  61. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/dl.py +0 -0
  62. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/dt.py +0 -0
  63. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/dw.py +0 -0
  64. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/fx.py +0 -0
  65. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/he.py +0 -0
  66. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/mb.py +0 -0
  67. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/nk.py +0 -0
  68. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/nx.py +0 -0
  69. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/sf.py +0 -0
  70. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/sj.py +0 -0
  71. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/sl.py +0 -0
  72. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/wd.py +0 -0
  73. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/search/sources/wx.py +0 -0
  74. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/storage/__init__.py +0 -0
  75. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/storage/config.py +0 -0
  76. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/storage/setup.py +0 -0
  77. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr/storage/sqlite_database.py +0 -0
  78. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr.egg-info/SOURCES.txt +0 -0
  79. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr.egg-info/dependency_links.txt +0 -0
  80. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr.egg-info/entry_points.txt +0 -0
  81. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr.egg-info/not-zip-safe +0 -0
  82. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr.egg-info/requires.txt +0 -0
  83. {quasarr-1.24.1 → quasarr-1.26.0}/quasarr.egg-info/top_level.txt +0 -0
  84. {quasarr-1.24.1 → quasarr-1.26.0}/setup.cfg +0 -0
  85. {quasarr-1.24.1 → quasarr-1.26.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quasarr
3
- Version: 1.24.1
3
+ Version: 1.26.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
@@ -36,12 +36,12 @@ Quasarr connects JDownloader with Radarr, Sonarr and LazyLibrarian. It also decr
36
36
  [![GitHub Sponsorship](https://img.shields.io/badge/support-me-red.svg)](https://github.com/users/rix1337/sponsorship)
37
37
 
38
38
  Quasarr pretends to be both `Newznab Indexer` and `SABnzbd client`. Therefore, do not try to use it with real usenet
39
- indexers or download clients. It simply does not know what NZB or torrent files are.
39
+ indexers. It simply does not know what NZB files are.
40
40
 
41
41
  Quasarr includes a solution to quickly and easily decrypt protected links.
42
42
  [Active monthly Sponsors get access to SponsorsHelper to do so automatically.](https://github.com/rix1337/Quasarr?tab=readme-ov-file#sponsorshelper)
43
43
  Alternatively, follow the link from the console output (or discord notification) to solve CAPTCHAs manually.
44
- Quasarr will confidently handle the rest.
44
+ Quasarr will confidently handle the rest. Some CAPTCHA types require [Tampermonkey](https://www.tampermonkey.net/) to be installed in your browser.
45
45
 
46
46
  # Instructions
47
47
  1. Set up and run [FlareSolverr 3](https://github.com/FlareSolverr/FlareSolverr).
@@ -9,12 +9,12 @@ Quasarr connects JDownloader with Radarr, Sonarr and LazyLibrarian. It also decr
9
9
  [![GitHub Sponsorship](https://img.shields.io/badge/support-me-red.svg)](https://github.com/users/rix1337/sponsorship)
10
10
 
11
11
  Quasarr pretends to be both `Newznab Indexer` and `SABnzbd client`. Therefore, do not try to use it with real usenet
12
- indexers or download clients. It simply does not know what NZB or torrent files are.
12
+ indexers. It simply does not know what NZB files are.
13
13
 
14
14
  Quasarr includes a solution to quickly and easily decrypt protected links.
15
15
  [Active monthly Sponsors get access to SponsorsHelper to do so automatically.](https://github.com/rix1337/Quasarr?tab=readme-ov-file#sponsorshelper)
16
16
  Alternatively, follow the link from the console output (or discord notification) to solve CAPTCHAs manually.
17
- Quasarr will confidently handle the rest.
17
+ Quasarr will confidently handle the rest. Some CAPTCHA types require [Tampermonkey](https://www.tampermonkey.net/) to be installed in your browser.
18
18
 
19
19
  # Instructions
20
20
  1. Set up and run [FlareSolverr 3](https://github.com/FlareSolverr/FlareSolverr).
@@ -108,6 +108,12 @@ def setup_captcha_routes(app):
108
108
 
109
109
  has_junkies_links = any(is_junkies_link(link) for link in prioritized_links)
110
110
 
111
+ # Hide uses nested arrays like FileCrypt: [["url", "mirror"]]
112
+ has_hide_links = any(
113
+ ("hide." in link[0] if isinstance(link, (list, tuple)) else "hide." in link)
114
+ for link in prioritized_links
115
+ )
116
+
111
117
  # KeepLinks uses nested arrays like FileCrypt: [["url", "mirror"]]
112
118
  has_keeplinks_links = any(
113
119
  ("keeplinks." in link[0] if isinstance(link, (list, tuple)) else "keeplinks." in link)
@@ -120,7 +126,10 @@ def setup_captcha_routes(app):
120
126
  for link in prioritized_links
121
127
  )
122
128
 
123
- if has_junkies_links:
129
+ if has_hide_links:
130
+ debug("Redirecting to Hide page")
131
+ redirect(f"/captcha/hide?data={quote(encoded_payload)}")
132
+ elif has_junkies_links:
124
133
  debug("Redirecting to Junkies CAPTCHA")
125
134
  redirect(f"/captcha/junkies?data={quote(encoded_payload)}")
126
135
  elif has_keeplinks_links:
@@ -151,7 +160,7 @@ def setup_captcha_routes(app):
151
160
  return {"error": f"Failed to decode payload: {str(e)}"}
152
161
 
153
162
  def render_userscript_section(url, package_id, title, password, provider_type="junkies"):
154
- """Render the userscript UI section for Junkies, KeepLinks, or ToLink pages
163
+ """Render the userscript UI section for Junkies, KeepLinks, ToLink, or Hide pages
155
164
 
156
165
  This is the MAIN solution for these providers (not a bypass/fallback).
157
166
 
@@ -160,10 +169,10 @@ def setup_captcha_routes(app):
160
169
  package_id: Package identifier
161
170
  title: Package title
162
171
  password: Package password
163
- provider_type: Either "junkies", "keeplinks", or "tolink"
172
+ provider_type: Either "hide", "junkies", "keeplinks", or "tolink"
164
173
  """
165
174
 
166
- provider_names = {"junkies": "Junkies", "keeplinks": "KeepLinks", "tolink": "ToLink"}
175
+ provider_names = {"hide": "Hide", "junkies": "Junkies", "keeplinks": "KeepLinks", "tolink": "ToLink"}
167
176
  provider_name = provider_names.get(provider_type, "Provider")
168
177
  userscript_url = f"/captcha/{provider_type}.user.js"
169
178
  storage_key = f"hide{provider_name}SetupInstructions"
@@ -182,45 +191,89 @@ def setup_captcha_routes(app):
182
191
 
183
192
  return f'''
184
193
  <div>
194
+ <!-- Info section explaining the process -->
195
+ <div class="info-box">
196
+ <h3>ℹ️ How This Works:</h3>
197
+ <p style="margin-bottom: 8px;">
198
+ 1. Click the link below to open {provider_name}
199
+ </p>
200
+ <p style="margin-top: 0; margin-bottom: 8px;">
201
+ 2. Solve any CAPTCHAs on their site to reveal the download links
202
+ </p>
203
+ <p style="margin-top: 0; margin-bottom: 0;">
204
+ 3. <b>With the userscript installed</b>, links are automatically sent back to Quasarr!
205
+ </p>
206
+ </div>
207
+
185
208
  <!-- One-time setup section - visually separated -->
186
- <div id="setup-instructions" style="background: #2a2a2a; border: 2px solid #444; border-radius: 8px; padding: 16px; margin-bottom: 24px;">
187
- <h3 style="margin-top: 0; color: #58a6ff;">First Time Setup:</h3>
209
+ <div id="setup-instructions" class="setup-box">
210
+ <h3>📦 First Time Setup:</h3>
188
211
  <p style="margin-bottom: 8px;">
189
212
  <a href="https://www.tampermonkey.net/" target="_blank" rel="noopener noreferrer">1. Install Tampermonkey</a>
190
213
  </p>
191
214
  <p style="margin-top: 0; margin-bottom: 12px;">
192
- <a href="{userscript_url}" target="_blank">2. Install this userscript</a>
215
+ <a href="{userscript_url}" target="_blank">2. Install the {provider_name} userscript</a>
193
216
  </p>
194
217
  <p style="margin-top: 0;">
195
- <button id="hide-setup-btn" type="button" style="background: #444; color: #fff; border: 1px solid #666; padding: 6px 12px; border-radius: 4px; cursor: pointer;">
218
+ <button id="hide-setup-btn" type="button" class="btn-subtle">
196
219
  ✅ Don't show this again
197
220
  </button>
198
221
  </p>
199
222
  </div>
200
223
 
201
- <!-- Hidden "show instructions" link -->
224
+ <!-- Hidden "show instructions" button -->
202
225
  <div id="show-instructions-link" style="display: none; margin-bottom: 16px;">
203
- <a href="#" id="show-setup-btn" style="color: #58a6ff;">ℹ️ Show instructions again</a>
226
+ <button id="show-setup-btn" type="button" class="btn-subtle">
227
+ ℹ️ Show setup instructions
228
+ </button>
204
229
  </div>
205
230
 
206
- <strong><a href="{url_with_quick_transfer_params}" target="_self">🔗 Obtain the download links here!</a></strong><br><br>
207
-
208
- <form id="bypass-form" action="/captcha/bypass-submit" method="post" enctype="multipart/form-data">
209
- <input type="hidden" name="package_id" value="{package_id}" />
210
- <input type="hidden" name="title" value="{title}" />
211
- <input type="hidden" name="password" value="{password}" />
212
-
213
- <div>
214
- <strong>Paste the download links (one per line):</strong>
215
- <textarea id="links-input" name="links" rows="5" style="width: 100%; padding: 8px; font-family: monospace; resize: vertical;"></textarea>
216
- </div>
231
+ <!-- Primary action - the quick transfer link -->
232
+ <p>
233
+ {render_button(f"Open {provider_name} & Get Download Links", "primary", {"onclick": f"location.href='{url_with_quick_transfer_params}'"})}
234
+ </p>
217
235
 
218
- <div>
219
- {render_button("Submit", "primary", {"type": "submit"})}
220
- </div>
221
- </form>
236
+ <!-- Manual submission - collapsible -->
237
+ <div class="section-divider">
238
+ <details id="manualSubmitDetails">
239
+ <summary id="manualSubmitSummary" style="cursor: pointer;">Show Manual Submission</summary>
240
+ <div style="margin-top: 16px;">
241
+ <p style="font-size: 0.9em;">
242
+ If the userscript doesn't work, you can manually paste the links below:
243
+ </p>
244
+ <form id="bypass-form" action="/captcha/bypass-submit" method="post" enctype="multipart/form-data">
245
+ <input type="hidden" name="package_id" value="{package_id}" />
246
+ <input type="hidden" name="title" value="{title}" />
247
+ <input type="hidden" name="password" value="{password}" />
248
+
249
+ <div>
250
+ <strong>Paste the download links (one per line):</strong>
251
+ <textarea id="links-input" name="links" rows="5" style="width: 100%; padding: 8px; font-family: monospace; resize: vertical;"></textarea>
252
+ </div>
253
+
254
+ <div>
255
+ {render_button("Submit", "primary", {"type": "submit"})}
256
+ </div>
257
+ </form>
258
+ </div>
259
+ </details>
260
+ </div>
222
261
  </div>
223
262
  <script>
263
+ // Handle manual submission toggle text
264
+ const manualDetails = document.getElementById('manualSubmitDetails');
265
+ const manualSummary = document.getElementById('manualSubmitSummary');
266
+
267
+ if (manualDetails && manualSummary) {{
268
+ manualDetails.addEventListener('toggle', () => {{
269
+ if (manualDetails.open) {{
270
+ manualSummary.textContent = 'Hide Manual Submission';
271
+ }} else {{
272
+ manualSummary.textContent = 'Show Manual Submission';
273
+ }}
274
+ }});
275
+ }}
276
+
224
277
  // Handle setup instructions hide/show
225
278
  const hideSetup = localStorage.getItem('{storage_key}');
226
279
  const setupBox = document.getElementById('setup-instructions');
@@ -239,8 +292,7 @@ def setup_captcha_routes(app):
239
292
  }});
240
293
 
241
294
  // Show setup instructions again
242
- document.getElementById('show-setup-btn').addEventListener('click', function(e) {{
243
- e.preventDefault();
295
+ document.getElementById('show-setup-btn').addEventListener('click', function() {{
244
296
  localStorage.setItem('{storage_key}', 'false');
245
297
  setupBox.style.display = 'block';
246
298
  showLink.style.display = 'none';
@@ -248,6 +300,42 @@ def setup_captcha_routes(app):
248
300
  </script>
249
301
  '''
250
302
 
303
+ @app.get("/captcha/hide")
304
+ def serve_hide_captcha():
305
+ payload = decode_payload()
306
+
307
+ if "error" in payload:
308
+ return render_centered_html(f'''<h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
309
+ <p>{payload["error"]}</p>
310
+ <p>
311
+ {render_button("Back", "secondary", {"onclick": "location.href='/'"})}
312
+ </p>''')
313
+
314
+ package_id = payload.get("package_id")
315
+ title = payload.get("title")
316
+ password = payload.get("password")
317
+ urls = payload.get("links")
318
+ url = urls[0][0] if isinstance(urls[0], (list, tuple)) else urls[0]
319
+
320
+ check_package_exists(package_id)
321
+
322
+ return render_centered_html(f"""
323
+ <!DOCTYPE html>
324
+ <html>
325
+ <body>
326
+ <h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
327
+ <p><b>Package:</b> {title}</p>
328
+ {render_userscript_section(url, package_id, title, password, "hide")}
329
+ <p>
330
+ {render_button("Delete Package", "secondary", {"onclick": f"location.href='/captcha/delete/{package_id}'"})}
331
+ </p>
332
+ <p>
333
+ {render_button("Back", "secondary", {"onclick": "location.href='/'"})}
334
+ </p>
335
+
336
+ </body>
337
+ </html>""")
338
+
251
339
  @app.get("/captcha/junkies")
252
340
  def serve_junkies_captcha():
253
341
  payload = decode_payload()
@@ -358,6 +446,18 @@ def setup_captcha_routes(app):
358
446
  </body>
359
447
  </html>""")
360
448
 
449
+ @app.get('/captcha/filecrypt.user.js')
450
+ def serve_filecrypt_user_js():
451
+ content = obfuscated.filecrypt_user_js()
452
+ response.content_type = 'application/javascript'
453
+ return content
454
+
455
+ @app.get('/captcha/hide.user.js')
456
+ def serve_hide_user_js():
457
+ content = obfuscated.hide_user_js()
458
+ response.content_type = 'application/javascript'
459
+ return content
460
+
361
461
  @app.get('/captcha/junkies.user.js')
362
462
  def serve_junkies_user_js():
363
463
  sj = shared_state.values["config"]("Hostnames").get("sj")
@@ -379,12 +479,6 @@ def setup_captcha_routes(app):
379
479
  response.content_type = 'application/javascript'
380
480
  return content
381
481
 
382
- @app.get('/captcha/filecrypt.user.js')
383
- def serve_filecrypt_user_js():
384
- content = obfuscated.filecrypt_user_js()
385
- response.content_type = 'application/javascript'
386
- return content
387
-
388
482
  def render_filecrypt_bypass_section(url, package_id, title, password):
389
483
  """Render the bypass UI section for both cutcaptcha and circle captcha pages"""
390
484
 
@@ -402,52 +496,77 @@ def setup_captcha_routes(app):
402
496
  )
403
497
 
404
498
  return f'''
405
- <div style="margin-top: 40px; padding-top: 20px; border-top: 2px solid #ccc;">
499
+ <div class="section-divider" style="max-width: 370px; margin-left: auto; margin-right: auto;">
406
500
  <details id="bypassDetails">
407
501
  <summary id="bypassSummary">Show CAPTCHA Bypass</summary><br>
408
502
 
503
+ <!-- Info section explaining the process -->
504
+ <div class="info-box">
505
+ <h3>ℹ️ How This Works:</h3>
506
+ <p style="margin-bottom: 8px;">
507
+ 1. Click the button below to open FileCrypt directly
508
+ </p>
509
+ <p style="margin-top: 0; margin-bottom: 8px;">
510
+ 2. Solve any CAPTCHAs on their site to reveal the download links
511
+ </p>
512
+ <p style="margin-top: 0; margin-bottom: 0;">
513
+ 3. <b>With the userscript installed</b>, links are automatically sent back to Quasarr!
514
+ </p>
515
+ </div>
516
+
409
517
  <!-- One-time setup section - visually separated -->
410
- <div id="setup-instructions" style="background: #2a2a2a; border: 2px solid #444; border-radius: 8px; padding: 16px; margin-bottom: 24px;">
411
- <h3 style="margin-top: 0; color: #58a6ff;">First Time Setup:</h3>
518
+ <div id="setup-instructions" class="setup-box">
519
+ <h3>📦 First Time Setup:</h3>
412
520
  <p style="margin-bottom: 8px;">
413
521
  <a href="https://www.tampermonkey.net/" target="_blank" rel="noopener noreferrer">1. Install Tampermonkey</a>
414
522
  </p>
415
523
  <p style="margin-top: 0; margin-bottom: 12px;">
416
- <a href="/captcha/filecrypt.user.js" target="_blank">2. Install this userscript</a>
524
+ <a href="/captcha/filecrypt.user.js" target="_blank">2. Install the FileCrypt userscript</a>
417
525
  </p>
418
526
  <p style="margin-top: 0;">
419
- <button id="hide-setup-btn" type="button" style="background: #444; color: #fff; border: 1px solid #666; padding: 6px 12px; border-radius: 4px; cursor: pointer;">
527
+ <button id="hide-setup-btn" type="button" class="btn-subtle">
420
528
  ✅ Don't show this again
421
529
  </button>
422
530
  </p>
423
531
  </div>
424
532
 
425
- <!-- Hidden "show instructions" link -->
533
+ <!-- Hidden "show instructions" button -->
426
534
  <div id="show-instructions-link" style="display: none; margin-bottom: 16px;">
427
- <a href="#" id="show-setup-btn" style="color: #58a6ff;">ℹ️ Show instructions again</a>
535
+ <button id="show-setup-btn" type="button" class="btn-subtle">
536
+ ℹ️ Show setup instructions
537
+ </button>
428
538
  </div>
429
539
 
430
- <strong><a href="{url_with_quick_transfer_params}" target="_self">🔗 Obtain the download links here!</a></strong><br><br>
431
-
432
- <form id="bypass-form" action="/captcha/bypass-submit" method="post" enctype="multipart/form-data">
433
- <input type="hidden" name="package_id" value="{package_id}" />
434
- <input type="hidden" name="title" value="{title}" />
435
- <input type="hidden" name="password" value="{password}" />
436
-
437
- <div>
438
- <strong>Paste the download links (one per line):</strong>
439
- <textarea id="links-input" name="links" rows="5" style="width: 100%; padding: 8px; font-family: monospace; resize: vertical;"></textarea>
440
- </div>
441
-
442
- <div>
443
- <strong>Or upload DLC file:</strong><br>
444
- <input type="file" id="dlc-file" name="dlc_file" accept=".dlc" />
445
- </div>
540
+ <!-- Primary action button -->
541
+ <p>
542
+ {render_button("Open FileCrypt & Get Download Links", "primary", {"onclick": f"location.href='{url_with_quick_transfer_params}'"})}
543
+ </p>
446
544
 
447
- <div>
448
- {render_button("Submit", "primary", {"type": "submit"})}
449
- </div>
450
- </form>
545
+ <!-- Manual submission section -->
546
+ <div class="section-divider">
547
+ <p style="font-size: 0.9em; margin-bottom: 16px;">
548
+ If the userscript doesn't work, you can manually paste the links or upload a DLC file:
549
+ </p>
550
+ <form id="bypass-form" action="/captcha/bypass-submit" method="post" enctype="multipart/form-data">
551
+ <input type="hidden" name="package_id" value="{package_id}" />
552
+ <input type="hidden" name="title" value="{title}" />
553
+ <input type="hidden" name="password" value="{password}" />
554
+
555
+ <div>
556
+ <strong>Paste the download links (one per line):</strong>
557
+ <textarea id="links-input" name="links" rows="5" style="width: 100%; padding: 8px; font-family: monospace; resize: vertical;"></textarea>
558
+ </div>
559
+
560
+ <div>
561
+ <strong>Or upload DLC file:</strong><br>
562
+ <input type="file" id="dlc-file" name="dlc_file" accept=".dlc" />
563
+ </div>
564
+
565
+ <div>
566
+ {render_button("Submit", "primary", {"type": "submit"})}
567
+ </div>
568
+ </form>
569
+ </div>
451
570
  </details>
452
571
  </div>
453
572
  <script>
@@ -483,8 +602,7 @@ def setup_captcha_routes(app):
483
602
  }});
484
603
 
485
604
  // Show setup instructions again
486
- document.getElementById('show-setup-btn').addEventListener('click', function(e) {{
487
- e.preventDefault();
605
+ document.getElementById('show-setup-btn').addEventListener('click', function() {{
488
606
  localStorage.setItem('hideFileCryptSetupInstructions', 'false');
489
607
  setupBox.style.display = 'block';
490
608
  showLink.style.display = 'none';
@@ -764,7 +882,7 @@ def setup_captcha_routes(app):
764
882
  reloadSection.style.display = "block";
765
883
  });
766
884
  }
767
- ''' + obfuscated.captcha_js() + f'''</script>
885
+ ''' + obfuscated.cutcaptcha_custom_js() + f'''</script>
768
886
  <div>
769
887
  <h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
770
888
  <p id="package-title" style="max-width: 370px; word-wrap: break-word; overflow-wrap: break-word;"><b>Package:</b> {title}</p>
@@ -202,7 +202,8 @@ def process_links(shared_state, source_result, title, password, package_id, imdb
202
202
  if result["success"]:
203
203
  send_discord_message(shared_state, title=title, case="unprotected", imdb_id=imdb_id, source=source_url)
204
204
  return {"success": True, "title": title}
205
- info(f"Auto-decrypt failed for {title}, checking for protected fallback...")
205
+ info(f"Auto-decrypt failed for {title}, falling back to manual CAPTCHA...")
206
+ classified['protected'].extend(classified['auto'])
206
207
 
207
208
  # PRIORITY 3: Protected (filecrypt, tolink, keeplinks, junkies)
208
209
  if classified['protected']:
@@ -3,6 +3,7 @@
3
3
  # Project by https://github.com/rix1337
4
4
 
5
5
  import re
6
+ from concurrent.futures import ThreadPoolExecutor, as_completed
6
7
  from typing import List, Dict, Any
7
8
 
8
9
  import requests
@@ -29,18 +30,31 @@ def unhide_links(shared_state, url):
29
30
  response = requests.get(container_url, headers=headers)
30
31
  data = response.json()
31
32
 
32
- for link in data.get("links", []):
33
- link_id = link.get("id")
34
- if not link_id:
35
- continue
33
+ link_ids = [link.get("id") for link in data.get("links", []) if link.get("id")]
34
+
35
+ if not link_ids:
36
+ debug(f"No link IDs found in container {container_id}")
37
+ return []
36
38
 
39
+ def fetch_link(link_id):
37
40
  debug(f"Fetching hide.cx link with ID: {link_id}")
38
41
  link_url = f"https://api.hide.cx/containers/{container_id}/links/{link_id}"
39
42
  link_data = requests.get(link_url, headers=headers).json()
40
-
41
- final_url = link_data.get("url")
42
- if final_url and final_url not in links:
43
- links.append(final_url)
43
+ return link_data.get("url")
44
+
45
+ # Process links in batches of 10
46
+ batch_size = 10
47
+ for i in range(0, len(link_ids), batch_size):
48
+ batch = link_ids[i:i + batch_size]
49
+ with ThreadPoolExecutor(max_workers=batch_size) as executor:
50
+ futures = [executor.submit(fetch_link, link_id) for link_id in batch]
51
+ for future in as_completed(futures):
52
+ try:
53
+ final_url = future.result()
54
+ if final_url and final_url not in links:
55
+ links.append(final_url)
56
+ except Exception as e:
57
+ info(f"Error fetching link: {e}")
44
58
 
45
59
  success = bool(links)
46
60
  if success:
@@ -0,0 +1,22 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Quasarr
3
+ # Project by https://github.com/rix1337
4
+
5
+ logo = 'data:image/webp;base64,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'
6
+ al = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAA9UlEQVR4nBXBv0rDQADA4d8ld8EQ2yJiGxAchOqgQSFEEEGfQV18DgcHwYcQ36GDg07+wUlwcBApCnUrbYcKIrRIa0qTy534fSI+vbMCi7GSUe7zr6QmOEJjEUhhDcZReHLEQXiLNoKn4S66CHBMjjTKJ0+nHPZPOJrtEPguc+17GuE5fjCD9H57rHYuCT/H3HxUmaYp5aDPRnZBd2kfp9d6I15fYe/qmdryJiVvge3GI1tRnW7rHbF2fG0rXy9E5QFJnoFSNF1Jc1jhp5YgkrMHm2kYtF9R+TdYS6aqzNdjPBek0RolYDHaoSgsWHClQE/GGA1/SzpdtJMWu3cAAAAASUVORK5CYII='
7
+ by = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAMAAADz0U65AAAATlBMVEX///8crf/29fQDo//J7f/u7u7w+v/2/P/8/PyytLWenp6IhoWTwNfPz8+mpaVYxP94eHhryf+75fyB0f+oxNPl5eW4yNGrr68xsfmUlJQWKuGqAAAAQklEQVQI1yWLSRKAIBADMzOAsgiC+/8/aiwvSR+6AUB15mJyZ42ev8ldclI4MWvXuvwQCaGblPxQCmNP9fgyqGf+AkFFAeZ3L10cAAAAAElFTkSuQmCC'
8
+ dd = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABCElEQVR4nAXBP0sCYRzA8e89d6eQ4SRkdNKQOEVSvQQbegdB4dAgLY1tLS1NvYFoDIIIGtqKXPq3JJGWEQkaDQliGJnaPXf6e/p8rGrt4/CxUl777fZQylYGg4iReHyMxez8kXN1c5c/Pj1D7AhKQqKujQgqCDSrK35evTca/InD+sYmyXSWdqeLFvCHhka9bpyvdgsdCpl0msRkin1/xMLcLKXbIs3mp6UG/R5vL2VKzzV29g4oFAokkh7XxQuGgcbxtTYy6Fr97xaZ6QmeHu55rVYIf9poHeAMR8ZCNLvbW8TGY5x0OrgK7KhLEIY4U6nUeS63tBwGWowR5dgziME4bsTyPO/yH3LufRKlNxKMAAAAAElFTkSuQmCC'
9
+ dj = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAMAAADz0U65AAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAC9UExURY2NjYWGhoeIiYiIiIiIiYWFhoODg4mKillZWYSEhFBMS3x7e0xIR3Bsa0E+PldWVi4wMT4/Px0eHiQkJA8PDwoMDAsMDAsMDAsLCw0NDWxqaHZtZnBrZ2dnZ25qZ21oZJ94XtWNWr6DXG9mYpp2X6p5V6JwVNJ9TsN4UXBgWZhtVaxuTZBbR6xiRMpoQKlgQ6JfRZ5cQHFGN6dOMqFOM5lMM6tPMmZBNCYlJS8mIywlJCglJC8mJCMhIf////64+awAAAAadFJOU1SosLCwokSyl7ugup+6n7ugr5RLlJmYmZA8Hg7dEgAAAAFiS0dEPklkAOMAAAAHdElNRQfpDBESHCL8f1a2AAAAUElEQVQI12NgYGRiYmZhZWNgl5KWkZWT52DgVFBUUlZR5WLgVlPX0NTS5mHg1dHV0zcw5GPgNzI2MTUzF2AQtLC0sraxFWIQFhEVExOXkAQA4WIHfGNBCckAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjUtMTItMTdUMTg6Mjg6MjgrMDA6MDDR5B2HAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDI1LTEyLTE3VDE4OjI4OjI4KzAwOjAwoLmlOwAAACh0RVh0ZGF0ZTp0aW1lc3RhbXAAMjAyNS0xMi0xN1QxODoyODozNCswMDowMPym7g4AAAAASUVORK5CYII='
10
+ dl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAACKADAAQAAAABAAAACAAAAACVhHtSAAAAw0lEQVQYGV2PMQrCQBBF/+6OSUy1aYQgSI7gCSR4Bhs78RKCtQfxAnqGVLYewE6ENELSqHFJdt0hCOIvhtl9zPw/Al55voqCNikFoPntgNpQlRbFvhEMwzZ5MfjXm6oh8aQigdZZ+A2QfpxlJcCMiISeqgwRxYgfGuflHLjfMLuMcbzuND3JYGPXiE2IMjAYHU6YIIMNFKzoQEEzqBdqqzu/21nX20hvZgRkp2ritBxSdr33b/UhU8mncFo+7Qu55z9mH8f7S3Ax+Zg1AAAAAElFTkSuQmCC'
11
+ dt = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAA4ElEQVR4nD2PsWrCUBSGv3ubBoq+QevQUhAXV6dMRcyTZBV8CCfxCTq4ZOmSJwg+giB4t16p1NCpSFECyY2n5EL7LeeD8/PDr5qmEa01+XrNz35PaAxFUXCrNQ/DIVophbWW19WKt+WSp16PYLvlvtNBHY8ESZLg6ppPaxmMRgymUz4WC577fR5nM3SapryMx0RRxPf5jDjHXRi21bQEWZZhjCGeTIiDgOvpxM3hQLXb+QAiImVZSlVV0tS1XEXkPY7laz5vX0K74h/n/KnyXNxm41351B+tKoVcLt5Vt8svgsSBPKnRQSAAAAAASUVORK5CYII='
12
+ dw = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAA70lEQVR4nAXBTUrDQBiA4febmfjbpqZUsFkJxS6UIq4EvYS46CUExRPkBL2HFM/gIdxIFq4Ff2JNia3JzBefR+bzuZ1Op+E+y/q6Fd/GSe8qijZH1lpfN00uADdZNt5NDh7GJ8dng2HKxvYOBtB6jctms/5S3ePpxeWklw69tZE1IqKqrIxT81U1d4PReCJJ338HXBVURCCokhelMZW467YTt9pi1rUntPCHYJ0ljiyubMJh0yJFuZSk26ENjufPFV0CCeAUfLEo8V7pRBHWOMJvBc5Q+xpnIf8pl+fvi1Lztw9zlO6T7sWw9jy9vPIPUL1kPErau3YAAAAASUVORK5CYII='
13
+ fx = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABB0lEQVR4nB2LP0sCcQBA3/2Uzugu8E4IoT9cSYhUENFWYSA1FFTQ4hK5SENNDW19hoYcImhvaGgLguYgcIk0haLOE7wrNGzo5A4v9K3vPWknmwt0LYrrunieTygkAAm300EIQTg5nWB3e5O39w9s54t4fISEYfBZq/H4VETIsoxp1ftyJpWkR7lSZXFhnsFIhLBtOxR9n1KliqZFeX4po+saZ4VLTMtC9M61TJrTk2OarR9WV5bIpJfZ2ljH8zxEw3EovVaZm01xd//AsKpwfXNLLKaTmDSQ9vJHwWE+x3ezRbv9i6oqTBkTmLU65xdXSNn9g0CWB2jYDuNjo/y5Lqoy1A+63S7/GeVj+5KBt3UAAAAASUVORK5CYII='
14
+ nk = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYYAAB2GAV2iE4EAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8AAADrmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4NCjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4NCgk8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPg0KCQk8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxNCAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4RUJCOThFRkQ5RTcxMUU1QTcyN0EzQzFGN0YzNjcwNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo4RUJCOThGMEQ5RTcxMUU1QTcyN0EzQzFGN0YzNjcwNCI+DQoJCQk8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4RUJCOThFREQ5RTcxMUU1QTcyN0EzQzFGN0YzNjcwNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4RUJCOThFRUQ5RTcxMUU1QTcyN0EzQzFGN0YzNjcwNCIvPg0KCQk8L3JkZjpEZXNjcmlwdGlvbj4NCgkJPHJkZjpEZXNjcmlwdGlvbiB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+PHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj48L3JkZjpEZXNjcmlwdGlvbj48L3JkZjpSREY+DQo8L3g6eG1wbWV0YT4NCjw/eHBhY2tldCBlbmQ9J3cnPz7hg4LgAAAA3UlEQVQoU2NgIAAYGRgYGP7//8/4cHak44//jJfY//3T+S0vdpz/zXvZZ8+/yTIzMDAwhP490ir479ecx89/H2X69nH123vvn/1592HVy6+/rjNeyrPR+/jz927mf3+5v7Pz+vH8/DSXm51Z7NW3v8ec5p11Zfr44fOSv5+/MDB+/fqP4dOnoJ9fvjN++/TtE/P376IMDAwMzL5qYt8Y/v5+ws383+ndb8ajLP9+2T36xZ7O/f9HTIyhFCMjzLVLIw28Hv8ROarA8sL64Ve+wyocX+R//mOQQfIQdgAAAghnscY2ZXIAAAAASUVORK5CYII='
15
+ he = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYYAAB2GAV2iE4EAAACsSURBVChTY2CAgv///zPC2DA+uhh28IiBxZ5FQTWdS8tUgktOLZlHTq2eR05tAoeschxYwTsGpnpRVq6bDMxscxhYue4xsPM8ZBIULWVg57nAwM6uyPCPg0N+DwPD4zgGhnMyDAz32BgY7jAwMJQxMDCeMmdgCAeb8oeBIeEfA4PjMgaG3FQGhnlODIzHHBgY18UzMARjur6+numfuDj3f0lJLogA1DvoCmEAAJVNNF84URktAAAAAElFTkSuQmCC'
16
+ mb = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABE0lEQVR4nAEIAff+AQAAAACHttAxAQ0Kjd7o7ToDAQH/GREOxcDT4HK+cErSAYy70DHs/AG6tNjoFNzn6gD5+fwAQjElAIE/JuqWxdZEBBwVD5RSIhsUnsTV/JBRNwT9/v4AjcDU/DsgOxbW6RqUArra5zf39vb/KRwTBKPN3gBMKhwA3unyBJHE2P/i7/U6Auvz+ACkzt4AaTkmAN/u9AARCQYAQysdALXW5ADl8fYAAi4bEMbD5vQBMxoR/CwYEACVxtkAfUMr/PoECAEZDwrFAiAPCmwpEAjq/gcKBAUDAQCQwtcAMSIZBDkbDukIBAJtAmU5KNIP//1F1ePrvScWD/hSLR/4uNXkvAHz9kZ+RS/T0LpzUht5tWAAAAAASUVORK5CYII='
17
+ nx = 'data:image/png;base64,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'
18
+ sf = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAA8ElEQVR4nBXBv0rDQADA4d/lz1lqmlREECUIFTq4FBeHDA6uPkRfwwfxBRx19gmk4GZBBAcjCAUrWEkaTUzucjnx+0R6kzQboZDCb7AWdNmiMo3KLHUmlCcDR/Z2DujvJXRth6kNqqgoXufo6kk6lpzwcEr7fUrx7POTugh9xPZkilE5jh9MsJ2mreaUi0eKdIa7CUIogjjB8wcnyOiM0rmkH+8ih8dY1gTxOeHoA69eLRCOxBvsE21JOmVo8gy3N6T+fMNbPdwiwzG6XFJ/vWC1xY9GvN9dsZxdI+4vxo3rtxLH8q/TFvPbYhqL0Z76AydWbEnTqqOqAAAAAElFTkSuQmCC'
19
+ sj = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAMAAADz0U65AAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAC9UExURY2NjYWGhoeIiYiIiIiIiYWFhoODg4mKillZWYSEhFBMS3x7e0xIR3Bsa0E+PldWVi4wMT4/Px0eHiQkJA8PDwoMDAsMDAsMDAsLCw0NDWxqaHZtZnBrZ2dnZ25qZ21oZJ94XtWNWr6DXG9mYpp2X6p5V6JwVNJ9TsN4UXBgWZhtVaxuTZBbR6xiRMpoQKlgQ6JfRZ5cQHFGN6dOMqFOM5lMM6tPMmZBNCYlJS8mIywlJCglJC8mJCMhIf////64+awAAAAadFJOU1SosLCwokSyl7ugup+6n7ugr5RLlJmYmZA8Hg7dEgAAAAFiS0dEPklkAOMAAAAHdElNRQfpDBESHCL8f1a2AAAAUElEQVQI12NgYGRiYmZhZWNgl5KWkZWT52DgVFBUUlZR5WLgVlPX0NTS5mHg1dHV0zcw5GPgNzI2MTUzF2AQtLC0sraxFWIQFhEVExOXkAQA4WIHfGNBCckAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjUtMTItMTdUMTg6Mjg6MjgrMDA6MDDR5B2HAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDI1LTEyLTE3VDE4OjI4OjI4KzAwOjAwoLmlOwAAACh0RVh0ZGF0ZTp0aW1lc3RhbXAAMjAyNS0xMi0xN1QxODoyODozNCswMDowMPym7g4AAAAASUVORK5CYII='
20
+ sl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAA/ElEQVR4nE3PrUpDYRzH8d/z5tnRozugImIYgjAwGY5egGUG64JRkxgWvIFV0zCIwVsQLLYhWgSDLizJiowVFUHZdJ7zvP7Ftu8NfPgybOwvImcFet0C6FhMtrYTSQg1Ru/id3W7sfTxmc3rQpeE4gk8BZJiigF1kWYLe9rSGZEvR4o/AvRAxBLB8SObzXVqXb/tBjFdJjOCtliJI/U6fDo/CQSwf4ozIM2OGrm2xyGEClQCReO76tZmTabZYcsHmnHOfSvJ+8axCiNtlJDtztdNkLmxbViyEIx7669YxPPlZG44uD99wXs9ltqNbvF8aSbvBgBH9WAWMZX+AJIYacQLryqFAAAAAElFTkSuQmCC'
21
+ wd = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAA90lEQVR4nC3OsS5DUQAG4P8/Pb2kiS4mliaEkMYgTBiQiMVk6C6RsHmAxuoFbHTzCmIR9QSWLko6kIiQCnqHq+fec84vEt8TfMS/5s3jniFf36uD9sTH6Fg9rQ8aDQY2r7trhJmFUY1kW9I4gF0I9wGuZUOM04x+B4o9EvMSZ0D1EVWYkklsnn51TFIOhJ2DwgaN6QiqgsbRh00rseu9J1WsUngGOBmhSvaZnZwdbA1MkWfrwQ2PvXMvwbk7n7taLPKppFpe+cvb8DN8gi09RGmkbEv9GHQLqQZi+bB19WZDdN+WlYskRe/0aNsBaO+fXy4ZxEWKC7/R+XkmFbstdAAAAABJRU5ErkJggg=='
22
+ wx = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAACKADAAQAAAABAAAACAAAAACVhHtSAAAAcUlEQVQYGWNgYGD4B8S4wD+mf7y2jEDZ/0CsiKRKEiT2j9cGLMQGJH8DMUwhyEQQGyTGBcRg3T+hNMgkGP4EYsN0AdlgAHMPE0wARMN0XEASBLHB4ixQQUGgg94xMjCCBMEyTJ8PiwKZr0F8kDW4ACMASI8hUcBfWMAAAAAASUVORK5CYII='
@@ -24,6 +24,11 @@ def render_centered_html(inner_content):
24
24
  --secondary: #6c757d;
25
25
  --code-bg: #f8f9fa;
26
26
  --spacing: 1rem;
27
+ --info-border: #2d5a2d;
28
+ --setup-border: var(--primary);
29
+ --divider-color: #dee2e6;
30
+ --btn-subtle-bg: #e9ecef;
31
+ --btn-subtle-border: #ced4da;
27
32
  }
28
33
  @media (prefers-color-scheme: dark) {
29
34
  :root {
@@ -32,8 +37,50 @@ def render_centered_html(inner_content):
32
37
  --card-bg: #242526;
33
38
  --card-shadow: rgba(0, 0, 0, 0.5);
34
39
  --code-bg: #2c2f33;
40
+ --info-border: #4a8c4a;
41
+ --setup-border: var(--primary);
42
+ --divider-color: #444;
43
+ --btn-subtle-bg: #444;
44
+ --btn-subtle-border: #666;
35
45
  }
36
46
  }
47
+ /* Info box styling */
48
+ .info-box {
49
+ border: 1px solid var(--info-border);
50
+ border-radius: 8px;
51
+ padding: 16px;
52
+ margin-bottom: 24px;
53
+ }
54
+ .info-box h3 {
55
+ margin-top: 0;
56
+ color: var(--info-border);
57
+ }
58
+ /* Setup box styling */
59
+ .setup-box {
60
+ border: 1px solid var(--setup-border);
61
+ border-radius: 8px;
62
+ padding: 16px;
63
+ margin-bottom: 24px;
64
+ }
65
+ .setup-box h3 {
66
+ margin-top: 0;
67
+ color: var(--setup-border);
68
+ }
69
+ /* Subtle button styling */
70
+ .btn-subtle {
71
+ background: var(--btn-subtle-bg);
72
+ color: var(--fg-color);
73
+ border: 1px solid var(--btn-subtle-border);
74
+ padding: 6px 12px;
75
+ border-radius: 4px;
76
+ cursor: pointer;
77
+ }
78
+ /* Divider styling */
79
+ .section-divider {
80
+ margin-top: 20px;
81
+ padding-top: 20px;
82
+ border-top: 1px solid var(--divider-color);
83
+ }
37
84
  /* Logo and heading alignment */
38
85
  h1 {
39
86
  display: inline-flex;
@@ -159,7 +206,7 @@ def render_centered_html(inner_content):
159
206
  text-decoration: none;
160
207
  }
161
208
  a:hover {
162
-
209
+
163
210
  }
164
211
  /* footer styling */
165
212
  footer {