quasarr 1.31.0__py3-none-any.whl → 2.0.0__py3-none-any.whl

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.

@@ -7,6 +7,7 @@ import os
7
7
  import re
8
8
  import time
9
9
  import traceback
10
+ import unicodedata
10
11
  from datetime import datetime, timedelta, date
11
12
  from urllib import parse
12
13
 
@@ -826,6 +827,8 @@ def get_recently_searched(shared_state, context, timeout_seconds):
826
827
 
827
828
 
828
829
  def download_package(links, title, password, package_id):
830
+ links = [sanitize_url(link) for link in links]
831
+
829
832
  device = get_device()
830
833
  downloaded = device.linkgrabber.add_links(params=[
831
834
  {
@@ -841,3 +844,24 @@ def download_package(links, title, password, package_id):
841
844
  }
842
845
  ])
843
846
  return downloaded
847
+
848
+
849
+ def sanitize_url(url: str) -> str:
850
+ # normalize first
851
+ url = unicodedata.normalize("NFKC", url)
852
+
853
+ # 1) real control characters (U+0000–U+001F, U+007F–U+009F)
854
+ _REAL_CTRL_RE = re.compile(r'[\u0000-\u001f\u007f-\u009f]')
855
+
856
+ # 2) *literal* escaped unicode junk: \u0010, \x10, repeated variants
857
+ _ESCAPED_CTRL_RE = re.compile(
858
+ r'(?:\\u00[0-1][0-9a-fA-F]|\\x[0-1][0-9a-fA-F])'
859
+ )
860
+
861
+ # remove literal escaped control sequences like "\u0010"
862
+ url = _ESCAPED_CTRL_RE.sub("", url)
863
+
864
+ # remove actual control characters if already decoded
865
+ url = _REAL_CTRL_RE.sub("", url)
866
+
867
+ return url.strip()
@@ -8,7 +8,7 @@ import requests
8
8
 
9
9
 
10
10
  def get_version():
11
- return "1.31.0"
11
+ return "2.0.0"
12
12
 
13
13
 
14
14
  def get_latest_version():
@@ -97,7 +97,7 @@ def dl_feed(shared_state, start_time, request_from, mirror=None):
97
97
  if not title_elem:
98
98
  continue
99
99
 
100
- title = title_elem.get_text(strip=True)
100
+ title = ''.join(title_elem.strings)
101
101
  if not title:
102
102
  continue
103
103
 
@@ -230,7 +230,8 @@ def _search_single_page(shared_state, host, search_string, search_id, page_num,
230
230
  if not title_elem:
231
231
  continue
232
232
 
233
- title = title_elem.get_text(separator=' ', strip=True)
233
+ title = ''.join(title_elem.strings)
234
+
234
235
  title = re.sub(r'\s+', ' ', title)
235
236
  title = unescape(title)
236
237
  title_normalized = normalize_title_for_sonarr(title)
quasarr/storage/setup.py CHANGED
@@ -15,6 +15,7 @@ import quasarr.providers.sessions.al
15
15
  import quasarr.providers.sessions.dd
16
16
  import quasarr.providers.sessions.dl
17
17
  import quasarr.providers.sessions.nx
18
+ from quasarr.providers.auth import add_auth_routes, add_auth_hook
18
19
  from quasarr.providers.html_templates import render_button, render_form, render_success, render_fail, \
19
20
  render_centered_html
20
21
  from quasarr.providers.log import info
@@ -91,9 +92,16 @@ def add_no_cache_headers(app):
91
92
  response.set_header('Expires', '0')
92
93
 
93
94
 
95
+ def setup_auth(app):
96
+ """Add authentication to setup app if enabled."""
97
+ add_auth_routes(app)
98
+ add_auth_hook(app)
99
+
100
+
94
101
  def path_config(shared_state):
95
102
  app = Bottle()
96
103
  add_no_cache_headers(app)
104
+ setup_auth(app)
97
105
 
98
106
  current_path = os.path.dirname(os.path.abspath(sys.argv[0]))
99
107
 
@@ -239,7 +247,9 @@ def hostname_form_html(shared_state, message, show_restart_button=False, show_sk
239
247
  .import-status {{
240
248
  margin-top: 0.5rem;
241
249
  font-size: 0.875rem;
242
- min-height: 1.25rem;
250
+ }}
251
+ .import-status:empty {{
252
+ display: none;
243
253
  }}
244
254
  .import-status.success {{ color: #198754; }}
245
255
  .import-status.error {{ color: #dc3545; }}
@@ -558,6 +568,7 @@ def save_hostnames(shared_state, timeout=5, first_run=True):
558
568
  def hostnames_config(shared_state):
559
569
  app = Bottle()
560
570
  add_no_cache_headers(app)
571
+ setup_auth(app)
561
572
 
562
573
  @app.get('/')
563
574
  def hostname_form():
@@ -636,6 +647,7 @@ def hostnames_config(shared_state):
636
647
  def hostname_credentials_config(shared_state, shorthand, domain):
637
648
  app = Bottle()
638
649
  add_no_cache_headers(app)
650
+ setup_auth(app)
639
651
 
640
652
  shorthand = shorthand.upper()
641
653
 
@@ -791,6 +803,7 @@ def hostname_credentials_config(shared_state, shorthand, domain):
791
803
  def flaresolverr_config(shared_state):
792
804
  app = Bottle()
793
805
  add_no_cache_headers(app)
806
+ setup_auth(app)
794
807
 
795
808
  @app.get('/')
796
809
  def url_form():
@@ -931,6 +944,7 @@ def flaresolverr_config(shared_state):
931
944
  def jdownloader_config(shared_state):
932
945
  app = Bottle()
933
946
  add_no_cache_headers(app)
947
+ setup_auth(app)
934
948
 
935
949
  @app.get('/')
936
950
  def jd_form():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quasarr
3
- Version: 1.31.0
3
+ Version: 2.0.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
@@ -75,6 +75,11 @@ http://192.168.1.1:8191/v1
75
75
 
76
76
  📋 Alternatively, browse community suggestions via [pastebin search](https://pastebin.com/search?q=hostnames+quasarr) (login required).
77
77
 
78
+ > Authentication is optional but strongly recommended.
79
+ >
80
+ > - 🔐 Set `USER` and `PASS` to enable form-based login (30-day session)
81
+ > - 🔑 Set `AUTH=basic` to use HTTP Basic Authentication instead
82
+
78
83
  ---
79
84
 
80
85
  ## JDownloader
@@ -170,7 +175,10 @@ docker run -d \
170
175
  -e 'INTERNAL_ADDRESS'='http://192.168.0.1:8080' \
171
176
  -e 'EXTERNAL_ADDRESS'='https://foo.bar/' \
172
177
  -e 'DISCORD'='https://discord.com/api/webhooks/1234567890/ABCDEFGHIJKLMN' \
173
- -e 'HOSTNAMES'='https://quasarr-host.name/ini?token=123...'
178
+ -e 'HOSTNAMES'='https://quasarr-host.name/ini?token=123...' \
179
+ -e 'USER'='admin' \
180
+ -e 'PASS'='change-me' \
181
+ -e 'AUTH'='form' \
174
182
  -e 'SILENT'='True' \
175
183
  -e 'DEBUG'='' \
176
184
  -e 'TZ'='Europe/Berlin' \
@@ -184,6 +192,8 @@ docker run -d \
184
192
  * Must be a publicly available `HTTP` or `HTTPs` link
185
193
  * Must be a raw `.ini` / text file (not HTML or JSON)
186
194
  * Must contain at least one valid Hostname per line `ab = xyz`
195
+ * `USER` / `PASS` are credentials to protect the web UI
196
+ * `AUTH` is the authentication mode (`form` or `basic`)
187
197
  * `SILENT` is optional and silences all discord notifications except for error messages from SponsorsHelper if `True`.
188
198
  * `DEBUG` is optional and enables debug logging if `True`.
189
199
  * `TZ` is optional, wrong timezone can cause HTTPS/SSL issues
@@ -1,16 +1,17 @@
1
1
  quasarr/__init__.py,sha256=cEtxN2AuwKvrxpIvAR7UL997VtYQ4iN3Eo3ZnP-WjZQ,14682
2
- quasarr/api/__init__.py,sha256=UOyyuOjF2WN6Um2wwQNHjFA-Rj0prb11z8SCjbifKJU,6940
2
+ quasarr/api/__init__.py,sha256=CejYHdwOf2hkk4HQuBEjfU4Tqg3fKn3kIOZUkaqzERk,16321
3
3
  quasarr/api/arr/__init__.py,sha256=BNEugX1hUF5kn8MIgdoyE1HeyabpojjxAa6RlXSem74,17518
4
- quasarr/api/captcha/__init__.py,sha256=2d7fTTo-FOXufsG_MxyaDPt8r1KVbXbSXnJAgrt8Qvo,72591
4
+ quasarr/api/captcha/__init__.py,sha256=2pca9jnJMWZECHeAvKP9Lwsa3_GkVsvQEn7rof8Ep1s,73504
5
5
  quasarr/api/config/__init__.py,sha256=m0DrbanI0lK_PaZA6ey3osj5l1_tMjjYjoFKkzrdPu0,13692
6
- quasarr/api/sponsors_helper/__init__.py,sha256=kAZabPlplPYRG6Uw7ZHTk5uypualwvhs-NoTOjQhhhA,6369
6
+ quasarr/api/packages/__init__.py,sha256=22XTb5ovtFFt4EEAle9vhC35u3hl5VhJIZpvUv-OF9w,20642
7
+ quasarr/api/sponsors_helper/__init__.py,sha256=aXkZJmqxM5q04V1pnC__FANL9vm_gLtOWONO6AVvs6s,6465
7
8
  quasarr/api/statistics/__init__.py,sha256=NrBAjjHkIUE95HhPUGIfNqh2IqBqJ_zm00S90Y-Qnus,7038
8
- quasarr/downloads/__init__.py,sha256=6COdDlJkNuWuJq7DLVqqb0fuH1b3HtsbvA0mlmtLeIs,11972
9
+ quasarr/downloads/__init__.py,sha256=YUO1q9FelAYwTPNCNza6GTOwjJcWl5E2iqsHFj1jugQ,12122
9
10
  quasarr/downloads/linkcrypters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
11
  quasarr/downloads/linkcrypters/al.py,sha256=mfUG5VclC_-FcGoZL9zHYD7dz7X_YpaNmoKkgiyl9-0,8812
11
12
  quasarr/downloads/linkcrypters/filecrypt.py,sha256=He8b7HjoPA-LmRwVwY0l_5JAVlJ3sYOXs5tcWXooqI4,17055
12
- quasarr/downloads/linkcrypters/hide.py,sha256=8YmNm49JmVa1zZdTHpjK9gnQrX435Cq5fo4JTNsIpds,4850
13
- quasarr/downloads/packages/__init__.py,sha256=6U4rk7hWoQCidIZy5O3-Pt5P7ZWEavHdbqZB0arxN1Y,18679
13
+ quasarr/downloads/linkcrypters/hide.py,sha256=H4hJWhENkszV1u_ULC3aOW2fu9infC-Nv-7wx2DYqrA,6266
14
+ quasarr/downloads/packages/__init__.py,sha256=zDMhLJz-16r4WSv2zzSlcBJEFrIUky2dRDkQaQLVSXY,32055
14
15
  quasarr/downloads/sources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
16
  quasarr/downloads/sources/al.py,sha256=g587VESZRZHZ03uxHKpufEr5qAtzbyGLmoijksU35jk,27297
16
17
  quasarr/downloads/sources/by.py,sha256=kmUTn3izayRCV7W-t0E4kYE8qTbt3L3reCLozfvRGcU,3807
@@ -29,19 +30,20 @@ quasarr/downloads/sources/sl.py,sha256=jWprFt1Hew1T67fB1O_pc9YWgc3NVh30KXSwSyS50
29
30
  quasarr/downloads/sources/wd.py,sha256=kr1I1uJa7ZkEPH2LA6alXTJEn0LBPgLCwIh3wLXwCv8,4447
30
31
  quasarr/downloads/sources/wx.py,sha256=NzNNeqVL6sKkFKyreW-oerrreb5QP2tUGHTWHM5pMCU,7013
31
32
  quasarr/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
+ quasarr/providers/auth.py,sha256=hqSVaHBljf4yTx8JVqfxmi65T5Q36aewUmqfJbHtCNg,8730
32
34
  quasarr/providers/cloudflare.py,sha256=9iet8runc2VHVcA0_2z1qkrL6D5JKqz1ndktqCgsJFs,7873
33
35
  quasarr/providers/html_images.py,sha256=2n82gTJg7E7q2ytPFN4FWouYTIlmPYu_iHFtG7uktIA,28482
34
36
  quasarr/providers/html_templates.py,sha256=YMwdi7l_tHL0-qsUnwi4aPrE5Q6ZDxbjsPIfr-6uemY,10265
35
37
  quasarr/providers/imdb_metadata.py,sha256=10L4kZkt6Fg0HGdNcc6KCtIQHRYEqdarLyaMVN6mT8w,4843
36
- quasarr/providers/jd_cache.py,sha256=TfoQWetKZA7vO_qj2wFOGhgmVSXiMhwGpjl7HtLc_-c,5372
38
+ quasarr/providers/jd_cache.py,sha256=mSvMrs3UwTn3sd9yGSJKGT-qwYeyYKC_l8whpXTVn7s,13530
37
39
  quasarr/providers/log.py,sha256=_g5RwtfuksARXnvryhsngzoJyFcNzj6suqd3ndqZM0Y,313
38
40
  quasarr/providers/myjd_api.py,sha256=Z3PEiO3c3UfDSr4Up5rgwTAnjloWHb-H1RkJ6BLKZv8,34140
39
41
  quasarr/providers/notifications.py,sha256=bohT-6yudmFnmZMc3BwCGX0n1HdzSVgQG_LDZm_38dI,4630
40
- quasarr/providers/obfuscated.py,sha256=8FcmY9cjvjOoth-6LrPZ1CZZbpKplcaMMBE06FkdYgA,1339187
41
- quasarr/providers/shared_state.py,sha256=-TIiH2lkCfovq7bzUZicpUjXEjS87ZHCcevsFgySOqw,29944
42
+ quasarr/providers/obfuscated.py,sha256=9z8nwcLFezazsfAgOz4S679VJJMe1AP-6odtlILDkE8,1404048
43
+ quasarr/providers/shared_state.py,sha256=5a_ZbGqTvt4-OqBt2a1WtR9I5J_Ky7IlkEY8EGtKVu8,30646
42
44
  quasarr/providers/statistics.py,sha256=cEQixYnDMDqtm5wWe40E_2ucyo4mD0n3SrfelhQi1L8,6452
43
45
  quasarr/providers/utils.py,sha256=mcUPbcXMsLmrYv0CTZO5a9aOt2-JLyL3SZxu6N8OyjU,12075
44
- quasarr/providers/version.py,sha256=bjolkJyZ2IG4sc5GvrvZfwTeYTHWNvMAMp2vgnpPVZ0,4004
46
+ quasarr/providers/version.py,sha256=KkR4lHopacRigrcdM-fst1j72YqiWFcE2T2Yk-aBhTo,4003
45
47
  quasarr/providers/web_server.py,sha256=AYd0KRxdDWMBr87BP8wlSMuL4zZo0I_rY-vHBai6Pfg,1688
46
48
  quasarr/providers/sessions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
49
  quasarr/providers/sessions/al.py,sha256=WXue9LaT4y0BzsbKtHbN6bb_72c4AZZWR9NP-vg9-cg,12462
@@ -54,7 +56,7 @@ quasarr/search/sources/al.py,sha256=AmTQDc6voMXh8Sh_IrJX_3gQ5UYKMri-aGaE-wbI3ik,
54
56
  quasarr/search/sources/by.py,sha256=vnE3L43V8suPhPHcn6LVxKO1e3mJaDRqIIMg2BGxr_g,7915
55
57
  quasarr/search/sources/dd.py,sha256=pVpdHLZlw2CYklBf_YLkeDWbCNsDLR2iecccR2c2RyI,4889
56
58
  quasarr/search/sources/dj.py,sha256=2HIdg5ddXP4DtjHlyXmuQ8QVhOPt3Hh2kL4uxhFJK-8,7074
57
- quasarr/search/sources/dl.py,sha256=Ci1OvOppZ_mJ6esA9qvWd_NQfN-FEK8lWhkev7W-JP8,12539
59
+ quasarr/search/sources/dl.py,sha256=kf8FPUn7BIjolDt5ApNDZpjTSMxkcz9xbHrJ_8c-qhg,12517
58
60
  quasarr/search/sources/dt.py,sha256=m1kQ7mC43QlWZyVIkw-OXJGjWiT9IbQuFtHWiR8CjhA,9580
59
61
  quasarr/search/sources/dw.py,sha256=-daUTBTA5izeatrE7TITVlnzNCQ5HfovYMMZ8UTM-2o,7636
60
62
  quasarr/search/sources/fx.py,sha256=JAyD727yDAFIP14bzfi2SkX9paysXGkQdIybShYtdko,8596
@@ -69,11 +71,11 @@ quasarr/search/sources/wd.py,sha256=O02j3irSlVw2qES82g_qHuavAk-njjSRH1dHSCnOUas,
69
71
  quasarr/search/sources/wx.py,sha256=zlRvg7Ls-DFRo4sUBMRAXZRMfE2mnaXCkzP7pu53pIY,13842
70
72
  quasarr/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
73
  quasarr/storage/config.py,sha256=SSTgIce2FVYoVTK_6OCU3msknhxuLA3EC4Kcrrf_dxQ,6378
72
- quasarr/storage/setup.py,sha256=0Gm6sHLmlcKvOGeli9eVuRVEP0Slz8-K5jZG0cNXaew,42041
74
+ quasarr/storage/setup.py,sha256=eXtgUqMacbaUDy-dszPtT15O9lJKSO1eI3v0jGjMiUA,42361
73
75
  quasarr/storage/sqlite_database.py,sha256=yMqFQfKf0k7YS-6Z3_7pj4z1GwWSXJ8uvF4IydXsuTE,3554
74
- quasarr-1.31.0.dist-info/licenses/LICENSE,sha256=QQFCAfDgt7lSA8oSWDHIZ9aTjFbZaBJdjnGOHkuhK7k,1060
75
- quasarr-1.31.0.dist-info/METADATA,sha256=4jot6IFM_BIQwf8Ftx74l9W26M-2EaK8LXxC9XUn3qE,11009
76
- quasarr-1.31.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
77
- quasarr-1.31.0.dist-info/entry_points.txt,sha256=gXi8mUKsIqKVvn-bOc8E5f04sK_KoMCC-ty6b2Hf-jc,40
78
- quasarr-1.31.0.dist-info/top_level.txt,sha256=dipJdaRda5ruTZkoGfZU60bY4l9dtPlmOWwxK_oGSF0,8
79
- quasarr-1.31.0.dist-info/RECORD,,
76
+ quasarr-2.0.0.dist-info/licenses/LICENSE,sha256=QQFCAfDgt7lSA8oSWDHIZ9aTjFbZaBJdjnGOHkuhK7k,1060
77
+ quasarr-2.0.0.dist-info/METADATA,sha256=fAV2_D6qwGXSdtXQT5AhiNX55XbA8zIlBhS0DUC_NMY,11391
78
+ quasarr-2.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
79
+ quasarr-2.0.0.dist-info/entry_points.txt,sha256=gXi8mUKsIqKVvn-bOc8E5f04sK_KoMCC-ty6b2Hf-jc,40
80
+ quasarr-2.0.0.dist-info/top_level.txt,sha256=dipJdaRda5ruTZkoGfZU60bY4l9dtPlmOWwxK_oGSF0,8
81
+ quasarr-2.0.0.dist-info/RECORD,,