copyparty 1.13.5__py3-none-any.whl → 1.13.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.
Files changed (46) hide show
  1. copyparty/__main__.py +25 -7
  2. copyparty/__version__.py +2 -2
  3. copyparty/authsrv.py +9 -6
  4. copyparty/cert.py +1 -1
  5. copyparty/fsutil.py +3 -3
  6. copyparty/ftpd.py +15 -2
  7. copyparty/httpcli.py +221 -81
  8. copyparty/httpconn.py +3 -0
  9. copyparty/httpsrv.py +35 -11
  10. copyparty/ico.py +1 -1
  11. copyparty/mtag.py +15 -6
  12. copyparty/pwhash.py +10 -0
  13. copyparty/smbd.py +20 -2
  14. copyparty/ssdp.py +3 -3
  15. copyparty/stolen/dnslib/dns.py +6 -0
  16. copyparty/stolen/ifaddr/__init__.py +15 -1
  17. copyparty/stolen/ifaddr/_shared.py +1 -0
  18. copyparty/stolen/qrcodegen.py +6 -0
  19. copyparty/sutil.py +1 -1
  20. copyparty/svchub.py +72 -3
  21. copyparty/szip.py +1 -3
  22. copyparty/tcpsrv.py +60 -8
  23. copyparty/tftpd.py +30 -4
  24. copyparty/th_srv.py +22 -1
  25. copyparty/u2idx.py +4 -1
  26. copyparty/up2k.py +222 -94
  27. copyparty/util.py +166 -31
  28. copyparty/web/a/u2c.py +22 -9
  29. copyparty/web/baguettebox.js.gz +0 -0
  30. copyparty/web/browser.css.gz +0 -0
  31. copyparty/web/browser.js.gz +0 -0
  32. copyparty/web/browser2.html +0 -1
  33. copyparty/web/md.html +3 -0
  34. copyparty/web/mde.html +3 -0
  35. copyparty/web/msg.html +3 -0
  36. copyparty/web/splash.html +3 -0
  37. copyparty/web/svcs.html +5 -2
  38. copyparty/web/ui.css.gz +0 -0
  39. copyparty/web/up2k.js.gz +0 -0
  40. copyparty/web/util.js.gz +0 -0
  41. {copyparty-1.13.5.dist-info → copyparty-1.13.7.dist-info}/METADATA +66 -14
  42. {copyparty-1.13.5.dist-info → copyparty-1.13.7.dist-info}/RECORD +46 -46
  43. {copyparty-1.13.5.dist-info → copyparty-1.13.7.dist-info}/WHEEL +1 -1
  44. {copyparty-1.13.5.dist-info → copyparty-1.13.7.dist-info}/LICENSE +0 -0
  45. {copyparty-1.13.5.dist-info → copyparty-1.13.7.dist-info}/entry_points.txt +0 -0
  46. {copyparty-1.13.5.dist-info → copyparty-1.13.7.dist-info}/top_level.txt +0 -0
copyparty/util.py CHANGED
@@ -26,7 +26,6 @@ import threading
26
26
  import time
27
27
  import traceback
28
28
  from collections import Counter
29
- from email.utils import formatdate
30
29
 
31
30
  from ipaddress import IPv4Address, IPv4Network, IPv6Address, IPv6Network
32
31
  from queue import Queue
@@ -60,6 +59,10 @@ except:
60
59
  UTC = _UTC()
61
60
 
62
61
 
62
+ if PY2:
63
+ range = xrange # type: ignore
64
+
65
+
63
66
  if sys.version_info >= (3, 7) or (
64
67
  sys.version_info >= (3, 6) and platform.python_implementation() == "CPython"
65
68
  ):
@@ -99,6 +102,9 @@ except:
99
102
  pass
100
103
 
101
104
  try:
105
+ if os.environ.get("PRTY_NO_SQLITE"):
106
+ raise Exception()
107
+
102
108
  HAVE_SQLITE3 = True
103
109
  import sqlite3
104
110
 
@@ -107,6 +113,9 @@ except:
107
113
  HAVE_SQLITE3 = False
108
114
 
109
115
  try:
116
+ if os.environ.get("PRTY_NO_PSUTIL"):
117
+ raise Exception()
118
+
110
119
  HAVE_PSUTIL = True
111
120
  import psutil
112
121
  except:
@@ -116,10 +125,15 @@ if TYPE_CHECKING:
116
125
  import magic
117
126
 
118
127
  from .authsrv import VFS
128
+ from .broker_util import BrokerCli
129
+ from .up2k import Up2k
119
130
 
120
131
  FAKE_MP = False
121
132
 
122
133
  try:
134
+ if os.environ.get("PRTY_NO_MP"):
135
+ raise ImportError()
136
+
123
137
  import multiprocessing as mp
124
138
 
125
139
  # import multiprocessing.dummy as mp
@@ -138,6 +152,9 @@ else:
138
152
 
139
153
 
140
154
  try:
155
+ if os.environ.get("PRTY_NO_IPV6"):
156
+ raise Exception()
157
+
141
158
  socket.inet_pton(socket.AF_INET6, "::1")
142
159
  HAVE_IPV6 = True
143
160
  except:
@@ -773,7 +790,7 @@ class CachedSet(object):
773
790
 
774
791
  c = self.c = {k: v for k, v in self.c.items() if now - v < self.maxage}
775
792
  try:
776
- self.oldest = c[min(c, key=c.get)]
793
+ self.oldest = c[min(c, key=c.get)] # type: ignore
777
794
  except:
778
795
  self.oldest = now
779
796
 
@@ -1800,10 +1817,21 @@ def gen_filekey_dbg(
1800
1817
  return ret
1801
1818
 
1802
1819
 
1820
+ WKDAYS = "Mon Tue Wed Thu Fri Sat Sun".split()
1821
+ MONTHS = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split()
1822
+ RFC2822 = "%s, %02d %s %04d %02d:%02d:%02d GMT"
1823
+
1824
+
1825
+ def formatdate(ts = None) :
1826
+ # gmtime ~= datetime.fromtimestamp(ts, UTC).timetuple()
1827
+ y, mo, d, h, mi, s, wd, _, _ = time.gmtime(ts)
1828
+ return RFC2822 % (WKDAYS[wd], d, MONTHS[mo - 1], y, h, mi, s)
1829
+
1830
+
1803
1831
  def gencookie(k , v , r , tls , dur = 0, txt = "") :
1804
1832
  v = v.replace("%", "%25").replace(";", "%3B")
1805
1833
  if dur:
1806
- exp = formatdate(time.time() + dur, usegmt=True)
1834
+ exp = formatdate(time.time() + dur)
1807
1835
  else:
1808
1836
  exp = "Fri, 15 Aug 1997 01:00:00 GMT"
1809
1837
 
@@ -1818,12 +1846,10 @@ def humansize(sz , terse = False) :
1818
1846
 
1819
1847
  sz /= 1024.0
1820
1848
 
1821
- ret = " ".join([str(sz)[:4].rstrip("."), unit])
1822
-
1823
- if not terse:
1824
- return ret
1825
-
1826
- return ret.replace("iB", "").replace(" ", "")
1849
+ if terse:
1850
+ return "%s%s" % (str(sz)[:4].rstrip("."), unit[:1])
1851
+ else:
1852
+ return "%s %s" % (str(sz)[:4].rstrip("."), unit)
1827
1853
 
1828
1854
 
1829
1855
  def unhumanize(sz ) :
@@ -1875,7 +1901,7 @@ def uncyg(path ) :
1875
1901
  def undot(path ) :
1876
1902
  ret = []
1877
1903
  for node in path.split("/"):
1878
- if node in ["", "."]:
1904
+ if node == "." or not node:
1879
1905
  continue
1880
1906
 
1881
1907
  if node == "..":
@@ -2028,7 +2054,7 @@ def _quotep2(txt ) :
2028
2054
  """url quoter which deals with bytes correctly"""
2029
2055
  btxt = w8enc(txt)
2030
2056
  quot = quote(btxt, safe=b"/")
2031
- return w8dec(quot.replace(b" ", b"+"))
2057
+ return w8dec(quot.replace(b" ", b"+")) # type: ignore
2032
2058
 
2033
2059
 
2034
2060
  def _quotep3(txt ) :
@@ -2072,6 +2098,72 @@ def ujoin(rd , fn ) :
2072
2098
  return rd or fn
2073
2099
 
2074
2100
 
2101
+ def log_reloc(
2102
+ log ,
2103
+ re ,
2104
+ pm ,
2105
+ ap ,
2106
+ vp ,
2107
+ fn ,
2108
+ vn ,
2109
+ rem ,
2110
+ ) :
2111
+ nap, nvp, nfn, (nvn, nrem) = pm
2112
+ t = "reloc %s:\nold ap [%s]\nnew ap [%s\033[36m/%s\033[0m]\nold vp [%s]\nnew vp [%s\033[36m/%s\033[0m]\nold fn [%s]\nnew fn [%s]\nold vfs [%s]\nnew vfs [%s]\nold rem [%s]\nnew rem [%s]"
2113
+ log(t % (re, ap, nap, nfn, vp, nvp, nfn, fn, nfn, vn.vpath, nvn.vpath, rem, nrem))
2114
+
2115
+
2116
+ def pathmod(
2117
+ vfs , ap , vp , mod
2118
+ ) :
2119
+ # vfs: authsrv.vfs
2120
+ # ap: original abspath to a file
2121
+ # vp: original urlpath to a file
2122
+ # mod: modification (ap/vp/fn)
2123
+
2124
+ nvp = "\n" # new vpath
2125
+ ap = os.path.dirname(ap)
2126
+ vp, fn = vsplit(vp)
2127
+ if mod.get("fn"):
2128
+ fn = mod["fn"]
2129
+ nvp = vp
2130
+
2131
+ for ref, k in ((ap, "ap"), (vp, "vp")):
2132
+ if k not in mod:
2133
+ continue
2134
+
2135
+ ms = mod[k].replace(os.sep, "/")
2136
+ if ms.startswith("/"):
2137
+ np = ms
2138
+ elif k == "vp":
2139
+ np = undot(vjoin(ref, ms))
2140
+ else:
2141
+ np = os.path.abspath(os.path.join(ref, ms))
2142
+
2143
+ if k == "vp":
2144
+ nvp = np.lstrip("/")
2145
+ continue
2146
+
2147
+ # try to map abspath to vpath
2148
+ np = np.replace("/", os.sep)
2149
+ for vn_ap, vn in vfs.all_aps:
2150
+ if not np.startswith(vn_ap):
2151
+ continue
2152
+ zs = np[len(vn_ap) :].replace(os.sep, "/")
2153
+ nvp = vjoin(vn.vpath, zs)
2154
+ break
2155
+
2156
+ if nvp == "\n":
2157
+ return None
2158
+
2159
+ vn, rem = vfs.get(nvp, "*", False, False)
2160
+ if not vn.realpath:
2161
+ raise Exception("unmapped vfs")
2162
+
2163
+ ap = vn.canonical(rem)
2164
+ return ap, nvp, fn, (vn, rem)
2165
+
2166
+
2075
2167
  def _w8dec2(txt ) :
2076
2168
  """decodes filesystem-bytes to wtf8"""
2077
2169
  return surrogateescape.decodefilename(txt)
@@ -2688,30 +2780,30 @@ def rmdirs_up(top , stop ) :
2688
2780
 
2689
2781
  def unescape_cookie(orig ) :
2690
2782
  # mw=idk; doot=qwe%2Crty%3Basd+fgh%2Bjkl%25zxc%26vbn # qwe,rty;asd fgh+jkl%zxc&vbn
2691
- ret = ""
2783
+ ret = []
2692
2784
  esc = ""
2693
2785
  for ch in orig:
2694
2786
  if ch == "%":
2695
- if len(esc) > 0:
2696
- ret += esc
2787
+ if esc:
2788
+ ret.append(esc)
2697
2789
  esc = ch
2698
2790
 
2699
- elif len(esc) > 0:
2791
+ elif esc:
2700
2792
  esc += ch
2701
2793
  if len(esc) == 3:
2702
2794
  try:
2703
- ret += chr(int(esc[1:], 16))
2795
+ ret.append(chr(int(esc[1:], 16)))
2704
2796
  except:
2705
- ret += esc
2797
+ ret.append(esc)
2706
2798
  esc = ""
2707
2799
 
2708
2800
  else:
2709
- ret += ch
2801
+ ret.append(ch)
2710
2802
 
2711
- if len(esc) > 0:
2712
- ret += esc
2803
+ if esc:
2804
+ ret.append(esc)
2713
2805
 
2714
- return ret
2806
+ return "".join(ret)
2715
2807
 
2716
2808
 
2717
2809
  def guess_mime(url , fallback = "application/octet-stream") :
@@ -3085,6 +3177,7 @@ def runihook(
3085
3177
 
3086
3178
  def _runhook(
3087
3179
  log ,
3180
+ src ,
3088
3181
  cmd ,
3089
3182
  ap ,
3090
3183
  vp ,
@@ -3096,14 +3189,16 @@ def _runhook(
3096
3189
  ip ,
3097
3190
  at ,
3098
3191
  txt ,
3099
- ) :
3192
+ ) :
3193
+ ret = {"rc": 0}
3100
3194
  areq, chk, fork, jtxt, wait, sp_ka, acmd = _parsehook(log, cmd)
3101
3195
  if areq:
3102
3196
  for ch in areq:
3103
3197
  if ch not in perms:
3104
3198
  t = "user %s not allowed to run hook %s; need perms %s, have %s"
3105
- log(t % (uname, cmd, areq, perms))
3106
- return True # fallthrough to next hook
3199
+ if log:
3200
+ log(t % (uname, cmd, areq, perms))
3201
+ return ret # fallthrough to next hook
3107
3202
  if jtxt:
3108
3203
  ja = {
3109
3204
  "ap": ap,
@@ -3115,6 +3210,7 @@ def _runhook(
3115
3210
  "host": host,
3116
3211
  "user": uname,
3117
3212
  "perms": perms,
3213
+ "src": src,
3118
3214
  "txt": txt,
3119
3215
  }
3120
3216
  arg = json.dumps(ja)
@@ -3133,18 +3229,34 @@ def _runhook(
3133
3229
  else:
3134
3230
  rc, v, err = runcmd(bcmd, **sp_ka) # type: ignore
3135
3231
  if chk and rc:
3232
+ ret["rc"] = rc
3136
3233
  retchk(rc, bcmd, err, log, 5)
3137
- return False
3234
+ else:
3235
+ try:
3236
+ ret = json.loads(v)
3237
+ except:
3238
+ ret = {}
3239
+
3240
+ try:
3241
+ if "stdout" not in ret:
3242
+ ret["stdout"] = v
3243
+ if "rc" not in ret:
3244
+ ret["rc"] = rc
3245
+ except:
3246
+ ret = {"rc": rc, "stdout": v}
3138
3247
 
3139
3248
  wait -= time.time() - t0
3140
3249
  if wait > 0:
3141
3250
  time.sleep(wait)
3142
3251
 
3143
- return True
3252
+ return ret
3144
3253
 
3145
3254
 
3146
3255
  def runhook(
3147
3256
  log ,
3257
+ broker ,
3258
+ up2k ,
3259
+ src ,
3148
3260
  cmds ,
3149
3261
  ap ,
3150
3262
  vp ,
@@ -3156,19 +3268,42 @@ def runhook(
3156
3268
  ip ,
3157
3269
  at ,
3158
3270
  txt ,
3159
- ) :
3271
+ ) :
3272
+ assert broker or up2k
3273
+ asrv = (broker or up2k).asrv
3274
+ args = (broker or up2k).args
3160
3275
  vp = vp.replace("\\", "/")
3276
+ ret = {"rc": 0}
3161
3277
  for cmd in cmds:
3162
3278
  try:
3163
- if not _runhook(log, cmd, ap, vp, host, uname, perms, mt, sz, ip, at, txt):
3164
- return False
3279
+ hr = _runhook(
3280
+ log, src, cmd, ap, vp, host, uname, perms, mt, sz, ip, at, txt
3281
+ )
3282
+ if log and args.hook_v:
3283
+ log("hook(%s) [%s] => \033[32m%s" % (src, cmd, hr), 6)
3284
+ if not hr:
3285
+ return {}
3286
+ for k, v in hr.items():
3287
+ if k in ("idx", "del") and v:
3288
+ if broker:
3289
+ broker.say("up2k.hook_fx", k, v, vp)
3290
+ else:
3291
+ up2k.fx_backlog.append((k, v, vp))
3292
+ elif k == "reloc" and v:
3293
+ # idk, just take the last one ig
3294
+ ret["reloc"] = v
3295
+ elif k in ret:
3296
+ if k == "rc" and v:
3297
+ ret[k] = v
3298
+ else:
3299
+ ret[k] = v
3165
3300
  except Exception as ex:
3166
3301
  (log or print)("hook: {}".format(ex))
3167
3302
  if ",c," in "," + cmd:
3168
- return False
3303
+ return {}
3169
3304
  break
3170
3305
 
3171
- return True
3306
+ return ret
3172
3307
 
3173
3308
 
3174
3309
  def loadpy(ap , hot ) :
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 = "1.20"
5
- S_BUILD_DT = "2024-07-22"
4
+ S_VERSION = "1.22"
5
+ S_BUILD_DT = "2024-08-08"
6
6
 
7
7
  """
8
8
  u2c.py: upload to copyparty
@@ -660,8 +660,15 @@ def upload(fsl, pw, stats):
660
660
  # type: (FileSlice, str, str) -> None
661
661
  """upload a range of file data, defined by one or more `cid` (chunk-hash)"""
662
662
 
663
+ ctxt = fsl.cids[0]
664
+ if len(fsl.cids) > 1:
665
+ n = 192 // len(fsl.cids)
666
+ n = 9 if n > 9 else 2 if n < 2 else n
667
+ zsl = [zs[:n] for zs in fsl.cids[1:]]
668
+ ctxt += ",%d,%s" % (n, "".join(zsl))
669
+
663
670
  headers = {
664
- "X-Up2k-Hash": ",".join(fsl.cids),
671
+ "X-Up2k-Hash": ctxt,
665
672
  "X-Up2k-Wark": fsl.file.wark,
666
673
  "Content-Type": "application/octet-stream",
667
674
  }
@@ -755,6 +762,7 @@ class Ctl(object):
755
762
  else:
756
763
  self.at_hash = 0.0
757
764
  self.at_up = 0.0
765
+ self.at_upr = 0.0
758
766
  self.hash_f = 0
759
767
  self.hash_c = 0
760
768
  self.hash_b = 0
@@ -903,8 +911,9 @@ class Ctl(object):
903
911
  t = "{0} eta @ {1}/s, {2}, {3}# left".format(self.eta, spd, sleft, nleft)
904
912
  eprint(txt + "\033]0;{0}\033\\\r{0}{1}".format(t, tail))
905
913
 
906
- spd = humansize(self.hash_b / self.at_hash)
907
- eprint("\nhasher: %.2f sec, %s/s\n" % (self.at_hash, spd))
914
+ if self.hash_b and self.at_hash:
915
+ spd = humansize(self.hash_b / self.at_hash)
916
+ eprint("\nhasher: %.2f sec, %s/s\n" % (self.at_hash, spd))
908
917
  if self.up_b and self.at_up:
909
918
  spd = humansize(self.up_b / self.at_up)
910
919
  eprint("upload: %.2f sec, %s/s\n" % (self.at_up, spd))
@@ -1099,9 +1108,6 @@ class Ctl(object):
1099
1108
 
1100
1109
  if not hs:
1101
1110
  self.at_hash += file.t_hash
1102
- if file.up_b:
1103
- t_up = file.t1_up - file.t0_up
1104
- self.at_up += t_up
1105
1111
 
1106
1112
  if self.ar.spd:
1107
1113
  if VT100:
@@ -1112,6 +1118,7 @@ class Ctl(object):
1112
1118
 
1113
1119
  spd_h = humansize(file.size / file.t_hash, True)
1114
1120
  if file.up_b:
1121
+ t_up = file.t1_up - file.t0_up
1115
1122
  spd_u = humansize(file.size / t_up, True)
1116
1123
 
1117
1124
  t = "uploaded %s %s(h:%.2fs,%s/s,up:%.2fs,%s/s)%s"
@@ -1123,13 +1130,15 @@ class Ctl(object):
1123
1130
  kw = "uploaded" if file.up_b else " found"
1124
1131
  print("{0} {1}".format(kw, upath))
1125
1132
 
1133
+ chunksz = up2k_chunksize(file.size)
1134
+ njoin = (self.ar.sz * 1024 * 1024) // chunksz
1126
1135
  cs = hs[:]
1127
1136
  while cs:
1128
1137
  fsl = FileSlice(file, cs[:1])
1129
1138
  try:
1130
1139
  if file.nojoin:
1131
1140
  raise Exception()
1132
- for n in range(2, min(len(cs), self.ar.sz) + 1):
1141
+ for n in range(2, min(len(cs), njoin + 1)):
1133
1142
  fsl = FileSlice(file, cs[:n])
1134
1143
  except:
1135
1144
  pass
@@ -1147,6 +1156,8 @@ class Ctl(object):
1147
1156
  cids = fsl.cids
1148
1157
 
1149
1158
  with self.mutex:
1159
+ if not self.uploader_busy:
1160
+ self.at_upr = time.time()
1150
1161
  self.uploader_busy += 1
1151
1162
  if not file.t0_up:
1152
1163
  file.t0_up = time.time()
@@ -1185,6 +1196,8 @@ class Ctl(object):
1185
1196
  file.up_c += 1
1186
1197
  self.up_c += 1
1187
1198
  self.uploader_busy -= 1
1199
+ if not self.uploader_busy:
1200
+ self.at_up += time.time() - self.at_upr
1188
1201
 
1189
1202
  def up_done(self, file):
1190
1203
  if self.ar.dl:
Binary file
Binary file
Binary file
@@ -11,7 +11,6 @@
11
11
  td{border:1px solid #999;border-width:1px 1px 0 0;padding:0 5px}
12
12
  a{display:block}
13
13
  </style>
14
- {{ html_head }}
15
14
  </head>
16
15
 
17
16
  <body>
copyparty/web/md.html CHANGED
@@ -159,5 +159,8 @@ try { l.light = drk? 0:1; } catch (ex) { }
159
159
  {%- if edit %}
160
160
  <script src="{{ r }}/.cpr/md2.js?_={{ ts }}"></script>
161
161
  {%- endif %}
162
+ {%- if js %}
163
+ <script src="{{ js }}_={{ ts }}"></script>
164
+ {%- endif %}
162
165
  </body></html>
163
166
 
copyparty/web/mde.html CHANGED
@@ -53,5 +53,8 @@ try { l.light = drk? 0:1; } catch (ex) { }
53
53
  <script src="{{ r }}/.cpr/deps/marked.js?_={{ ts }}"></script>
54
54
  <script src="{{ r }}/.cpr/deps/easymde.js?_={{ ts }}"></script>
55
55
  <script src="{{ r }}/.cpr/mde.js?_={{ ts }}"></script>
56
+ {%- if js %}
57
+ <script src="{{ js }}_={{ ts }}"></script>
58
+ {%- endif %}
56
59
  </body></html>
57
60
 
copyparty/web/msg.html CHANGED
@@ -46,6 +46,9 @@
46
46
  }, 1000);
47
47
  </script>
48
48
  {%- endif %}
49
+ {%- if js %}
50
+ <script src="{{ js }}_={{ ts }}"></script>
51
+ {%- endif %}
49
52
  </body>
50
53
 
51
54
  </html>
copyparty/web/splash.html CHANGED
@@ -119,6 +119,9 @@ document.documentElement.className = (STG && STG.cpp_thm) || "{{ this.args.theme
119
119
  </script>
120
120
  <script src="{{ r }}/.cpr/util.js?_={{ ts }}"></script>
121
121
  <script src="{{ r }}/.cpr/splash.js?_={{ ts }}"></script>
122
+ {%- if js %}
123
+ <script src="{{ js }}_={{ ts }}"></script>
124
+ {%- endif %}
122
125
  </body>
123
126
  </html>
124
127
 
copyparty/web/svcs.html CHANGED
@@ -56,7 +56,7 @@
56
56
  <li>running <code>rclone mount</code> as root? add <code>--allow-other</code></li>
57
57
  <li>old version of rclone? replace all <code>=</code> with <code>&nbsp;</code> (space)</li>
58
58
  </ul>
59
-
59
+
60
60
  <p>if you want to use the native WebDAV client in windows instead (slow and buggy), first run <a href="{{ r }}/.cpr/a/webdav-cfg.bat">webdav-cfg.bat</a> to remove the 47 MiB filesize limit (also fixes latency and password login), then connect:</p>
61
61
  <pre>
62
62
  net use <b>w:</b> http{{ s }}://{{ ep }}/{{ rvp }}{% if accs %} k /user:<b>{{ pw }}</b>{% endif %}
@@ -105,7 +105,7 @@
105
105
  <pre>
106
106
  http{{ s }}://k:<b>{{ pw }}</b>@{{ ep }}/{{ rvp }}
107
107
  </pre>
108
-
108
+
109
109
  {% if s %}
110
110
  <p><em>replace <code>https</code> with <code>http</code> if it doesn't work</em></p>
111
111
  {% endif %}
@@ -245,6 +245,9 @@ document.documentElement.className = (STG && STG.cpp_thm) || "{{ args.theme }}";
245
245
  </script>
246
246
  <script src="{{ r }}/.cpr/util.js?_={{ ts }}"></script>
247
247
  <script src="{{ r }}/.cpr/svcs.js?_={{ ts }}"></script>
248
+ {%- if js %}
249
+ <script src="{{ js }}_={{ ts }}"></script>
250
+ {%- endif %}
248
251
  </body>
249
252
  </html>
250
253
 
copyparty/web/ui.css.gz CHANGED
Binary file
copyparty/web/up2k.js.gz CHANGED
Binary file
copyparty/web/util.js.gz CHANGED
Binary file