quasarr 1.17.3__tar.gz → 1.18.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 (72) hide show
  1. {quasarr-1.17.3 → quasarr-1.18.0}/PKG-INFO +1 -1
  2. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/api/captcha/__init__.py +56 -1
  3. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/__init__.py +3 -0
  4. quasarr-1.18.0/quasarr/downloads/sources/nk.py +55 -0
  5. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/html_images.py +1 -0
  6. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/obfuscated.py +18 -0
  7. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/version.py +1 -1
  8. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/search/__init__.py +4 -0
  9. quasarr-1.18.0/quasarr/search/sources/nk.py +189 -0
  10. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/storage/config.py +1 -0
  11. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr.egg-info/PKG-INFO +1 -1
  12. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr.egg-info/SOURCES.txt +2 -0
  13. {quasarr-1.17.3 → quasarr-1.18.0}/LICENSE +0 -0
  14. {quasarr-1.17.3 → quasarr-1.18.0}/README.md +0 -0
  15. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/__init__.py +0 -0
  16. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/api/__init__.py +0 -0
  17. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/api/arr/__init__.py +0 -0
  18. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/api/config/__init__.py +0 -0
  19. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/api/sponsors_helper/__init__.py +0 -0
  20. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/api/statistics/__init__.py +0 -0
  21. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/linkcrypters/__init__.py +0 -0
  22. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/linkcrypters/al.py +0 -0
  23. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/linkcrypters/filecrypt.py +0 -0
  24. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/linkcrypters/hide.py +0 -0
  25. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/packages/__init__.py +0 -0
  26. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/sources/__init__.py +0 -0
  27. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/sources/al.py +0 -0
  28. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/sources/by.py +0 -0
  29. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/sources/dd.py +0 -0
  30. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/sources/dt.py +0 -0
  31. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/sources/dw.py +0 -0
  32. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/sources/mb.py +0 -0
  33. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/sources/nx.py +0 -0
  34. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/sources/sf.py +0 -0
  35. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/sources/sl.py +0 -0
  36. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/downloads/sources/wd.py +0 -0
  37. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/__init__.py +0 -0
  38. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/cloudflare.py +0 -0
  39. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/html_templates.py +0 -0
  40. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/imdb_metadata.py +0 -0
  41. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/log.py +0 -0
  42. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/myjd_api.py +0 -0
  43. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/notifications.py +0 -0
  44. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/sessions/__init__.py +0 -0
  45. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/sessions/al.py +0 -0
  46. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/sessions/dd.py +0 -0
  47. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/sessions/nx.py +0 -0
  48. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/shared_state.py +0 -0
  49. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/statistics.py +0 -0
  50. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/providers/web_server.py +0 -0
  51. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/search/sources/__init__.py +0 -0
  52. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/search/sources/al.py +0 -0
  53. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/search/sources/by.py +0 -0
  54. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/search/sources/dd.py +0 -0
  55. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/search/sources/dt.py +0 -0
  56. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/search/sources/dw.py +0 -0
  57. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/search/sources/fx.py +0 -0
  58. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/search/sources/mb.py +0 -0
  59. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/search/sources/nx.py +0 -0
  60. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/search/sources/sf.py +0 -0
  61. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/search/sources/sl.py +0 -0
  62. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/search/sources/wd.py +0 -0
  63. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/storage/__init__.py +0 -0
  64. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/storage/setup.py +0 -0
  65. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr/storage/sqlite_database.py +0 -0
  66. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr.egg-info/dependency_links.txt +0 -0
  67. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr.egg-info/entry_points.txt +0 -0
  68. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr.egg-info/not-zip-safe +0 -0
  69. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr.egg-info/requires.txt +0 -0
  70. {quasarr-1.17.3 → quasarr-1.18.0}/quasarr.egg-info/top_level.txt +0 -0
  71. {quasarr-1.17.3 → quasarr-1.18.0}/setup.cfg +0 -0
  72. {quasarr-1.17.3 → quasarr-1.18.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quasarr
3
- Version: 1.17.3
3
+ Version: 1.18.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
@@ -16,7 +16,7 @@ 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
18
18
  from quasarr.providers.log import info, debug
19
- from quasarr.providers.obfuscated import captcha_js, captcha_values
19
+ from quasarr.providers.obfuscated import captcha_js, captcha_values, filecrypt_quasarr_helper_user_js
20
20
  from quasarr.providers.statistics import StatsHelper
21
21
 
22
22
 
@@ -101,6 +101,12 @@ def setup_captcha_routes(app):
101
101
  except Exception as e:
102
102
  return {"error": f"Failed to decode payload: {str(e)}"}
103
103
 
104
+ @app.get('/captcha/quasarr.user.js')
105
+ def serve_quasarr_user_js():
106
+ content = filecrypt_quasarr_helper_user_js()
107
+ response.content_type = 'application/javascript'
108
+ return content
109
+
104
110
  def render_bypass_section(url, package_id, title, password):
105
111
  """Render the bypass UI section for both cutcaptcha and circle captcha pages"""
106
112
 
@@ -121,7 +127,30 @@ def setup_captcha_routes(app):
121
127
  <div style="margin-top: 40px; padding-top: 20px; border-top: 2px solid #ccc;">
122
128
  <details id="bypassDetails">
123
129
  <summary id="bypassSummary">Show CAPTCHA Bypass</summary><br>
130
+
131
+ <!-- One-time setup section - visually separated -->
132
+ <div id="setup-instructions" style="background: #2a2a2a; border: 2px solid #444; border-radius: 8px; padding: 16px; margin-bottom: 24px;">
133
+ <h3 style="margin-top: 0; color: #58a6ff;">First Time Setup:</h3>
134
+ <p style="margin-bottom: 8px;">
135
+ <a href="https://www.tampermonkey.net/" target="_blank" rel="noopener noreferrer">1. Install Tampermonkey</a>
136
+ </p>
137
+ <p style="margin-top: 0; margin-bottom: 12px;">
138
+ <a href="/captcha/quasarr.user.js" target="_blank">2. Install this userscript</a>
139
+ </p>
140
+ <p style="margin-top: 0;">
141
+ <button id="hide-setup-btn" type="button" style="background: #444; color: #fff; border: 1px solid #666; padding: 6px 12px; border-radius: 4px; cursor: pointer;">
142
+ ✅ Don't show this again
143
+ </button>
144
+ </p>
145
+ </div>
146
+
147
+ <!-- Hidden "show instructions" link -->
148
+ <div id="show-instructions-link" style="display: none; margin-bottom: 16px;">
149
+ <a href="#" id="show-setup-btn" style="color: #58a6ff; text-decoration: underline;">ℹ️ Show instructions again</a>
150
+ </div>
151
+
124
152
  <strong><a href="{url_with_quick_transfer_params}" target="_blank">🔗 Obtain the download links here!</a></strong><br><br>
153
+
125
154
  <form id="bypass-form" action="/captcha/bypass-submit" method="post" enctype="multipart/form-data">
126
155
  <input type="hidden" name="package_id" value="{package_id}" />
127
156
  <input type="hidden" name="title" value="{title}" />
@@ -144,6 +173,7 @@ def setup_captcha_routes(app):
144
173
  </details>
145
174
  </div>
146
175
  <script>
176
+ // Handle CAPTCHA Bypass toggle
147
177
  const bypassDetails = document.getElementById('bypassDetails');
148
178
  const bypassSummary = document.getElementById('bypassSummary');
149
179
 
@@ -156,6 +186,31 @@ def setup_captcha_routes(app):
156
186
  }}
157
187
  }});
158
188
  }}
189
+
190
+ // Handle setup instructions hide/show
191
+ const hideSetup = localStorage.getItem('hideSetupInstructions');
192
+ const setupBox = document.getElementById('setup-instructions');
193
+ const showLink = document.getElementById('show-instructions-link');
194
+
195
+ if (hideSetup === 'true') {{
196
+ setupBox.style.display = 'none';
197
+ showLink.style.display = 'block';
198
+ }}
199
+
200
+ // Hide setup instructions
201
+ document.getElementById('hide-setup-btn').addEventListener('click', function() {{
202
+ localStorage.setItem('hideSetupInstructions', 'true');
203
+ setupBox.style.display = 'none';
204
+ showLink.style.display = 'block';
205
+ }});
206
+
207
+ // Show setup instructions again
208
+ document.getElementById('show-setup-btn').addEventListener('click', function(e) {{
209
+ e.preventDefault();
210
+ localStorage.setItem('hideSetupInstructions', 'false');
211
+ setupBox.style.display = 'block';
212
+ showLink.style.display = 'none';
213
+ }});
159
214
  </script>
160
215
  '''
161
216
 
@@ -14,6 +14,7 @@ from quasarr.downloads.sources.dd import get_dd_download_links
14
14
  from quasarr.downloads.sources.dt import get_dt_download_links
15
15
  from quasarr.downloads.sources.dw import get_dw_download_links
16
16
  from quasarr.downloads.sources.mb import get_mb_download_links
17
+ from quasarr.downloads.sources.nk import get_nk_download_links
17
18
  from quasarr.downloads.sources.nx import get_nx_download_links
18
19
  from quasarr.downloads.sources.sf import get_sf_download_links, resolve_sf_redirect
19
20
  from quasarr.downloads.sources.sl import get_sl_download_links
@@ -202,6 +203,7 @@ def download(shared_state, request_from, title, url, mirror, size_mb, password,
202
203
  'DT': config.get("dt"),
203
204
  'DW': config.get("dw"),
204
205
  'MB': config.get("mb"),
206
+ 'NK': config.get("nk"),
205
207
  'NX': config.get("nx"),
206
208
  'SF': config.get("sf"),
207
209
  'SL': config.get("sl"),
@@ -215,6 +217,7 @@ def download(shared_state, request_from, title, url, mirror, size_mb, password,
215
217
  (flags['DT'], lambda *a: handle_unprotected(*a, func=get_dt_download_links, label='DT')),
216
218
  (flags['DW'], lambda *a: handle_protected(*a, func=get_dw_download_links, label='DW')),
217
219
  (flags['MB'], lambda *a: handle_protected(*a, func=get_mb_download_links, label='MB')),
220
+ (flags['NK'], lambda *a: handle_protected(*a, func=get_nk_download_links, label='NK')),
218
221
  (flags['NX'], lambda *a: handle_unprotected(*a, func=get_nx_download_links, label='NX')),
219
222
  (flags['SF'], handle_sf),
220
223
  (flags['SL'], handle_sl),
@@ -0,0 +1,55 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Quasarr
3
+ # Project by https://github.com/rix1337
4
+
5
+ import re
6
+
7
+ import requests
8
+ from bs4 import BeautifulSoup
9
+
10
+ from quasarr.providers.log import info, debug
11
+ from urllib.parse import urlparse, urljoin
12
+
13
+ hostname = "nk"
14
+
15
+
16
+ def get_nk_download_links(shared_state, url, mirror, title):
17
+ host = shared_state.values["config"]("Hostnames").get(hostname)
18
+ headers = {
19
+ 'User-Agent': shared_state.values["user_agent"],
20
+ }
21
+
22
+ session = requests.Session()
23
+
24
+ try:
25
+ resp = session.get(url, headers=headers, timeout=20)
26
+ soup = BeautifulSoup(resp.text, 'html.parser')
27
+ except Exception as e:
28
+ info(f"{hostname}: could not fetch release page for {title}: {e}")
29
+ return False
30
+
31
+ # download links are provided as anchors with class 'dl-button'
32
+ anchors = soup.select('a.btn-orange')
33
+ candidates = []
34
+ for a in anchors:
35
+
36
+ href = a.get('href', '').strip()
37
+ hoster = href.split('/')[3].lower()
38
+ if not href.lower().startswith(('http://', 'https://')):
39
+ href = 'https://' + host + href
40
+
41
+ try:
42
+ href = requests.head(href, headers=headers, allow_redirects=True, timeout=20).url
43
+ except Exception as e:
44
+ info(f"{hostname}: could not resolve download link for {title}: {e}")
45
+ continue
46
+
47
+ if hoster == 'ddl.to':
48
+ hoster = 'ddownload'
49
+
50
+ candidates.append([href, hoster])
51
+
52
+ if not candidates:
53
+ info(f"No external download links found on {hostname} page for {title}")
54
+
55
+ return candidates
@@ -9,6 +9,7 @@ dd = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABCElE
9
9
  dt = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAA4ElEQVR4nD2PsWrCUBSGv3ubBoq+QevQUhAXV6dMRcyTZBV8CCfxCTq4ZOmSJwg+giB4t16p1NCpSFECyY2n5EL7LeeD8/PDr5qmEa01+XrNz35PaAxFUXCrNQ/DIVophbWW19WKt+WSp16PYLvlvtNBHY8ESZLg6ppPaxmMRgymUz4WC577fR5nM3SapryMx0RRxPf5jDjHXRi21bQEWZZhjCGeTIiDgOvpxM3hQLXb+QAiImVZSlVV0tS1XEXkPY7laz5vX0K74h/n/KnyXNxm41351B+tKoVcLt5Vt8svgsSBPKnRQSAAAAAASUVORK5CYII='
10
10
  dw = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAA70lEQVR4nAXBTUrDQBiA4febmfjbpqZUsFkJxS6UIq4EvYS46CUExRPkBL2HFM/gIdxIFq4Ff2JNia3JzBefR+bzuZ1Op+E+y/q6Fd/GSe8qijZH1lpfN00uADdZNt5NDh7GJ8dng2HKxvYOBtB6jctms/5S3ePpxeWklw69tZE1IqKqrIxT81U1d4PReCJJ338HXBVURCCokhelMZW467YTt9pi1rUntPCHYJ0ljiyubMJh0yJFuZSk26ENjufPFV0CCeAUfLEo8V7pRBHWOMJvBc5Q+xpnIf8pl+fvi1Lztw9zlO6T7sWw9jy9vPIPUL1kPErau3YAAAAASUVORK5CYII='
11
11
  fx = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABB0lEQVR4nB2LP0sCcQBA3/2Uzugu8E4IoT9cSYhUENFWYSA1FFTQ4hK5SENNDW19hoYcImhvaGgLguYgcIk0haLOE7wrNGzo5A4v9K3vPWknmwt0LYrrunieTygkAAm300EIQTg5nWB3e5O39w9s54t4fISEYfBZq/H4VETIsoxp1ftyJpWkR7lSZXFhnsFIhLBtOxR9n1KliqZFeX4po+saZ4VLTMtC9M61TJrTk2OarR9WV5bIpJfZ2ljH8zxEw3EovVaZm01xd//AsKpwfXNLLKaTmDSQ9vJHwWE+x3ezRbv9i6oqTBkTmLU65xdXSNn9g0CWB2jYDuNjo/y5Lqoy1A+63S7/GeVj+5KBt3UAAAAASUVORK5CYII='
12
+ 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='
12
13
  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='
13
14
  nx = 'data:image/png;base64,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'
14
15
  sf = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAA8ElEQVR4nBXBv0rDQADA4d/lz1lqmlREECUIFTq4FBeHDA6uPkRfwwfxBRx19gmk4GZBBAcjCAUrWEkaTUzucjnx+0R6kzQboZDCb7AWdNmiMo3KLHUmlCcDR/Z2DujvJXRth6kNqqgoXufo6kk6lpzwcEr7fUrx7POTugh9xPZkilE5jh9MsJ2mreaUi0eKdIa7CUIogjjB8wcnyOiM0rmkH+8ih8dY1gTxOeHoA69eLRCOxBvsE21JOmVo8gy3N6T+fMNbPdwiwzG6XFJ/vWC1xY9GvN9dsZxdI+4vxo3rtxLH8q/TFvPbYhqL0Z76AydWbEnTqqOqAAAAAElFTkSuQmCC'