quasarr 2.4.7__py3-none-any.whl → 2.4.9__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.
- quasarr/__init__.py +134 -70
- quasarr/api/__init__.py +40 -31
- quasarr/api/arr/__init__.py +116 -108
- quasarr/api/captcha/__init__.py +262 -137
- quasarr/api/config/__init__.py +76 -46
- quasarr/api/packages/__init__.py +138 -102
- quasarr/api/sponsors_helper/__init__.py +29 -16
- quasarr/api/statistics/__init__.py +19 -19
- quasarr/downloads/__init__.py +165 -72
- quasarr/downloads/linkcrypters/al.py +35 -18
- quasarr/downloads/linkcrypters/filecrypt.py +107 -52
- quasarr/downloads/linkcrypters/hide.py +5 -6
- quasarr/downloads/packages/__init__.py +342 -177
- quasarr/downloads/sources/al.py +191 -100
- quasarr/downloads/sources/by.py +31 -13
- quasarr/downloads/sources/dd.py +27 -14
- quasarr/downloads/sources/dj.py +1 -3
- quasarr/downloads/sources/dl.py +126 -71
- quasarr/downloads/sources/dt.py +11 -5
- quasarr/downloads/sources/dw.py +28 -14
- quasarr/downloads/sources/he.py +32 -24
- quasarr/downloads/sources/mb.py +19 -9
- quasarr/downloads/sources/nk.py +14 -10
- quasarr/downloads/sources/nx.py +8 -18
- quasarr/downloads/sources/sf.py +45 -20
- quasarr/downloads/sources/sj.py +1 -3
- quasarr/downloads/sources/sl.py +9 -5
- quasarr/downloads/sources/wd.py +32 -12
- quasarr/downloads/sources/wx.py +35 -21
- quasarr/providers/auth.py +42 -37
- quasarr/providers/cloudflare.py +28 -30
- quasarr/providers/hostname_issues.py +2 -1
- quasarr/providers/html_images.py +2 -2
- quasarr/providers/html_templates.py +22 -14
- quasarr/providers/imdb_metadata.py +149 -80
- quasarr/providers/jd_cache.py +131 -39
- quasarr/providers/log.py +1 -1
- quasarr/providers/myjd_api.py +260 -196
- quasarr/providers/notifications.py +53 -41
- quasarr/providers/obfuscated.py +9 -4
- quasarr/providers/sessions/al.py +71 -55
- quasarr/providers/sessions/dd.py +21 -14
- quasarr/providers/sessions/dl.py +30 -19
- quasarr/providers/sessions/nx.py +23 -14
- quasarr/providers/shared_state.py +292 -141
- quasarr/providers/statistics.py +75 -43
- quasarr/providers/utils.py +33 -27
- quasarr/providers/version.py +45 -14
- quasarr/providers/web_server.py +10 -5
- quasarr/search/__init__.py +30 -18
- quasarr/search/sources/al.py +124 -73
- quasarr/search/sources/by.py +110 -59
- quasarr/search/sources/dd.py +57 -35
- quasarr/search/sources/dj.py +69 -48
- quasarr/search/sources/dl.py +159 -100
- quasarr/search/sources/dt.py +110 -74
- quasarr/search/sources/dw.py +121 -61
- quasarr/search/sources/fx.py +108 -62
- quasarr/search/sources/he.py +78 -49
- quasarr/search/sources/mb.py +96 -48
- quasarr/search/sources/nk.py +80 -50
- quasarr/search/sources/nx.py +91 -62
- quasarr/search/sources/sf.py +171 -106
- quasarr/search/sources/sj.py +69 -48
- quasarr/search/sources/sl.py +115 -71
- quasarr/search/sources/wd.py +67 -44
- quasarr/search/sources/wx.py +188 -123
- quasarr/storage/config.py +65 -52
- quasarr/storage/setup.py +238 -140
- quasarr/storage/sqlite_database.py +10 -4
- {quasarr-2.4.7.dist-info → quasarr-2.4.9.dist-info}/METADATA +2 -2
- quasarr-2.4.9.dist-info/RECORD +81 -0
- quasarr-2.4.7.dist-info/RECORD +0 -81
- {quasarr-2.4.7.dist-info → quasarr-2.4.9.dist-info}/WHEEL +0 -0
- {quasarr-2.4.7.dist-info → quasarr-2.4.9.dist-info}/entry_points.txt +0 -0
- {quasarr-2.4.7.dist-info → quasarr-2.4.9.dist-info}/licenses/LICENSE +0 -0
quasarr/storage/config.py
CHANGED
|
@@ -17,18 +17,18 @@ from quasarr.storage.sqlite_database import DataBase
|
|
|
17
17
|
|
|
18
18
|
class Config(object):
|
|
19
19
|
_DEFAULT_CONFIG = {
|
|
20
|
-
|
|
20
|
+
"API": [
|
|
21
21
|
("key", "secret", ""),
|
|
22
22
|
],
|
|
23
|
-
|
|
23
|
+
"JDownloader": [
|
|
24
24
|
("user", "secret", ""),
|
|
25
25
|
("password", "secret", ""),
|
|
26
26
|
("device", "str", ""),
|
|
27
27
|
],
|
|
28
|
-
|
|
28
|
+
"Settings": [
|
|
29
29
|
("hostnames_url", "secret", ""),
|
|
30
30
|
],
|
|
31
|
-
|
|
31
|
+
"Hostnames": [
|
|
32
32
|
("al", "secret", ""),
|
|
33
33
|
("by", "secret", ""),
|
|
34
34
|
("dd", "secret", ""),
|
|
@@ -45,27 +45,15 @@ class Config(object):
|
|
|
45
45
|
("sj", "secret", ""),
|
|
46
46
|
("sl", "secret", ""),
|
|
47
47
|
("wd", "secret", ""),
|
|
48
|
-
("wx", "secret", "")
|
|
48
|
+
("wx", "secret", ""),
|
|
49
49
|
],
|
|
50
|
-
|
|
50
|
+
"FlareSolverr": [
|
|
51
51
|
("url", "str", ""),
|
|
52
52
|
],
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
],
|
|
57
|
-
'DD': [
|
|
58
|
-
("user", "secret", ""),
|
|
59
|
-
("password", "secret", "")
|
|
60
|
-
],
|
|
61
|
-
'DL': [
|
|
62
|
-
("user", "secret", ""),
|
|
63
|
-
("password", "secret", "")
|
|
64
|
-
],
|
|
65
|
-
'NX': [
|
|
66
|
-
("user", "secret", ""),
|
|
67
|
-
("password", "secret", "")
|
|
68
|
-
]
|
|
53
|
+
"AL": [("user", "secret", ""), ("password", "secret", "")],
|
|
54
|
+
"DD": [("user", "secret", ""), ("password", "secret", "")],
|
|
55
|
+
"DL": [("user", "secret", ""), ("password", "secret", "")],
|
|
56
|
+
"NX": [("user", "secret", ""), ("password", "secret", "")],
|
|
69
57
|
}
|
|
70
58
|
__config__ = []
|
|
71
59
|
|
|
@@ -75,70 +63,95 @@ class Config(object):
|
|
|
75
63
|
self._config = configparser.RawConfigParser()
|
|
76
64
|
try:
|
|
77
65
|
self._config.read(self._configfile)
|
|
78
|
-
self._config.has_section(
|
|
79
|
-
self._section
|
|
66
|
+
self._config.has_section(self._section) or self._set_default_config(
|
|
67
|
+
self._section
|
|
68
|
+
)
|
|
80
69
|
self.__config__ = self._read_config(self._section)
|
|
81
70
|
except configparser.DuplicateSectionError:
|
|
82
|
-
print(
|
|
71
|
+
print("Duplicate Section in Config File")
|
|
83
72
|
raise
|
|
84
73
|
except Exception as e:
|
|
85
|
-
print(f
|
|
74
|
+
print(f"Unknown error while reading config file: {e}")
|
|
86
75
|
raise
|
|
87
76
|
|
|
88
77
|
def _set_default_config(self, section):
|
|
89
78
|
self._config.add_section(section)
|
|
90
|
-
for
|
|
79
|
+
for key, key_type, value in self._DEFAULT_CONFIG[section]:
|
|
91
80
|
self._config.set(section, key, value)
|
|
92
|
-
with open(self._configfile,
|
|
81
|
+
with open(self._configfile, "w") as configfile:
|
|
93
82
|
self._config.write(configfile)
|
|
94
83
|
|
|
95
84
|
def _get_encryption_params(self):
|
|
96
|
-
crypt_key = DataBase(
|
|
97
|
-
crypt_iv = DataBase(
|
|
85
|
+
crypt_key = DataBase("secrets").retrieve("key")
|
|
86
|
+
crypt_iv = DataBase("secrets").retrieve("iv")
|
|
98
87
|
if crypt_iv and crypt_key:
|
|
99
88
|
return base64.b64decode(crypt_key), base64.b64decode(crypt_iv)
|
|
100
89
|
else:
|
|
101
90
|
crypt_key = get_random_bytes(32)
|
|
102
91
|
crypt_iv = get_random_bytes(16)
|
|
103
|
-
DataBase(
|
|
104
|
-
|
|
92
|
+
DataBase("secrets").update_store(
|
|
93
|
+
"key", base64.b64encode(crypt_key).decode()
|
|
94
|
+
)
|
|
95
|
+
DataBase("secrets").update_store("iv", base64.b64encode(crypt_iv).decode())
|
|
105
96
|
return crypt_key, crypt_iv
|
|
106
97
|
|
|
107
98
|
def _set_to_config(self, section, key, value):
|
|
108
|
-
default_value_type = [
|
|
109
|
-
|
|
99
|
+
default_value_type = [
|
|
100
|
+
param[1] for param in self._DEFAULT_CONFIG[section] if param[0] == key
|
|
101
|
+
]
|
|
102
|
+
if default_value_type and default_value_type[0] == "secret" and len(value):
|
|
110
103
|
crypt_key, crypt_iv = self._get_encryption_params()
|
|
111
104
|
cipher = AES.new(crypt_key, AES.MODE_CBC, crypt_iv)
|
|
112
|
-
value = base64.b64encode(
|
|
113
|
-
|
|
105
|
+
value = base64.b64encode(
|
|
106
|
+
cipher.encrypt(pad(value.encode(), AES.block_size))
|
|
107
|
+
)
|
|
108
|
+
value = "secret|" + value.decode()
|
|
114
109
|
self._config.set(section, key, value)
|
|
115
|
-
with open(self._configfile,
|
|
110
|
+
with open(self._configfile, "w") as configfile:
|
|
116
111
|
self._config.write(configfile)
|
|
117
112
|
|
|
118
113
|
def _read_config(self, section):
|
|
119
|
-
return [
|
|
114
|
+
return [
|
|
115
|
+
(key, "", self._config.get(section, key))
|
|
116
|
+
for key in self._config.options(section)
|
|
117
|
+
]
|
|
120
118
|
|
|
121
119
|
def _get_from_config(self, scope, key):
|
|
122
120
|
res = [param[2] for param in scope if param[0] == key]
|
|
123
121
|
if not res:
|
|
124
|
-
res = [
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
122
|
+
res = [
|
|
123
|
+
param[2]
|
|
124
|
+
for param in self._DEFAULT_CONFIG[self._section]
|
|
125
|
+
if param[0] == key
|
|
126
|
+
]
|
|
127
|
+
if [
|
|
128
|
+
param
|
|
129
|
+
for param in self._DEFAULT_CONFIG[self._section]
|
|
130
|
+
if param[0] == key and param[1] == "secret"
|
|
131
|
+
]:
|
|
132
|
+
value = res[0].strip("'\"")
|
|
128
133
|
if value.startswith("secret|"):
|
|
129
134
|
crypt_key, crypt_iv = self._get_encryption_params()
|
|
130
135
|
cipher = AES.new(crypt_key, AES.MODE_CBC, crypt_iv)
|
|
131
|
-
decrypted_payload =
|
|
132
|
-
|
|
136
|
+
decrypted_payload = (
|
|
137
|
+
cipher.decrypt(base64.b64decode(value[7:])).decode("utf-8").strip()
|
|
138
|
+
)
|
|
139
|
+
final_payload = "".join(
|
|
140
|
+
filter(lambda c: c in string.printable, decrypted_payload)
|
|
141
|
+
)
|
|
133
142
|
return final_payload
|
|
134
143
|
else: ## Loaded value is not encrypted, return as is
|
|
135
144
|
if len(value) > 0:
|
|
136
145
|
self.save(key, value)
|
|
137
146
|
return value
|
|
138
|
-
elif [
|
|
139
|
-
|
|
147
|
+
elif [
|
|
148
|
+
param
|
|
149
|
+
for param in self._DEFAULT_CONFIG[self._section]
|
|
150
|
+
if param[0] == key and param[1] == "bool"
|
|
151
|
+
]:
|
|
152
|
+
return True if len(res) and res[0].strip("'\"").lower() == "true" else False
|
|
140
153
|
else:
|
|
141
|
-
return res[0].strip('\
|
|
154
|
+
return res[0].strip("'\"") if len(res) > 0 else False
|
|
142
155
|
|
|
143
156
|
def save(self, key, value):
|
|
144
157
|
self._set_to_config(self._section, key, value)
|
|
@@ -149,15 +162,15 @@ class Config(object):
|
|
|
149
162
|
|
|
150
163
|
|
|
151
164
|
def get_clean_hostnames(shared_state):
|
|
152
|
-
hostnames = Config(
|
|
165
|
+
hostnames = Config("Hostnames")
|
|
153
166
|
set_hostnames = {}
|
|
154
167
|
|
|
155
168
|
def clean_up_hostname(host, strg, hostnames):
|
|
156
|
-
if strg and
|
|
157
|
-
strg = strg.replace(
|
|
158
|
-
strg = re.findall(r
|
|
169
|
+
if strg and "/" in strg:
|
|
170
|
+
strg = strg.replace("https://", "").replace("http://", "")
|
|
171
|
+
strg = re.findall(r"([a-z-.]*\.[a-z]*)", strg)[0]
|
|
159
172
|
hostnames.save(host, strg)
|
|
160
|
-
if strg and re.match(r
|
|
173
|
+
if strg and re.match(r".*[A-Z].*", strg):
|
|
161
174
|
hostnames.save(host, strg.lower())
|
|
162
175
|
if strg:
|
|
163
176
|
print(f'Using "{strg}" as hostname for "{host}"')
|