copyparty 1.15.5__py3-none-any.whl → 1.15.7__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/up2k.py CHANGED
@@ -353,17 +353,18 @@ class Up2k(object):
353
353
  return '[{"timeout":1}]'
354
354
 
355
355
  ret = []
356
+ userset = set([(uname or "\n"), "*"])
356
357
  try:
357
358
  for ptop, tab2 in self.registry.items():
358
359
  cfg = self.flags.get(ptop, {}).get("u2abort", 1)
359
360
  if not cfg:
360
361
  continue
361
362
  addr = (ip or "\n") if cfg in (1, 2) else ""
362
- user = (uname or "\n") if cfg in (1, 3) else ""
363
+ user = userset if cfg in (1, 3) else None
363
364
  for job in tab2.values():
364
365
  if (
365
366
  "done" in job
366
- or (user and user != job["user"])
367
+ or (user and job["user"] not in user)
367
368
  or (addr and addr != job["addr"])
368
369
  ):
369
370
  continue
@@ -1008,6 +1009,7 @@ class Up2k(object):
1008
1009
  vpath = k
1009
1010
 
1010
1011
  _, flags = self._expr_idx_filter(flags)
1012
+ n4g = bool(flags.get("noforget"))
1011
1013
 
1012
1014
  ft = "\033[0;32m{}{:.0}"
1013
1015
  ff = "\033[0;35m{}{:.0}"
@@ -1061,20 +1063,39 @@ class Up2k(object):
1061
1063
  except:
1062
1064
  pass
1063
1065
 
1066
+ if reg2 and "dwrk" not in reg2[next(iter(reg2))]:
1067
+ for job in reg2.values():
1068
+ job["dwrk"] = job["wark"]
1069
+
1070
+ rm = []
1064
1071
  for k, job in reg2.items():
1065
- fp = djoin(job["ptop"], job["prel"], job["name"])
1072
+ job["ptop"] = ptop
1073
+ if "done" in job:
1074
+ job["need"] = job["hash"] = emptylist
1075
+ else:
1076
+ if "need" not in job:
1077
+ job["need"] = []
1078
+ if "hash" not in job:
1079
+ job["hash"] = []
1080
+
1081
+ fp = djoin(ptop, job["prel"], job["name"])
1066
1082
  if bos.path.exists(fp):
1067
1083
  reg[k] = job
1068
1084
  if "done" in job:
1069
- job["need"] = job["hash"] = emptylist
1070
1085
  continue
1071
1086
  job["poke"] = time.time()
1072
1087
  job["busy"] = {}
1073
1088
  else:
1074
1089
  self.log("ign deleted file in snap: [{}]".format(fp))
1090
+ if not n4g:
1091
+ rm.append(k)
1092
+ continue
1093
+
1094
+ for x in rm:
1095
+ del reg2[x]
1075
1096
 
1076
1097
  if drp is None:
1077
- drp = [k for k, v in reg.items() if not v.get("need", [])]
1098
+ drp = [k for k, v in reg.items() if not v["need"]]
1078
1099
  else:
1079
1100
  drp = [x for x in drp if x in reg]
1080
1101
 
@@ -1536,7 +1557,7 @@ class Up2k(object):
1536
1557
  at = 0
1537
1558
 
1538
1559
  # skip upload hooks by not providing vflags
1539
- self.db_add(db.c, {}, rd, fn, lmod, sz, "", "", wark, "", "", ip, at)
1560
+ self.db_add(db.c, {}, rd, fn, lmod, sz, "", "", wark, wark, "", "", ip, at)
1540
1561
  db.n += 1
1541
1562
  tfa += 1
1542
1563
  td = time.time() - db.t
@@ -2764,9 +2785,10 @@ class Up2k(object):
2764
2785
 
2765
2786
  cj["name"] = sanitize_fn(cj["name"], "")
2766
2787
  cj["poke"] = now = self.db_act = self.vol_act[ptop] = time.time()
2767
- wark = self._get_wark(cj)
2788
+ wark = dwark = self._get_wark(cj)
2768
2789
  job = None
2769
2790
  pdir = djoin(ptop, cj["prel"])
2791
+ inc_ap = djoin(pdir, cj["name"])
2770
2792
  try:
2771
2793
  dev = bos.stat(pdir).st_dev
2772
2794
  except:
@@ -2781,6 +2803,7 @@ class Up2k(object):
2781
2803
  reg = self.registry[ptop]
2782
2804
  vfs = self.asrv.vfs.all_vols[cj["vtop"]]
2783
2805
  n4g = bool(vfs.flags.get("noforget"))
2806
+ noclone = bool(vfs.flags.get("noclone"))
2784
2807
  rand = vfs.flags.get("rand") or cj.get("rand")
2785
2808
  lost = []
2786
2809
 
@@ -2790,6 +2813,12 @@ class Up2k(object):
2790
2813
  vols = [(ptop, jcur)] if jcur else []
2791
2814
  if vfs.flags.get("xlink"):
2792
2815
  vols += [(k, v) for k, v in self.cur.items() if k != ptop]
2816
+
2817
+ if noclone:
2818
+ wark = up2k_wark_from_metadata(
2819
+ self.salt, cj["size"], cj["lmod"], cj["prel"], cj["name"]
2820
+ )
2821
+
2793
2822
  if vfs.flags.get("up_ts", "") == "fu" or not cj["lmod"]:
2794
2823
  # force upload time rather than last-modified
2795
2824
  cj["lmod"] = int(time.time())
@@ -2802,10 +2831,10 @@ class Up2k(object):
2802
2831
 
2803
2832
  if self.no_expr_idx:
2804
2833
  q = r"select * from up where w = ?"
2805
- argv = [wark]
2834
+ argv = [dwark]
2806
2835
  else:
2807
2836
  q = r"select * from up where substr(w,1,16)=? and +w=?"
2808
- argv = [wark[:16], wark]
2837
+ argv = [dwark[:16], dwark]
2809
2838
 
2810
2839
  c2 = cur.execute(q, tuple(argv))
2811
2840
  for _, dtime, dsize, dp_dir, dp_fn, ip, at in c2:
@@ -2813,6 +2842,9 @@ class Up2k(object):
2813
2842
  dp_dir, dp_fn = s3dec(dp_dir, dp_fn)
2814
2843
 
2815
2844
  dp_abs = djoin(ptop, dp_dir, dp_fn)
2845
+ if noclone and dp_abs != inc_ap:
2846
+ continue
2847
+
2816
2848
  try:
2817
2849
  st = bos.stat(dp_abs)
2818
2850
  if stat.S_ISLNK(st.st_mode):
@@ -2821,7 +2853,7 @@ class Up2k(object):
2821
2853
  if st.st_size != dsize:
2822
2854
  t = "candidate ignored (db/fs desync): {}, size fs={} db={}, mtime fs={} db={}, file: {}"
2823
2855
  t = t.format(
2824
- wark, st.st_size, dsize, st.st_mtime, dtime, dp_abs
2856
+ dwark, st.st_size, dsize, st.st_mtime, dtime, dp_abs
2825
2857
  )
2826
2858
  self.log(t)
2827
2859
  raise Exception()
@@ -2868,7 +2900,6 @@ class Up2k(object):
2868
2900
  alts.append((score, -len(alts), j, cur, dp_dir, dp_fn))
2869
2901
 
2870
2902
  job = None
2871
- inc_ap = djoin(cj["ptop"], cj["prel"], cj["name"])
2872
2903
  for dupe in sorted(alts, reverse=True):
2873
2904
  rj = dupe[2]
2874
2905
  orig_ap = djoin(rj["ptop"], rj["prel"], rj["name"])
@@ -2878,11 +2909,11 @@ class Up2k(object):
2878
2909
  break
2879
2910
  else:
2880
2911
  self.log("asserting contents of %s" % (orig_ap,))
2881
- dhashes, st = self._hashlist_from_file(orig_ap)
2882
- dwark = up2k_wark_from_hashlist(self.salt, st.st_size, dhashes)
2883
- if wark != dwark:
2912
+ hashes2, st = self._hashlist_from_file(orig_ap)
2913
+ wark2 = up2k_wark_from_hashlist(self.salt, st.st_size, hashes2)
2914
+ if dwark != wark2:
2884
2915
  t = "will not dedup (fs index desync): fs=%s, db=%s, file: %s"
2885
- self.log(t % (dwark, wark, orig_ap))
2916
+ self.log(t % (wark2, dwark, orig_ap))
2886
2917
  lost.append(dupe[3:])
2887
2918
  continue
2888
2919
  data_ok = True
@@ -2946,11 +2977,11 @@ class Up2k(object):
2946
2977
 
2947
2978
  elif inc_ap != orig_ap and not data_ok and "done" in reg[wark]:
2948
2979
  self.log("asserting contents of %s" % (orig_ap,))
2949
- dhashes, _ = self._hashlist_from_file(orig_ap)
2950
- dwark = up2k_wark_from_hashlist(self.salt, st.st_size, dhashes)
2951
- if wark != dwark:
2980
+ hashes2, _ = self._hashlist_from_file(orig_ap)
2981
+ wark2 = up2k_wark_from_hashlist(self.salt, st.st_size, hashes2)
2982
+ if wark != wark2:
2952
2983
  t = "will not dedup (fs index desync): fs=%s, idx=%s, file: %s"
2953
- self.log(t % (dwark, wark, orig_ap))
2984
+ self.log(t % (wark2, wark, orig_ap))
2954
2985
  del reg[wark]
2955
2986
 
2956
2987
  if job or wark in reg:
@@ -3007,6 +3038,7 @@ class Up2k(object):
3007
3038
 
3008
3039
  job = deepcopy(job)
3009
3040
  job["wark"] = wark
3041
+ job["dwrk"] = dwark
3010
3042
  job["at"] = cj.get("at") or now
3011
3043
  zs = "vtop ptop prel name lmod host user addr poke"
3012
3044
  for k in zs.split():
@@ -3077,7 +3109,7 @@ class Up2k(object):
3077
3109
  raise
3078
3110
 
3079
3111
  if cur and not self.args.nw:
3080
- zs = "prel name lmod size ptop vtop wark host user addr at"
3112
+ zs = "prel name lmod size ptop vtop wark dwrk host user addr at"
3081
3113
  a = [job[x] for x in zs.split()]
3082
3114
  self.db_add(cur, vfs.flags, *a)
3083
3115
  cur.connection.commit()
@@ -3107,6 +3139,7 @@ class Up2k(object):
3107
3139
 
3108
3140
  job = {
3109
3141
  "wark": wark,
3142
+ "dwrk": dwark,
3110
3143
  "t0": now,
3111
3144
  "sprs": sprs,
3112
3145
  "hash": deepcopy(cj["hash"]),
@@ -3149,6 +3182,7 @@ class Up2k(object):
3149
3182
  "lmod": job["lmod"],
3150
3183
  "sprs": job.get("sprs", sprs),
3151
3184
  "hash": job["need"],
3185
+ "dwrk": dwark,
3152
3186
  "wark": wark,
3153
3187
  }
3154
3188
 
@@ -3175,7 +3209,7 @@ class Up2k(object):
3175
3209
  ):
3176
3210
  sql = "update up set mt=? where substr(w,1,16)=? and +rd=? and +fn=?"
3177
3211
  try:
3178
- cur.execute(sql, (cj["lmod"], wark[:16], job["prel"], job["name"]))
3212
+ cur.execute(sql, (cj["lmod"], dwark[:16], job["prel"], job["name"]))
3179
3213
  cur.connection.commit()
3180
3214
 
3181
3215
  ap = djoin(job["ptop"], job["prel"], job["name"])
@@ -3416,19 +3450,19 @@ class Up2k(object):
3416
3450
  self.mutex.release()
3417
3451
  return -1, ""
3418
3452
  try:
3419
- return self._confirm_chunks(ptop, wark, chashes)
3453
+ return self._confirm_chunks(ptop, wark, chashes, chashes)
3420
3454
  finally:
3421
3455
  self.reg_mutex.release()
3422
3456
  self.mutex.release()
3423
3457
 
3424
3458
  def confirm_chunks(
3425
- self, ptop , wark , chashes
3459
+ self, ptop , wark , written , locked
3426
3460
  ) :
3427
3461
  with self.mutex, self.reg_mutex:
3428
- return self._confirm_chunks(ptop, wark, chashes)
3462
+ return self._confirm_chunks(ptop, wark, written, locked)
3429
3463
 
3430
3464
  def _confirm_chunks(
3431
- self, ptop , wark , chashes
3465
+ self, ptop , wark , written , locked
3432
3466
  ) :
3433
3467
  if True:
3434
3468
  self.db_act = self.vol_act[ptop] = time.time()
@@ -3440,11 +3474,11 @@ class Up2k(object):
3440
3474
  except Exception as ex:
3441
3475
  return -2, "confirm_chunk, wark(%r)" % (ex,) # type: ignore
3442
3476
 
3443
- for chash in chashes:
3477
+ for chash in locked:
3444
3478
  job["busy"].pop(chash, None)
3445
3479
 
3446
3480
  try:
3447
- for chash in chashes:
3481
+ for chash in written:
3448
3482
  job["need"].remove(chash)
3449
3483
  except Exception as ex:
3450
3484
  return -2, "confirm_chunk, chash(%s) %r" % (chash, ex) # type: ignore
@@ -3496,7 +3530,7 @@ class Up2k(object):
3496
3530
  except:
3497
3531
  self.log("failed to utime ({}, {})".format(dst, times))
3498
3532
 
3499
- zs = "prel name lmod size ptop vtop wark host user addr"
3533
+ zs = "prel name lmod size ptop vtop wark dwrk host user addr"
3500
3534
  z2 = [job[x] for x in zs.split()]
3501
3535
  wake_sr = False
3502
3536
  try:
@@ -3569,6 +3603,7 @@ class Up2k(object):
3569
3603
  ptop ,
3570
3604
  vtop ,
3571
3605
  wark ,
3606
+ dwark ,
3572
3607
  host ,
3573
3608
  usr ,
3574
3609
  ip ,
@@ -3591,6 +3626,7 @@ class Up2k(object):
3591
3626
  ptop,
3592
3627
  vtop,
3593
3628
  wark,
3629
+ dwark,
3594
3630
  host,
3595
3631
  usr,
3596
3632
  ip,
@@ -3604,7 +3640,7 @@ class Up2k(object):
3604
3640
  raise
3605
3641
 
3606
3642
  if "e2t" in self.flags[ptop]:
3607
- self.tagq.put((ptop, wark, rd, fn, sz, ip, at))
3643
+ self.tagq.put((ptop, dwark, rd, fn, sz, ip, at))
3608
3644
  self.n_tagq += 1
3609
3645
 
3610
3646
  return True
@@ -3631,6 +3667,7 @@ class Up2k(object):
3631
3667
  ptop ,
3632
3668
  vtop ,
3633
3669
  wark ,
3670
+ dwark ,
3634
3671
  host ,
3635
3672
  usr ,
3636
3673
  ip ,
@@ -3647,12 +3684,12 @@ class Up2k(object):
3647
3684
  db_ip = "1.1.1.1" if self.args.no_db_ip else ip
3648
3685
 
3649
3686
  sql = "insert into up values (?,?,?,?,?,?,?)"
3650
- v = (wark, int(ts), sz, rd, fn, db_ip, int(at or 0))
3687
+ v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0))
3651
3688
  try:
3652
3689
  db.execute(sql, v)
3653
3690
  except:
3654
3691
  rd, fn = s3enc(self.mem_cur, rd, fn)
3655
- v = (wark, int(ts), sz, rd, fn, db_ip, int(at or 0))
3692
+ v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0))
3656
3693
  db.execute(sql, v)
3657
3694
 
3658
3695
  self.volsize[db] += sz
@@ -3696,10 +3733,10 @@ class Up2k(object):
3696
3733
  for cd in cds:
3697
3734
  # one for each unique cooldown duration
3698
3735
  try:
3699
- db.execute(q, (cd, wark[:16], rd, fn))
3736
+ db.execute(q, (cd, dwark[:16], rd, fn))
3700
3737
  except:
3701
3738
  rd, fn = s3enc(self.mem_cur, rd, fn)
3702
- db.execute(q, (cd, wark[:16], rd, fn))
3739
+ db.execute(q, (cd, dwark[:16], rd, fn))
3703
3740
 
3704
3741
  if self.xiu_asleep:
3705
3742
  self.xiu_asleep = False
@@ -3791,10 +3828,12 @@ class Up2k(object):
3791
3828
  with self.mutex, self.reg_mutex:
3792
3829
  abrt_cfg = self.flags.get(ptop, {}).get("u2abort", 1)
3793
3830
  addr = (ip or "\n") if abrt_cfg in (1, 2) else ""
3794
- user = (uname or "\n") if abrt_cfg in (1, 3) else ""
3831
+ user = ((uname or "\n"), "*") if abrt_cfg in (1, 3) else None
3795
3832
  reg = self.registry.get(ptop, {}) if abrt_cfg else {}
3796
3833
  for wark, job in reg.items():
3797
- if (user and user != job["user"]) or (addr and addr != job["addr"]):
3834
+ if (addr and addr != job["addr"]) or (
3835
+ user and job["user"] not in user
3836
+ ):
3798
3837
  continue
3799
3838
  jrem = djoin(job["prel"], job["name"])
3800
3839
  if ANYWIN:
@@ -4153,6 +4192,7 @@ class Up2k(object):
4153
4192
  dvn.realpath,
4154
4193
  dvn.vpath,
4155
4194
  w,
4195
+ w,
4156
4196
  "",
4157
4197
  "",
4158
4198
  ip or "",
@@ -4834,6 +4874,7 @@ class Up2k(object):
4834
4874
  ptop,
4835
4875
  vtop,
4836
4876
  wark,
4877
+ wark,
4837
4878
  "",
4838
4879
  usr,
4839
4880
  ip,
copyparty/util.py CHANGED
@@ -643,11 +643,15 @@ class HLog(logging.Handler):
643
643
 
644
644
 
645
645
  class NetMap(object):
646
- def __init__(self, ips , cidrs , keep_lo=False) :
646
+ def __init__(
647
+ self, ips , cidrs , keep_lo=False, strict_cidr=False
648
+ ) :
647
649
  """
648
650
  ips: list of plain ipv4/ipv6 IPs, not cidr
649
651
  cidrs: list of cidr-notation IPs (ip/prefix)
650
652
  """
653
+ self.mutex = threading.Lock()
654
+
651
655
  if "::" in ips:
652
656
  ips = [x for x in ips if x != "::"] + list(
653
657
  [x.split("/")[0] for x in cidrs if ":" in x]
@@ -674,7 +678,7 @@ class NetMap(object):
674
678
  bip = socket.inet_pton(fam, ip.split("/")[0])
675
679
  self.bip.append(bip)
676
680
  self.b2sip[bip] = ip.split("/")[0]
677
- self.b2net[bip] = (IPv6Network if v6 else IPv4Network)(ip, False)
681
+ self.b2net[bip] = (IPv6Network if v6 else IPv4Network)(ip, strict_cidr)
678
682
 
679
683
  self.bip.sort(reverse=True)
680
684
 
@@ -685,8 +689,10 @@ class NetMap(object):
685
689
  try:
686
690
  return self.cache[ip]
687
691
  except:
688
- pass
692
+ with self.mutex:
693
+ return self._map(ip)
689
694
 
695
+ def _map(self, ip ) :
690
696
  v6 = ":" in ip
691
697
  ci = IPv6Address(ip) if v6 else IPv4Address(ip)
692
698
  bip = next((x for x in self.bip if ci in self.b2net[x]), None)
@@ -1082,7 +1088,7 @@ class Magician(object):
1082
1088
  return ret
1083
1089
 
1084
1090
  mime = magic.from_file(fpath, mime=True)
1085
- mime = re.split("[; ]", mime, 1)[0]
1091
+ mime = re.split("[; ]", mime, maxsplit=1)[0]
1086
1092
  try:
1087
1093
  return EXTS[mime]
1088
1094
  except:
@@ -2592,6 +2598,31 @@ def build_netmap(csv ):
2592
2598
  return NetMap(ips, cidrs, True)
2593
2599
 
2594
2600
 
2601
+ def load_ipu(log , ipus ) :
2602
+ ip_u = {"": "*"}
2603
+ cidr_u = {}
2604
+ for ipu in ipus:
2605
+ try:
2606
+ cidr, uname = ipu.split("=")
2607
+ cip, csz = cidr.split("/")
2608
+ except:
2609
+ t = "\n invalid value %r for argument --ipu; must be CIDR=UNAME (192.168.0.0/16=amelia)"
2610
+ raise Exception(t % (ipu,))
2611
+ uname2 = cidr_u.get(cidr)
2612
+ if uname2 is not None:
2613
+ t = "\n invalid value %r for argument --ipu; cidr %s already mapped to %r"
2614
+ raise Exception(t % (ipu, cidr, uname2))
2615
+ cidr_u[cidr] = uname
2616
+ ip_u[cip] = uname
2617
+ try:
2618
+ nm = NetMap(["::"], list(cidr_u.keys()), True, True)
2619
+ except Exception as ex:
2620
+ t = "failed to translate --ipu into netmap, probably due to invalid config: %r"
2621
+ log("root", t % (ex,), 1)
2622
+ raise
2623
+ return ip_u, nm
2624
+
2625
+
2595
2626
  def yieldfile(fn , bufsz ) :
2596
2627
  readsz = min(bufsz, 128 * 1024)
2597
2628
  with open(fsenc(fn), "rb", bufsz) as f:
copyparty/web/a/u2c.py CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env python3
2
2
  from __future__ import print_function, unicode_literals
3
3
 
4
- S_VERSION = "2.1"
5
- S_BUILD_DT = "2024-09-23"
4
+ S_VERSION = "2.2"
5
+ S_BUILD_DT = "2024-10-13"
6
6
 
7
7
  """
8
8
  u2c.py: upload to copyparty
@@ -728,6 +728,7 @@ def handshake(ar, file, search):
728
728
  while True:
729
729
  sc = 600
730
730
  txt = ""
731
+ t0 = time.time()
731
732
  try:
732
733
  zs = json.dumps(req, separators=(",\n", ": "))
733
734
  sc, txt = web.req("POST", url, {}, zs.encode("utf-8"), MJ)
@@ -752,7 +753,9 @@ def handshake(ar, file, search):
752
753
  print("\nERROR: login required, or wrong password:\n%s" % (txt,))
753
754
  raise BadAuth()
754
755
 
755
- eprint("handshake failed, retrying: %s\n %s\n\n" % (file.name, em))
756
+ t = "handshake failed, retrying: %s\n t0=%.3f t1=%.3f td=%.3f\n %s\n\n"
757
+ now = time.time()
758
+ eprint(t % (file.name, t0, now, now - t0, em))
756
759
  time.sleep(ar.cd)
757
760
 
758
761
  try:
@@ -869,8 +872,8 @@ class Ctl(object):
869
872
  self.hash_b = 0
870
873
  self.up_f = 0
871
874
  self.up_c = 0
872
- self.up_b = 0
873
- self.up_br = 0
875
+ self.up_b = 0 # num bytes handled
876
+ self.up_br = 0 # num bytes actually transferred
874
877
  self.uploader_busy = 0
875
878
  self.serialized = False
876
879
 
@@ -1013,11 +1016,14 @@ class Ctl(object):
1013
1016
  t = "%s eta @ %s/s, %s, %d# left\033[K" % (self.eta, spd, sleft, nleft)
1014
1017
  eprint(txt + "\033]0;{0}\033\\\r{0}{1}".format(t, tail))
1015
1018
 
1019
+ if self.ar.wlist:
1020
+ self.at_hash = time.time() - self.t0
1021
+
1016
1022
  if self.hash_b and self.at_hash:
1017
1023
  spd = humansize(self.hash_b / self.at_hash)
1018
1024
  eprint("\nhasher: %.2f sec, %s/s\n" % (self.at_hash, spd))
1019
- if self.up_b and self.at_up:
1020
- spd = humansize(self.up_b / self.at_up)
1025
+ if self.up_br and self.at_up:
1026
+ spd = humansize(self.up_br / self.at_up)
1021
1027
  eprint("upload: %.2f sec, %s/s\n" % (self.at_up, spd))
1022
1028
 
1023
1029
  if not self.recheck:
@@ -1136,10 +1142,16 @@ class Ctl(object):
1136
1142
  self.up_b = self.hash_b
1137
1143
 
1138
1144
  if self.ar.wlist:
1145
+ vp = file.rel.decode("utf-8")
1146
+ if self.ar.chs:
1147
+ zsl = [
1148
+ "%s %d %d" % (zsii[0], n, zsii[1])
1149
+ for n, zsii in enumerate(file.cids)
1150
+ ]
1151
+ print("chs: %s\n%s" % (vp, "\n".join(zsl)))
1139
1152
  zsl = [self.ar.wsalt, str(file.size)] + [x[0] for x in file.kchunks]
1140
1153
  zb = hashlib.sha512("\n".join(zsl).encode("utf-8")).digest()[:33]
1141
1154
  wark = ub64enc(zb).decode("utf-8")
1142
- vp = file.rel.decode("utf-8")
1143
1155
  if self.ar.jw:
1144
1156
  print("%s %s" % (wark, vp))
1145
1157
  else:
@@ -1177,6 +1189,7 @@ class Ctl(object):
1177
1189
  self.q_upload.put(None)
1178
1190
  return
1179
1191
 
1192
+ chunksz = up2k_chunksize(file.size)
1180
1193
  upath = file.abs.decode("utf-8", "replace")
1181
1194
  if not VT100:
1182
1195
  upath = upath.lstrip("\\?")
@@ -1236,9 +1249,14 @@ class Ctl(object):
1236
1249
  file.up_c -= len(hs)
1237
1250
  for cid in hs:
1238
1251
  sz = file.kchunks[cid][1]
1252
+ self.up_br -= sz
1239
1253
  self.up_b -= sz
1240
1254
  file.up_b -= sz
1241
1255
 
1256
+ if hs and not file.up_b:
1257
+ # first hs of this file; is this an upload resume?
1258
+ file.up_b = chunksz * max(0, len(file.kchunks) - len(hs))
1259
+
1242
1260
  file.ucids = hs
1243
1261
 
1244
1262
  if not hs:
@@ -1252,7 +1270,7 @@ class Ctl(object):
1252
1270
  c1 = c2 = ""
1253
1271
 
1254
1272
  spd_h = humansize(file.size / file.t_hash, True)
1255
- if file.up_b:
1273
+ if file.up_c:
1256
1274
  t_up = file.t1_up - file.t0_up
1257
1275
  spd_u = humansize(file.size / t_up, True)
1258
1276
 
@@ -1262,13 +1280,12 @@ class Ctl(object):
1262
1280
  t = " found %s %s(%.2fs,%s/s)%s"
1263
1281
  print(t % (upath, c1, file.t_hash, spd_h, c2))
1264
1282
  else:
1265
- kw = "uploaded" if file.up_b else " found"
1283
+ kw = "uploaded" if file.up_c else " found"
1266
1284
  print("{0} {1}".format(kw, upath))
1267
1285
 
1268
1286
  self._check_if_done()
1269
1287
  continue
1270
1288
 
1271
- chunksz = up2k_chunksize(file.size)
1272
1289
  njoin = (self.ar.sz * 1024 * 1024) // chunksz
1273
1290
  cs = hs[:]
1274
1291
  while cs:
@@ -1365,7 +1382,7 @@ def main():
1365
1382
  cores = (os.cpu_count() if hasattr(os, "cpu_count") else 0) or 2
1366
1383
  hcores = min(cores, 3) # 4% faster than 4+ on py3.9 @ r5-4500U
1367
1384
 
1368
- ver = "{0} v{1} https://youtu.be/BIcOO6TLKaY".format(S_BUILD_DT, S_VERSION)
1385
+ ver = "{0}, v{1}".format(S_BUILD_DT, S_VERSION)
1369
1386
  if "--version" in sys.argv:
1370
1387
  print(ver)
1371
1388
  return
@@ -1403,6 +1420,7 @@ source file/folder selection uses rsync syntax, meaning that:
1403
1420
 
1404
1421
  ap = app.add_argument_group("file-ID calculator; enable with url '-' to list warks (file identifiers) instead of upload/search")
1405
1422
  ap.add_argument("--wsalt", type=unicode, metavar="S", default="hunter2", help="salt to use when creating warks; must match server config")
1423
+ ap.add_argument("--chs", action="store_true", help="verbose (print the hash/offset of each chunk in each file)")
1406
1424
  ap.add_argument("--jw", action="store_true", help="just identifier+filepath, not mtime/size too")
1407
1425
 
1408
1426
  ap = app.add_argument_group("performance tweaks")
Binary file
Binary file