copyparty 1.14.2__py3-none-any.whl → 1.14.4__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.
copyparty/__main__.py CHANGED
@@ -969,9 +969,10 @@ def add_fs(ap):
969
969
  def add_share(ap):
970
970
  db_path = os.path.join(E.cfg, "shares.db")
971
971
  ap2 = ap.add_argument_group('share-url options')
972
- ap2.add_argument("--shr", metavar="DIR", default="", help="toplevel virtual folder for shared files/folders, for example [\033[32m/share\033[0m]")
973
- ap2.add_argument("--shr-db", metavar="FILE", default=db_path, help="database to store shares in")
974
- ap2.add_argument("--shr-adm", metavar="U,U", default="", help="comma-separated list of users allowed to view/delete any share")
972
+ ap2.add_argument("--shr", metavar="DIR", type=u, default="", help="toplevel virtual folder for shared files/folders, for example [\033[32m/share\033[0m]")
973
+ ap2.add_argument("--shr-db", metavar="FILE", type=u, default=db_path, help="database to store shares in")
974
+ ap2.add_argument("--shr-adm", metavar="U,U", type=u, default="", help="comma-separated list of users allowed to view/delete any share")
975
+ ap2.add_argument("--shr-rt", metavar="MIN", type=int, default=1440, help="shares can be revived by their owner if they expired less than MIN minutes ago; [\033[32m60\033[0m]=hour, [\033[32m1440\033[0m]=day, [\033[32m10080\033[0m]=week")
975
976
  ap2.add_argument("--shr-v", action="store_true", help="debug")
976
977
 
977
978
 
copyparty/__version__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 14, 2)
3
+ VERSION = (1, 14, 4)
4
4
  CODENAME = "one step forward"
5
- BUILD_DT = (2024, 8, 23)
5
+ BUILD_DT = (2024, 9, 2)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
copyparty/httpcli.py CHANGED
@@ -1607,8 +1607,8 @@ class HttpCli(object):
1607
1607
  if "delete" in self.uparam:
1608
1608
  return self.handle_rm([])
1609
1609
 
1610
- if "unshare" in self.uparam:
1611
- return self.handle_unshare()
1610
+ if "eshare" in self.uparam:
1611
+ return self.handle_eshare()
1612
1612
 
1613
1613
  if "application/octet-stream" in ctype:
1614
1614
  return self.handle_post_binary()
@@ -4300,7 +4300,7 @@ class HttpCli(object):
4300
4300
  self.reply(html.encode("utf-8"), status=200)
4301
4301
  return True
4302
4302
 
4303
- def handle_unshare(self) :
4303
+ def handle_eshare(self) :
4304
4304
  idx = self.conn.get_u2idx()
4305
4305
  if not idx or not hasattr(idx, "p_end"):
4306
4306
  if not HAVE_SQLITE3:
@@ -4308,7 +4308,7 @@ class HttpCli(object):
4308
4308
  raise Pebkac(500, "server busy, cannot create share; please retry in a bit")
4309
4309
 
4310
4310
  if self.args.shr_v:
4311
- self.log("handle_unshare: " + self.req)
4311
+ self.log("handle_eshare: " + self.req)
4312
4312
 
4313
4313
  cur = idx.get_shr()
4314
4314
  if not cur:
@@ -4316,18 +4316,36 @@ class HttpCli(object):
4316
4316
 
4317
4317
  skey = self.vpath.split("/")[-1]
4318
4318
 
4319
- uns = cur.execute("select un from sh where k = ?", (skey,)).fetchall()
4320
- un = uns[0][0] if uns and uns[0] else ""
4319
+ rows = cur.execute("select un, t1 from sh where k = ?", (skey,)).fetchall()
4320
+ un = rows[0][0] if rows and rows[0] else ""
4321
4321
 
4322
4322
  if not un:
4323
4323
  raise Pebkac(400, "that sharekey didn't match anything")
4324
4324
 
4325
+ expiry = rows[0][1]
4326
+
4325
4327
  if un != self.uname and self.uname != self.args.shr_adm:
4326
4328
  t = "your username (%r) does not match the sharekey's owner (%r) and you're not admin"
4327
4329
  raise Pebkac(400, t % (self.uname, un))
4328
4330
 
4329
- cur.execute("delete from sh where k = ?", (skey,))
4331
+ reload = False
4332
+ act = self.uparam["eshare"]
4333
+ if act == "rm":
4334
+ cur.execute("delete from sh where k = ?", (skey,))
4335
+ if skey in self.asrv.vfs.nodes[self.args.shr.strip("/")].nodes:
4336
+ reload = True
4337
+ else:
4338
+ now = time.time()
4339
+ if expiry < now:
4340
+ expiry = now
4341
+ reload = True
4342
+ expiry += int(act) * 60
4343
+ cur.execute("update sh set t1 = ? where k = ?", (expiry, skey))
4344
+
4330
4345
  cur.connection.commit()
4346
+ if reload:
4347
+ self.conn.hsrv.broker.ask("_reload_blocking", False, False).get()
4348
+ self.conn.hsrv.broker.ask("up2k.wake_rescanner").get()
4331
4349
 
4332
4350
  self.redirect(self.args.SRS + "?shares")
4333
4351
  return True
copyparty/svchub.py CHANGED
@@ -100,6 +100,7 @@ class SvcHub(object):
100
100
  self.no_ansi = args.no_ansi
101
101
  self.logf = None
102
102
  self.logf_base_fn = ""
103
+ self.is_dut = False # running in unittest; always False
103
104
  self.stop_req = False
104
105
  self.stopping = False
105
106
  self.stopped = False
copyparty/tftpd.py CHANGED
@@ -400,7 +400,7 @@ class Tftpd(object):
400
400
  bos.stat(ap)
401
401
  return True
402
402
  except:
403
- return False
403
+ return vpath == "/"
404
404
 
405
405
  def _p_isdir(self, vpath ) :
406
406
  try:
@@ -408,7 +408,7 @@ class Tftpd(object):
408
408
  ret = stat.S_ISDIR(st.st_mode)
409
409
  return ret
410
410
  except:
411
- return False
411
+ return vpath == "/"
412
412
 
413
413
  def _hook(self, *a , **ka ) :
414
414
  src = inspect.currentframe().f_back.f_code.co_name
copyparty/up2k.py CHANGED
@@ -233,6 +233,9 @@ class Up2k(object):
233
233
  if not self.pp and self.args.exit == "idx":
234
234
  return self.hub.sigterm()
235
235
 
236
+ if self.hub.is_dut:
237
+ return
238
+
236
239
  Daemon(self._snapshot, "up2k-snapshot")
237
240
  if have_e2d:
238
241
  Daemon(self._hasher, "up2k-hasher")
@@ -429,7 +432,7 @@ class Up2k(object):
429
432
  def _sched_rescan(self) :
430
433
  volage = {}
431
434
  cooldown = timeout = time.time() + 3.0
432
- while True:
435
+ while not self.stop:
433
436
  now = time.time()
434
437
  timeout = max(timeout, cooldown)
435
438
  wait = timeout - time.time()
@@ -437,6 +440,9 @@ class Up2k(object):
437
440
  with self.rescan_cond:
438
441
  self.rescan_cond.wait(wait)
439
442
 
443
+ if self.stop:
444
+ return
445
+
440
446
  now = time.time()
441
447
  if now < cooldown:
442
448
  # self.log("SR: cd - now = {:.2f}".format(cooldown - now), 5)
@@ -460,6 +466,7 @@ class Up2k(object):
460
466
  if self.args.shr:
461
467
  timeout = min(self._check_shares(), timeout)
462
468
  except Exception as ex:
469
+ timeout = min(timeout, now + 60)
463
470
  t = "could not check for expiring shares: %r"
464
471
  self.log(t % (ex,), 1)
465
472
 
@@ -569,27 +576,53 @@ class Up2k(object):
569
576
 
570
577
  now = time.time()
571
578
  timeout = now + 9001
579
+ maxage = self.args.shr_rt * 60
580
+ low = now - maxage
581
+
582
+ vn = self.asrv.vfs.nodes.get(self.args.shr.strip("/"))
583
+ active = vn and vn.nodes
572
584
 
573
585
  db = sqlite3.connect(self.args.shr_db, timeout=2)
574
586
  cur = db.cursor()
575
587
 
576
588
  q = "select k from sh where t1 and t1 <= ?"
577
- rm = [x[0] for x in cur.execute(q, (now,))]
589
+ rm = [x[0] for x in cur.execute(q, (now,))] if active else []
590
+ if rm:
591
+ assert vn and vn.nodes # type: ignore
592
+ # self.log("chk_shr: %d" % (len(rm),))
593
+ zss = set(rm)
594
+ rm = [zs for zs in vn.nodes if zs in zss]
595
+ reload = bool(rm)
596
+ if reload:
597
+ self.log("disabling expired shares %s" % (rm,))
598
+
599
+ rm = [x[0] for x in cur.execute(q, (low,))]
578
600
  if rm:
579
601
  self.log("forgetting expired shares %s" % (rm,))
580
602
  cur.executemany("delete from sh where k=?", [(x,) for x in rm])
581
603
  cur.executemany("delete from sf where k=?", [(x,) for x in rm])
582
604
  db.commit()
605
+
606
+ if reload:
583
607
  Daemon(self.hub._reload_blocking, "sharedrop", (False, False))
584
608
 
585
- q = "select min(t1) from sh where t1 > 1"
586
- (earliest,) = cur.execute(q).fetchone()
609
+ q = "select min(t1) from sh where t1 > ?"
610
+ (earliest,) = cur.execute(q, (1,)).fetchone()
587
611
  if earliest:
588
- timeout = earliest - now
612
+ # deadline for revoking regular access
613
+ timeout = min(timeout, earliest + maxage)
614
+
615
+ (earliest,) = cur.execute(q, (now - 2,)).fetchone()
616
+ if earliest:
617
+ # deadline for revival; drop entirely
618
+ timeout = min(timeout, earliest)
589
619
 
590
620
  cur.close()
591
621
  db.close()
592
622
 
623
+ if self.args.shr_v:
624
+ self.log("next shr_chk = %d (%d)" % (timeout, timeout - time.time()))
625
+
593
626
  return timeout
594
627
 
595
628
  def _check_xiu(self) :
@@ -713,6 +746,8 @@ class Up2k(object):
713
746
  continue
714
747
 
715
748
  self.pp = ProgressPrinter(self.log, self.args)
749
+ if not self.hub.is_dut:
750
+ self.pp.start()
716
751
 
717
752
  break
718
753
 
@@ -1402,7 +1437,7 @@ class Up2k(object):
1402
1437
  if dts == lmod and dsz == sz and (nohash or dw[0] != "#" or not sz):
1403
1438
  continue
1404
1439
 
1405
- t = "reindex [{}] => [{}] ({}/{}) ({}/{})".format(
1440
+ t = "reindex [{}] => [{}] mtime({}/{}) size({}/{})".format(
1406
1441
  top, rp, dts, lmod, dsz, sz
1407
1442
  )
1408
1443
  self.log(t)
@@ -2317,7 +2352,9 @@ class Up2k(object):
2317
2352
 
2318
2353
  def _open_db_wd(self, db_path ) :
2319
2354
  ok = []
2320
- Daemon(self._open_db_timeout, "opendb_watchdog", [db_path, ok])
2355
+ if not self.hub.is_dut:
2356
+ Daemon(self._open_db_timeout, "opendb_watchdog", [db_path, ok])
2357
+
2321
2358
  try:
2322
2359
  return self._open_db(db_path)
2323
2360
  finally:
@@ -2661,11 +2698,19 @@ class Up2k(object):
2661
2698
  if stat.S_ISLNK(st.st_mode):
2662
2699
  # broken symlink
2663
2700
  raise Exception()
2664
- except:
2701
+ if st.st_size != dsize:
2702
+ t = "candidate ignored (db/fs desync): {}, size fs={} db={}, mtime fs={} db={}, file: {}"
2703
+ t = t.format(
2704
+ wark, st.st_size, dsize, st.st_mtime, dtime, dp_abs
2705
+ )
2706
+ self.log(t)
2707
+ raise Exception("desync")
2708
+ except Exception as ex:
2665
2709
  if n4g:
2666
2710
  st = os.stat_result((0, -1, -1, 0, 0, 0, 0, 0, 0, 0))
2667
2711
  else:
2668
- lost.append((cur, dp_dir, dp_fn))
2712
+ if str(ex) != "desync":
2713
+ lost.append((cur, dp_dir, dp_fn))
2669
2714
  continue
2670
2715
 
2671
2716
  j = {
@@ -2723,13 +2768,16 @@ class Up2k(object):
2723
2768
  ptop = None # use cj or job as appropriate
2724
2769
 
2725
2770
  if not job and wark in reg:
2726
- # ensure the files haven't been deleted manually
2771
+ # ensure the files haven't been edited or deleted
2772
+ path = ""
2773
+ st = None
2727
2774
  rj = reg[wark]
2728
2775
  names = [rj[x] for x in ["name", "tnam"] if x in rj]
2729
2776
  for fn in names:
2730
2777
  path = djoin(rj["ptop"], rj["prel"], fn)
2731
2778
  try:
2732
- if bos.path.getsize(path) > 0 or not rj["need"]:
2779
+ st = bos.stat(path)
2780
+ if st.st_size > 0 or not rj["need"]:
2733
2781
  # upload completed or both present
2734
2782
  break
2735
2783
  except:
@@ -2740,6 +2788,14 @@ class Up2k(object):
2740
2788
  del reg[wark]
2741
2789
  break
2742
2790
 
2791
+ if st and not self.args.nw and not n4g and st.st_size != rj["size"]:
2792
+ t = "will not dedup (fs index desync): {}, size fs={} db={}, mtime fs={} db={}, file: {}"
2793
+ t = t.format(
2794
+ wark, st.st_size, rj["size"], st.st_mtime, rj["lmod"], path
2795
+ )
2796
+ self.log(t)
2797
+ del reg[wark]
2798
+
2743
2799
  if job or wark in reg:
2744
2800
  job = job or reg[wark]
2745
2801
  if (
@@ -2847,6 +2903,7 @@ class Up2k(object):
2847
2903
  return self._handle_json(job, depth + 1)
2848
2904
 
2849
2905
  job["name"] = self._untaken(pdir, job, now)
2906
+ dst = djoin(job["ptop"], job["prel"], job["name"])
2850
2907
 
2851
2908
  if not self.args.nw:
2852
2909
  dvf = vfs.flags
copyparty/util.py CHANGED
@@ -894,7 +894,6 @@ class ProgressPrinter(threading.Thread):
894
894
  self.msg = ""
895
895
  self.end = False
896
896
  self.n = -1
897
- self.start()
898
897
 
899
898
  def run(self) :
900
899
  sigblock()
Binary file
copyparty/web/shares.html CHANGED
@@ -33,6 +33,7 @@
33
33
  <th>expires</th>
34
34
  <th>min</th>
35
35
  <th>hrs</th>
36
+ <th>add time</th>
36
37
  </tr></thead><tbody>
37
38
  {% for k, pw, vp, pr, st, un, t0, t1 in rows %}
38
39
  <tr>
@@ -45,8 +46,9 @@
45
46
  <td>{{ un|e }}</td>
46
47
  <td>{{ t0 }}</td>
47
48
  <td>{{ t1 }}</td>
48
- <td>{{ ((t1 - now) / 60) | round(1) if t1 else "inf" }}</td>
49
- <td>{{ ((t1 - now) / 3600) | round(1) if t1 else "inf" }}</td>
49
+ <td>{{ "inf" if not t1 else "dead" if t1 < now else ((t1 - now) / 60) | round(1) }}</td>
50
+ <td>{{ "inf" if not t1 else "dead" if t1 < now else ((t1 - now) / 3600) | round(1) }}</td>
51
+ <td></td>
50
52
  </tr>
51
53
  {% endfor %}
52
54
  </tbody></table>
Binary file
Binary file
copyparty/web/ui.css.gz CHANGED
Binary file
copyparty/web/up2k.js.gz CHANGED
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: copyparty
3
- Version: 1.14.2
3
+ Version: 1.14.4
4
4
  Summary: Portable file server with accelerated resumable uploads, deduplication, WebDAV, FTP, zeroconf, media indexer, video thumbnails, audio transcoding, and write-only folders
5
5
  Author-email: ed <copyparty@ocv.me>
6
6
  License: MIT
@@ -52,7 +52,9 @@ Requires-Dist: Pillow; extra == "thumbnails"
52
52
  Provides-Extra: thumbnails2
53
53
  Requires-Dist: pyvips; extra == "thumbnails2"
54
54
 
55
- # 💾🎉 copyparty
55
+ <img src="docs/logo.svg" width="250" align="right"/>
56
+
57
+ ### 💾🎉 copyparty
56
58
 
57
59
  turn almost any device into a file server with resumable uploads/downloads using [*any*](#browser-support) web browser
58
60
 
@@ -831,6 +833,8 @@ specify `--shr /foobar` to enable this feature; a toplevel virtual folder named
831
833
 
832
834
  users can delete their own shares in the controlpanel, and a list of privileged users (`--shr-adm`) are allowed to see and/or delet any share on the server
833
835
 
836
+ after a share has expired, it remains visible in the controlpanel for `--shr-rt` minutes (default is 1 day), and the owner can revive it by extending the expiration time there
837
+
834
838
  **security note:** using this feature does not mean that you can skip the [accounts and volumes](#accounts-and-volumes) section -- you still need to restrict access to volumes that you do not intend to share with unauthenticated users! it is not sufficient to use rules in the reverseproxy to restrict access to just the `/share` folder.
835
839
 
836
840
 
@@ -1,6 +1,6 @@
1
1
  copyparty/__init__.py,sha256=fUINM1abqDGzCCH_JcXdOnLdKOV-SrTI2Xo2QgQW2P4,1703
2
- copyparty/__main__.py,sha256=T9fipgzSsr9hc5ApvEpoWMe0CPoYpm1jOE2kNznV9Fg,107828
3
- copyparty/__version__.py,sha256=wDXrZl0rzt2I3ONTXVvCQwVk8Dh7I819X_mTUcCs4bI,258
2
+ copyparty/__main__.py,sha256=0fQcGqHkcYXRqtkh9as-xyBfV_5R6z2fQf41WFtjRe8,108093
3
+ copyparty/__version__.py,sha256=nvd8B5TJuiwdP2pBNNqiQlQQi41vsmExvBjCabqN22Q,257
4
4
  copyparty/authsrv.py,sha256=jWXTjZLT8cGymfa9wBwGJqyBIi80aXcvzAMgI74G8iA,95750
5
5
  copyparty/broker_mp.py,sha256=YFe1S6Zziht8Qc__dCLj_ff8z0DDny9lqk_Mi5ajsJk,3868
6
6
  copyparty/broker_mpw.py,sha256=4ZI7bJYOwUibeAJVv9_FPGNmHrr9eOtkj_Kz0JEppTU,3197
@@ -11,7 +11,7 @@ copyparty/cfg.py,sha256=i8-bjWgbguQooxiA172RcptqR_SEOwDHJ4cqldrZ8oQ,9792
11
11
  copyparty/dxml.py,sha256=lZpg-kn-kQsXRtNY1n6fRaS-b7uXzMCyv8ovKnhZcZc,1548
12
12
  copyparty/fsutil.py,sha256=hnEHgySI43-XJJKbI8n6t1A6oVHzR_nYdsBcAwtreBk,4610
13
13
  copyparty/ftpd.py,sha256=1vD-KTy07xfEEEk1dx37pUYModpNO2gIhVXvFUr205M,17497
14
- copyparty/httpcli.py,sha256=nlSmnZ6ejFzUGdfyWZ1v2C9ycuDdhDArXL3MZJcxYBI,181984
14
+ copyparty/httpcli.py,sha256=QTOrZOuVh__C_r5REpTiUeGEhrRAke2ejEOOi4zyfbo,182619
15
15
  copyparty/httpconn.py,sha256=mwIDup85cBowIfJOse8rla5bqTz7nf-ChgfR-5-V0JM,6938
16
16
  copyparty/httpsrv.py,sha256=8_1Ivg3eco7HJDjqL_rUB58IOUaUnoXGhO62bOMXLBk,17242
17
17
  copyparty/ico.py,sha256=eWSxEae4wOCfheHl-m-wchYvFRAR_97kJDb4NGaB-Z8,3561
@@ -24,15 +24,15 @@ copyparty/smbd.py,sha256=8zkC9BjVtGiKXMLajbdakxoKeFzACdM75SW0_SvqXJA,14490
24
24
  copyparty/ssdp.py,sha256=8iyF5sqIjATJLWcAtnJa8eadHosOn0CP4ywltzJ7bVY,7023
25
25
  copyparty/star.py,sha256=tV5BbX6AiQ7N4UU8DYtSTckNYeoeey4DBqq4LjfymbY,3818
26
26
  copyparty/sutil.py,sha256=JTMrQwcWH85hXB_cKG206eDZ967WZDGaP00AWvl_gB0,3214
27
- copyparty/svchub.py,sha256=fiH7d8UXiW_LC7m2MU0AtS6KWwy49QXbSp4gIoyT2SM,38364
27
+ copyparty/svchub.py,sha256=v0f8KU65dj2MXOjrd-7kckgiFZSu8kpnTUMyLfn9NIM,38429
28
28
  copyparty/szip.py,sha256=tor4yjdHhEL4Ox-Xg7-cuUFrMO0IwQD29aRX5Cp8MYs,8605
29
29
  copyparty/tcpsrv.py,sha256=jM_Za64O8LEMfMrU4irJluIJZrU494e2b759r_KhaUQ,19881
30
- copyparty/tftpd.py,sha256=i1-oZ05DJq2_nDOW3g3PfTkMoUCr2lAcDYFMWArwtKA,13568
30
+ copyparty/tftpd.py,sha256=jZbf2JpeJmkuQWJErmAPG-dKhtYNvIUHbkAgodSXw9Y,13582
31
31
  copyparty/th_cli.py,sha256=o6FMkerYvAXS455z3DUossVztu_nzFlYSQhs6qN6Jt8,4636
32
32
  copyparty/th_srv.py,sha256=27IftjIXUQzRRiUytt-CgXkybEoP3HHHoXaDAvxEmLo,29217
33
33
  copyparty/u2idx.py,sha256=t4mzjj2GDrkjIHt0RM68y1EgT5qOBoz6mkYgjMbqA38,13526
34
- copyparty/up2k.py,sha256=HNn5EzOM-pExt3waU-0n6_tqGweI--QAO4gKhNSACsU,153107
35
- copyparty/util.py,sha256=S7FuQBPbl2FX7ULIH9VNgiB7Z_rceqTJssXz4SGErwA,88625
34
+ copyparty/up2k.py,sha256=K2FIxvL4RQ9Er8pH7gQnIU-OHdOJJ5P60oW-SX-silQ,155373
35
+ copyparty/util.py,sha256=aWKONxHJh73PCvq9aKKJW9LcU3EqiZynpQ-b1MvK_50,88604
36
36
  copyparty/bos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
37
  copyparty/bos/bos.py,sha256=Wb7eWsXJgR5AFlBR9ZOyKrLTwy-Kct9RrGiOu4Jo37Y,1622
38
38
  copyparty/bos/path.py,sha256=yEjCq2ki9CvxA5sCT8pS0keEXwugs0ZeUyUhdBziOCI,777
@@ -57,7 +57,7 @@ copyparty/stolen/ifaddr/_win32.py,sha256=EE-QyoBgeB7lYQ6z62VjXNaRozaYfCkaJBHGNA8
57
57
  copyparty/web/baguettebox.js.gz,sha256=4dS8-r4si84ca71l98672ahnRI86Aq95MU-bc5knykk,7962
58
58
  copyparty/web/browser.css.gz,sha256=PoW_IIwFigZaMo3atpPU0o05Jj5Flbsm1bhW_KfcX-U,11491
59
59
  copyparty/web/browser.html,sha256=vvfWiu_aOFRar8u5lridMRKQSPF4R0YkA41zrsh82Qs,4878
60
- copyparty/web/browser.js.gz,sha256=0eqpm-iVRYZc-A9rpNNj8I1S_cV90nBb8VOWq-Lk6Zc,80814
60
+ copyparty/web/browser.js.gz,sha256=vbgNK0skbOyQHqUeYbLxi0qkdAXzWBTGxhgFaKBhxx8,80863
61
61
  copyparty/web/browser2.html,sha256=NRUZ08GH-e2YcGXcoz0UjYg6JIVF42u4IMX4HHwWTmg,1587
62
62
  copyparty/web/cf.html,sha256=lJThtNFNAQT1ClCHHlivAkDGE0LutedwopXD62Z8Nys,589
63
63
  copyparty/web/dbg-audio.js.gz,sha256=Ma-KZtK8LnmiwNvNKFKXMPYl_Nn_3U7GsJ6-DRWC2HE,688
@@ -72,15 +72,15 @@ copyparty/web/mde.js.gz,sha256=kN2eUSvr4mFuksfK4-4LimJmWdwsao39Sea2lWtu8L0,2224
72
72
  copyparty/web/msg.css.gz,sha256=u90fXYAVrMD-jqwf5XFVC1ptSpSHZUe8Mez6PX101P8,300
73
73
  copyparty/web/msg.html,sha256=w9CM3hkLLGJX9fWEaG4gSbTOPe2GcPqW8BpSCDiFzOI,977
74
74
  copyparty/web/shares.css.gz,sha256=m-nRqTGPiU3ohZxvGaROzFr98F_jmohQnjieqEAyjBo,496
75
- copyparty/web/shares.html,sha256=j3nXy0cWBXHx0kx-NsFwxbtCSRbRowGy5DhF_n6y7iQ,2276
76
- copyparty/web/shares.js.gz,sha256=St2Ep6md8XhpmJhxcppYjHCER5Q1GmIdfHtIM3x4iWU,514
75
+ copyparty/web/shares.html,sha256=d--9tyg6u3JzszpEtMmU5S4XdUF_mfUAhzCvwl-XAXw,2384
76
+ copyparty/web/shares.js.gz,sha256=296fTZV4sW7CxT-YNnDufUZL-aIy4E4r8q-XtSy6bHs,652
77
77
  copyparty/web/splash.css.gz,sha256=4DOtEKBWyaDKel7fdnwvnc9FrKlkht-ec7R2nRlruPU,1023
78
78
  copyparty/web/splash.html,sha256=dAo4KXKmXUMGcIwetZkFtVxk-mCMNkscD36BxLwRdow,4804
79
- copyparty/web/splash.js.gz,sha256=0HMVU21vpeA4Jbi6cGsPtKTwhdtT8-puVOQOiQAHGIc,2592
79
+ copyparty/web/splash.js.gz,sha256=pxEHaRDpxTnW6WdRWpKlRux8jtI7B5RImRjUVs9gdQQ,2582
80
80
  copyparty/web/svcs.html,sha256=v0C3cOFWXYlvp3GEifz1Qj0W3MD8JANT3WTON05GZ9o,11797
81
81
  copyparty/web/svcs.js.gz,sha256=k81ZvZ3I-f4fMHKrNGGOgOlvXnCBz0mVjD-8mieoWCA,520
82
- copyparty/web/ui.css.gz,sha256=GnR_PxnZGcNs2IJnb5hFffnhlW3cUHkPad3tNIm-7DQ,2637
83
- copyparty/web/up2k.js.gz,sha256=KufMtRViAZQo2rVj67iEWbdPxlVeXW85emRYVJoY3aA,22946
82
+ copyparty/web/ui.css.gz,sha256=ae1JosPYS8d2F9e_b95bTwa7qYwk8Ur_UhoVpRYEp0Y,2658
83
+ copyparty/web/up2k.js.gz,sha256=M9n-VLZP7olT-kL5FfT_X_FW1FrQV-bB3_xPhhFic3U,22943
84
84
  copyparty/web/util.js.gz,sha256=dPuhXEBJ_T-d2tYUUufGTUul4FYIbuh6GQmtK7iBkEo,14682
85
85
  copyparty/web/w.hash.js.gz,sha256=7wP9EZQNXQxwZnCCFUVsi_-6TM9PLZJeZ9krutXRRj8,1060
86
86
  copyparty/web/a/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -105,9 +105,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
105
105
  copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
106
106
  copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
107
107
  copyparty/web/deps/sha512.hw.js.gz,sha256=vqoXeracj-99Z5MfY3jK2N4WiSzYQdfjy0RnUlQDhSU,8110
108
- copyparty-1.14.2.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
109
- copyparty-1.14.2.dist-info/METADATA,sha256=fDuptsXfw2sOmf6nms6NIZRX5cgtutScqpw_UA0DqmE,131543
110
- copyparty-1.14.2.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
111
- copyparty-1.14.2.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
112
- copyparty-1.14.2.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
113
- copyparty-1.14.2.dist-info/RECORD,,
108
+ copyparty-1.14.4.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
109
+ copyparty-1.14.4.dist-info/METADATA,sha256=9NRNTHBaC3QV4ruHXtp6kuOcakkBtme8iY17_lRmaPI,131776
110
+ copyparty-1.14.4.dist-info/WHEEL,sha256=UvcQYKBHoFqaQd6LKyqHw9fxEolWLQnlzP0h_LgJAfI,91
111
+ copyparty-1.14.4.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
112
+ copyparty-1.14.4.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
113
+ copyparty-1.14.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (73.0.1)
2
+ Generator: setuptools (74.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5