quasarr 1.3.5__py3-none-any.whl → 1.20.4__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/__init__.py +157 -56
- quasarr/api/__init__.py +141 -36
- quasarr/api/arr/__init__.py +197 -78
- quasarr/api/captcha/__init__.py +897 -42
- quasarr/api/config/__init__.py +23 -0
- quasarr/api/sponsors_helper/__init__.py +84 -22
- quasarr/api/statistics/__init__.py +196 -0
- quasarr/downloads/__init__.py +237 -434
- quasarr/downloads/linkcrypters/al.py +237 -0
- quasarr/downloads/linkcrypters/filecrypt.py +178 -31
- quasarr/downloads/linkcrypters/hide.py +123 -0
- quasarr/downloads/packages/__init__.py +461 -0
- quasarr/downloads/sources/al.py +697 -0
- quasarr/downloads/sources/by.py +106 -0
- quasarr/downloads/sources/dd.py +6 -78
- quasarr/downloads/sources/dj.py +7 -0
- quasarr/downloads/sources/dt.py +1 -1
- quasarr/downloads/sources/dw.py +2 -2
- quasarr/downloads/sources/he.py +112 -0
- quasarr/downloads/sources/mb.py +47 -0
- quasarr/downloads/sources/nk.py +51 -0
- quasarr/downloads/sources/nx.py +36 -81
- quasarr/downloads/sources/sf.py +27 -4
- quasarr/downloads/sources/sj.py +7 -0
- quasarr/downloads/sources/sl.py +90 -0
- quasarr/downloads/sources/wd.py +110 -0
- quasarr/providers/cloudflare.py +204 -0
- quasarr/providers/html_images.py +20 -0
- quasarr/providers/html_templates.py +210 -108
- quasarr/providers/imdb_metadata.py +15 -2
- quasarr/providers/myjd_api.py +36 -5
- quasarr/providers/notifications.py +30 -5
- quasarr/providers/obfuscated.py +35 -0
- quasarr/providers/sessions/__init__.py +0 -0
- quasarr/providers/sessions/al.py +286 -0
- quasarr/providers/sessions/dd.py +78 -0
- quasarr/providers/sessions/nx.py +76 -0
- quasarr/providers/shared_state.py +368 -23
- quasarr/providers/statistics.py +154 -0
- quasarr/providers/version.py +60 -1
- quasarr/search/__init__.py +112 -36
- quasarr/search/sources/al.py +448 -0
- quasarr/search/sources/by.py +203 -0
- quasarr/search/sources/dd.py +17 -6
- quasarr/search/sources/dj.py +213 -0
- quasarr/search/sources/dt.py +37 -7
- quasarr/search/sources/dw.py +27 -47
- quasarr/search/sources/fx.py +27 -29
- quasarr/search/sources/he.py +196 -0
- quasarr/search/sources/mb.py +195 -0
- quasarr/search/sources/nk.py +188 -0
- quasarr/search/sources/nx.py +22 -6
- quasarr/search/sources/sf.py +143 -151
- quasarr/search/sources/sj.py +213 -0
- quasarr/search/sources/sl.py +246 -0
- quasarr/search/sources/wd.py +208 -0
- quasarr/storage/config.py +20 -4
- quasarr/storage/setup.py +224 -56
- quasarr-1.20.4.dist-info/METADATA +304 -0
- quasarr-1.20.4.dist-info/RECORD +72 -0
- {quasarr-1.3.5.dist-info → quasarr-1.20.4.dist-info}/WHEEL +1 -1
- quasarr/providers/tvmaze_metadata.py +0 -23
- quasarr-1.3.5.dist-info/METADATA +0 -174
- quasarr-1.3.5.dist-info/RECORD +0 -43
- {quasarr-1.3.5.dist-info → quasarr-1.20.4.dist-info}/entry_points.txt +0 -0
- {quasarr-1.3.5.dist-info → quasarr-1.20.4.dist-info}/licenses/LICENSE +0 -0
- {quasarr-1.3.5.dist-info → quasarr-1.20.4.dist-info}/top_level.txt +0 -0
|
@@ -2,138 +2,240 @@
|
|
|
2
2
|
# Quasarr
|
|
3
3
|
# Project by https://github.com/rix1337
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
justify-content: center;
|
|
9
|
-
align-items: center;
|
|
10
|
-
height: 100vh;
|
|
11
|
-
max-height: 100vh;
|
|
12
|
-
overflow-y: auto;
|
|
13
|
-
overflow-x: hidden; /* Prevent horizontal scrolling */
|
|
14
|
-
width: 100%; /* Ensure it spans full width */
|
|
15
|
-
background-color: #212529;
|
|
16
|
-
color: #fff;
|
|
17
|
-
font-family: system-ui,-apple-system,'Segoe UI',Roboto,'Helvetica Neue',
|
|
18
|
-
'Noto Sans','Liberation Sans',Arial,sans-serif,'Apple Color Emoji',
|
|
19
|
-
'Segoe UI Emoji','Segoe UI Symbol','Noto Color Emoji';
|
|
20
|
-
"""
|
|
21
|
-
style_inner = """
|
|
22
|
-
background-color: #fff;
|
|
23
|
-
border-radius: 0.375rem;
|
|
24
|
-
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
|
25
|
-
padding: 4px;
|
|
26
|
-
text-align: center;
|
|
27
|
-
color: #212529;
|
|
28
|
-
font-size: 1rem;
|
|
29
|
-
font-weight: 400;
|
|
30
|
-
line-height: 1.5;
|
|
31
|
-
box-sizing: border-box; /* Ensure padding doesn’t exceed boundaries */
|
|
32
|
-
max-width: 100%; /* Allow content to shrink */
|
|
33
|
-
margin: auto;
|
|
34
|
-
"""
|
|
5
|
+
import quasarr.providers.html_images as images
|
|
6
|
+
from quasarr.providers.version import get_version
|
|
7
|
+
|
|
35
8
|
|
|
36
|
-
|
|
37
|
-
|
|
9
|
+
def render_centered_html(inner_content):
|
|
10
|
+
head = '''
|
|
38
11
|
<head>
|
|
12
|
+
<meta charset="utf-8">
|
|
39
13
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
40
14
|
<title>Quasarr</title>
|
|
41
|
-
|
|
15
|
+
<link rel="icon" href="''' + images.logo + '''" type="image/png">
|
|
16
|
+
<style>
|
|
17
|
+
/* Theme variables */
|
|
18
|
+
:root {
|
|
19
|
+
--bg-color: #ffffff;
|
|
20
|
+
--fg-color: #212529;
|
|
21
|
+
--card-bg: #ffffff;
|
|
22
|
+
--card-shadow: rgba(0, 0, 0, 0.1);
|
|
23
|
+
--primary: #0d6efd;
|
|
24
|
+
--secondary: #6c757d;
|
|
25
|
+
--code-bg: #f8f9fa;
|
|
26
|
+
--spacing: 1rem;
|
|
27
|
+
}
|
|
28
|
+
@media (prefers-color-scheme: dark) {
|
|
29
|
+
:root {
|
|
30
|
+
--bg-color: #181a1b;
|
|
31
|
+
--fg-color: #f1f1f1;
|
|
32
|
+
--card-bg: #242526;
|
|
33
|
+
--card-shadow: rgba(0, 0, 0, 0.5);
|
|
34
|
+
--code-bg: #2c2f33;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/* Logo and heading alignment */
|
|
38
|
+
h1 {
|
|
39
|
+
display: inline-flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: center;
|
|
42
|
+
margin-bottom: 0.5rem;
|
|
43
|
+
font-size: 2rem;
|
|
44
|
+
}
|
|
45
|
+
.logo {
|
|
46
|
+
width: 48px;
|
|
47
|
+
height: 48px;
|
|
48
|
+
margin-right: 0.5rem;
|
|
49
|
+
}
|
|
50
|
+
/* Form labels and inputs */
|
|
51
|
+
label {
|
|
52
|
+
display: block;
|
|
53
|
+
font-weight: 600;
|
|
54
|
+
margin-bottom: 0.5rem;
|
|
55
|
+
}
|
|
56
|
+
input, select {
|
|
57
|
+
display: block;
|
|
58
|
+
width: 100%;
|
|
59
|
+
padding: 0.5rem;
|
|
60
|
+
font-size: 1rem;
|
|
61
|
+
border: 1px solid #ced4da;
|
|
62
|
+
border-radius: 0.5rem;
|
|
63
|
+
background-color: var(--card-bg);
|
|
64
|
+
color: var(--fg-color);
|
|
65
|
+
box-sizing: border-box;
|
|
66
|
+
}
|
|
67
|
+
*, *::before, *::after {
|
|
68
|
+
box-sizing: border-box;
|
|
69
|
+
}
|
|
70
|
+
/* make body a column flex so footer can stick to bottom */
|
|
71
|
+
html, body {
|
|
72
|
+
margin: 0;
|
|
73
|
+
padding: 0;
|
|
74
|
+
width: 100%;
|
|
75
|
+
height: 100%;
|
|
76
|
+
background-color: var(--bg-color);
|
|
77
|
+
color: var(--fg-color);
|
|
78
|
+
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue',
|
|
79
|
+
'Noto Sans', Arial, sans-serif;
|
|
80
|
+
line-height: 1.6;
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-direction: column;
|
|
83
|
+
min-height: 100vh;
|
|
84
|
+
}
|
|
85
|
+
.outer {
|
|
86
|
+
flex: 1;
|
|
87
|
+
display: flex;
|
|
88
|
+
justify-content: center;
|
|
89
|
+
align-items: center;
|
|
90
|
+
padding: var(--spacing);
|
|
91
|
+
}
|
|
92
|
+
.inner {
|
|
93
|
+
background-color: var(--card-bg);
|
|
94
|
+
border-radius: 1rem;
|
|
95
|
+
box-shadow: 0 0.5rem 1.5rem var(--card-shadow);
|
|
96
|
+
padding: calc(var(--spacing) * 2);
|
|
97
|
+
text-align: center;
|
|
98
|
+
width: 100%;
|
|
99
|
+
max-width: fit-content;
|
|
100
|
+
}
|
|
101
|
+
/* No padding on the sides for captcha view on small screens */
|
|
102
|
+
@media (max-width: 600px) {
|
|
103
|
+
body:has(iframe) .outer {
|
|
104
|
+
padding-left: 0;
|
|
105
|
+
padding-right: 0;
|
|
106
|
+
}
|
|
107
|
+
body:has(iframe) .inner {
|
|
108
|
+
padding-left: 0;
|
|
109
|
+
padding-right: 0;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
h2 {
|
|
113
|
+
margin-top: var(--spacing);
|
|
114
|
+
margin-bottom: 0.75rem;
|
|
115
|
+
font-size: 1.5rem;
|
|
116
|
+
}
|
|
117
|
+
h3 {
|
|
118
|
+
margin-top: var(--spacing);
|
|
119
|
+
margin-bottom: 0.5rem;
|
|
120
|
+
font-size: 1.125rem;
|
|
121
|
+
font-weight: 500;
|
|
122
|
+
}
|
|
123
|
+
p {
|
|
124
|
+
margin: 0.5rem 0;
|
|
125
|
+
}
|
|
126
|
+
.copy-input {
|
|
127
|
+
background-color: var(--code-bg);
|
|
128
|
+
}
|
|
129
|
+
.url-wrapper .api-key-wrapper {
|
|
130
|
+
display: flex;
|
|
131
|
+
gap: 0.5rem;
|
|
132
|
+
flex-wrap: wrap;
|
|
133
|
+
justify-content: center;
|
|
134
|
+
margin-bottom: var(--spacing);
|
|
135
|
+
}
|
|
136
|
+
.captcha-container {
|
|
137
|
+
background-color: var(--secondary);
|
|
138
|
+
}
|
|
139
|
+
button {
|
|
140
|
+
padding: 0.5rem 1rem;
|
|
141
|
+
font-size: 1rem;
|
|
142
|
+
border-radius: 0.5rem;
|
|
143
|
+
font-weight: 500;
|
|
144
|
+
cursor: pointer;
|
|
145
|
+
transition: background-color 0.2s ease, border-color 0.2s ease;
|
|
146
|
+
border: none;
|
|
147
|
+
margin-top: 0.5rem;
|
|
148
|
+
}
|
|
149
|
+
.btn-primary {
|
|
150
|
+
background-color: var(--primary);
|
|
151
|
+
color: #fff;
|
|
152
|
+
}
|
|
153
|
+
.btn-secondary {
|
|
154
|
+
background-color: var(--secondary);
|
|
155
|
+
color: #fff;
|
|
156
|
+
}
|
|
157
|
+
a {
|
|
158
|
+
color: var(--primary);
|
|
159
|
+
text-decoration: none;
|
|
160
|
+
}
|
|
161
|
+
a:hover {
|
|
162
|
+
|
|
163
|
+
}
|
|
164
|
+
/* footer styling */
|
|
165
|
+
footer {
|
|
166
|
+
text-align: center;
|
|
167
|
+
font-size: 0.75rem;
|
|
168
|
+
color: var(--secondary);
|
|
169
|
+
padding: 0.5rem 0;
|
|
170
|
+
}
|
|
171
|
+
</style>
|
|
172
|
+
</head>'''
|
|
173
|
+
|
|
174
|
+
body = f'''
|
|
175
|
+
{head}
|
|
42
176
|
<body>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
177
|
+
<div class="outer">
|
|
178
|
+
<div class="inner">
|
|
179
|
+
{inner_content}
|
|
180
|
+
</div>
|
|
46
181
|
</div>
|
|
47
|
-
|
|
182
|
+
<footer>
|
|
183
|
+
Quasarr v.{get_version()}
|
|
184
|
+
</footer>
|
|
48
185
|
</body>
|
|
49
|
-
</html>
|
|
50
186
|
'''
|
|
187
|
+
return f'<html>{body}</html>'
|
|
51
188
|
|
|
52
189
|
|
|
53
190
|
def render_button(text, button_type="primary", attributes=None):
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
"user-select: none; transition: color 0.15s ease-in-out, "
|
|
60
|
-
"background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, "
|
|
61
|
-
"box-shadow 0.15s ease-in-out; "
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
if button_type == "primary":
|
|
65
|
-
style = base_style + "background-color: #0d6efd; border: 1px solid #0d6efd; "
|
|
66
|
-
else:
|
|
67
|
-
style = base_style + "background-color: #6c757d; border: 1px solid #6c757d; "
|
|
68
|
-
|
|
69
|
-
attr_str = ' '.join(f'{key}="{value}"' for key, value in attributes.items()) if attributes else ""
|
|
70
|
-
|
|
71
|
-
return f'<button style="{style}" {attr_str}>{text}</button>'
|
|
191
|
+
cls = "btn-primary" if button_type == "primary" else "btn-secondary"
|
|
192
|
+
attr_str = ''
|
|
193
|
+
if attributes:
|
|
194
|
+
attr_str = ' '.join(f'{key}="{value}"' for key, value in attributes.items())
|
|
195
|
+
return f'<button class="{cls}" {attr_str}>{text}</button>'
|
|
72
196
|
|
|
73
197
|
|
|
74
198
|
def render_form(header, form="", script=""):
|
|
75
|
-
styles = """
|
|
76
|
-
<style>
|
|
77
|
-
input, select {
|
|
78
|
-
display: block;
|
|
79
|
-
padding: .375rem .75rem;
|
|
80
|
-
width: 100%;
|
|
81
|
-
font-size: 1rem;
|
|
82
|
-
font-weight: 400;
|
|
83
|
-
line-height: 1.5;
|
|
84
|
-
color: #212529;
|
|
85
|
-
background-color: #fff;
|
|
86
|
-
border: 1px solid #dee2e6;
|
|
87
|
-
border-radius: .375rem;
|
|
88
|
-
transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
|
|
89
|
-
text-align: center;
|
|
90
|
-
margin: 10px auto;
|
|
91
|
-
}
|
|
92
|
-
</style>
|
|
93
|
-
"""
|
|
94
199
|
content = f'''
|
|
95
|
-
<h1
|
|
96
|
-
<
|
|
97
|
-
{styles}
|
|
200
|
+
<h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
|
|
201
|
+
<h2>{header}</h2>
|
|
98
202
|
{form}
|
|
99
203
|
{script}
|
|
100
204
|
'''
|
|
101
205
|
return render_centered_html(content)
|
|
102
206
|
|
|
103
207
|
|
|
104
|
-
def render_success(message, timeout=10):
|
|
208
|
+
def render_success(message, timeout=10, optional_text=""):
|
|
105
209
|
button_html = render_button(f"Wait time... {timeout}", "secondary", {"id": "nextButton", "disabled": "true"})
|
|
106
|
-
script = f
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
{script}
|
|
130
|
-
"""
|
|
210
|
+
script = f'''
|
|
211
|
+
<script>
|
|
212
|
+
let counter = {timeout};
|
|
213
|
+
const btn = document.getElementById('nextButton');
|
|
214
|
+
const interval = setInterval(() => {{
|
|
215
|
+
counter--;
|
|
216
|
+
btn.innerText = `Wait time... ${{counter}}`;
|
|
217
|
+
if (counter === 0) {{
|
|
218
|
+
clearInterval(interval);
|
|
219
|
+
btn.innerText = 'Continue';
|
|
220
|
+
btn.disabled = false;
|
|
221
|
+
btn.className = 'btn-primary';
|
|
222
|
+
btn.onclick = () => window.location.href = '/';
|
|
223
|
+
}}
|
|
224
|
+
}}, 1000);
|
|
225
|
+
</script>
|
|
226
|
+
'''
|
|
227
|
+
content = f'''<h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
|
|
228
|
+
<h2>{message}</h2>
|
|
229
|
+
{optional_text}
|
|
230
|
+
{button_html}
|
|
231
|
+
{script}
|
|
232
|
+
'''
|
|
131
233
|
return render_centered_html(content)
|
|
132
234
|
|
|
133
235
|
|
|
134
236
|
def render_fail(message):
|
|
135
237
|
button_html = render_button("Back", "secondary", {"onclick": "window.location.href='/'"})
|
|
136
|
-
return render_centered_html(f"""<h1
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
238
|
+
return render_centered_html(f"""<h1><img src="{images.logo}" type="image/png" alt="Quasarr logo" class="logo"/>Quasarr</h1>
|
|
239
|
+
<h2>{message}</h2>
|
|
240
|
+
{button_html}
|
|
241
|
+
""")
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# Quasarr
|
|
3
3
|
# Project by https://github.com/rix1337
|
|
4
4
|
|
|
5
|
+
import html
|
|
5
6
|
import re
|
|
6
7
|
from datetime import datetime, timedelta
|
|
7
8
|
from json import loads
|
|
@@ -61,6 +62,11 @@ def get_localized_title(shared_state, imdb_id, language='de'):
|
|
|
61
62
|
if not localized_title:
|
|
62
63
|
debug(f"Could not get localized title for {imdb_id} in {language} from IMDb")
|
|
63
64
|
|
|
65
|
+
localized_title = html.unescape(localized_title)
|
|
66
|
+
localized_title = re.sub(r"[^a-zA-Z0-9äöüÄÖÜß&-']", ' ', localized_title).strip()
|
|
67
|
+
localized_title = localized_title.replace(" - ", "-")
|
|
68
|
+
localized_title = re.sub(r'\s{2,}', ' ', localized_title)
|
|
69
|
+
|
|
64
70
|
return localized_title
|
|
65
71
|
|
|
66
72
|
|
|
@@ -111,8 +117,15 @@ def get_imdb_id_from_title(shared_state, title, language="de"):
|
|
|
111
117
|
|
|
112
118
|
if len(search_results) > 0:
|
|
113
119
|
for result in search_results:
|
|
114
|
-
|
|
115
|
-
|
|
120
|
+
try:
|
|
121
|
+
found_title = result["listItem"]["titleText"]
|
|
122
|
+
found_id = result["listItem"]["titleId"]
|
|
123
|
+
except KeyError:
|
|
124
|
+
found_title = result["titleNameText"]
|
|
125
|
+
found_id = result['id']
|
|
126
|
+
|
|
127
|
+
if shared_state.search_string_in_sanitized_title(title, found_title):
|
|
128
|
+
imdb_id = found_id
|
|
116
129
|
break
|
|
117
130
|
else:
|
|
118
131
|
debug(f"Request on IMDb failed: {results.status_code}")
|
quasarr/providers/myjd_api.py
CHANGED
|
@@ -39,6 +39,7 @@ import urllib3
|
|
|
39
39
|
from Cryptodome.Cipher import AES
|
|
40
40
|
|
|
41
41
|
from quasarr.providers.log import debug
|
|
42
|
+
from quasarr.providers.version import get_version
|
|
42
43
|
|
|
43
44
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
44
45
|
BS = 16
|
|
@@ -399,6 +400,29 @@ class Downloads:
|
|
|
399
400
|
return resp
|
|
400
401
|
|
|
401
402
|
|
|
403
|
+
class Extraction:
|
|
404
|
+
"""
|
|
405
|
+
Class that represents the extraction details of a Device
|
|
406
|
+
"""
|
|
407
|
+
|
|
408
|
+
def __init__(self, device):
|
|
409
|
+
self.device = device
|
|
410
|
+
self.url = "/extraction"
|
|
411
|
+
|
|
412
|
+
def get_archive_info(self, link_ids=[], package_ids=[]):
|
|
413
|
+
"""
|
|
414
|
+
Get ArchiveStatus for links and/or packages.
|
|
415
|
+
|
|
416
|
+
:param package_ids: Package UUID's.
|
|
417
|
+
:type: list of strings.
|
|
418
|
+
:param link_ids: link UUID's.
|
|
419
|
+
:type: list of strings
|
|
420
|
+
"""
|
|
421
|
+
params = [link_ids, package_ids]
|
|
422
|
+
resp = self.device.action(self.url + "/getArchiveInfo", params)
|
|
423
|
+
return resp
|
|
424
|
+
|
|
425
|
+
|
|
402
426
|
class Jddevice:
|
|
403
427
|
"""
|
|
404
428
|
Class that represents a JDownloader device and it's functions
|
|
@@ -417,6 +441,7 @@ class Jddevice:
|
|
|
417
441
|
self.config = Config(self)
|
|
418
442
|
self.linkgrabber = Linkgrabber(self)
|
|
419
443
|
self.downloads = Downloads(self)
|
|
444
|
+
self.extraction = Extraction(self)
|
|
420
445
|
self.downloadcontroller = DownloadController(self)
|
|
421
446
|
self.update = Update(self)
|
|
422
447
|
self.__direct_connection_info = None
|
|
@@ -552,7 +577,7 @@ class Myjdapi:
|
|
|
552
577
|
"""
|
|
553
578
|
self.__request_id = int(time.time() * 1000)
|
|
554
579
|
self.__api_url = "https://api.jdownloader.org"
|
|
555
|
-
self.__app_key = "
|
|
580
|
+
self.__app_key = "quasarr"
|
|
556
581
|
self.__api_version = 1
|
|
557
582
|
self.__devices = None
|
|
558
583
|
self.__login_secret = None
|
|
@@ -795,10 +820,14 @@ class Myjdapi:
|
|
|
795
820
|
query[0] + "&".join(query[1:])))
|
|
796
821
|
]
|
|
797
822
|
query = query[0] + "&".join(query[1:])
|
|
823
|
+
|
|
824
|
+
headers = {
|
|
825
|
+
"User-Agent": f"Quasarr/{get_version()}"
|
|
826
|
+
}
|
|
798
827
|
try:
|
|
799
|
-
encrypted_response = requests.get(api + query, timeout=timeout)
|
|
828
|
+
encrypted_response = requests.get(api + query, timeout=timeout, headers=headers)
|
|
800
829
|
except Exception:
|
|
801
|
-
encrypted_response = requests.get(api + query, timeout=timeout, verify=False)
|
|
830
|
+
encrypted_response = requests.get(api + query, timeout=timeout, headers=headers, verify=False)
|
|
802
831
|
debug("Could not establish secure connection to JDownloader.")
|
|
803
832
|
else:
|
|
804
833
|
params_request = []
|
|
@@ -828,7 +857,8 @@ class Myjdapi:
|
|
|
828
857
|
encrypted_response = requests.post(
|
|
829
858
|
request_url,
|
|
830
859
|
headers={
|
|
831
|
-
"Content-Type": "application/aesjson-jd; charset=utf-8"
|
|
860
|
+
"Content-Type": "application/aesjson-jd; charset=utf-8",
|
|
861
|
+
"User-Agent": f"Quasarr/{get_version()}"
|
|
832
862
|
},
|
|
833
863
|
data=encrypted_data,
|
|
834
864
|
timeout=timeout
|
|
@@ -838,7 +868,8 @@ class Myjdapi:
|
|
|
838
868
|
encrypted_response = requests.post(
|
|
839
869
|
request_url,
|
|
840
870
|
headers={
|
|
841
|
-
"Content-Type": "application/aesjson-jd; charset=utf-8"
|
|
871
|
+
"Content-Type": "application/aesjson-jd; charset=utf-8",
|
|
872
|
+
"User-Agent": f"Quasarr/{get_version()}"
|
|
842
873
|
},
|
|
843
874
|
data=encrypted_data,
|
|
844
875
|
timeout=timeout,
|
|
@@ -19,7 +19,7 @@ if os.getenv('SILENT'):
|
|
|
19
19
|
silent = True
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def send_discord_message(shared_state, title, case, imdb_id=None):
|
|
22
|
+
def send_discord_message(shared_state, title, case, imdb_id=None, details=None, source=None):
|
|
23
23
|
"""
|
|
24
24
|
Sends a Discord message to the webhook provided in the shared state, based on the specified case.
|
|
25
25
|
|
|
@@ -27,6 +27,8 @@ def send_discord_message(shared_state, title, case, imdb_id=None):
|
|
|
27
27
|
:param title: Title of the embed to be sent.
|
|
28
28
|
:param case: A string representing the scenario (e.g., 'captcha', 'failed', 'unprotected').
|
|
29
29
|
:param imdb_id: A string starting with "tt" followed by at least 7 digits, representing an object on IMDb
|
|
30
|
+
:param details: A dictionary containing additional details, such as version and link for updates.
|
|
31
|
+
:param source: Optional source of the notification, sent as a field in the embed.
|
|
30
32
|
:return: True if the message was sent successfully, False otherwise.
|
|
31
33
|
"""
|
|
32
34
|
if not shared_state.values.get("discord"):
|
|
@@ -34,7 +36,7 @@ def send_discord_message(shared_state, title, case, imdb_id=None):
|
|
|
34
36
|
|
|
35
37
|
poster_object = None
|
|
36
38
|
if case == "unprotected" or case == "captcha":
|
|
37
|
-
if not imdb_id:
|
|
39
|
+
if not imdb_id and " " not in title: # this should prevent imdb_search for ebooks and magazines
|
|
38
40
|
imdb_id = get_imdb_id_from_title(shared_state, title)
|
|
39
41
|
if imdb_id:
|
|
40
42
|
poster_link = get_poster_link(shared_state, imdb_id)
|
|
@@ -64,8 +66,19 @@ def send_discord_message(shared_state, title, case, imdb_id=None):
|
|
|
64
66
|
if not shared_state.values.get("helper_active"):
|
|
65
67
|
fields.append({
|
|
66
68
|
'name': 'SponsorsHelper',
|
|
67
|
-
'value': f'[
|
|
68
|
-
}
|
|
69
|
+
'value': f'[Sponsors get automated CAPTCHA solutions!](https://github.com/rix1337/Quasarr?tab=readme-ov-file#sponsorshelper)',
|
|
70
|
+
})
|
|
71
|
+
elif case == "quasarr_update":
|
|
72
|
+
description = f'Please update to {details["version"]} as soon as possible!'
|
|
73
|
+
if details:
|
|
74
|
+
fields = [
|
|
75
|
+
{
|
|
76
|
+
'name': 'Release notes at: ',
|
|
77
|
+
'value': f'[GitHub.com: rix1337/Quasarr/{details["version"]}]({details["link"]})',
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
else:
|
|
81
|
+
fields = None
|
|
69
82
|
else:
|
|
70
83
|
info(f"Unknown notification case: {case}")
|
|
71
84
|
return False
|
|
@@ -79,15 +92,27 @@ def send_discord_message(shared_state, title, case, imdb_id=None):
|
|
|
79
92
|
}]
|
|
80
93
|
}
|
|
81
94
|
|
|
95
|
+
if source and source.startswith("http"):
|
|
96
|
+
if not fields:
|
|
97
|
+
fields = []
|
|
98
|
+
fields.append({
|
|
99
|
+
'name': 'Source',
|
|
100
|
+
'value': f'[View release details here]({source})',
|
|
101
|
+
})
|
|
102
|
+
|
|
82
103
|
if fields:
|
|
83
104
|
data['embeds'][0]['fields'] = fields
|
|
84
105
|
|
|
85
106
|
if poster_object:
|
|
86
107
|
data['embeds'][0]['thumbnail'] = poster_object
|
|
87
108
|
data['embeds'][0]['image'] = poster_object
|
|
109
|
+
elif case == "quasarr_update":
|
|
110
|
+
data['embeds'][0]['thumbnail'] = {
|
|
111
|
+
'url': "https://raw.githubusercontent.com/rix1337/Quasarr/main/Quasarr.png"
|
|
112
|
+
}
|
|
88
113
|
|
|
89
114
|
# Apply silent mode: suppress notifications for all cases except 'deleted'
|
|
90
|
-
if silent and case
|
|
115
|
+
if silent and case not in ["failed", "quasarr_update"]:
|
|
91
116
|
data['flags'] = SUPPRESS_NOTIFICATIONS
|
|
92
117
|
|
|
93
118
|
response = requests.post(shared_state.values["discord"], data=json.dumps(data),
|