quasarr 1.26.1__py3-none-any.whl → 1.26.3__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/providers/obfuscated.py +6 -6
- quasarr/providers/shared_state.py +1 -0
- quasarr/providers/version.py +1 -1
- quasarr/storage/setup.py +97 -27
- {quasarr-1.26.1.dist-info → quasarr-1.26.3.dist-info}/METADATA +1 -1
- {quasarr-1.26.1.dist-info → quasarr-1.26.3.dist-info}/RECORD +10 -10
- {quasarr-1.26.1.dist-info → quasarr-1.26.3.dist-info}/WHEEL +0 -0
- {quasarr-1.26.1.dist-info → quasarr-1.26.3.dist-info}/entry_points.txt +0 -0
- {quasarr-1.26.1.dist-info → quasarr-1.26.3.dist-info}/licenses/LICENSE +0 -0
- {quasarr-1.26.1.dist-info → quasarr-1.26.3.dist-info}/top_level.txt +0 -0
|
@@ -120,6 +120,7 @@ def connect_to_jd(jd, user, password, device_name):
|
|
|
120
120
|
info("Error connecting to JDownloader: " + str(e).strip())
|
|
121
121
|
return False
|
|
122
122
|
if not device or not isinstance(device, (type, Jddevice)):
|
|
123
|
+
info(f'Device "{device_name}" not found. Available devices may differ or be offline.')
|
|
123
124
|
return False
|
|
124
125
|
else:
|
|
125
126
|
device.downloadcontroller.get_current_state() # request forces direct_connection info update
|
quasarr/providers/version.py
CHANGED
quasarr/storage/setup.py
CHANGED
|
@@ -6,7 +6,7 @@ import os
|
|
|
6
6
|
import sys
|
|
7
7
|
|
|
8
8
|
import requests
|
|
9
|
-
from bottle import Bottle, request
|
|
9
|
+
from bottle import Bottle, request, response
|
|
10
10
|
|
|
11
11
|
import quasarr
|
|
12
12
|
import quasarr.providers.html_images as images
|
|
@@ -21,19 +21,40 @@ from quasarr.providers.web_server import Server
|
|
|
21
21
|
from quasarr.storage.config import Config
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
def add_no_cache_headers(app):
|
|
25
|
+
"""Add hooks to prevent browser caching of setup pages."""
|
|
26
|
+
|
|
27
|
+
@app.hook('after_request')
|
|
28
|
+
def set_no_cache():
|
|
29
|
+
response.set_header('Cache-Control', 'no-cache, no-store, must-revalidate')
|
|
30
|
+
response.set_header('Pragma', 'no-cache')
|
|
31
|
+
response.set_header('Expires', '0')
|
|
32
|
+
|
|
33
|
+
|
|
24
34
|
def path_config(shared_state):
|
|
25
35
|
app = Bottle()
|
|
36
|
+
add_no_cache_headers(app)
|
|
26
37
|
|
|
27
38
|
current_path = os.path.dirname(os.path.abspath(sys.argv[0]))
|
|
28
39
|
|
|
29
40
|
@app.get('/')
|
|
30
41
|
def config_form():
|
|
31
42
|
config_form_html = f'''
|
|
32
|
-
<form action="/api/config" method="post">
|
|
43
|
+
<form action="/api/config" method="post" onsubmit="return handleSubmit(this)">
|
|
33
44
|
<label for="config_path">Path</label>
|
|
34
45
|
<input type="text" id="config_path" name="config_path" placeholder="{current_path}"><br>
|
|
35
|
-
{render_button("Save", "primary", {"type": "submit"})}
|
|
46
|
+
{render_button("Save", "primary", {"type": "submit", "id": "submitBtn"})}
|
|
36
47
|
</form>
|
|
48
|
+
<script>
|
|
49
|
+
var formSubmitted = false;
|
|
50
|
+
function handleSubmit(form) {{
|
|
51
|
+
if (formSubmitted) return false;
|
|
52
|
+
formSubmitted = true;
|
|
53
|
+
var btn = document.getElementById('submitBtn');
|
|
54
|
+
if (btn) {{ btn.disabled = true; btn.textContent = 'Saving...'; }}
|
|
55
|
+
return true;
|
|
56
|
+
}}
|
|
57
|
+
</script>
|
|
37
58
|
'''
|
|
38
59
|
return render_form("Press 'Save' to set desired path for configuration",
|
|
39
60
|
config_form_html)
|
|
@@ -99,7 +120,7 @@ def hostname_form_html(shared_state, message):
|
|
|
99
120
|
))
|
|
100
121
|
|
|
101
122
|
hostname_form_content = "".join(field_html)
|
|
102
|
-
button_html = render_button("Save", "primary", {"type": "submit"})
|
|
123
|
+
button_html = render_button("Save", "primary", {"type": "submit", "id": "submitBtn"})
|
|
103
124
|
|
|
104
125
|
template = """
|
|
105
126
|
<div id="message" style="margin-bottom:0.5em;">{message}</div>
|
|
@@ -111,13 +132,19 @@ def hostname_form_html(shared_state, message):
|
|
|
111
132
|
</form>
|
|
112
133
|
|
|
113
134
|
<script>
|
|
135
|
+
var formSubmitted = false;
|
|
114
136
|
function validateHostnames(form) {{
|
|
137
|
+
if (formSubmitted) return false;
|
|
138
|
+
|
|
115
139
|
var errorDiv = document.getElementById('error-msg');
|
|
116
140
|
errorDiv.textContent = '';
|
|
117
141
|
|
|
118
142
|
var inputs = form.querySelectorAll('input[type="text"]');
|
|
119
143
|
for (var i = 0; i < inputs.length; i++) {{
|
|
120
144
|
if (inputs[i].value.trim() !== '') {{
|
|
145
|
+
formSubmitted = true;
|
|
146
|
+
var btn = document.getElementById('submitBtn');
|
|
147
|
+
if (btn) {{ btn.disabled = true; btn.textContent = 'Saving...'; }}
|
|
121
148
|
return true;
|
|
122
149
|
}}
|
|
123
150
|
}}
|
|
@@ -205,13 +232,14 @@ def save_hostnames(shared_state, timeout=5, first_run=True):
|
|
|
205
232
|
|
|
206
233
|
def hostnames_config(shared_state):
|
|
207
234
|
app = Bottle()
|
|
235
|
+
add_no_cache_headers(app)
|
|
208
236
|
|
|
209
237
|
@app.get('/')
|
|
210
238
|
def hostname_form():
|
|
211
239
|
message = """<p>
|
|
212
240
|
If you're having trouble setting this up, take a closer look at
|
|
213
|
-
<a href="https://github.com/rix1337/Quasarr?tab=readme-ov-file#
|
|
214
|
-
|
|
241
|
+
<a href="https://github.com/rix1337/Quasarr?tab=readme-ov-file#quasarr" target="_blank" rel="noopener noreferrer">
|
|
242
|
+
the instructions.
|
|
215
243
|
</a>
|
|
216
244
|
</p>"""
|
|
217
245
|
return render_form("Set at least one valid hostname", hostname_form_html(shared_state, message))
|
|
@@ -227,6 +255,7 @@ def hostnames_config(shared_state):
|
|
|
227
255
|
|
|
228
256
|
def hostname_credentials_config(shared_state, shorthand, domain):
|
|
229
257
|
app = Bottle()
|
|
258
|
+
add_no_cache_headers(app)
|
|
230
259
|
|
|
231
260
|
shorthand = shorthand.upper()
|
|
232
261
|
|
|
@@ -242,16 +271,30 @@ def hostname_credentials_config(shared_state, shorthand, domain):
|
|
|
242
271
|
'''
|
|
243
272
|
|
|
244
273
|
form_html = f'''
|
|
245
|
-
<form action="/api/credentials/{shorthand}" method="post">
|
|
274
|
+
<form id="credentialsForm" action="/api/credentials/{shorthand}" method="post" onsubmit="return handleSubmit(this)">
|
|
246
275
|
{form_content}
|
|
247
|
-
{render_button("Save", "primary", {"type": "submit"})}
|
|
276
|
+
{render_button("Save", "primary", {"type": "submit", "id": "submitBtn"})}
|
|
248
277
|
</form>
|
|
278
|
+
<script>
|
|
279
|
+
var formSubmitted = false;
|
|
280
|
+
function handleSubmit(form) {{
|
|
281
|
+
if (formSubmitted) return false;
|
|
282
|
+
formSubmitted = true;
|
|
283
|
+
var btn = document.getElementById('submitBtn');
|
|
284
|
+
if (btn) {{ btn.disabled = true; btn.textContent = 'Saving...'; }}
|
|
285
|
+
return true;
|
|
286
|
+
}}
|
|
287
|
+
</script>
|
|
249
288
|
'''
|
|
250
289
|
|
|
251
290
|
return render_form(f"Set User and Password for {shorthand}", form_html)
|
|
252
291
|
|
|
253
292
|
@app.post("/api/credentials/<sh>")
|
|
254
293
|
def set_credentials(sh):
|
|
294
|
+
# Guard against duplicate submissions (e.g., double-click)
|
|
295
|
+
if quasarr.providers.web_server.temp_server_success:
|
|
296
|
+
return render_success(f"{sh} credentials already being processed", 5)
|
|
297
|
+
|
|
255
298
|
user = request.forms.get('user')
|
|
256
299
|
password = request.forms.get('password')
|
|
257
300
|
config = Config(shorthand)
|
|
@@ -294,6 +337,7 @@ def hostname_credentials_config(shared_state, shorthand, domain):
|
|
|
294
337
|
|
|
295
338
|
def flaresolverr_config(shared_state):
|
|
296
339
|
app = Bottle()
|
|
340
|
+
add_no_cache_headers(app)
|
|
297
341
|
|
|
298
342
|
@app.get('/')
|
|
299
343
|
def url_form():
|
|
@@ -304,10 +348,20 @@ def flaresolverr_config(shared_state):
|
|
|
304
348
|
<input type="text" id="url" name="url" placeholder="http://192.168.0.1:8191/v1"><br>
|
|
305
349
|
'''
|
|
306
350
|
form_html = f'''
|
|
307
|
-
<form action="/api/flaresolverr" method="post">
|
|
351
|
+
<form action="/api/flaresolverr" method="post" onsubmit="return handleSubmit(this)">
|
|
308
352
|
{form_content}
|
|
309
|
-
{render_button("Save", "primary", {"type": "submit"})}
|
|
353
|
+
{render_button("Save", "primary", {"type": "submit", "id": "submitBtn"})}
|
|
310
354
|
</form>
|
|
355
|
+
<script>
|
|
356
|
+
var formSubmitted = false;
|
|
357
|
+
function handleSubmit(form) {{
|
|
358
|
+
if (formSubmitted) return false;
|
|
359
|
+
formSubmitted = true;
|
|
360
|
+
var btn = document.getElementById('submitBtn');
|
|
361
|
+
if (btn) {{ btn.disabled = true; btn.textContent = 'Saving...'; }}
|
|
362
|
+
return true;
|
|
363
|
+
}}
|
|
364
|
+
</script>
|
|
311
365
|
'''
|
|
312
366
|
return render_form("Set FlareSolverr URL", form_html)
|
|
313
367
|
|
|
@@ -316,6 +370,9 @@ def flaresolverr_config(shared_state):
|
|
|
316
370
|
url = request.forms.get('url').strip()
|
|
317
371
|
config = Config("FlareSolverr")
|
|
318
372
|
|
|
373
|
+
if not url.startswith("http://") and not url.startswith("https://"):
|
|
374
|
+
url = "http://" + url
|
|
375
|
+
|
|
319
376
|
if url:
|
|
320
377
|
try:
|
|
321
378
|
headers = {"Content-Type": "application/json"}
|
|
@@ -324,8 +381,8 @@ def flaresolverr_config(shared_state):
|
|
|
324
381
|
"url": "http://www.google.com/",
|
|
325
382
|
"maxTimeout": 30000
|
|
326
383
|
}
|
|
327
|
-
|
|
328
|
-
if
|
|
384
|
+
resp = requests.post(url, headers=headers, json=data, timeout=30)
|
|
385
|
+
if resp.status_code == 200:
|
|
329
386
|
config.save("url", url)
|
|
330
387
|
print(f'Using Flaresolverr URL: "{url}"')
|
|
331
388
|
quasarr.providers.web_server.temp_server_success = True
|
|
@@ -347,6 +404,7 @@ def flaresolverr_config(shared_state):
|
|
|
347
404
|
|
|
348
405
|
def jdownloader_config(shared_state):
|
|
349
406
|
app = Bottle()
|
|
407
|
+
add_no_cache_headers(app)
|
|
350
408
|
|
|
351
409
|
@app.get('/')
|
|
352
410
|
def jd_form():
|
|
@@ -368,19 +426,26 @@ def jdownloader_config(shared_state):
|
|
|
368
426
|
|
|
369
427
|
<p>Some JDownloader settings will be enforced by Quasarr on startup.</p>
|
|
370
428
|
|
|
371
|
-
<form action="/api/store_jdownloader" method="post" id="deviceForm" style="display: none;">
|
|
429
|
+
<form action="/api/store_jdownloader" method="post" id="deviceForm" style="display: none;" onsubmit="return handleStoreSubmit(this)">
|
|
372
430
|
<input type="hidden" id="hiddenUser" name="user">
|
|
373
431
|
<input type="hidden" id="hiddenPass" name="pass">
|
|
374
432
|
<label for="device">JDownloader</label>
|
|
375
433
|
<select id="device" name="device"></select><br>
|
|
376
|
-
{render_button("Save", "primary", {"type": "submit"})}
|
|
434
|
+
{render_button("Save", "primary", {"type": "submit", "id": "storeBtn"})}
|
|
377
435
|
</form>
|
|
378
436
|
<p><strong>Saving may take a while!</strong></p><br>
|
|
379
437
|
'''
|
|
380
438
|
|
|
381
439
|
verify_script = '''
|
|
382
440
|
<script>
|
|
441
|
+
var verifyInProgress = false;
|
|
442
|
+
var storeSubmitted = false;
|
|
383
443
|
function verifyCredentials() {
|
|
444
|
+
if (verifyInProgress) return;
|
|
445
|
+
verifyInProgress = true;
|
|
446
|
+
var btn = document.getElementById('verifyButton');
|
|
447
|
+
if (btn) { btn.disabled = true; btn.textContent = 'Verifying...'; }
|
|
448
|
+
|
|
384
449
|
var user = document.getElementById('user').value;
|
|
385
450
|
var pass = document.getElementById('pass').value;
|
|
386
451
|
fetch('/api/verify_jdownloader', {
|
|
@@ -406,12 +471,23 @@ def jdownloader_config(shared_state):
|
|
|
406
471
|
document.getElementById('deviceForm').style.display = 'block';
|
|
407
472
|
} else {
|
|
408
473
|
alert('Fehler! Bitte die Zugangsdaten überprüfen.');
|
|
474
|
+
verifyInProgress = false;
|
|
475
|
+
if (btn) { btn.disabled = false; btn.textContent = 'Verify Credentials'; }
|
|
409
476
|
}
|
|
410
477
|
})
|
|
411
478
|
.catch((error) => {
|
|
412
479
|
console.error('Error:', error);
|
|
480
|
+
verifyInProgress = false;
|
|
481
|
+
if (btn) { btn.disabled = false; btn.textContent = 'Verify Credentials'; }
|
|
413
482
|
});
|
|
414
483
|
}
|
|
484
|
+
function handleStoreSubmit(form) {
|
|
485
|
+
if (storeSubmitted) return false;
|
|
486
|
+
storeSubmitted = true;
|
|
487
|
+
var btn = document.getElementById('storeBtn');
|
|
488
|
+
if (btn) { btn.disabled = true; btn.textContent = 'Saving...'; }
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
415
491
|
</script>
|
|
416
492
|
'''
|
|
417
493
|
return render_form("Set your credentials for My JDownloader", verify_form_html, verify_script)
|
|
@@ -440,21 +516,15 @@ def jdownloader_config(shared_state):
|
|
|
440
516
|
password = request.forms.get('pass')
|
|
441
517
|
device = request.forms.get('device')
|
|
442
518
|
|
|
443
|
-
config = Config('JDownloader')
|
|
444
|
-
|
|
445
519
|
if username and password and device:
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
config.save('
|
|
452
|
-
config.save('password', "")
|
|
453
|
-
config.save('device', "")
|
|
454
|
-
else:
|
|
520
|
+
# Verify connection works before saving credentials
|
|
521
|
+
if shared_state.set_device(username, password, device):
|
|
522
|
+
config = Config('JDownloader')
|
|
523
|
+
config.save('user', username)
|
|
524
|
+
config.save('password', password)
|
|
525
|
+
config.save('device', device)
|
|
455
526
|
quasarr.providers.web_server.temp_server_success = True
|
|
456
|
-
return render_success("Credentials set",
|
|
457
|
-
15)
|
|
527
|
+
return render_success("Credentials set", 15)
|
|
458
528
|
|
|
459
529
|
return render_fail("Could not set credentials!")
|
|
460
530
|
|
|
@@ -36,10 +36,10 @@ quasarr/providers/imdb_metadata.py,sha256=10L4kZkt6Fg0HGdNcc6KCtIQHRYEqdarLyaMVN
|
|
|
36
36
|
quasarr/providers/log.py,sha256=_g5RwtfuksARXnvryhsngzoJyFcNzj6suqd3ndqZM0Y,313
|
|
37
37
|
quasarr/providers/myjd_api.py,sha256=Z3PEiO3c3UfDSr4Up5rgwTAnjloWHb-H1RkJ6BLKZv8,34140
|
|
38
38
|
quasarr/providers/notifications.py,sha256=bohT-6yudmFnmZMc3BwCGX0n1HdzSVgQG_LDZm_38dI,4630
|
|
39
|
-
quasarr/providers/obfuscated.py,sha256=
|
|
40
|
-
quasarr/providers/shared_state.py,sha256
|
|
39
|
+
quasarr/providers/obfuscated.py,sha256=xPI3WrteOiZN5BgNDp0CURcYfkRrdnRCz_cT7BpzIJU,1363310
|
|
40
|
+
quasarr/providers/shared_state.py,sha256=-TIiH2lkCfovq7bzUZicpUjXEjS87ZHCcevsFgySOqw,29944
|
|
41
41
|
quasarr/providers/statistics.py,sha256=cEQixYnDMDqtm5wWe40E_2ucyo4mD0n3SrfelhQi1L8,6452
|
|
42
|
-
quasarr/providers/version.py,sha256=
|
|
42
|
+
quasarr/providers/version.py,sha256=vB7GtEbW1DfhRSEYx_GSfI0llKYAfVu3VTVFVwRZGW8,4004
|
|
43
43
|
quasarr/providers/web_server.py,sha256=AYd0KRxdDWMBr87BP8wlSMuL4zZo0I_rY-vHBai6Pfg,1688
|
|
44
44
|
quasarr/providers/sessions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
45
|
quasarr/providers/sessions/al.py,sha256=F1d76eAJcbTvb6YvAIlQ4gfrYC_256QAKiWEQcuWe8k,10612
|
|
@@ -67,11 +67,11 @@ quasarr/search/sources/wd.py,sha256=O02j3irSlVw2qES82g_qHuavAk-njjSRH1dHSCnOUas,
|
|
|
67
67
|
quasarr/search/sources/wx.py,sha256=HIGElGKoBkxBdgoYiaC70T4Y22xRUmgMzzbJpFq1cxw,12897
|
|
68
68
|
quasarr/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
69
69
|
quasarr/storage/config.py,sha256=hOI7vvIo1YaML3dtAkTmp0HSedWF6brVhRk3d8pJtXI,6300
|
|
70
|
-
quasarr/storage/setup.py,sha256=
|
|
70
|
+
quasarr/storage/setup.py,sha256=V_dWfyCYxRE_ahmBtD-DmHOKP2L6brBcVYydPRcHjns,21448
|
|
71
71
|
quasarr/storage/sqlite_database.py,sha256=yMqFQfKf0k7YS-6Z3_7pj4z1GwWSXJ8uvF4IydXsuTE,3554
|
|
72
|
-
quasarr-1.26.
|
|
73
|
-
quasarr-1.26.
|
|
74
|
-
quasarr-1.26.
|
|
75
|
-
quasarr-1.26.
|
|
76
|
-
quasarr-1.26.
|
|
77
|
-
quasarr-1.26.
|
|
72
|
+
quasarr-1.26.3.dist-info/licenses/LICENSE,sha256=QQFCAfDgt7lSA8oSWDHIZ9aTjFbZaBJdjnGOHkuhK7k,1060
|
|
73
|
+
quasarr-1.26.3.dist-info/METADATA,sha256=FEvSChsXKwehAV-G8x14lOP3v1cDw5-qpsK0YTdmAwY,12805
|
|
74
|
+
quasarr-1.26.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
75
|
+
quasarr-1.26.3.dist-info/entry_points.txt,sha256=gXi8mUKsIqKVvn-bOc8E5f04sK_KoMCC-ty6b2Hf-jc,40
|
|
76
|
+
quasarr-1.26.3.dist-info/top_level.txt,sha256=dipJdaRda5ruTZkoGfZU60bY4l9dtPlmOWwxK_oGSF0,8
|
|
77
|
+
quasarr-1.26.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|