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

@@ -2,7 +2,6 @@
2
2
  # Quasarr
3
3
  # Project by https://github.com/rix1337
4
4
 
5
- import time
6
5
  import traceback
7
6
  import xml.sax.saxutils as sax_utils
8
7
  from base64 import urlsafe_b64decode
@@ -304,38 +303,29 @@ def setup_arr_routes(app):
304
303
  </caps>"""
305
304
  elif mode in ["movie", "tvsearch", "book", "search"]:
306
305
  releases = []
307
- cache_key = None
308
306
 
309
307
  try:
310
- offset = int(getattr(request.query, "offset", 0))
308
+ offset = int(getattr(request.query, "offset", 0) or 0)
311
309
  except (AttributeError, ValueError) as e:
312
310
  debug(f"Error parsing offset parameter: {e}")
313
311
  offset = 0
314
312
 
315
313
  try:
316
- limit = int(getattr(request.query, "limit", 100))
314
+ limit = int(getattr(request.query, "limit", 9999) or 9999)
317
315
  except (AttributeError, ValueError) as e:
318
316
  debug(f"Error parsing limit parameter: {e}")
319
- limit = 100
317
+ limit = 1000
320
318
 
321
319
  if mode == "movie":
322
320
  # supported params: imdbid
323
321
  imdb_id = getattr(request.query, "imdbid", "")
324
-
325
- if imdb_id != "":
326
- cache_key = f"{request_from}::{imdb_id}::${mirror}"
327
-
328
- if result := results_cache.get(cache_key, offset, limit):
329
- debug(
330
- f"Returning offset {offset}, limit {limit} for {cache_key}"
331
- )
332
- return result
333
-
334
322
  releases = get_search_results(
335
323
  shared_state,
336
324
  request_from,
337
325
  imdb_id=imdb_id,
338
326
  mirror=mirror,
327
+ offset=offset,
328
+ limit=limit,
339
329
  )
340
330
 
341
331
  elif mode == "tvsearch":
@@ -343,16 +333,6 @@ def setup_arr_routes(app):
343
333
  imdb_id = getattr(request.query, "imdbid", "")
344
334
  season = getattr(request.query, "season", None)
345
335
  episode = getattr(request.query, "ep", None)
346
-
347
- if imdb_id != "":
348
- cache_key = f"{request_from}::{imdb_id}::${mirror}::{season}::{episode}"
349
-
350
- if result := results_cache.get(cache_key, offset, limit):
351
- debug(
352
- f"Returning offset {offset}, limit {limit} for {cache_key}"
353
- )
354
- return result
355
-
356
336
  releases = get_search_results(
357
337
  shared_state,
358
338
  request_from,
@@ -360,7 +340,10 @@ def setup_arr_routes(app):
360
340
  mirror=mirror,
361
341
  season=season,
362
342
  episode=episode,
343
+ offset=offset,
344
+ limit=limit,
363
345
  )
346
+
364
347
  elif mode == "book":
365
348
  author = getattr(request.query, "author", "")
366
349
  title = getattr(request.query, "title", "")
@@ -370,6 +353,8 @@ def setup_arr_routes(app):
370
353
  request_from,
371
354
  search_phrase=search_phrase,
372
355
  mirror=mirror,
356
+ offset=offset,
357
+ limit=limit,
373
358
  )
374
359
 
375
360
  elif mode == "search":
@@ -380,6 +365,8 @@ def setup_arr_routes(app):
380
365
  request_from,
381
366
  search_phrase=search_phrase,
382
367
  mirror=mirror,
368
+ offset=offset,
369
+ limit=limit,
383
370
  )
384
371
  else:
385
372
  # sonarr expects this but we will not support non-imdbid searches
@@ -387,9 +374,8 @@ def setup_arr_routes(app):
387
374
  f"Ignoring search request from {request_from} - only imdbid searches are supported"
388
375
  )
389
376
 
377
+ # XML Generation (releases are already sliced)
390
378
  items = ""
391
- items_amount = 0
392
- items_processed = 0
393
379
  for release in releases:
394
380
  release = release.get("details", {})
395
381
 
@@ -412,43 +398,6 @@ def setup_arr_routes(app):
412
398
  <pubDate>{pub_date}</pubDate>
413
399
  <enclosure url="{release.get("link", "")}" length="{release.get("size", 0)}" type="application/x-nzb" />
414
400
  </item>'''
415
- items_amount += 1
416
-
417
- if cache_key and items_amount == limit:
418
- items_processed += items_amount
419
- debug(
420
- f"Processed {items_processed}/{len(releases)} releases"
421
- )
422
- results_cache.set(
423
- cache_key,
424
- f"""<?xml version="1.0" encoding="UTF-8"?>
425
- <rss>
426
- <channel>
427
- {items}
428
- </channel>
429
- </rss>""",
430
- items_processed - items_amount,
431
- limit,
432
- )
433
- items = ""
434
- items_amount = 0
435
-
436
- if cache_key and items_amount > 0:
437
- items_processed += items_amount
438
- debug(f"Processed {items_processed}/{len(releases)} releases")
439
- results_cache.set(
440
- cache_key,
441
- f"""<?xml version="1.0" encoding="UTF-8"?>
442
- <rss>
443
- <channel>
444
- {items}
445
- </channel>
446
- </rss>""",
447
- items_processed - items_amount,
448
- limit,
449
- )
450
- items = ""
451
- items_amount = 0
452
401
 
453
402
  requires_placeholder_item = not getattr(
454
403
  request.query, "imdbid", ""
@@ -464,13 +413,6 @@ def setup_arr_routes(app):
464
413
  <enclosure url="https://github.com/rix1337/Quasarr" length="0" type="application/x-nzb" />
465
414
  </item>"""
466
415
 
467
- if cache_key:
468
- if result := results_cache.get(cache_key, offset, limit):
469
- debug(
470
- f"Returning offset {offset}, limit {limit} for {cache_key}"
471
- )
472
- return result
473
-
474
416
  return f"""<?xml version="1.0" encoding="UTF-8"?>
475
417
  <rss>
476
418
  <channel>
@@ -492,39 +434,3 @@ def setup_arr_routes(app):
492
434
 
493
435
  info(f"[ERROR] Unknown general request: {dict(request.query)}")
494
436
  return {"error": True}
495
-
496
-
497
- class ResultsCache:
498
- def __init__(self):
499
- self.last_cleaned = time.time()
500
- self.cache = {}
501
-
502
- def clean(self, now):
503
- if now - self.last_cleaned < 60:
504
- return
505
-
506
- keys_to_delete = [
507
- key for key, (_, expiry) in self.cache.items() if now >= expiry
508
- ]
509
-
510
- for key in keys_to_delete:
511
- del self.cache[key]
512
-
513
- self.last_cleaned = now
514
-
515
- def get(self, key, offset, limit):
516
- key = key + f"::{offset}::{limit}"
517
- value, expiry = self.cache.get(key, (None, 0))
518
- if time.time() < expiry:
519
- return value
520
-
521
- return None
522
-
523
- def set(self, key, value, offset, limit, ttl=300):
524
- now = time.time()
525
- key = key + f"::{offset}::{limit}"
526
- self.cache[key] = (value, now + ttl)
527
- self.clean(now)
528
-
529
-
530
- results_cache = ResultsCache()
@@ -254,7 +254,7 @@ def solve_captcha(
254
254
  method="POST",
255
255
  target_url=captcha_base,
256
256
  post_data={"cID": 0, "pC": identified_captcha_image, "rT": 2},
257
- timeout=60,
257
+ timeout=30,
258
258
  )
259
259
 
260
260
  return {"response": result["text"], "captcha_id": identified_captcha_image}
@@ -169,7 +169,7 @@ class FlareSolverrResponse:
169
169
  raise requests.HTTPError(f"{self.status_code} Error at {self.url}")
170
170
 
171
171
 
172
- def flaresolverr_get(shared_state, url, timeout=60, session_id=None):
172
+ def flaresolverr_get(shared_state, url, timeout=30, session_id=None):
173
173
  """
174
174
  Core function for performing a GET request via FlareSolverr only.
175
175
  Used internally by FlareSolverrSession.get()
@@ -236,7 +236,7 @@ class IMDbFlareSolverr:
236
236
  flaresolverr_url,
237
237
  json=post_data,
238
238
  headers={"Content-Type": "application/json"},
239
- timeout=60,
239
+ timeout=30,
240
240
  )
241
241
  if response.status_code == 200:
242
242
  json_response = response.json()
@@ -5,7 +5,7 @@
5
5
  import re
6
6
  import sys
7
7
 
8
- __version__ = "2.7.0"
8
+ __version__ = "2.7.1"
9
9
 
10
10
 
11
11
  def get_version():
@@ -35,6 +35,8 @@ def get_search_results(
35
35
  mirror=None,
36
36
  season="",
37
37
  episode="",
38
+ offset=0,
39
+ limit=1000,
38
40
  ):
39
41
  if imdb_id and not imdb_id.startswith("tt"):
40
42
  imdb_id = f"tt{imdb_id}"
@@ -150,25 +152,41 @@ def get_search_results(
150
152
 
151
153
  # Clean description for Console UI
152
154
  if imdb_id:
153
- desc_text = f"Searching for IMDb-ID {imdb_id}"
154
155
  stype = f"IMDb-ID <b>{imdb_id}</b>"
155
156
  elif search_phrase:
156
- desc_text = f"Searching for '{search_phrase}'"
157
157
  stype = f"Search-Phrase <b>{search_phrase}</b>"
158
158
  else:
159
- desc_text = "Running Feed Search"
160
159
  stype = "<b>feed</b> search"
161
160
 
162
161
  debug(f"Starting <g>{len(search_executor.searches)}</g> searches for {stype}...")
163
162
 
164
- results = search_executor.run_all(desc_text)
163
+ # Unpack the new return values (all_cached, min_ttl)
164
+ results, status_bar, all_cached, min_ttl = search_executor.run_all()
165
165
 
166
166
  elapsed_time = time.time() - start_time
167
+
168
+ # Calculate pagination for logging and return
169
+ total_count = len(results)
170
+
171
+ # Slicing
172
+ sliced_results = results[offset : offset + limit]
173
+
174
+ # Formatting for log (1-based index for humans)
175
+ log_start = min(offset + 1, total_count) if total_count > 0 else 0
176
+ log_end = min(offset + limit, total_count)
177
+
178
+ # Logic to switch between "Time taken" and "from cache"
179
+ if all_cached:
180
+ time_info = f"from cache ({int(min_ttl)}s left)"
181
+ else:
182
+ time_info = f"Time taken: {elapsed_time:.2f} seconds"
183
+
167
184
  info(
168
- f"Providing <g>{len(results)} releases</g> to <d>{request_from}</d> for {stype}. <blue>Time taken: {elapsed_time:.2f} seconds</blue>"
185
+ f"Providing releases <g>{log_start}-{log_end}</g> of <g>{total_count}</g> to <d>{request_from}</d> "
186
+ f"for {stype}{status_bar} <blue>{time_info}</blue>"
169
187
  )
170
188
 
171
- return results
189
+ return sliced_results
172
190
 
173
191
 
174
192
  class SearchExecutor:
@@ -182,63 +200,62 @@ class SearchExecutor:
182
200
  key = hash((func.__name__, key_args, frozenset(kwargs.items())))
183
201
  self.searches.append((key, lambda: func(*args, **kwargs), use_cache))
184
202
 
185
- def run_all(self, description):
203
+ def run_all(self):
186
204
  results = []
187
205
  future_to_meta = {}
188
206
 
207
+ # Track cache state
208
+ all_cached = len(self.searches) > 0
209
+ min_ttl = float("inf")
210
+ bar_str = "" # Initialize to prevent UnboundLocalError on full cache
211
+
189
212
  with ThreadPoolExecutor() as executor:
190
213
  current_index = 0
191
214
  pending_futures = []
192
- cache_used = False
193
215
 
194
216
  for key, func, use_cache in self.searches:
195
217
  cached_result = None
218
+ exp = 0
219
+
196
220
  if use_cache:
197
- cached_result = search_cache.get(key)
221
+ # Get both result and expiry
222
+ cached_result, exp = search_cache.get(key)
198
223
 
199
224
  if cached_result is not None:
200
225
  debug(f"Using cached result for {key}")
201
- cache_used = True
202
226
  results.extend(cached_result)
227
+
228
+ # Calculate TTL for this cached item
229
+ ttl = exp - time.time()
230
+ if ttl < min_ttl:
231
+ min_ttl = ttl
203
232
  else:
233
+ all_cached = False
204
234
  future = executor.submit(func)
205
235
  cache_key = key if use_cache else None
206
236
  future_to_meta[future] = (current_index, cache_key)
207
237
  pending_futures.append(future)
208
238
  current_index += 1
209
239
 
210
- # Prepare list to track status of each provider
211
- # Icons will be filled in as threads complete
212
- total_active = len(pending_futures)
213
- icons = ["▪️"] * total_active
214
-
215
- for future in as_completed(pending_futures):
216
- index, cache_key = future_to_meta[future]
217
- try:
218
- res = future.result()
219
- if res and len(res) > 0:
220
- status = "✅"
221
- else:
222
- status = "⚪"
223
-
224
- icons[index] = status
225
-
226
- results.extend(res)
227
- if cache_key:
228
- search_cache.set(cache_key, res)
229
- except Exception as e:
230
- icons[index] = "❌"
231
- info(f"Search error: {e}")
240
+ if pending_futures:
241
+ icons = ["▪️"] * len(pending_futures)
232
242
 
233
- # Log the final status summary if any searches were performed
234
- if total_active > 0:
235
- bar_str = "".join(icons)
236
- info(f"{description} [{bar_str}]")
243
+ for future in as_completed(pending_futures):
244
+ index, cache_key = future_to_meta[future]
245
+ try:
246
+ res = future.result()
247
+ status = "✅" if res and len(res) > 0 else "⚪"
248
+ icons[index] = status
249
+ results.extend(res)
250
+ if cache_key:
251
+ search_cache.set(cache_key, res)
252
+ except Exception as e:
253
+ icons[index] = "❌"
254
+ info(f"Search error: {e}")
237
255
 
238
- if cache_used:
239
- info("Presenting cached results for some items.")
256
+ bar_str = f" [{''.join(icons)}]"
240
257
 
241
- return results
258
+ return results, bar_str, all_cached, min_ttl
242
259
 
243
260
 
244
261
  class SearchCache:
@@ -256,7 +273,8 @@ class SearchCache:
256
273
 
257
274
  def get(self, key):
258
275
  val, exp = self.cache.get(key, (None, 0))
259
- return val if time.time() < exp else None
276
+ # Return tuple (value, expiry) if valid, else (None, 0)
277
+ return (val, exp) if time.time() < exp else (None, 0)
260
278
 
261
279
  def set(self, key, value, ttl=300):
262
280
  now = time.time()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quasarr
3
- Version: 2.7.0
3
+ Version: 2.7.1
4
4
  Summary: Quasarr connects JDownloader with Radarr, Sonarr and LazyLibrarian. It also decrypts links protected by CAPTCHAs.
5
5
  Author-email: rix1337 <rix1337@users.noreply.github.com>
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  quasarr/__init__.py,sha256=nh1MU1Evh0G1Pm657qtMMWWX4NSHm6PpETqGFtK2QLE,17197
2
2
  quasarr/api/__init__.py,sha256=2CXR0JEjC3zooTB8Bk-z_aZgVM2cPE9ijfO5yJAE9CE,20142
3
- quasarr/api/arr/__init__.py,sha256=1NcjcfNOjzTQCUDedTGJluK0xU-6krh0T8QSGu7eoeU,22283
3
+ quasarr/api/arr/__init__.py,sha256=0g-2yJC73xfaPz7dWplWI5_ggGqw5kYwGm38wOYjGXM,18844
4
4
  quasarr/api/captcha/__init__.py,sha256=9wBmdYKn0DImiFatHe4y2icV57d4710vfXFncvPKki8,78030
5
5
  quasarr/api/config/__init__.py,sha256=FJZHALhL6NExonhCk53vOYnM1ICkmbTRue5UMCy5Yzg,8813
6
6
  quasarr/api/jdownloader/__init__.py,sha256=SixcV-sgMAunjAT5LawASb1qSuOOokorQo2F7cQ3jZ4,9427
@@ -9,7 +9,7 @@ quasarr/api/sponsors_helper/__init__.py,sha256=QAFXK_JTtAnstRAlieCbbCsoTwIcBu7ZX
9
9
  quasarr/api/statistics/__init__.py,sha256=rJz6S4jSnpFDWtjU7O-2jECUEqlueOHOEfRUjSb3cMY,7943
10
10
  quasarr/downloads/__init__.py,sha256=571QRloySskkg-JRi7JjyrKyfZIRnd9WgotbOOZ9k0s,17364
11
11
  quasarr/downloads/linkcrypters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- quasarr/downloads/linkcrypters/al.py,sha256=sNdEl1gogVn2xerd5fSOkAOgEF2sslQr81g34Jhu5So,8996
12
+ quasarr/downloads/linkcrypters/al.py,sha256=_a5kbSLtwOf6y6gN_8_ZpJxvzWJa3lLQTqcTvk_Cjgo,8996
13
13
  quasarr/downloads/linkcrypters/filecrypt.py,sha256=yMkDM_GVOd3Bl9lgPkL1BDDuYOpMgxnVwlqRtskZ0Xo,17729
14
14
  quasarr/downloads/linkcrypters/hide.py,sha256=t9p_Hb5taJDuRAPaWZw7T1GTcLVgd8keD9LlZJ1-Gsg,6266
15
15
  quasarr/downloads/packages/__init__.py,sha256=MdKug4D-ex6sJBJuM0mi3_IjXX7AjV5ryUonOs2aupc,34887
@@ -33,11 +33,11 @@ quasarr/downloads/sources/wd.py,sha256=Xh5cvsGGBfM7iYWmBktmRHaWX6vZBRerBy-L8ycJW
33
33
  quasarr/downloads/sources/wx.py,sha256=b3_--zovX4BrknzGEmdh_QQw72dtyPfIrI_me_KyVjo,6772
34
34
  quasarr/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  quasarr/providers/auth.py,sha256=qDdXr28SJ078Q8AVZ_50Z1FwVhfuinOtRl6JHF4RgnM,10412
36
- quasarr/providers/cloudflare.py,sha256=oLtwQ_UElLIWJ-c-qH2c9NUjsZtlmzYpXlAWhQcE1FM,9076
36
+ quasarr/providers/cloudflare.py,sha256=gwcznTQXZ0Xc0G5kLy42Ix90_MicAdN55BSLjrw9LyE,9076
37
37
  quasarr/providers/hostname_issues.py,sha256=SpnZAxOLejSXJGFnYkCrRzR8D0IQsTMtylM-O0h21Z0,1462
38
38
  quasarr/providers/html_images.py,sha256=xmxfNwqAqQimVaOq7IelkxlBdcRpPZZLGli_MJDOacI,19755
39
39
  quasarr/providers/html_templates.py,sha256=e5b66N47y5Uq7Ikwcm6kOWiyXZ7Bz4gqg2DcajIBGgE,16360
40
- quasarr/providers/imdb_metadata.py,sha256=JP9YQ7jU1H2-dify6q-qE7gpbJ9ospY4evNLQaa4FDY,21946
40
+ quasarr/providers/imdb_metadata.py,sha256=IOsmk3-e8b-CUJmbfn-dwFZbP33VOAjt095ji8tyM7A,21946
41
41
  quasarr/providers/jd_cache.py,sha256=RZsjw9X8wouVH__T2EG7w18CLUrxKh73BHnk_rpHdgE,13534
42
42
  quasarr/providers/log.py,sha256=E5g5Angdn9iflW_Z0PNbAmhVK_ZC6IwLnOaJ_mVarqM,7018
43
43
  quasarr/providers/myjd_api.py,sha256=hCWVU5IAl7QQV_icMF0B91y7CLLM_j2xfyByTP7an0g,35206
@@ -46,14 +46,14 @@ quasarr/providers/obfuscated.py,sha256=IAN0-5m6UblLjaFdPhRy75ryqDMF0nlbkClq5-n1b
46
46
  quasarr/providers/shared_state.py,sha256=alUxC0KJQEGsERcHUSn-nSY53PcUjmgHk5R04kj_hOs,33247
47
47
  quasarr/providers/statistics.py,sha256=1X_Aa7TE3W7ovwkemVMsgIx55Jw3eYMiyUxuCUDgO5s,8666
48
48
  quasarr/providers/utils.py,sha256=FR0tGwao1ytYtWbmUocaHwt29pHKqskKMH2YE2bgSFI,12481
49
- quasarr/providers/version.py,sha256=vYbQKxf4PPBZ1AradCg9Rn9q7TQrQLaNkfDHTi2Cs_k,4424
49
+ quasarr/providers/version.py,sha256=rlliPV3fGhAn-87DmPCAlY0Q74O2bvpzbG_pIl3ikSE,4424
50
50
  quasarr/providers/web_server.py,sha256=tHkMxhV6eaHC8cWsEpbUqD_U29IFE24VsU6tjk-xCEM,1765
51
51
  quasarr/providers/sessions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
52
  quasarr/providers/sessions/al.py,sha256=AOWl1v-wcwxUeo1bRizd7zAzbUludsFbgCGICHCVZFQ,13270
53
53
  quasarr/providers/sessions/dd.py,sha256=K503Ny-3zWolzpGVane4ag5Gu1yzPv49eni0I8Hw4v8,3353
54
54
  quasarr/providers/sessions/dl.py,sha256=PnyuX_h4gQIk81w0NKYCFxpg-Il0gi72BQxbdLED1ds,5820
55
55
  quasarr/providers/sessions/nx.py,sha256=BkEMEVAiJQBlsGQYw4ZTSyys8Ua-WToAmqL0Il41OAg,3491
56
- quasarr/search/__init__.py,sha256=ggQG8NreFQ4IU6SAigh3YXCScUcQbcQjf3-nyfICOoQ,8162
56
+ quasarr/search/__init__.py,sha256=O3rj40brKvimHRA2YAlk9aBL9lh3NvKDZ5c_MSJc2pQ,8790
57
57
  quasarr/search/sources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
58
  quasarr/search/sources/al.py,sha256=2RsaIfA4o3uMZuJZwPh1tETdLkNeAQ6-ymFVBL706Po,18206
59
59
  quasarr/search/sources/by.py,sha256=cgy39DN0LIMqO9Yfs6mx7Uio9unuEk4Our562BKQWz0,8971
@@ -77,8 +77,8 @@ quasarr/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
77
  quasarr/storage/config.py,sha256=sjpfVq_Bxkj9gVwCXB_MAreB9ezf-YEJQKxQmQhUv9s,6540
78
78
  quasarr/storage/setup.py,sha256=zb83kvQfxMFHxC7EvWWaVTy0MtG7iEjMRyfY4hdcbOk,61520
79
79
  quasarr/storage/sqlite_database.py,sha256=tmHUotMWIwtyH-g244WvcGhMQMMjGokncv7JpFSi8NM,3639
80
- quasarr-2.7.0.dist-info/METADATA,sha256=NtSLKAF7rVh-4Y_5M1ibnP8fgG7EOS8jdy1h_qZsjdA,14822
81
- quasarr-2.7.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
82
- quasarr-2.7.0.dist-info/entry_points.txt,sha256=gXi8mUKsIqKVvn-bOc8E5f04sK_KoMCC-ty6b2Hf-jc,40
83
- quasarr-2.7.0.dist-info/licenses/LICENSE,sha256=QQFCAfDgt7lSA8oSWDHIZ9aTjFbZaBJdjnGOHkuhK7k,1060
84
- quasarr-2.7.0.dist-info/RECORD,,
80
+ quasarr-2.7.1.dist-info/METADATA,sha256=ZDpw6B-2AFHJZo-jop8bZa9CbFmaRAjTrKjpSDmp-bE,14822
81
+ quasarr-2.7.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
82
+ quasarr-2.7.1.dist-info/entry_points.txt,sha256=gXi8mUKsIqKVvn-bOc8E5f04sK_KoMCC-ty6b2Hf-jc,40
83
+ quasarr-2.7.1.dist-info/licenses/LICENSE,sha256=QQFCAfDgt7lSA8oSWDHIZ9aTjFbZaBJdjnGOHkuhK7k,1060
84
+ quasarr-2.7.1.dist-info/RECORD,,