quasarr 2.4.8__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.
Files changed (76) hide show
  1. quasarr/__init__.py +134 -70
  2. quasarr/api/__init__.py +40 -31
  3. quasarr/api/arr/__init__.py +116 -108
  4. quasarr/api/captcha/__init__.py +262 -137
  5. quasarr/api/config/__init__.py +76 -46
  6. quasarr/api/packages/__init__.py +138 -102
  7. quasarr/api/sponsors_helper/__init__.py +29 -16
  8. quasarr/api/statistics/__init__.py +19 -19
  9. quasarr/downloads/__init__.py +165 -72
  10. quasarr/downloads/linkcrypters/al.py +35 -18
  11. quasarr/downloads/linkcrypters/filecrypt.py +107 -52
  12. quasarr/downloads/linkcrypters/hide.py +5 -6
  13. quasarr/downloads/packages/__init__.py +342 -177
  14. quasarr/downloads/sources/al.py +191 -100
  15. quasarr/downloads/sources/by.py +31 -13
  16. quasarr/downloads/sources/dd.py +27 -14
  17. quasarr/downloads/sources/dj.py +1 -3
  18. quasarr/downloads/sources/dl.py +126 -71
  19. quasarr/downloads/sources/dt.py +11 -5
  20. quasarr/downloads/sources/dw.py +28 -14
  21. quasarr/downloads/sources/he.py +32 -24
  22. quasarr/downloads/sources/mb.py +19 -9
  23. quasarr/downloads/sources/nk.py +14 -10
  24. quasarr/downloads/sources/nx.py +8 -18
  25. quasarr/downloads/sources/sf.py +45 -20
  26. quasarr/downloads/sources/sj.py +1 -3
  27. quasarr/downloads/sources/sl.py +9 -5
  28. quasarr/downloads/sources/wd.py +32 -12
  29. quasarr/downloads/sources/wx.py +35 -21
  30. quasarr/providers/auth.py +42 -37
  31. quasarr/providers/cloudflare.py +28 -30
  32. quasarr/providers/hostname_issues.py +2 -1
  33. quasarr/providers/html_images.py +2 -2
  34. quasarr/providers/html_templates.py +22 -14
  35. quasarr/providers/imdb_metadata.py +149 -80
  36. quasarr/providers/jd_cache.py +131 -39
  37. quasarr/providers/log.py +1 -1
  38. quasarr/providers/myjd_api.py +260 -196
  39. quasarr/providers/notifications.py +53 -41
  40. quasarr/providers/obfuscated.py +9 -4
  41. quasarr/providers/sessions/al.py +71 -55
  42. quasarr/providers/sessions/dd.py +21 -14
  43. quasarr/providers/sessions/dl.py +30 -19
  44. quasarr/providers/sessions/nx.py +23 -14
  45. quasarr/providers/shared_state.py +292 -141
  46. quasarr/providers/statistics.py +75 -43
  47. quasarr/providers/utils.py +33 -27
  48. quasarr/providers/version.py +45 -14
  49. quasarr/providers/web_server.py +10 -5
  50. quasarr/search/__init__.py +30 -18
  51. quasarr/search/sources/al.py +124 -73
  52. quasarr/search/sources/by.py +110 -59
  53. quasarr/search/sources/dd.py +57 -35
  54. quasarr/search/sources/dj.py +69 -48
  55. quasarr/search/sources/dl.py +159 -100
  56. quasarr/search/sources/dt.py +110 -74
  57. quasarr/search/sources/dw.py +121 -61
  58. quasarr/search/sources/fx.py +108 -62
  59. quasarr/search/sources/he.py +78 -49
  60. quasarr/search/sources/mb.py +96 -48
  61. quasarr/search/sources/nk.py +80 -50
  62. quasarr/search/sources/nx.py +91 -62
  63. quasarr/search/sources/sf.py +171 -106
  64. quasarr/search/sources/sj.py +69 -48
  65. quasarr/search/sources/sl.py +115 -71
  66. quasarr/search/sources/wd.py +67 -44
  67. quasarr/search/sources/wx.py +188 -123
  68. quasarr/storage/config.py +65 -52
  69. quasarr/storage/setup.py +238 -140
  70. quasarr/storage/sqlite_database.py +10 -4
  71. {quasarr-2.4.8.dist-info → quasarr-2.4.9.dist-info}/METADATA +2 -2
  72. quasarr-2.4.9.dist-info/RECORD +81 -0
  73. quasarr-2.4.8.dist-info/RECORD +0 -81
  74. {quasarr-2.4.8.dist-info → quasarr-2.4.9.dist-info}/WHEEL +0 -0
  75. {quasarr-2.4.8.dist-info → quasarr-2.4.9.dist-info}/entry_points.txt +0 -0
  76. {quasarr-2.4.8.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
- 'API': [
20
+ "API": [
21
21
  ("key", "secret", ""),
22
22
  ],
23
- 'JDownloader': [
23
+ "JDownloader": [
24
24
  ("user", "secret", ""),
25
25
  ("password", "secret", ""),
26
26
  ("device", "str", ""),
27
27
  ],
28
- 'Settings': [
28
+ "Settings": [
29
29
  ("hostnames_url", "secret", ""),
30
30
  ],
31
- 'Hostnames': [
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
- 'FlareSolverr': [
50
+ "FlareSolverr": [
51
51
  ("url", "str", ""),
52
52
  ],
53
- 'AL': [
54
- ("user", "secret", ""),
55
- ("password", "secret", "")
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) or self._set_default_config(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('Duplicate Section in Config File')
71
+ print("Duplicate Section in Config File")
83
72
  raise
84
73
  except Exception as e:
85
- print(f'Unknown error while reading config file: {e}')
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 (key, key_type, value) in self._DEFAULT_CONFIG[section]:
79
+ for key, key_type, value in self._DEFAULT_CONFIG[section]:
91
80
  self._config.set(section, key, value)
92
- with open(self._configfile, 'w') as 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('secrets').retrieve("key")
97
- crypt_iv = DataBase('secrets').retrieve("iv")
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('secrets').update_store("key", base64.b64encode(crypt_key).decode())
104
- DataBase('secrets').update_store("iv", base64.b64encode(crypt_iv).decode())
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 = [param[1] for param in self._DEFAULT_CONFIG[section] if param[0] == key]
109
- if default_value_type and default_value_type[0] == 'secret' and len(value):
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(cipher.encrypt(pad(value.encode(), AES.block_size)))
113
- value = 'secret|' + value.decode()
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, 'w') as 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 [(key, '', self._config.get(section, key)) for key in self._config.options(section)]
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 = [param[2]
125
- for param in self._DEFAULT_CONFIG[self._section] if param[0] == key]
126
- if [param for param in self._DEFAULT_CONFIG[self._section] if param[0] == key and param[1] == 'secret']:
127
- value = res[0].strip('\'"')
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 = cipher.decrypt(base64.b64decode(value[7:])).decode("utf-8").strip()
132
- final_payload = "".join(filter(lambda c: c in string.printable, decrypted_payload))
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 [param for param in self._DEFAULT_CONFIG[self._section] if param[0] == key and param[1] == 'bool']:
139
- return True if len(res) and res[0].strip('\'"').lower() == 'true' else False
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('\'"') if len(res) > 0 else False
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('Hostnames')
165
+ hostnames = Config("Hostnames")
153
166
  set_hostnames = {}
154
167
 
155
168
  def clean_up_hostname(host, strg, hostnames):
156
- if strg and '/' in strg:
157
- strg = strg.replace('https://', '').replace('http://', '')
158
- strg = re.findall(r'([a-z-.]*\.[a-z]*)', strg)[0]
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'.*[A-Z].*', strg):
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}"')