quasarr 2.6.0__py3-none-any.whl → 2.6.1__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.
- quasarr/api/__init__.py +2 -2
- quasarr/api/config/__init__.py +1 -1
- quasarr/api/jdownloader/__init__.py +19 -12
- quasarr/downloads/sources/wd.py +88 -74
- quasarr/providers/cloudflare.py +46 -1
- quasarr/providers/version.py +1 -1
- quasarr/search/sources/wd.py +29 -5
- quasarr/storage/setup.py +13 -4
- {quasarr-2.6.0.dist-info → quasarr-2.6.1.dist-info}/METADATA +1 -1
- {quasarr-2.6.0.dist-info → quasarr-2.6.1.dist-info}/RECORD +13 -13
- {quasarr-2.6.0.dist-info → quasarr-2.6.1.dist-info}/WHEEL +0 -0
- {quasarr-2.6.0.dist-info → quasarr-2.6.1.dist-info}/entry_points.txt +0 -0
- {quasarr-2.6.0.dist-info → quasarr-2.6.1.dist-info}/licenses/LICENSE +0 -0
quasarr/api/__init__.py
CHANGED
|
@@ -124,10 +124,10 @@ def get_api(shared_state_dict, shared_state_lock):
|
|
|
124
124
|
# Status bars
|
|
125
125
|
status_bars = f"""
|
|
126
126
|
<div class="status-bar">
|
|
127
|
-
<span class="status-pill {jd_status[
|
|
127
|
+
<span class="status-pill {jd_status["status_class"]}"
|
|
128
128
|
onclick="openJDownloaderModal()"
|
|
129
129
|
title="Click to configure JDownloader">
|
|
130
|
-
{jd_status[
|
|
130
|
+
{jd_status["status_text"]}
|
|
131
131
|
</span>
|
|
132
132
|
<span class="status-pill {hostname_status_class}"
|
|
133
133
|
onclick="location.href='/hostnames'"
|
quasarr/api/config/__init__.py
CHANGED
|
@@ -16,9 +16,14 @@ def get_jdownloader_status(shared_state):
|
|
|
16
16
|
|
|
17
17
|
jd_config = Config("JDownloader")
|
|
18
18
|
jd_device = jd_config.get("device") or ""
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
dev_name = jd_device if jd_device else "JDownloader"
|
|
21
|
-
dev_name_safe =
|
|
21
|
+
dev_name_safe = (
|
|
22
|
+
dev_name.replace("&", "&")
|
|
23
|
+
.replace("<", "<")
|
|
24
|
+
.replace(">", ">")
|
|
25
|
+
.replace('"', """)
|
|
26
|
+
)
|
|
22
27
|
|
|
23
28
|
if jd_connected:
|
|
24
29
|
status_text = f"✅ {dev_name_safe} connected"
|
|
@@ -34,7 +39,7 @@ def get_jdownloader_status(shared_state):
|
|
|
34
39
|
"connected": jd_connected,
|
|
35
40
|
"device_name": jd_device,
|
|
36
41
|
"status_text": status_text,
|
|
37
|
-
"status_class": status_class
|
|
42
|
+
"status_class": status_class,
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
|
|
@@ -44,7 +49,7 @@ def get_jdownloader_modal_script():
|
|
|
44
49
|
jd_user = jd_config.get("user") or ""
|
|
45
50
|
jd_pass = jd_config.get("password") or ""
|
|
46
51
|
jd_device = jd_config.get("device") or ""
|
|
47
|
-
|
|
52
|
+
|
|
48
53
|
jd_user_js = jd_user.replace("\\", "\\\\").replace("'", "\\'")
|
|
49
54
|
jd_pass_js = jd_pass.replace("\\", "\\\\").replace("'", "\\'")
|
|
50
55
|
jd_device_js = jd_device.replace("\\", "\\\\").replace("'", "\\'")
|
|
@@ -166,13 +171,13 @@ def get_jdownloader_modal_script():
|
|
|
166
171
|
def get_jdownloader_status_pill(shared_state):
|
|
167
172
|
"""Return the HTML for the JDownloader status pill."""
|
|
168
173
|
status = get_jdownloader_status(shared_state)
|
|
169
|
-
|
|
174
|
+
|
|
170
175
|
return f"""
|
|
171
|
-
<span class="status-pill {status[
|
|
176
|
+
<span class="status-pill {status["status_class"]}"
|
|
172
177
|
onclick="openJDownloaderModal()"
|
|
173
178
|
style="cursor: pointer;"
|
|
174
179
|
title="Click to configure JDownloader">
|
|
175
|
-
{status[
|
|
180
|
+
{status["status_text"]}
|
|
176
181
|
</span>
|
|
177
182
|
"""
|
|
178
183
|
|
|
@@ -181,12 +186,14 @@ def get_jdownloader_disconnected_page(shared_state, back_url="/"):
|
|
|
181
186
|
"""Return a full error page when JDownloader is disconnected."""
|
|
182
187
|
import quasarr.providers.html_images as images
|
|
183
188
|
from quasarr.providers.html_templates import render_centered_html
|
|
184
|
-
|
|
189
|
+
|
|
185
190
|
status_pill = get_jdownloader_status_pill(shared_state)
|
|
186
191
|
modal_script = get_jdownloader_modal_script()
|
|
187
|
-
|
|
188
|
-
back_btn = render_button(
|
|
189
|
-
|
|
192
|
+
|
|
193
|
+
back_btn = render_button(
|
|
194
|
+
"Back", "secondary", {"onclick": f"location.href='{back_url}'"}
|
|
195
|
+
)
|
|
196
|
+
|
|
190
197
|
content = f'''
|
|
191
198
|
<h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
|
|
192
199
|
<div class="status-bar">
|
|
@@ -228,5 +235,5 @@ def get_jdownloader_disconnected_page(shared_state, back_url="/"):
|
|
|
228
235
|
</style>
|
|
229
236
|
{modal_script}
|
|
230
237
|
'''
|
|
231
|
-
|
|
238
|
+
|
|
232
239
|
return render_centered_html(content)
|
quasarr/downloads/sources/wd.py
CHANGED
|
@@ -3,12 +3,17 @@
|
|
|
3
3
|
# Project by https://github.com/rix1337
|
|
4
4
|
|
|
5
5
|
import re
|
|
6
|
+
import uuid
|
|
6
7
|
from urllib.parse import urljoin
|
|
7
8
|
|
|
8
|
-
import requests
|
|
9
9
|
from bs4 import BeautifulSoup
|
|
10
10
|
|
|
11
|
-
from quasarr.providers.cloudflare import
|
|
11
|
+
from quasarr.providers.cloudflare import (
|
|
12
|
+
flaresolverr_create_session,
|
|
13
|
+
flaresolverr_destroy_session,
|
|
14
|
+
flaresolverr_get,
|
|
15
|
+
is_cloudflare_challenge,
|
|
16
|
+
)
|
|
12
17
|
from quasarr.providers.hostname_issues import mark_hostname_issue
|
|
13
18
|
from quasarr.providers.log import debug, info
|
|
14
19
|
from quasarr.providers.utils import is_flaresolverr_available
|
|
@@ -16,26 +21,22 @@ from quasarr.providers.utils import is_flaresolverr_available
|
|
|
16
21
|
hostname = "wd"
|
|
17
22
|
|
|
18
23
|
|
|
19
|
-
def resolve_wd_redirect(url,
|
|
24
|
+
def resolve_wd_redirect(shared_state, url, session_id=None):
|
|
20
25
|
"""
|
|
21
26
|
Follow redirects for a WD mirror URL and return the final destination.
|
|
22
27
|
"""
|
|
23
28
|
try:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
for resp in r.history:
|
|
33
|
-
debug(f"Redirected from {resp.url} to {r.url}")
|
|
29
|
+
# Use FlareSolverr to follow redirects as well, since the redirector might be protected
|
|
30
|
+
r = flaresolverr_get(shared_state, url, session_id=session_id)
|
|
31
|
+
|
|
32
|
+
# FlareSolverr follows redirects automatically and returns the final URL
|
|
33
|
+
if r.status_code == 200:
|
|
34
|
+
# Check if we landed on a 404 page (soft 404)
|
|
35
|
+
if r.url.endswith("/404.html"):
|
|
36
|
+
return None
|
|
34
37
|
return r.url
|
|
35
38
|
else:
|
|
36
|
-
info(
|
|
37
|
-
f"WD blocked attempt to resolve {url}. Your IP may be banned. Try again later."
|
|
38
|
-
)
|
|
39
|
+
info(f"WD blocked attempt to resolve {url}. Status: {r.status_code}")
|
|
39
40
|
except Exception as e:
|
|
40
41
|
info(f"Error fetching redirected URL for {url}: {e}")
|
|
41
42
|
mark_hostname_issue(
|
|
@@ -52,25 +53,30 @@ def get_wd_download_links(shared_state, url, mirror, title, password):
|
|
|
52
53
|
"""
|
|
53
54
|
|
|
54
55
|
wd = shared_state.values["config"]("Hostnames").get("wd")
|
|
55
|
-
|
|
56
|
+
|
|
57
|
+
if not is_flaresolverr_available(shared_state):
|
|
58
|
+
info(
|
|
59
|
+
"WD is protected by Cloudflare but FlareSolverr is not configured. "
|
|
60
|
+
"Please configure FlareSolverr in the web UI to access this site."
|
|
61
|
+
)
|
|
62
|
+
mark_hostname_issue(hostname, "download", "FlareSolverr required but missing.")
|
|
63
|
+
return {"links": [], "imdb_id": None}
|
|
64
|
+
|
|
65
|
+
# Create a temporary FlareSolverr session for this download attempt
|
|
66
|
+
session_id = str(uuid.uuid4())
|
|
67
|
+
created_session = flaresolverr_create_session(shared_state, session_id)
|
|
68
|
+
if not created_session:
|
|
69
|
+
info("Could not create FlareSolverr session. Proceeding without session...")
|
|
70
|
+
session_id = None
|
|
71
|
+
else:
|
|
72
|
+
debug(f"Created FlareSolverr session: {session_id}")
|
|
56
73
|
|
|
57
74
|
try:
|
|
58
|
-
r =
|
|
59
|
-
if r.status_code
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
)
|
|
64
|
-
r = flaresolverr_get(shared_state, url)
|
|
65
|
-
else:
|
|
66
|
-
info(
|
|
67
|
-
"WD is protected by Cloudflare but FlareSolverr is not configured. "
|
|
68
|
-
"Please configure FlareSolverr in the web UI to access this site."
|
|
69
|
-
)
|
|
70
|
-
mark_hostname_issue(
|
|
71
|
-
hostname, "download", "FlareSolverr required but missing."
|
|
72
|
-
)
|
|
73
|
-
return {"links": [], "imdb_id": None}
|
|
75
|
+
r = flaresolverr_get(shared_state, url, session_id=session_id)
|
|
76
|
+
if r.status_code == 403 or is_cloudflare_challenge(r.text):
|
|
77
|
+
info("Could not bypass Cloudflare protection with FlareSolverr!")
|
|
78
|
+
mark_hostname_issue(hostname, "download", "Cloudflare challenge failed")
|
|
79
|
+
return {"links": [], "imdb_id": None}
|
|
74
80
|
|
|
75
81
|
if r.status_code >= 400:
|
|
76
82
|
mark_hostname_issue(
|
|
@@ -105,51 +111,59 @@ def get_wd_download_links(shared_state, url, mirror, title, password):
|
|
|
105
111
|
link_tags = body.find_all(
|
|
106
112
|
"a", href=True, class_=lambda c: c and "background-" in c
|
|
107
113
|
)
|
|
114
|
+
|
|
115
|
+
results = []
|
|
116
|
+
try:
|
|
117
|
+
for a in link_tags:
|
|
118
|
+
raw_href = a["href"]
|
|
119
|
+
full_link = urljoin(f"https://{wd}", raw_href)
|
|
120
|
+
|
|
121
|
+
# resolve any redirects using the same session
|
|
122
|
+
resolved = resolve_wd_redirect(
|
|
123
|
+
shared_state, full_link, session_id=session_id
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if resolved:
|
|
127
|
+
if resolved.endswith("/404.html"):
|
|
128
|
+
info(f"Link {resolved} is dead!")
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
# determine hoster
|
|
132
|
+
hoster = a.get_text(strip=True) or None
|
|
133
|
+
if not hoster:
|
|
134
|
+
for cls in a.get("class", []):
|
|
135
|
+
if cls.startswith("background-"):
|
|
136
|
+
hoster = cls.split("-", 1)[1]
|
|
137
|
+
break
|
|
138
|
+
|
|
139
|
+
if mirror and mirror.lower() not in hoster.lower():
|
|
140
|
+
debug(
|
|
141
|
+
f'Skipping link from "{hoster}" (not the desired mirror "{mirror}")!'
|
|
142
|
+
)
|
|
143
|
+
continue
|
|
144
|
+
|
|
145
|
+
results.append([resolved, hoster])
|
|
146
|
+
except Exception as e:
|
|
147
|
+
info(
|
|
148
|
+
f"WD site has been updated. Parsing download links for {title} not possible! Error: {e}"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
"links": results,
|
|
153
|
+
"imdb_id": imdb_id,
|
|
154
|
+
}
|
|
155
|
+
|
|
108
156
|
except RuntimeError as e:
|
|
109
157
|
# Catch FlareSolverr not configured error
|
|
110
158
|
info(f"WD access failed: {e}")
|
|
111
159
|
return {"links": [], "imdb_id": None}
|
|
112
|
-
except Exception:
|
|
160
|
+
except Exception as e:
|
|
113
161
|
info(
|
|
114
|
-
f"WD site has been updated. Grabbing download links for {title} not possible!"
|
|
162
|
+
f"WD site has been updated. Grabbing download links for {title} not possible! Error: {e}"
|
|
115
163
|
)
|
|
116
164
|
return {"links": [], "imdb_id": None}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
full_link = urljoin(f"https://{wd}", raw_href)
|
|
123
|
-
|
|
124
|
-
# resolve any redirects
|
|
125
|
-
resolved = resolve_wd_redirect(full_link, user_agent)
|
|
126
|
-
|
|
127
|
-
if resolved:
|
|
128
|
-
if resolved.endswith("/404.html"):
|
|
129
|
-
info(f"Link {resolved} is dead!")
|
|
130
|
-
continue
|
|
131
|
-
|
|
132
|
-
# determine hoster
|
|
133
|
-
hoster = a.get_text(strip=True) or None
|
|
134
|
-
if not hoster:
|
|
135
|
-
for cls in a.get("class", []):
|
|
136
|
-
if cls.startswith("background-"):
|
|
137
|
-
hoster = cls.split("-", 1)[1]
|
|
138
|
-
break
|
|
139
|
-
|
|
140
|
-
if mirror and mirror.lower() not in hoster.lower():
|
|
141
|
-
debug(
|
|
142
|
-
f'Skipping link from "{hoster}" (not the desired mirror "{mirror}")!'
|
|
143
|
-
)
|
|
144
|
-
continue
|
|
145
|
-
|
|
146
|
-
results.append([resolved, hoster])
|
|
147
|
-
except Exception:
|
|
148
|
-
info(
|
|
149
|
-
f"WD site has been updated. Parsing download links for {title} not possible!"
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
return {
|
|
153
|
-
"links": results,
|
|
154
|
-
"imdb_id": imdb_id,
|
|
155
|
-
}
|
|
165
|
+
finally:
|
|
166
|
+
# Always destroy the session
|
|
167
|
+
if session_id:
|
|
168
|
+
debug(f"Destroying FlareSolverr session: {session_id}")
|
|
169
|
+
flaresolverr_destroy_session(shared_state, session_id)
|
quasarr/providers/cloudflare.py
CHANGED
|
@@ -168,7 +168,7 @@ class FlareSolverrResponse:
|
|
|
168
168
|
raise requests.HTTPError(f"{self.status_code} Error at {self.url}")
|
|
169
169
|
|
|
170
170
|
|
|
171
|
-
def flaresolverr_get(shared_state, url, timeout=60):
|
|
171
|
+
def flaresolverr_get(shared_state, url, timeout=60, session_id=None):
|
|
172
172
|
"""
|
|
173
173
|
Core function for performing a GET request via FlareSolverr only.
|
|
174
174
|
Used internally by FlareSolverrSession.get()
|
|
@@ -186,6 +186,8 @@ def flaresolverr_get(shared_state, url, timeout=60):
|
|
|
186
186
|
raise RuntimeError("FlareSolverr URL not configured in shared_state.")
|
|
187
187
|
|
|
188
188
|
payload = {"cmd": "request.get", "url": url, "maxTimeout": timeout * 1000}
|
|
189
|
+
if session_id:
|
|
190
|
+
payload["session"] = session_id
|
|
189
191
|
|
|
190
192
|
try:
|
|
191
193
|
resp = requests.post(
|
|
@@ -219,3 +221,46 @@ def flaresolverr_get(shared_state, url, timeout=60):
|
|
|
219
221
|
return FlareSolverrResponse(
|
|
220
222
|
url=url, status_code=status_code, headers=fs_headers, text=html
|
|
221
223
|
)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def flaresolverr_create_session(shared_state, session_id=None):
|
|
227
|
+
if not is_flaresolverr_available(shared_state):
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
flaresolverr_url = shared_state.values["config"]("FlareSolverr").get("url")
|
|
231
|
+
payload = {"cmd": "sessions.create"}
|
|
232
|
+
if session_id:
|
|
233
|
+
payload["session"] = session_id
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
resp = requests.post(
|
|
237
|
+
flaresolverr_url,
|
|
238
|
+
json=payload,
|
|
239
|
+
headers={"Content-Type": "application/json"},
|
|
240
|
+
timeout=10,
|
|
241
|
+
)
|
|
242
|
+
resp.raise_for_status()
|
|
243
|
+
data = resp.json()
|
|
244
|
+
if data.get("status") == "ok":
|
|
245
|
+
return data.get("session")
|
|
246
|
+
except Exception:
|
|
247
|
+
pass
|
|
248
|
+
return None
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def flaresolverr_destroy_session(shared_state, session_id):
|
|
252
|
+
if not is_flaresolverr_available(shared_state):
|
|
253
|
+
return
|
|
254
|
+
|
|
255
|
+
flaresolverr_url = shared_state.values["config"]("FlareSolverr").get("url")
|
|
256
|
+
payload = {"cmd": "sessions.destroy", "session": session_id}
|
|
257
|
+
|
|
258
|
+
try:
|
|
259
|
+
requests.post(
|
|
260
|
+
flaresolverr_url,
|
|
261
|
+
json=payload,
|
|
262
|
+
headers={"Content-Type": "application/json"},
|
|
263
|
+
timeout=10,
|
|
264
|
+
)
|
|
265
|
+
except Exception:
|
|
266
|
+
pass
|
quasarr/providers/version.py
CHANGED
quasarr/search/sources/wd.py
CHANGED
|
@@ -9,12 +9,13 @@ from base64 import urlsafe_b64encode
|
|
|
9
9
|
from datetime import datetime, timedelta
|
|
10
10
|
from urllib.parse import quote, quote_plus
|
|
11
11
|
|
|
12
|
-
import requests
|
|
13
12
|
from bs4 import BeautifulSoup
|
|
14
13
|
|
|
14
|
+
from quasarr.providers.cloudflare import flaresolverr_get, is_cloudflare_challenge
|
|
15
15
|
from quasarr.providers.hostname_issues import clear_hostname_issue, mark_hostname_issue
|
|
16
16
|
from quasarr.providers.imdb_metadata import get_localized_title, get_year
|
|
17
17
|
from quasarr.providers.log import debug, info
|
|
18
|
+
from quasarr.providers.utils import is_flaresolverr_available
|
|
18
19
|
|
|
19
20
|
hostname = "wd"
|
|
20
21
|
supported_mirrors = ["rapidgator", "ddownload", "katfile", "fikper", "turbobit"]
|
|
@@ -170,9 +171,21 @@ def wd_feed(shared_state, start_time, request_from, mirror=None):
|
|
|
170
171
|
feed_type = "Serien"
|
|
171
172
|
|
|
172
173
|
url = f"https://{wd}/{feed_type}"
|
|
173
|
-
|
|
174
|
+
|
|
175
|
+
if not is_flaresolverr_available(shared_state):
|
|
176
|
+
info(
|
|
177
|
+
f"FlareSolverr is not configured. Cannot access {hostname.upper()} feed due to Cloudflare protection."
|
|
178
|
+
)
|
|
179
|
+
mark_hostname_issue(hostname, "feed", "FlareSolverr missing")
|
|
180
|
+
return []
|
|
181
|
+
|
|
174
182
|
try:
|
|
175
|
-
r =
|
|
183
|
+
r = flaresolverr_get(shared_state, url)
|
|
184
|
+
if r.status_code == 403 or is_cloudflare_challenge(r.text):
|
|
185
|
+
info(f"Cloudflare challenge failed for {hostname} feed.")
|
|
186
|
+
mark_hostname_issue(hostname, "feed", "Cloudflare challenge failed")
|
|
187
|
+
return []
|
|
188
|
+
|
|
176
189
|
r.raise_for_status()
|
|
177
190
|
soup = BeautifulSoup(r.content, "html.parser")
|
|
178
191
|
releases = _parse_rows(soup, shared_state, wd, password, mirror)
|
|
@@ -215,10 +228,21 @@ def wd_search(
|
|
|
215
228
|
|
|
216
229
|
q = quote_plus(search_string)
|
|
217
230
|
url = f"https://{wd}/search?q={q}"
|
|
218
|
-
|
|
231
|
+
|
|
232
|
+
if not is_flaresolverr_available(shared_state):
|
|
233
|
+
info(
|
|
234
|
+
f"FlareSolverr is not configured. Cannot access {hostname.upper()} search due to Cloudflare protection."
|
|
235
|
+
)
|
|
236
|
+
mark_hostname_issue(hostname, "search", "FlareSolverr missing")
|
|
237
|
+
return []
|
|
219
238
|
|
|
220
239
|
try:
|
|
221
|
-
r =
|
|
240
|
+
r = flaresolverr_get(shared_state, url)
|
|
241
|
+
if r.status_code == 403 or is_cloudflare_challenge(r.text):
|
|
242
|
+
info(f"Cloudflare challenge failed for {hostname} search.")
|
|
243
|
+
mark_hostname_issue(hostname, "search", "Cloudflare challenge failed")
|
|
244
|
+
return []
|
|
245
|
+
|
|
222
246
|
r.raise_for_status()
|
|
223
247
|
soup = BeautifulSoup(r.content, "html.parser")
|
|
224
248
|
releases = _parse_rows(
|
quasarr/storage/setup.py
CHANGED
|
@@ -1446,7 +1446,10 @@ def verify_jdownloader_credentials(shared_state):
|
|
|
1446
1446
|
if device_names:
|
|
1447
1447
|
return {"success": True, "devices": device_names}
|
|
1448
1448
|
else:
|
|
1449
|
-
return {
|
|
1449
|
+
return {
|
|
1450
|
+
"success": False,
|
|
1451
|
+
"message": "No devices found or invalid credentials",
|
|
1452
|
+
}
|
|
1450
1453
|
except Exception as e:
|
|
1451
1454
|
return {"success": False, "message": str(e)}
|
|
1452
1455
|
|
|
@@ -1470,19 +1473,25 @@ def save_jdownloader_settings(shared_state, is_setup=False):
|
|
|
1470
1473
|
config.save("user", username)
|
|
1471
1474
|
config.save("password", password)
|
|
1472
1475
|
config.save("device", device)
|
|
1473
|
-
|
|
1476
|
+
|
|
1474
1477
|
if is_setup:
|
|
1475
1478
|
quasarr.providers.web_server.temp_server_success = True
|
|
1476
1479
|
return render_reconnect_success("Credentials set")
|
|
1477
1480
|
else:
|
|
1478
1481
|
response.content_type = "application/json"
|
|
1479
|
-
return {
|
|
1482
|
+
return {
|
|
1483
|
+
"success": True,
|
|
1484
|
+
"message": "JDownloader configured successfully",
|
|
1485
|
+
}
|
|
1480
1486
|
else:
|
|
1481
1487
|
if is_setup:
|
|
1482
1488
|
return render_fail("Could not connect to selected device!")
|
|
1483
1489
|
else:
|
|
1484
1490
|
response.content_type = "application/json"
|
|
1485
|
-
return {
|
|
1491
|
+
return {
|
|
1492
|
+
"success": False,
|
|
1493
|
+
"message": "Could not connect to selected device",
|
|
1494
|
+
}
|
|
1486
1495
|
|
|
1487
1496
|
if is_setup:
|
|
1488
1497
|
return render_fail("Could not set credentials!")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quasarr
|
|
3
|
-
Version: 2.6.
|
|
3
|
+
Version: 2.6.1
|
|
4
4
|
Summary: Quasarr connects JDownloader with Radarr, Sonarr and LazyLibrarian. It also decrypts links protected by CAPTCHAs.
|
|
5
5
|
Author-email: rix1337 <rix1337@users.noreply.github.com>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
quasarr/__init__.py,sha256=ZgxPUmVmiI3XYfocjzWNJNAGda6sO0o3OqOEFOPOQZc,16982
|
|
2
|
-
quasarr/api/__init__.py,sha256=
|
|
2
|
+
quasarr/api/__init__.py,sha256=KrbMcvcPPJ3q299dBOmCJEixdZ4ftAiC1MJ_3pu8ep0,20180
|
|
3
3
|
quasarr/api/arr/__init__.py,sha256=z6Cx9GqPti6zt_bVPHUmGVSdxvIYLRuL7TL7Sd5VPGI,18487
|
|
4
4
|
quasarr/api/captcha/__init__.py,sha256=PPiIaW4w5qZWcm9MkEidsKFS0uaooPMtca5Cc5dL8mM,75225
|
|
5
|
-
quasarr/api/config/__init__.py,sha256=
|
|
6
|
-
quasarr/api/jdownloader/__init__.py,sha256=
|
|
5
|
+
quasarr/api/config/__init__.py,sha256=FJZHALhL6NExonhCk53vOYnM1ICkmbTRue5UMCy5Yzg,8813
|
|
6
|
+
quasarr/api/jdownloader/__init__.py,sha256=SixcV-sgMAunjAT5LawASb1qSuOOokorQo2F7cQ3jZ4,9427
|
|
7
7
|
quasarr/api/packages/__init__.py,sha256=4T6pw0N1DKpTCj2mAgdPOjo__nhxr56aEqZOiFvPvb0,30679
|
|
8
8
|
quasarr/api/sponsors_helper/__init__.py,sha256=QAFXK_JTtAnstRAlieCbbCsoTwIcBu7ZX8C3U4jZpR0,6475
|
|
9
9
|
quasarr/api/statistics/__init__.py,sha256=rJz6S4jSnpFDWtjU7O-2jECUEqlueOHOEfRUjSb3cMY,7943
|
|
@@ -29,11 +29,11 @@ quasarr/downloads/sources/nx.py,sha256=Kn3Nn87NcrKada3j8jpTlunKJ7-ggDyQOiRSoCdxy
|
|
|
29
29
|
quasarr/downloads/sources/sf.py,sha256=f_jC4Shnl2GWCro6JcBLjbmZA8nSPVPr4vdf0WR_r7k,6927
|
|
30
30
|
quasarr/downloads/sources/sj.py,sha256=h3x7F8UUPvcyTf6gkKn6fBLTFeQjvqD7MJ1TtuiqhUU,390
|
|
31
31
|
quasarr/downloads/sources/sl.py,sha256=G5TehRfrdlNT-jJ1PiSEzq1IWqL2UCq73LIMqvYef2c,3573
|
|
32
|
-
quasarr/downloads/sources/wd.py,sha256=
|
|
32
|
+
quasarr/downloads/sources/wd.py,sha256=pmXkScWTrMvQHTOx770M_caF92VIKvHmUu1ncqrmxvY,6042
|
|
33
33
|
quasarr/downloads/sources/wx.py,sha256=-6SEY_3lvtONZXxkit4MIYLHsPY9Z1VzOvv5MYbDag8,7091
|
|
34
34
|
quasarr/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
35
|
quasarr/providers/auth.py,sha256=mLd22xsTO29rgPwEU9SkFHRLuYxev-_11TQXicXLon8,10570
|
|
36
|
-
quasarr/providers/cloudflare.py,sha256=
|
|
36
|
+
quasarr/providers/cloudflare.py,sha256=K5fd0cbVvppYpulz_aYQnyXhkFTZA6ZiwL6v_t_HrC8,9021
|
|
37
37
|
quasarr/providers/hostname_issues.py,sha256=SpnZAxOLejSXJGFnYkCrRzR8D0IQsTMtylM-O0h21Z0,1462
|
|
38
38
|
quasarr/providers/html_images.py,sha256=xmxfNwqAqQimVaOq7IelkxlBdcRpPZZLGli_MJDOacI,19755
|
|
39
39
|
quasarr/providers/html_templates.py,sha256=e5b66N47y5Uq7Ikwcm6kOWiyXZ7Bz4gqg2DcajIBGgE,16360
|
|
@@ -46,7 +46,7 @@ quasarr/providers/obfuscated.py,sha256=IAN0-5m6UblLjaFdPhRy75ryqDMF0nlbkClq5-n1b
|
|
|
46
46
|
quasarr/providers/shared_state.py,sha256=Pv7UUaoPtsl7Rp_vbK_fAnyzlEAdn9Hj_DcVJ4LROqs,32789
|
|
47
47
|
quasarr/providers/statistics.py,sha256=1X_Aa7TE3W7ovwkemVMsgIx55Jw3eYMiyUxuCUDgO5s,8666
|
|
48
48
|
quasarr/providers/utils.py,sha256=YXW34xi8569-Tr-cpr8GyOCrWvZ_GKWrX5x0-sxMpOU,12169
|
|
49
|
-
quasarr/providers/version.py,sha256=
|
|
49
|
+
quasarr/providers/version.py,sha256=peW1DQ2L5XTmtez6gPVEC0say4xucb1RNTyv0_NDmqY,4424
|
|
50
50
|
quasarr/providers/web_server.py,sha256=tHkMxhV6eaHC8cWsEpbUqD_U29IFE24VsU6tjk-xCEM,1765
|
|
51
51
|
quasarr/providers/sessions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
52
52
|
quasarr/providers/sessions/al.py,sha256=obDBotLA8vhbSsIw3VErj-FujVgYc4RimqxLqycfbVE,13569
|
|
@@ -71,14 +71,14 @@ quasarr/search/sources/nx.py,sha256=HvZfzNrteHvjSY6LuPEyKjUHiuuBDrL_zZ-a7bIvLSY,
|
|
|
71
71
|
quasarr/search/sources/sf.py,sha256=W_620AdArvINjhKjBKYvoAX3OKBSeRtOWzTpQFhOmZY,17102
|
|
72
72
|
quasarr/search/sources/sj.py,sha256=fQ1xhaDc1hvycLv2sVYXx_ZBNkjXsJ_s4-VcgJEWy8A,7887
|
|
73
73
|
quasarr/search/sources/sl.py,sha256=XvY7XC4AIXh6-GYsTwERoRKMi4euo1zk2X2gKff8S8E,11079
|
|
74
|
-
quasarr/search/sources/wd.py,sha256=
|
|
74
|
+
quasarr/search/sources/wd.py,sha256=Y_kSbri6u7QaO0JDCeIBtYeo5r_EJXgpDL4WzMrg268,9235
|
|
75
75
|
quasarr/search/sources/wx.py,sha256=3GMgOz2QhmeU1ip-lX5_JrltJi_uuSFGNHJr0Lk7l4c,15555
|
|
76
76
|
quasarr/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
77
77
|
quasarr/storage/config.py,sha256=IzPrfwuR_4fsgh-cBQigOtQwqzrtBAsZfdzDotCabJw,6618
|
|
78
|
-
quasarr/storage/setup.py,sha256=
|
|
78
|
+
quasarr/storage/setup.py,sha256=zb83kvQfxMFHxC7EvWWaVTy0MtG7iEjMRyfY4hdcbOk,61520
|
|
79
79
|
quasarr/storage/sqlite_database.py,sha256=tmHUotMWIwtyH-g244WvcGhMQMMjGokncv7JpFSi8NM,3639
|
|
80
|
-
quasarr-2.6.
|
|
81
|
-
quasarr-2.6.
|
|
82
|
-
quasarr-2.6.
|
|
83
|
-
quasarr-2.6.
|
|
84
|
-
quasarr-2.6.
|
|
80
|
+
quasarr-2.6.1.dist-info/METADATA,sha256=KOEKXAbtynYwQcL7NH32JJvXJ6cfdoN7_LVrPMkW8jE,14727
|
|
81
|
+
quasarr-2.6.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
82
|
+
quasarr-2.6.1.dist-info/entry_points.txt,sha256=gXi8mUKsIqKVvn-bOc8E5f04sK_KoMCC-ty6b2Hf-jc,40
|
|
83
|
+
quasarr-2.6.1.dist-info/licenses/LICENSE,sha256=QQFCAfDgt7lSA8oSWDHIZ9aTjFbZaBJdjnGOHkuhK7k,1060
|
|
84
|
+
quasarr-2.6.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|