quasarr 0.1.6__py3-none-any.whl → 1.23.0__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.

Files changed (77) hide show
  1. quasarr/__init__.py +316 -42
  2. quasarr/api/__init__.py +187 -0
  3. quasarr/api/arr/__init__.py +387 -0
  4. quasarr/api/captcha/__init__.py +1189 -0
  5. quasarr/api/config/__init__.py +23 -0
  6. quasarr/api/sponsors_helper/__init__.py +166 -0
  7. quasarr/api/statistics/__init__.py +196 -0
  8. quasarr/downloads/__init__.py +319 -256
  9. quasarr/downloads/linkcrypters/__init__.py +0 -0
  10. quasarr/downloads/linkcrypters/al.py +237 -0
  11. quasarr/downloads/linkcrypters/filecrypt.py +444 -0
  12. quasarr/downloads/linkcrypters/hide.py +123 -0
  13. quasarr/downloads/packages/__init__.py +476 -0
  14. quasarr/downloads/sources/al.py +697 -0
  15. quasarr/downloads/sources/by.py +106 -0
  16. quasarr/downloads/sources/dd.py +76 -0
  17. quasarr/downloads/sources/dj.py +7 -0
  18. quasarr/downloads/sources/dl.py +199 -0
  19. quasarr/downloads/sources/dt.py +66 -0
  20. quasarr/downloads/sources/dw.py +14 -7
  21. quasarr/downloads/sources/he.py +112 -0
  22. quasarr/downloads/sources/mb.py +47 -0
  23. quasarr/downloads/sources/nk.py +54 -0
  24. quasarr/downloads/sources/nx.py +42 -83
  25. quasarr/downloads/sources/sf.py +159 -0
  26. quasarr/downloads/sources/sj.py +7 -0
  27. quasarr/downloads/sources/sl.py +90 -0
  28. quasarr/downloads/sources/wd.py +110 -0
  29. quasarr/downloads/sources/wx.py +127 -0
  30. quasarr/providers/cloudflare.py +204 -0
  31. quasarr/providers/html_images.py +22 -0
  32. quasarr/providers/html_templates.py +211 -104
  33. quasarr/providers/imdb_metadata.py +108 -3
  34. quasarr/providers/log.py +19 -0
  35. quasarr/providers/myjd_api.py +201 -40
  36. quasarr/providers/notifications.py +99 -11
  37. quasarr/providers/obfuscated.py +65 -0
  38. quasarr/providers/sessions/__init__.py +0 -0
  39. quasarr/providers/sessions/al.py +286 -0
  40. quasarr/providers/sessions/dd.py +78 -0
  41. quasarr/providers/sessions/dl.py +175 -0
  42. quasarr/providers/sessions/nx.py +76 -0
  43. quasarr/providers/shared_state.py +656 -79
  44. quasarr/providers/statistics.py +154 -0
  45. quasarr/providers/version.py +60 -1
  46. quasarr/providers/web_server.py +1 -1
  47. quasarr/search/__init__.py +144 -15
  48. quasarr/search/sources/al.py +448 -0
  49. quasarr/search/sources/by.py +204 -0
  50. quasarr/search/sources/dd.py +135 -0
  51. quasarr/search/sources/dj.py +213 -0
  52. quasarr/search/sources/dl.py +354 -0
  53. quasarr/search/sources/dt.py +265 -0
  54. quasarr/search/sources/dw.py +94 -67
  55. quasarr/search/sources/fx.py +89 -33
  56. quasarr/search/sources/he.py +196 -0
  57. quasarr/search/sources/mb.py +195 -0
  58. quasarr/search/sources/nk.py +188 -0
  59. quasarr/search/sources/nx.py +75 -21
  60. quasarr/search/sources/sf.py +374 -0
  61. quasarr/search/sources/sj.py +213 -0
  62. quasarr/search/sources/sl.py +246 -0
  63. quasarr/search/sources/wd.py +208 -0
  64. quasarr/search/sources/wx.py +337 -0
  65. quasarr/storage/config.py +39 -10
  66. quasarr/storage/setup.py +269 -97
  67. quasarr/storage/sqlite_database.py +6 -1
  68. quasarr-1.23.0.dist-info/METADATA +306 -0
  69. quasarr-1.23.0.dist-info/RECORD +77 -0
  70. {quasarr-0.1.6.dist-info → quasarr-1.23.0.dist-info}/WHEEL +1 -1
  71. quasarr/arr/__init__.py +0 -423
  72. quasarr/captcha_solver/__init__.py +0 -284
  73. quasarr-0.1.6.dist-info/METADATA +0 -81
  74. quasarr-0.1.6.dist-info/RECORD +0 -31
  75. {quasarr-0.1.6.dist-info → quasarr-1.23.0.dist-info}/entry_points.txt +0 -0
  76. {quasarr-0.1.6.dist-info → quasarr-1.23.0.dist-info/licenses}/LICENSE +0 -0
  77. {quasarr-0.1.6.dist-info → quasarr-1.23.0.dist-info}/top_level.txt +0 -0
@@ -1,284 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- # Quasarr
3
- # Project by https://github.com/rix1337
4
-
5
- import base64
6
- import json
7
- import random
8
- import re
9
- import xml.dom.minidom
10
- from urllib.parse import urlparse
11
-
12
- import dukpy
13
- import requests
14
- from Cryptodome.Cipher import AES
15
- from bs4 import BeautifulSoup
16
-
17
-
18
- class CNL:
19
- def __init__(self, crypted_data):
20
- self.crypted_data = crypted_data
21
-
22
- def jk_eval(self, f_def):
23
- js_code = f"""
24
- {f_def}
25
- f();
26
- """
27
-
28
- result = dukpy.evaljs(js_code).strip()
29
-
30
- return result
31
-
32
- def aes_decrypt(self, data, key):
33
- try:
34
- encrypted_data = base64.b64decode(data)
35
- except Exception as e:
36
- raise ValueError("Failed to decode base64 data") from e
37
-
38
- try:
39
- key_bytes = bytes.fromhex(key)
40
- except Exception as e:
41
- raise ValueError("Failed to convert key to bytes") from e
42
-
43
- iv = key_bytes
44
- cipher = AES.new(key_bytes, AES.MODE_CBC, iv)
45
-
46
- try:
47
- decrypted_data = cipher.decrypt(encrypted_data)
48
- except ValueError as e:
49
- raise ValueError("Decryption failed") from e
50
-
51
- try:
52
- return decrypted_data.decode('utf-8').replace('\x00', '').replace('\x08', '')
53
- except UnicodeDecodeError as e:
54
- raise ValueError("Failed to decode decrypted data") from e
55
-
56
- def decrypt(self):
57
- crypted = self.crypted_data[2]
58
- jk = "function f(){ return \'" + self.crypted_data[1] + "';}"
59
- key = self.jk_eval(jk)
60
- uncrypted = self.aes_decrypt(crypted, key)
61
- urls = [result for result in uncrypted.split("\r\n") if len(result) > 0]
62
-
63
- return urls
64
-
65
-
66
- class DLC:
67
- def __init__(self, dlc_file):
68
- global user_agent
69
- self.data = dlc_file
70
- self.KEY = b"cb99b5cbc24db398"
71
- self.IV = b"9bc24cb995cb8db3"
72
- self.API_URL = "http://service.jdownloader.org/dlcrypt/service.php?srcType=dlc&destType=pylo&data="
73
-
74
- def parse_packages(self, start_node):
75
- return [
76
- (
77
- base64.b64decode(node.getAttribute("name")).decode("utf-8"),
78
- self.parse_links(node)
79
- )
80
- for node in start_node.getElementsByTagName("package")
81
- ]
82
-
83
- def parse_links(self, start_node):
84
- return [
85
- base64.b64decode(node.getElementsByTagName("url")[0].firstChild.data).decode("utf-8")
86
- for node in start_node.getElementsByTagName("file")
87
- ]
88
-
89
- def decrypt(self):
90
- if not isinstance(self.data, bytes):
91
- raise TypeError("data must be bytes.")
92
-
93
- all_urls = []
94
-
95
- try:
96
- data = self.data.strip()
97
-
98
- data += b"=" * (-len(data) % 4)
99
-
100
- dlc_key = data[-88:].decode("utf-8")
101
- dlc_data = base64.b64decode(data[:-88])
102
- dlc_content = requests.get(self.API_URL + dlc_key).content.decode("utf-8")
103
-
104
- rc = base64.b64decode(re.search(r"<rc>(.+)</rc>", dlc_content, re.S).group(1))[:16]
105
-
106
- cipher = AES.new(self.KEY, AES.MODE_CBC, self.IV)
107
- key = iv = cipher.decrypt(rc)
108
-
109
- cipher = AES.new(key, AES.MODE_CBC, iv)
110
- xml_data = base64.b64decode(cipher.decrypt(dlc_data)).decode("utf-8")
111
-
112
- root = xml.dom.minidom.parseString(xml_data).documentElement
113
- content_node = root.getElementsByTagName("content")[0]
114
-
115
- packages = self.parse_packages(content_node)
116
-
117
- for package in packages:
118
- urls = package[1]
119
- all_urls.extend(urls)
120
-
121
- except Exception as e:
122
- print("DLC Error: " + str(e))
123
- return None
124
-
125
- return all_urls
126
-
127
-
128
- def get_filecrypt_links(shared_state, token, title, url, password=None):
129
- print("Attempting to decrypt Filecrypt link: " + url)
130
- session = requests.Session()
131
-
132
- password_field = None
133
- if password:
134
- try:
135
- output = requests.get(url, headers={'User-Agent': shared_state.values["user_agent"]})
136
- soup = BeautifulSoup(output.text, 'html.parser')
137
- input_element = soup.find('input', placeholder=lambda value: value and 'password' in value.lower())
138
- password_field = input_element['name']
139
- print("Password field name identified: " + password_field)
140
- url = output.url
141
- except:
142
- print("No password field found. Skipping password entry!")
143
-
144
- if password and password_field:
145
- print("Using Password: " + password)
146
- output = session.post(url, data=password_field + "=" + password,
147
- headers={'User-Agent': shared_state.values["user_agent"],
148
- 'Content-Type': 'application/x-www-form-urlencoded'})
149
- else:
150
- output = session.get(url, headers={'User-Agent': shared_state.values["user_agent"]})
151
-
152
- url = output.url
153
- soup = BeautifulSoup(output.text, 'html.parser')
154
- if bool(soup.findAll("input", {"id": "p4assw0rt"})):
155
- print(f"Password was wrong or missing. Could not get links for {title}")
156
- return False
157
-
158
- no_captcha_present = bool(soup.find("form", {"class": "cnlform"}))
159
- if no_captcha_present:
160
- print("No CAPTCHA present. Skipping token!")
161
- else:
162
- circle_captcha = bool(soup.findAll("div", {"class": "circle_captcha"}))
163
- i = 0
164
- while circle_captcha and i < 3:
165
- print("Sending Fake solution to skip Circle-CAPTCHA...")
166
- random_x = str(random.randint(100, 200))
167
- random_y = str(random.randint(100, 200))
168
- output = session.post(url, data="buttonx.x=" + random_x + "&buttonx.y=" + random_y,
169
- headers={'User-Agent': shared_state.values["user_agent"],
170
- 'Content-Type': 'application/x-www-form-urlencoded'})
171
- url = output.url
172
- soup = BeautifulSoup(output.text, 'html.parser')
173
- circle_captcha = bool(soup.findAll("div", {"class": "circle_captcha"}))
174
-
175
- output = session.post(url, data="cap_token=" + token, headers={'User-Agent': shared_state.values["user_agent"],
176
- 'Content-Type': 'application/x-www-form-urlencoded'})
177
- url = output.url
178
- soup = BeautifulSoup(output.text, 'html.parser')
179
-
180
- solved = bool(soup.findAll("div", {"class": "container"}))
181
- if not solved:
182
- print(f"Filecrypt did did not accept the token! Could not get links for {title}")
183
- return False
184
- else:
185
- season_number = ""
186
- episode_number = ""
187
- episode_in_title = re.findall(r'.*\.s(\d{1,3})e(\d{1,3})\..*', title, re.IGNORECASE)
188
- season_in_title = re.findall(r'.*\.s(\d{1,3})\..*', title, re.IGNORECASE)
189
- if episode_in_title:
190
- try:
191
- season_number = str(int(episode_in_title[0][0]))
192
- episode_number = str(int(episode_in_title[0][1]))
193
- except:
194
- pass
195
- elif season_in_title:
196
- try:
197
- season_number = str(int(season_in_title[0]))
198
- except:
199
- pass
200
-
201
- season = ""
202
- episode = ""
203
- tv_show_selector = soup.find("div", {"class": "dlpart"})
204
- if tv_show_selector:
205
-
206
- season = "season="
207
- episode = "episode="
208
-
209
- season_selection = soup.find("div", {"id": "selbox_season"})
210
- try:
211
- if season_selection:
212
- season += str(season_number)
213
- except:
214
- pass
215
-
216
- episode_selection = soup.find("div", {"id": "selbox_episode"})
217
- try:
218
- if episode_selection:
219
- episode += str(episode_number)
220
- except:
221
- pass
222
-
223
- links = []
224
-
225
- mirrors = []
226
- mirrors_available = soup.select("a[href*=mirror]")
227
- if mirrors_available:
228
- for mirror in mirrors_available:
229
- try:
230
- mirror_query = mirror.get("href").split("?")[1]
231
- base_url = url.split("?")[0] if "mirror" in url else url
232
- mirrors.append(f"{base_url}?{mirror_query}")
233
- except IndexError:
234
- continue
235
- else:
236
- mirrors = [url]
237
-
238
- for mirror in mirrors:
239
- if not len(mirrors) == 1:
240
- output = session.get(mirror, headers={'User-Agent': shared_state.values["user_agent"]})
241
- url = output.url
242
- soup = BeautifulSoup(output.text, 'html.parser')
243
-
244
- try:
245
- crypted_payload = soup.find("form", {"class": "cnlform"}).get('onsubmit')
246
- crypted_data = re.findall(r"'(.*?)'", crypted_payload)
247
- if not title:
248
- title = crypted_data[3]
249
- crypted_data = [
250
- crypted_data[0],
251
- crypted_data[1],
252
- crypted_data[2],
253
- title
254
- ]
255
- if episode and season:
256
- domain = urlparse(url).netloc
257
- filtered_cnl_secret = soup.find("input", {"name": "hidden_cnl_id"}).attrs["value"]
258
- filtered_cnl_link = f"https://{domain}/_CNL/{filtered_cnl_secret}.html?{season}&{episode}"
259
- filtered_cnl_result = session.post(filtered_cnl_link,
260
- headers={'User-Agent': shared_state.values["user_agent"]})
261
- if filtered_cnl_result.status_code == 200:
262
- filtered_cnl_data = json.loads(filtered_cnl_result.text)
263
- if filtered_cnl_data["success"]:
264
- crypted_data = [
265
- crypted_data[0],
266
- filtered_cnl_data["data"][0],
267
- filtered_cnl_data["data"][1],
268
- title
269
- ]
270
- links.extend(CNL(crypted_data).decrypt())
271
- except:
272
- print("Click'n'Load not found! Falling back to DLC...")
273
- crypted_payload = soup.find("button", {"class": "dlcdownload"}).get("onclick")
274
- crypted_data = re.findall(r"'(.*?)'", crypted_payload)
275
- dlc_secret = crypted_data[0]
276
- domain = urlparse(url).netloc
277
- if episode and season:
278
- dlc_link = f"https://{domain}/DLC/{dlc_secret}.dlc?{episode}&{season}"
279
- else:
280
- dlc_link = f"https://{domain}/DLC/{dlc_secret}.dlc"
281
- dlc_file = session.get(dlc_link, headers={'User-Agent': shared_state.values["user_agent"]}).content
282
- links.extend(DLC(dlc_file).decrypt())
283
-
284
- return links
@@ -1,81 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: quasarr
3
- Version: 0.1.6
4
- Summary: Full template for python web projects with Docker, GitHub Actions, PyPI, and more.
5
- Home-page: https://github.com/rix1337/Quasarr
6
- Author: rix1337
7
- Author-email:
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Description-Content-Type: text/markdown
12
- License-File: LICENSE
13
- Requires-Dist: beautifulsoup4==4.12.3
14
- Requires-Dist: bottle==0.12.25
15
- Requires-Dist: dukpy==0.4.0
16
- Requires-Dist: pycryptodomex==3.20.0
17
- Requires-Dist: requests==2.32.3
18
-
19
- # Quasarr
20
-
21
- <img src="https://raw.githubusercontent.com/rix1337/Quasarr/main/Quasarr.png" data-canonical-src="https://raw.githubusercontent.com/rix1337/Quasarr/main/Quasarr.png" width="64" height="64" />
22
-
23
- Quasarr is a Bridge to use JDownloader in Radarr and (later also) Sonarr.
24
-
25
- [![PyPI version](https://badge.fury.io/py/quasarr.svg)](https://badge.fury.io/py/quasarr)
26
- [![GitHub Sponsorship](https://img.shields.io/badge/support-me-red.svg)](https://github.com/users/rix1337/sponsorship)
27
-
28
- Quasarr poses as a Newznab Indexer and a SABnzbd client.
29
- It will thus never work in parallel with a real NZB indexer and download client set up.
30
- Torrents are unaffected.
31
-
32
- Quasarr includes a solution to quickly and easily decrypt protected links.
33
- Just follow the link from the console output (or discord notification) and solve the CAPTCHA.
34
- Quasarr will confidently handle the rest.
35
-
36
- **Warning: this project is still in the proof-of-concept stage.
37
- It is only tested with Radarr and the three currently supported hostnames.**
38
-
39
- # Instructions
40
-
41
- * Follow instructions to set up at least one hostname for Quasarr
42
- * Provide your [MyJDownloader credentials](https://my.jdownloader.org)
43
- * Set up Quasarr's URL as 'Newznab Indexer' and 'SABnzbd Download Client' in Sonarr/Radarr.
44
- * Leave settings at default
45
- * Use this API key: `quasarr`
46
- * As with other download clients, you must ensure the download path used by JDownloader is accessible to *arr.
47
-
48
- # Setup
49
-
50
- `pip install quasarr`
51
-
52
- * Requires Python 3.12 or later
53
-
54
- # Run
55
-
56
- ```
57
- quasarr
58
- --port=8080
59
- --discord=https://discord.com/api/webhooks/1234567890/ABCDEFGHIJKLMN
60
- --external_address=http://foo.bar/
61
- ```
62
-
63
- * `--discord` must be a valid Discord Webhook URL and is optional.
64
- * `--external_address` is used in Discord notifications and is optional.
65
-
66
- # Docker
67
-
68
- ```
69
- docker run -d \
70
- --name="Quasarr" \
71
- -p port:8080 \
72
- -v /path/to/config/:/config:rw \
73
- -e 'INTERNAL_ADDRESS'='http://192.168.0.1:8080' \
74
- -e 'EXTERNAL_ADDRESS'='http://foo.bar/' \
75
- -e 'DISCORD'='https://discord.com/api/webhooks/1234567890/ABCDEFGHIJKLMN' \
76
- rix1337/docker-quasarr:latest
77
- ```
78
-
79
- * `INTERNAL_ADDRESS` is required so Radarr/Sonarr can reach Quasarr. **Must** include port!
80
- * `EXTERNAL_ADDRESS` is optional and used in Discord notifications.
81
- * `DISCORD` is optional and must be a valid Discord Webhook URL.
@@ -1,31 +0,0 @@
1
- quasarr/__init__.py,sha256=dA9ChjAr-rCMGruOOMw2SSMkhE45H0bTbxaNBf3lqy8,7153
2
- quasarr/arr/__init__.py,sha256=0JdXIXT6rm623feYE9CwKyPo-vbPlXeOkYpupSsHBzY,17708
3
- quasarr/captcha_solver/__init__.py,sha256=P0DTvDXPueQs_eAhQb9cSZnaZLfP_-wjopikln9-etM,10783
4
- quasarr/downloads/__init__.py,sha256=42aOQmGXam38ulUPIlxZLA2nVQ1h6PPTJ2z5Ykf-CXk,10336
5
- quasarr/downloads/sources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- quasarr/downloads/sources/dw.py,sha256=GE1x9lDME_Uaxrjef9mFj-DKJd3uS2ws1ttNbtvLqzc,2057
7
- quasarr/downloads/sources/nx.py,sha256=Tcb9fi-r36Bdh7Ky1cMujRLJAOQ9VGzNMjRhvupuCB4,4834
8
- quasarr/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- quasarr/providers/html_templates.py,sha256=wnce3vGoZq0lZpxE5AEtjTL8t6A007OZCQL7D46XO6Q,4587
10
- quasarr/providers/imdb_metadata.py,sha256=AKn9ob22LpbAQAVVO7Uoxk9bwrqAgmEwEug8yOGxBi8,970
11
- quasarr/providers/myjd_api.py,sha256=yDnaL3MTrstTPBwQOrki9in7BsFuATu8VnJ-XiSOrbw,28552
12
- quasarr/providers/notifications.py,sha256=zC87QuIewHVClRH9d3pVn8XJfqFO6eSM4iwGliiDVlU,1049
13
- quasarr/providers/obfuscated.py,sha256=XfiEblJizqixUoA4MIsillr5Nh1dwFqCgLvBQWM7Lwo,193865
14
- quasarr/providers/shared_state.py,sha256=rNCvLFF9qP0UAYSCImGPI6tcCUanKJFuQYECLJQdxlY,7924
15
- quasarr/providers/version.py,sha256=wfbOt1e-0KxktMEluyI9POzZqqfpjU6J2RNS7Zh0DEI,2186
16
- quasarr/providers/web_server.py,sha256=wc2enK8pWUocSgoA_5RXx8pCekBwMMf6z3fjyG71Osc,1389
17
- quasarr/search/__init__.py,sha256=ce9YKpOQMotRdCegNF-w_3W-cQdhnFIbyWXWAwMzBtI,1110
18
- quasarr/search/sources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- quasarr/search/sources/dw.py,sha256=4Uyp8ngU-41EnQqKRGcNy--6hWvdIeQqpayLsopVwHE,6372
20
- quasarr/search/sources/fx.py,sha256=AbfgJHAhoFvxqK45jckDbAoBjexlL8eqsRRxgQwo4JU,6129
21
- quasarr/search/sources/nx.py,sha256=da3_cPD_yKn5fa0lGiFeWTiVAbsj92qhEeE4jbRqul8,4329
22
- quasarr/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
- quasarr/storage/config.py,sha256=JneX7stV8dgdEuMAGHo9Es4CRhheuTbOWFlx69scgAA,5493
24
- quasarr/storage/setup.py,sha256=qr_DKqvqv_WoAv6DGQllAxFGeqVGzWFXaoLg2JF14Ss,11108
25
- quasarr/storage/sqlite_database.py,sha256=u-tU2dJV_vQQfGUlxKpl_HSSVFvHDYF48Yq9aAkNCKo,3436
26
- quasarr-0.1.6.dist-info/LICENSE,sha256=QQFCAfDgt7lSA8oSWDHIZ9aTjFbZaBJdjnGOHkuhK7k,1060
27
- quasarr-0.1.6.dist-info/METADATA,sha256=Tt-QWjWDh47454pYeAQEC6x0xWWqYxYzd857mAe4oLw,2927
28
- quasarr-0.1.6.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
29
- quasarr-0.1.6.dist-info/entry_points.txt,sha256=gXi8mUKsIqKVvn-bOc8E5f04sK_KoMCC-ty6b2Hf-jc,40
30
- quasarr-0.1.6.dist-info/top_level.txt,sha256=dipJdaRda5ruTZkoGfZU60bY4l9dtPlmOWwxK_oGSF0,8
31
- quasarr-0.1.6.dist-info/RECORD,,