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

@@ -9,11 +9,17 @@ from quasarr.providers.html_templates import render_button, render_centered_html
9
9
 
10
10
 
11
11
  def _get_category_emoji(cat):
12
- return {'movies': '🎬', 'tv': '📺', 'docs': '📄', 'not_quasarr': '📦'}.get(cat, '📦')
12
+ return {'movies': '🎬', 'tv': '📺', 'docs': '📄', 'not_quasarr': ''}.get(cat, '')
13
13
 
14
14
 
15
15
  def _format_size(mb=None, bytes_val=None):
16
16
  if bytes_val is not None:
17
+ if bytes_val == 0:
18
+ return "? MB"
19
+ if bytes_val < 1024:
20
+ return f"{bytes_val} B"
21
+ if bytes_val < 1024 * 1024:
22
+ return f"{bytes_val // 1024} KB"
17
23
  mb = bytes_val / (1024 * 1024)
18
24
  if mb is None or mb == 0:
19
25
  return "? MB"
@@ -30,6 +36,7 @@ def _render_queue_item(item):
30
36
  filename = item.get('filename', 'Unknown')
31
37
  percentage = item.get('percentage', 0)
32
38
  timeleft = item.get('timeleft', '??:??:??')
39
+ bytes_val = item.get('bytes', 0)
33
40
  mb = item.get('mb', 0)
34
41
  cat = item.get('cat', 'not_quasarr')
35
42
  is_archive = item.get('is_archive', False)
@@ -51,9 +58,9 @@ def _render_queue_item(item):
51
58
  for prefix in ['[Downloading] ', '[Extracting] ', '[Paused] ', '[Linkgrabber] ', '[CAPTCHA not solved!] ']:
52
59
  display_name = display_name.replace(prefix, '')
53
60
 
54
- archive_badge = '<span class="badge archive">📁 ARCHIVE</span>' if is_archive else ''
61
+ archive_badge = '<span class="badge archive">📁</span>' if is_archive else ''
55
62
  cat_emoji = _get_category_emoji(cat)
56
- size_str = _format_size(mb=mb)
63
+ size_str = _format_size(bytes_val=bytes_val) if bytes_val else _format_size(mb=mb)
57
64
 
58
65
  # Progress bar - show "waiting..." for 0%
59
66
  if percentage == 0:
@@ -84,7 +91,6 @@ def _render_queue_item(item):
84
91
  <div class="package-header">
85
92
  <span class="status-emoji">{status_emoji}</span>
86
93
  <span class="package-name">{display_name}</span>
87
- {archive_badge}
88
94
  </div>
89
95
  <div class="package-progress">
90
96
  {progress_html}
@@ -93,7 +99,8 @@ def _render_queue_item(item):
93
99
  <div class="package-details">
94
100
  <span>⏱️ {timeleft}</span>
95
101
  <span>💾 {size_str}</span>
96
- <span>{cat_emoji} {cat}</span>
102
+ <span>{cat_emoji}</span>
103
+ {archive_badge}
97
104
  </div>
98
105
  {actions}
99
106
  </div>
@@ -119,11 +126,11 @@ def _render_history_item(item):
119
126
  archive_badge = ''
120
127
  if is_archive:
121
128
  if extraction_status == 'SUCCESSFUL':
122
- archive_badge = '<span class="badge extracted">✅ EXTRACTED</span>'
129
+ archive_badge = '<span class="badge extracted">✅</span>'
123
130
  elif extraction_status == 'RUNNING':
124
- archive_badge = '<span class="badge pending">⏳ EXTRACTING</span>'
131
+ archive_badge = '<span class="badge pending">⏳</span>'
125
132
  else:
126
- archive_badge = '<span class="badge archive">📁 ARCHIVE</span>'
133
+ archive_badge = '<span class="badge archive">📁</span>'
127
134
 
128
135
  status_emoji = '❌' if is_error else '✅'
129
136
  error_html = f'<div class="package-error">⚠️ {fail_message}</div>' if fail_message else ''
@@ -143,11 +150,11 @@ def _render_history_item(item):
143
150
  <div class="package-header">
144
151
  <span class="status-emoji">{status_emoji}</span>
145
152
  <span class="package-name">{name}</span>
146
- {archive_badge}
147
153
  </div>
148
154
  <div class="package-details">
149
155
  <span>💾 {size_str}</span>
150
- <span>{cat_emoji} {category}</span>
156
+ <span>{cat_emoji}</span>
157
+ {archive_badge}
151
158
  </div>
152
159
  {error_html}
153
160
  {actions}
@@ -167,7 +174,12 @@ def _render_packages_content():
167
174
  quasarr_history = [p for p in history if p.get('category') != 'not_quasarr']
168
175
  other_history = [p for p in history if p.get('category') == 'not_quasarr']
169
176
 
170
- # Build queue section
177
+ # Check if there's anything at all
178
+ has_quasarr_content = quasarr_queue or quasarr_history
179
+ has_other_content = other_queue or other_history
180
+ has_any_content = has_quasarr_content or has_other_content
181
+
182
+ # Build queue section (only if has items)
171
183
  queue_html = ''
172
184
  if quasarr_queue:
173
185
  queue_items = ''.join(_render_queue_item(item) for item in quasarr_queue)
@@ -177,10 +189,8 @@ def _render_packages_content():
177
189
  <div class="packages-list">{queue_items}</div>
178
190
  </div>
179
191
  '''
180
- else:
181
- queue_html = '<div class="section"><p class="empty-message">No active downloads</p></div>'
182
192
 
183
- # Build history section
193
+ # Build history section (only if has items)
184
194
  history_html = ''
185
195
  if quasarr_history:
186
196
  history_items = ''.join(_render_history_item(item) for item in quasarr_history[:10])
@@ -204,20 +214,28 @@ def _render_packages_content():
204
214
  other_items += ''.join(_render_history_item(item) for item in other_history[:5])
205
215
 
206
216
  plural = 's' if other_count != 1 else ''
217
+ # Only add separator class if there's Quasarr content above
218
+ section_class = 'other-packages-section' if has_quasarr_content else 'other-packages-section no-separator'
207
219
  other_html = f'''
208
- <div class="other-packages-section">
220
+ <div class="{section_class}">
209
221
  <details id="otherPackagesDetails">
210
- <summary id="otherPackagesSummary">Show {other_count} non-Quasarr package{plural}</summary>
222
+ <summary id="otherPackagesSummary">Show {other_count} other package{plural}</summary>
211
223
  <div class="other-packages-content">{other_items}</div>
212
224
  </details>
213
225
  </div>
214
226
  '''
215
227
 
228
+ # Only show "no downloads" if there's literally nothing
229
+ empty_html = ''
230
+ if not has_any_content:
231
+ empty_html = '<p class="empty-message">No packages</p>'
232
+
216
233
  return f'''
217
234
  <div class="packages-container">
218
235
  {queue_html}
219
236
  {history_html}
220
237
  {other_html}
238
+ {empty_html}
221
239
  </div>
222
240
  '''
223
241
 
@@ -283,6 +301,8 @@ def setup_packages_routes(app):
283
301
 
284
302
  {status_message}
285
303
 
304
+ <div id="slow-warning" class="slow-warning" style="display:none;">⚠️ Slow connection detected</div>
305
+
286
306
  <div id="packages-content">
287
307
  {packages_content}
288
308
  </div>
@@ -349,7 +369,26 @@ def setup_packages_routes(app):
349
369
 
350
370
  .empty-message {{ color: var(--text-muted, #888); font-style: italic; text-align: center; padding: 20px; }}
351
371
 
372
+ .slow-warning {{
373
+ text-align: center;
374
+ font-size: 0.85em;
375
+ color: #856404;
376
+ background: #fff3cd;
377
+ border: 1px solid #ffc107;
378
+ padding: 8px 12px;
379
+ border-radius: 6px;
380
+ margin-bottom: 15px;
381
+ }}
382
+ @media (prefers-color-scheme: dark) {{
383
+ .slow-warning {{
384
+ color: #ffc107;
385
+ background: #3d3510;
386
+ border-color: #665c00;
387
+ }}
388
+ }}
389
+
352
390
  .other-packages-section {{ margin-top: 30px; padding-top: 20px; border-top: 1px solid var(--border-color, #ddd); }}
391
+ .other-packages-section.no-separator {{ margin-top: 0; padding-top: 0; border-top: none; }}
353
392
  .other-packages-section summary {{ cursor: pointer; padding: 8px 0; color: var(--text-muted, #666); }}
354
393
  .other-packages-section summary:hover {{ color: var(--link-color, #0066cc); }}
355
394
  .other-packages-content {{ margin-top: 15px; }}
@@ -405,24 +444,51 @@ def setup_packages_routes(app):
405
444
  <script>
406
445
  // Background refresh - fetches content via AJAX, waits 5s between refresh cycles
407
446
  let refreshPaused = false;
447
+ let slowConnection = false;
408
448
 
409
449
  async function refreshContent() {{
410
450
  if (refreshPaused) return;
451
+
452
+ const startTime = Date.now();
453
+ const warningEl = document.getElementById('slow-warning');
454
+
455
+ // Save scroll position before refresh
456
+ const scrollY = window.scrollY;
457
+
458
+ // Show warning after 5s if still loading
459
+ const slowTimer = setTimeout(() => {{
460
+ slowConnection = true;
461
+ if (warningEl) warningEl.style.display = 'block';
462
+ }}, 5000);
463
+
411
464
  try {{
412
465
  const response = await fetch('/api/packages/content');
466
+ const elapsed = Date.now() - startTime;
467
+
468
+ clearTimeout(slowTimer);
469
+
470
+ // Update slow connection state
471
+ if (elapsed < 5000) {{
472
+ slowConnection = false;
473
+ if (warningEl) warningEl.style.display = 'none';
474
+ }} else {{
475
+ slowConnection = true;
476
+ if (warningEl) warningEl.style.display = 'block';
477
+ }}
478
+
413
479
  if (response.ok) {{
414
480
  const html = await response.text();
415
481
  const container = document.getElementById('packages-content');
416
482
  if (container && html) {{
417
483
  container.innerHTML = html;
418
- // Re-apply collapse state after content update
419
484
  restoreCollapseState();
485
+ // Restore scroll position after content update
486
+ window.scrollTo(0, scrollY);
420
487
  }}
421
488
  }}
422
489
  }} catch (e) {{
423
- // Silent fail - will retry on next cycle
490
+ clearTimeout(slowTimer);
424
491
  }}
425
- // Schedule next refresh 5 seconds after this one completes
426
492
  setTimeout(refreshContent, 5000);
427
493
  }}
428
494
 
@@ -434,7 +500,7 @@ def setup_packages_routes(app):
434
500
  const plural = count !== '1' ? 's' : '';
435
501
  if (localStorage.getItem('otherPackagesOpen') === 'true') {{
436
502
  otherDetails.open = true;
437
- otherSummary.textContent = 'Hide ' + count + ' non-Quasarr package' + plural;
503
+ otherSummary.textContent = 'Hide ' + count + ' other package' + plural;
438
504
  }}
439
505
  // Re-attach event listener
440
506
  otherDetails.onclick = null;
@@ -442,23 +508,36 @@ def setup_packages_routes(app):
442
508
  localStorage.setItem('otherPackagesOpen', this.open);
443
509
  const summaryEl = document.getElementById('otherPackagesSummary');
444
510
  if (summaryEl) {{
445
- summaryEl.textContent = (this.open ? 'Hide ' : 'Show ') + count + ' non-Quasarr package' + plural;
511
+ summaryEl.textContent = (this.open ? 'Hide ' : 'Show ') + count + ' other package' + plural;
446
512
  }}
447
513
  }});
448
514
  }}
449
515
  }}
450
516
 
451
- // Start refresh cycle after initial 5s delay
452
- setTimeout(refreshContent, 5000);
453
-
454
517
  // Initial collapse state setup
455
518
  restoreCollapseState();
456
519
 
457
- // Clear status message from URL after display
520
+ // Clear status message from URL after display and auto-hide after 5s
458
521
  if (window.location.search.includes('deleted=')) {{
459
522
  const url = new URL(window.location);
460
523
  url.searchParams.delete('deleted');
461
524
  window.history.replaceState({{}}, '', url);
525
+
526
+ // Hide the status message after 5 seconds
527
+ const statusMsg = document.querySelector('.status-message');
528
+ if (statusMsg) {{
529
+ setTimeout(() => {{
530
+ statusMsg.style.transition = 'opacity 0.3s';
531
+ statusMsg.style.opacity = '0';
532
+ setTimeout(() => statusMsg.remove(), 300);
533
+ }}, 5000);
534
+ }}
535
+
536
+ // Reset refresh - start fresh 5s countdown after delete
537
+ setTimeout(refreshContent, 5000);
538
+ }} else {{
539
+ // Normal start - 5s delay
540
+ setTimeout(refreshContent, 5000);
462
541
  }}
463
542
 
464
543
  // Delete modal
@@ -468,8 +468,10 @@ def get_packages(shared_state, _cache=None):
468
468
  details = package["details"]
469
469
  name = f"[Linkgrabber] {details.get('name', 'unknown')}"
470
470
  try:
471
- mb = mb_left = int(details.get("bytesTotal", 0)) / (1024 * 1024)
471
+ bytes_total = int(details.get("bytesTotal", 0))
472
+ mb = mb_left = bytes_total / (1024 * 1024)
472
473
  except (KeyError, TypeError, ValueError):
474
+ bytes_total = 0
473
475
  mb = mb_left = 0
474
476
  package_id = package["comment"]
475
477
  category = get_category_from_package_id(package_id)
@@ -505,27 +507,30 @@ def get_packages(shared_state, _cache=None):
505
507
  details = package["details"]
506
508
  name = f"[CAPTCHA not solved!] {details.get('title', 'unknown')}"
507
509
  mb = mb_left = details.get("size_mb") or 0
510
+ bytes_total = 0 # Protected packages don't have reliable byte data
508
511
  package_id = package.get("package_id")
509
512
  category = get_category_from_package_id(package_id)
510
513
  package_type = "protected"
511
514
  package_uuid = None
512
515
 
513
- if package_id:
516
+ # Use package_id if available, otherwise use uuid as fallback for non-Quasarr packages
517
+ effective_id = package_id or package_uuid
518
+
519
+ if effective_id:
514
520
  try:
515
- mb_left = int(mb_left) if mb_left else 0
516
- mb = int(mb) if mb else 0
517
521
  percentage = int(100 * (mb - mb_left) / mb) if mb > 0 else 0
518
522
  except (ZeroDivisionError, ValueError, TypeError):
519
523
  percentage = 0
520
524
 
521
525
  downloads["queue"].append({
522
526
  "index": queue_index,
523
- "nzo_id": package_id,
527
+ "nzo_id": effective_id,
524
528
  "priority": "Normal",
525
529
  "filename": name,
526
530
  "cat": category,
527
- "mbleft": mb_left,
528
- "mb": mb,
531
+ "mbleft": int(mb_left) if mb_left else 0,
532
+ "mb": int(mb) if mb else 0,
533
+ "bytes": bytes_total,
529
534
  "status": "Downloading",
530
535
  "percentage": percentage,
531
536
  "timeleft": time_left,
@@ -535,18 +540,21 @@ def get_packages(shared_state, _cache=None):
535
540
  })
536
541
  queue_index += 1
537
542
  else:
538
- debug(f"get_packages: Skipping queue package without package_id: {name}")
543
+ debug(f"get_packages: Skipping queue package without package_id or uuid: {name}")
539
544
 
540
545
  elif package["location"] == "history":
541
546
  details = package["details"]
542
547
  name = details.get("name", "unknown")
543
548
  try:
544
- size = int(details.get("bytesLoaded", 0))
549
+ # Use bytesLoaded first, fall back to bytesTotal for failed/incomplete downloads
550
+ size = int(details.get("bytesLoaded", 0)) or int(details.get("bytesTotal", 0))
545
551
  except (KeyError, TypeError, ValueError):
546
552
  size = 0
547
553
  storage = details.get("saveTo", "/")
548
554
 
549
555
  package_id = package.get("comment")
556
+ # Use package_id if available, otherwise use uuid as fallback for non-Quasarr packages
557
+ effective_id = package_id or package.get("uuid")
550
558
  category = get_category_from_package_id(package_id)
551
559
 
552
560
  error = package.get("error")
@@ -562,7 +570,7 @@ def get_packages(shared_state, _cache=None):
562
570
  "category": category,
563
571
  "storage": storage,
564
572
  "status": status,
565
- "nzo_id": package_id,
573
+ "nzo_id": effective_id,
566
574
  "name": name,
567
575
  "bytes": int(size),
568
576
  "percentage": 100,
@@ -671,7 +679,8 @@ def delete_package(shared_state, package_id):
671
679
  found = False
672
680
  for package_location in packages:
673
681
  for package in packages[package_location]:
674
- if package.get("nzo_id") == package_id:
682
+ # Compare as strings to handle int UUIDs from JDownloader
683
+ if str(package.get("nzo_id", "")) == str(package_id):
675
684
  found = True
676
685
  package_type = package.get("type")
677
686
  package_uuid = package.get("uuid")
@@ -8,7 +8,7 @@ import requests
8
8
 
9
9
 
10
10
  def get_version():
11
- return "2.1.3"
11
+ return "2.1.5"
12
12
 
13
13
 
14
14
  def get_latest_version():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quasarr
3
- Version: 2.1.3
3
+ Version: 2.1.5
4
4
  Summary: Quasarr connects JDownloader with Radarr, Sonarr and LazyLibrarian. It also decrypts links protected by CAPTCHAs.
5
5
  Home-page: https://github.com/rix1337/Quasarr
6
6
  Author: rix1337
@@ -3,7 +3,7 @@ quasarr/api/__init__.py,sha256=yrDJM0qxmsOruOyqUwhSmmaT6E3DLK-yPy7Lutk1WYM,16384
3
3
  quasarr/api/arr/__init__.py,sha256=eEop8A5t936uT5azn4qz0bq1DMX84_Ja16wyleGFhyM,18495
4
4
  quasarr/api/captcha/__init__.py,sha256=2pca9jnJMWZECHeAvKP9Lwsa3_GkVsvQEn7rof8Ep1s,73504
5
5
  quasarr/api/config/__init__.py,sha256=HDoqRv6El-e5iejVpwEz6ttySjDeiTQGom5D__nachU,13682
6
- quasarr/api/packages/__init__.py,sha256=srQw71KSMr2gjvrISyXute606QJhPdVNwaGzRM9OrVA,22989
6
+ quasarr/api/packages/__init__.py,sha256=IJxAPiqN14zLXFn820pZrqWGq4wuXw1WtjUoZr2U00Y,26311
7
7
  quasarr/api/sponsors_helper/__init__.py,sha256=vZIFGkc5HTRozjvi47tqxz6XpwDe8sDXVyeydc9k0Y0,6708
8
8
  quasarr/api/statistics/__init__.py,sha256=NrBAjjHkIUE95HhPUGIfNqh2IqBqJ_zm00S90Y-Qnus,7038
9
9
  quasarr/downloads/__init__.py,sha256=BUZpqWN3VnYSM8ZX4lC3MXVfShwx95I6FKz8Xl95T1s,16004
@@ -11,7 +11,7 @@ quasarr/downloads/linkcrypters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5
11
11
  quasarr/downloads/linkcrypters/al.py,sha256=mfUG5VclC_-FcGoZL9zHYD7dz7X_YpaNmoKkgiyl9-0,8812
12
12
  quasarr/downloads/linkcrypters/filecrypt.py,sha256=nUZCTmvKylaNk1KAXcYUV1FgQCVAKNE3roXCNaqHLYA,17057
13
13
  quasarr/downloads/linkcrypters/hide.py,sha256=H4hJWhENkszV1u_ULC3aOW2fu9infC-Nv-7wx2DYqrA,6266
14
- quasarr/downloads/packages/__init__.py,sha256=zDMhLJz-16r4WSv2zzSlcBJEFrIUky2dRDkQaQLVSXY,32055
14
+ quasarr/downloads/packages/__init__.py,sha256=ZHU5eYNfl62IKyxYIzTgfzUYbPZIYgljHiPqHJAYogg,32747
15
15
  quasarr/downloads/sources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  quasarr/downloads/sources/al.py,sha256=g587VESZRZHZ03uxHKpufEr5qAtzbyGLmoijksU35jk,27297
17
17
  quasarr/downloads/sources/by.py,sha256=kmUTn3izayRCV7W-t0E4kYE8qTbt3L3reCLozfvRGcU,3807
@@ -43,7 +43,7 @@ quasarr/providers/obfuscated.py,sha256=d0dxu3oPqDqN8I7qmGPPIBqjLzTQbZTQsLAZ5auw1
43
43
  quasarr/providers/shared_state.py,sha256=5a_ZbGqTvt4-OqBt2a1WtR9I5J_Ky7IlkEY8EGtKVu8,30646
44
44
  quasarr/providers/statistics.py,sha256=cEQixYnDMDqtm5wWe40E_2ucyo4mD0n3SrfelhQi1L8,6452
45
45
  quasarr/providers/utils.py,sha256=mcUPbcXMsLmrYv0CTZO5a9aOt2-JLyL3SZxu6N8OyjU,12075
46
- quasarr/providers/version.py,sha256=ann8tOgDNixaxy3D0ASth3qD5mrqZy1_0RWcpePM5Rw,4003
46
+ quasarr/providers/version.py,sha256=yR-O_QV4gc8tIxIWrG7LptGuF-2qZ9GO43zdC6USiuc,4003
47
47
  quasarr/providers/web_server.py,sha256=AYd0KRxdDWMBr87BP8wlSMuL4zZo0I_rY-vHBai6Pfg,1688
48
48
  quasarr/providers/sessions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
49
  quasarr/providers/sessions/al.py,sha256=WXue9LaT4y0BzsbKtHbN6bb_72c4AZZWR9NP-vg9-cg,12462
@@ -73,9 +73,9 @@ quasarr/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
73
  quasarr/storage/config.py,sha256=SSTgIce2FVYoVTK_6OCU3msknhxuLA3EC4Kcrrf_dxQ,6378
74
74
  quasarr/storage/setup.py,sha256=eXtgUqMacbaUDy-dszPtT15O9lJKSO1eI3v0jGjMiUA,42361
75
75
  quasarr/storage/sqlite_database.py,sha256=yMqFQfKf0k7YS-6Z3_7pj4z1GwWSXJ8uvF4IydXsuTE,3554
76
- quasarr-2.1.3.dist-info/licenses/LICENSE,sha256=QQFCAfDgt7lSA8oSWDHIZ9aTjFbZaBJdjnGOHkuhK7k,1060
77
- quasarr-2.1.3.dist-info/METADATA,sha256=2CGHsGgnLrJgv0pboVyhAiKE9Sfy6RzfX3hGoD2DhHo,14914
78
- quasarr-2.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
79
- quasarr-2.1.3.dist-info/entry_points.txt,sha256=gXi8mUKsIqKVvn-bOc8E5f04sK_KoMCC-ty6b2Hf-jc,40
80
- quasarr-2.1.3.dist-info/top_level.txt,sha256=dipJdaRda5ruTZkoGfZU60bY4l9dtPlmOWwxK_oGSF0,8
81
- quasarr-2.1.3.dist-info/RECORD,,
76
+ quasarr-2.1.5.dist-info/licenses/LICENSE,sha256=QQFCAfDgt7lSA8oSWDHIZ9aTjFbZaBJdjnGOHkuhK7k,1060
77
+ quasarr-2.1.5.dist-info/METADATA,sha256=7rXMH9Ghh602E8meHbD3QEbHkBoW_NbNbFzjmr-DxBc,14914
78
+ quasarr-2.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
79
+ quasarr-2.1.5.dist-info/entry_points.txt,sha256=gXi8mUKsIqKVvn-bOc8E5f04sK_KoMCC-ty6b2Hf-jc,40
80
+ quasarr-2.1.5.dist-info/top_level.txt,sha256=dipJdaRda5ruTZkoGfZU60bY4l9dtPlmOWwxK_oGSF0,8
81
+ quasarr-2.1.5.dist-info/RECORD,,