copyparty 1.15.7__py3-none-any.whl → 1.15.8__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 +1 -1
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +1 -0
- copyparty/httpcli.py +54 -7
- copyparty/httpsrv.py +2 -0
- copyparty/up2k.py +32 -26
- copyparty/util.py +5 -3
- copyparty/web/a/u2c.py +100 -32
- copyparty/web/baguettebox.js.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/ui.css.gz +0 -0
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- copyparty/web/w.hash.js.gz +0 -0
- {copyparty-1.15.7.dist-info → copyparty-1.15.8.dist-info}/METADATA +5 -3
- {copyparty-1.15.7.dist-info → copyparty-1.15.8.dist-info}/RECORD +20 -20
- {copyparty-1.15.7.dist-info → copyparty-1.15.8.dist-info}/WHEEL +1 -1
- {copyparty-1.15.7.dist-info → copyparty-1.15.8.dist-info}/LICENSE +0 -0
- {copyparty-1.15.7.dist-info → copyparty-1.15.8.dist-info}/entry_points.txt +0 -0
- {copyparty-1.15.7.dist-info → copyparty-1.15.8.dist-info}/top_level.txt +0 -0
copyparty/__main__.py
CHANGED
@@ -1009,7 +1009,7 @@ def add_upload(ap):
|
|
1009
1009
|
ap2.add_argument("--sparse", metavar="MiB", type=int, default=4, help="windows-only: minimum size of incoming uploads through up2k before they are made into sparse files")
|
1010
1010
|
ap2.add_argument("--turbo", metavar="LVL", type=int, default=0, help="configure turbo-mode in up2k client; [\033[32m-1\033[0m] = forbidden/always-off, [\033[32m0\033[0m] = default-off and warn if enabled, [\033[32m1\033[0m] = default-off, [\033[32m2\033[0m] = on, [\033[32m3\033[0m] = on and disable datecheck")
|
1011
1011
|
ap2.add_argument("--u2j", metavar="JOBS", type=int, default=2, help="web-client: number of file chunks to upload in parallel; 1 or 2 is good for low-latency (same-country) connections, 4-8 for android clients, 16 for cross-atlantic (max=64)")
|
1012
|
-
ap2.add_argument("--u2sz", metavar="N,N,N", type=u, default="1,64,96", help="web-client: default upload chunksize (MiB); sets \033[33mmin,default,max\033[0m in the settings gui. Each HTTP POST will aim for
|
1012
|
+
ap2.add_argument("--u2sz", metavar="N,N,N", type=u, default="1,64,96", help="web-client: default upload chunksize (MiB); sets \033[33mmin,default,max\033[0m in the settings gui. Each HTTP POST will aim for \033[33mdefault\033[0m, and never exceed \033[33mmax\033[0m. Cloudflare max is 96. Big values are good for cross-atlantic but may increase HDD fragmentation on some FS. Disable this optimization with [\033[32m1,1,1\033[0m]")
|
1013
1013
|
ap2.add_argument("--u2sort", metavar="TXT", type=u, default="s", help="upload order; [\033[32ms\033[0m]=smallest-first, [\033[32mn\033[0m]=alphabetical, [\033[32mfs\033[0m]=force-s, [\033[32mfn\033[0m]=force-n -- alphabetical is a bit slower on fiber/LAN but makes it easier to eyeball if everything went fine")
|
1014
1014
|
ap2.add_argument("--write-uplog", action="store_true", help="write POST reports to textfiles in working-directory")
|
1015
1015
|
|
copyparty/__version__.py
CHANGED
copyparty/authsrv.py
CHANGED
copyparty/httpcli.py
CHANGED
@@ -1874,7 +1874,7 @@ class HttpCli(object):
|
|
1874
1874
|
f, fn = ren_open(fn, *open_a, **params)
|
1875
1875
|
try:
|
1876
1876
|
path = os.path.join(fdir, fn)
|
1877
|
-
post_sz, sha_hex, sha_b64 = hashcopy(reader, f, self.args.s_wr_slp)
|
1877
|
+
post_sz, sha_hex, sha_b64 = hashcopy(reader, f, None, 0, self.args.s_wr_slp)
|
1878
1878
|
finally:
|
1879
1879
|
f.close()
|
1880
1880
|
|
@@ -2337,7 +2337,7 @@ class HttpCli(object):
|
|
2337
2337
|
broker = self.conn.hsrv.broker
|
2338
2338
|
x = broker.ask("up2k.handle_chunks", ptop, wark, chashes)
|
2339
2339
|
response = x.get()
|
2340
|
-
chashes, chunksize, cstarts, path, lastmod, sprs = response
|
2340
|
+
chashes, chunksize, cstarts, path, lastmod, fsize, sprs = response
|
2341
2341
|
maxsize = chunksize * len(chashes)
|
2342
2342
|
cstart0 = cstarts[0]
|
2343
2343
|
locked = chashes # remaining chunks to be received in this request
|
@@ -2345,6 +2345,50 @@ class HttpCli(object):
|
|
2345
2345
|
num_left = -1 # num chunks left according to most recent up2k release
|
2346
2346
|
treport = time.time() # ratelimit up2k reporting to reduce overhead
|
2347
2347
|
|
2348
|
+
if "x-up2k-subc" in self.headers:
|
2349
|
+
sc_ofs = int(self.headers["x-up2k-subc"])
|
2350
|
+
chash = chashes[0]
|
2351
|
+
|
2352
|
+
u2sc = self.conn.hsrv.u2sc
|
2353
|
+
try:
|
2354
|
+
sc_pofs, hasher = u2sc[chash]
|
2355
|
+
if not sc_ofs:
|
2356
|
+
t = "client restarted the chunk; forgetting subchunk offset %d"
|
2357
|
+
self.log(t % (sc_pofs,))
|
2358
|
+
raise Exception()
|
2359
|
+
except:
|
2360
|
+
sc_pofs = 0
|
2361
|
+
hasher = hashlib.sha512()
|
2362
|
+
|
2363
|
+
et = "subchunk protocol error; resetting chunk "
|
2364
|
+
if sc_pofs != sc_ofs:
|
2365
|
+
u2sc.pop(chash, None)
|
2366
|
+
t = "%s[%s]: the expected resume-point was %d, not %d"
|
2367
|
+
raise Pebkac(400, t % (et, chash, sc_pofs, sc_ofs))
|
2368
|
+
if len(cstarts) > 1:
|
2369
|
+
u2sc.pop(chash, None)
|
2370
|
+
t = "%s[%s]: only a single subchunk can be uploaded in one request; you are sending %d chunks"
|
2371
|
+
raise Pebkac(400, t % (et, chash, len(cstarts)))
|
2372
|
+
csize = min(chunksize, fsize - cstart0[0])
|
2373
|
+
cstart0[0] += sc_ofs # also sets cstarts[0][0]
|
2374
|
+
sc_next_ofs = sc_ofs + postsize
|
2375
|
+
if sc_next_ofs > csize:
|
2376
|
+
u2sc.pop(chash, None)
|
2377
|
+
t = "%s[%s]: subchunk offset (%d) plus postsize (%d) exceeds chunksize (%d)"
|
2378
|
+
raise Pebkac(400, t % (et, chash, sc_ofs, postsize, csize))
|
2379
|
+
else:
|
2380
|
+
final_subchunk = sc_next_ofs == csize
|
2381
|
+
t = "subchunk %s %d:%d/%d %s"
|
2382
|
+
zs = "END" if final_subchunk else ""
|
2383
|
+
self.log(t % (chash[:15], sc_ofs, sc_next_ofs, csize, zs), 6)
|
2384
|
+
if final_subchunk:
|
2385
|
+
u2sc.pop(chash, None)
|
2386
|
+
else:
|
2387
|
+
u2sc[chash] = (sc_next_ofs, hasher)
|
2388
|
+
else:
|
2389
|
+
hasher = None
|
2390
|
+
final_subchunk = True
|
2391
|
+
|
2348
2392
|
try:
|
2349
2393
|
if self.args.nw:
|
2350
2394
|
path = os.devnull
|
@@ -2375,9 +2419,11 @@ class HttpCli(object):
|
|
2375
2419
|
reader = read_socket(
|
2376
2420
|
self.sr, self.args.s_rd_sz, min(remains, chunksize)
|
2377
2421
|
)
|
2378
|
-
post_sz, _, sha_b64 = hashcopy(
|
2422
|
+
post_sz, _, sha_b64 = hashcopy(
|
2423
|
+
reader, f, hasher, 0, self.args.s_wr_slp
|
2424
|
+
)
|
2379
2425
|
|
2380
|
-
if sha_b64 != chash:
|
2426
|
+
if sha_b64 != chash and final_subchunk:
|
2381
2427
|
try:
|
2382
2428
|
self.bakflip(
|
2383
2429
|
f, path, cstart[0], post_sz, chash, sha_b64, vfs.flags
|
@@ -2409,7 +2455,8 @@ class HttpCli(object):
|
|
2409
2455
|
|
2410
2456
|
# be quick to keep the tcp winsize scale;
|
2411
2457
|
# if we can't confirm rn then that's fine
|
2412
|
-
|
2458
|
+
if final_subchunk:
|
2459
|
+
written.append(chash)
|
2413
2460
|
now = time.time()
|
2414
2461
|
if now - treport < 1:
|
2415
2462
|
continue
|
@@ -2797,7 +2844,7 @@ class HttpCli(object):
|
|
2797
2844
|
tabspath = os.path.join(fdir, tnam)
|
2798
2845
|
self.log("writing to {}".format(tabspath))
|
2799
2846
|
sz, sha_hex, sha_b64 = hashcopy(
|
2800
|
-
p_data, f, self.args.s_wr_slp
|
2847
|
+
p_data, f, None, max_sz, self.args.s_wr_slp
|
2801
2848
|
)
|
2802
2849
|
if sz == 0:
|
2803
2850
|
raise Pebkac(400, "empty files in post")
|
@@ -3127,7 +3174,7 @@ class HttpCli(object):
|
|
3127
3174
|
wunlink(self.log, fp, vfs.flags)
|
3128
3175
|
|
3129
3176
|
with open(fsenc(fp), "wb", self.args.iobuf) as f:
|
3130
|
-
sz, sha512, _ = hashcopy(p_data, f, self.args.s_wr_slp)
|
3177
|
+
sz, sha512, _ = hashcopy(p_data, f, None, 0, self.args.s_wr_slp)
|
3131
3178
|
|
3132
3179
|
if lim:
|
3133
3180
|
lim.nup(self.ip)
|
copyparty/httpsrv.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
from __future__ import print_function, unicode_literals
|
3
3
|
|
4
|
+
import hashlib
|
4
5
|
import math
|
5
6
|
import os
|
6
7
|
import re
|
@@ -141,6 +142,7 @@ class HttpSrv(object):
|
|
141
142
|
self.t_periodic = None
|
142
143
|
|
143
144
|
self.u2fh = FHC()
|
145
|
+
self.u2sc = {}
|
144
146
|
self.pipes = CachedDict(0.2)
|
145
147
|
self.metrics = Metrics(self)
|
146
148
|
self.nreq = 0
|
copyparty/up2k.py
CHANGED
@@ -20,7 +20,7 @@ from copy import deepcopy
|
|
20
20
|
from queue import Queue
|
21
21
|
|
22
22
|
from .__init__ import ANYWIN, PY2, TYPE_CHECKING, WINDOWS, E
|
23
|
-
from .authsrv import LEELOO_DALLAS,
|
23
|
+
from .authsrv import LEELOO_DALLAS, SEESLOG, VFS, AuthSrv
|
24
24
|
from .bos import bos
|
25
25
|
from .cfg import vf_bmap, vf_cmap, vf_vmap
|
26
26
|
from .fsutil import Fstab
|
@@ -2876,9 +2876,6 @@ class Up2k(object):
|
|
2876
2876
|
"user": cj["user"],
|
2877
2877
|
"addr": ip,
|
2878
2878
|
"at": at,
|
2879
|
-
"hash": [],
|
2880
|
-
"need": [],
|
2881
|
-
"busy": {},
|
2882
2879
|
}
|
2883
2880
|
for k in ["life"]:
|
2884
2881
|
if k in cj:
|
@@ -2912,17 +2909,20 @@ class Up2k(object):
|
|
2912
2909
|
hashes2, st = self._hashlist_from_file(orig_ap)
|
2913
2910
|
wark2 = up2k_wark_from_hashlist(self.salt, st.st_size, hashes2)
|
2914
2911
|
if dwark != wark2:
|
2915
|
-
t = "will not dedup (fs index desync): fs=%s, db=%s, file: %s"
|
2916
|
-
self.log(t % (wark2, dwark, orig_ap))
|
2912
|
+
t = "will not dedup (fs index desync): fs=%s, db=%s, file: %s\n%s"
|
2913
|
+
self.log(t % (wark2, dwark, orig_ap, rj))
|
2917
2914
|
lost.append(dupe[3:])
|
2918
2915
|
continue
|
2919
2916
|
data_ok = True
|
2920
2917
|
job = rj
|
2921
2918
|
break
|
2922
2919
|
|
2923
|
-
if job
|
2924
|
-
|
2925
|
-
|
2920
|
+
if job:
|
2921
|
+
if wark in reg:
|
2922
|
+
del reg[wark]
|
2923
|
+
job["hash"] = job["need"] = []
|
2924
|
+
job["done"] = True
|
2925
|
+
job["busy"] = {}
|
2926
2926
|
|
2927
2927
|
if lost:
|
2928
2928
|
c2 = None
|
@@ -2950,7 +2950,7 @@ class Up2k(object):
|
|
2950
2950
|
path = djoin(rj["ptop"], rj["prel"], fn)
|
2951
2951
|
try:
|
2952
2952
|
st = bos.stat(path)
|
2953
|
-
if st.st_size > 0 or
|
2953
|
+
if st.st_size > 0 or "done" in rj:
|
2954
2954
|
# upload completed or both present
|
2955
2955
|
break
|
2956
2956
|
except:
|
@@ -2964,13 +2964,13 @@ class Up2k(object):
|
|
2964
2964
|
inc_ap = djoin(cj["ptop"], cj["prel"], cj["name"])
|
2965
2965
|
orig_ap = djoin(rj["ptop"], rj["prel"], rj["name"])
|
2966
2966
|
|
2967
|
-
if self.args.nw or n4g or not st:
|
2967
|
+
if self.args.nw or n4g or not st or "done" not in rj:
|
2968
2968
|
pass
|
2969
2969
|
|
2970
2970
|
elif st.st_size != rj["size"]:
|
2971
|
-
t = "will not dedup (fs index desync): {}, size fs={} db={}, mtime fs={} db={}, file: {}"
|
2971
|
+
t = "will not dedup (fs index desync): {}, size fs={} db={}, mtime fs={} db={}, file: {}\n{}"
|
2972
2972
|
t = t.format(
|
2973
|
-
wark, st.st_size, rj["size"], st.st_mtime, rj["lmod"], path
|
2973
|
+
wark, st.st_size, rj["size"], st.st_mtime, rj["lmod"], path, rj
|
2974
2974
|
)
|
2975
2975
|
self.log(t)
|
2976
2976
|
del reg[wark]
|
@@ -2980,8 +2980,8 @@ class Up2k(object):
|
|
2980
2980
|
hashes2, _ = self._hashlist_from_file(orig_ap)
|
2981
2981
|
wark2 = up2k_wark_from_hashlist(self.salt, st.st_size, hashes2)
|
2982
2982
|
if wark != wark2:
|
2983
|
-
t = "will not dedup (fs index desync): fs=%s, idx=%s, file: %s"
|
2984
|
-
self.log(t % (wark2, wark, orig_ap))
|
2983
|
+
t = "will not dedup (fs index desync): fs=%s, idx=%s, file: %s\n%s"
|
2984
|
+
self.log(t % (wark2, wark, orig_ap, rj))
|
2985
2985
|
del reg[wark]
|
2986
2986
|
|
2987
2987
|
if job or wark in reg:
|
@@ -2996,7 +2996,7 @@ class Up2k(object):
|
|
2996
2996
|
dst = djoin(cj["ptop"], cj["prel"], cj["name"])
|
2997
2997
|
vsrc = djoin(job["vtop"], job["prel"], job["name"])
|
2998
2998
|
vsrc = vsrc.replace("\\", "/") # just for prints anyways
|
2999
|
-
if
|
2999
|
+
if "done" not in job:
|
3000
3000
|
self.log("unfinished:\n {0}\n {1}".format(src, dst))
|
3001
3001
|
err = "partial upload exists at a different location; please resume uploading here instead:\n"
|
3002
3002
|
err += "/" + quotep(vsrc) + " "
|
@@ -3357,14 +3357,14 @@ class Up2k(object):
|
|
3357
3357
|
|
3358
3358
|
def handle_chunks(
|
3359
3359
|
self, ptop , wark , chashes
|
3360
|
-
)
|
3360
|
+
) :
|
3361
3361
|
with self.mutex, self.reg_mutex:
|
3362
3362
|
self.db_act = self.vol_act[ptop] = time.time()
|
3363
3363
|
job = self.registry[ptop].get(wark)
|
3364
3364
|
if not job:
|
3365
3365
|
known = " ".join([x for x in self.registry[ptop].keys()])
|
3366
3366
|
self.log("unknown wark [{}], known: {}".format(wark, known))
|
3367
|
-
raise Pebkac(400, "unknown wark" +
|
3367
|
+
raise Pebkac(400, "unknown wark" + SEESLOG)
|
3368
3368
|
|
3369
3369
|
if "t0c" not in job:
|
3370
3370
|
job["t0c"] = time.time()
|
@@ -3380,7 +3380,7 @@ class Up2k(object):
|
|
3380
3380
|
try:
|
3381
3381
|
nchunk = uniq.index(chashes[0])
|
3382
3382
|
except:
|
3383
|
-
raise Pebkac(400, "unknown chunk0 [%s]" % (chashes[0]))
|
3383
|
+
raise Pebkac(400, "unknown chunk0 [%s]" % (chashes[0],))
|
3384
3384
|
expanded = [chashes[0]]
|
3385
3385
|
for prefix in chashes[1:]:
|
3386
3386
|
nchunk += 1
|
@@ -3414,7 +3414,7 @@ class Up2k(object):
|
|
3414
3414
|
for chash in chashes:
|
3415
3415
|
nchunk = [n for n, v in enumerate(job["hash"]) if v == chash]
|
3416
3416
|
if not nchunk:
|
3417
|
-
raise Pebkac(400, "unknown chunk %s" % (chash))
|
3417
|
+
raise Pebkac(400, "unknown chunk %s" % (chash,))
|
3418
3418
|
|
3419
3419
|
ofs = [chunksize * x for x in nchunk]
|
3420
3420
|
coffsets.append(ofs)
|
@@ -3439,7 +3439,7 @@ class Up2k(object):
|
|
3439
3439
|
|
3440
3440
|
job["poke"] = time.time()
|
3441
3441
|
|
3442
|
-
return chashes, chunksize, coffsets, path, job["lmod"], job["sprs"]
|
3442
|
+
return chashes, chunksize, coffsets, path, job["lmod"], job["size"], job["sprs"]
|
3443
3443
|
|
3444
3444
|
def fast_confirm_chunks(
|
3445
3445
|
self, ptop , wark , chashes
|
@@ -3508,11 +3508,13 @@ class Up2k(object):
|
|
3508
3508
|
src = djoin(pdir, job["tnam"])
|
3509
3509
|
dst = djoin(pdir, job["name"])
|
3510
3510
|
except Exception as ex:
|
3511
|
-
|
3511
|
+
self.log(min_ex(), 1)
|
3512
|
+
raise Pebkac(500, "finish_upload, wark, %r%s" % (ex, SEESLOG))
|
3512
3513
|
|
3513
3514
|
if job["need"]:
|
3514
|
-
|
3515
|
-
|
3515
|
+
self.log(min_ex(), 1)
|
3516
|
+
t = "finish_upload %s with remaining chunks %s%s"
|
3517
|
+
raise Pebkac(500, t % (wark, job["need"], SEESLOG))
|
3516
3518
|
|
3517
3519
|
upt = job.get("at") or time.time()
|
3518
3520
|
vflags = self.flags[ptop]
|
@@ -4033,7 +4035,9 @@ class Up2k(object):
|
|
4033
4035
|
self.db_act = self.vol_act[dbv.realpath] = time.time()
|
4034
4036
|
svpf = "/".join(x for x in [dbv.vpath, vrem, fn[0]] if x)
|
4035
4037
|
if not svpf.startswith(svp + "/"): # assert
|
4036
|
-
|
4038
|
+
self.log(min_ex(), 1)
|
4039
|
+
t = "mv: bug at %s, top %s%s"
|
4040
|
+
raise Pebkac(500, t % (svpf, svp, SEESLOG))
|
4037
4041
|
|
4038
4042
|
dvpf = dvp + svpf[len(svp) :]
|
4039
4043
|
self._mv_file(uname, ip, svpf, dvpf, curs)
|
@@ -4048,7 +4052,9 @@ class Up2k(object):
|
|
4048
4052
|
for zsl in (rm_ok, rm_ng):
|
4049
4053
|
for ap in reversed(zsl):
|
4050
4054
|
if not ap.startswith(sabs):
|
4051
|
-
|
4055
|
+
self.log(min_ex(), 1)
|
4056
|
+
t = "mv_d: bug at %s, top %s%s"
|
4057
|
+
raise Pebkac(500, t % (ap, sabs, SEESLOG))
|
4052
4058
|
|
4053
4059
|
rem = ap[len(sabs) :].replace(os.sep, "/").lstrip("/")
|
4054
4060
|
vp = vjoin(dvp, rem)
|
copyparty/util.py
CHANGED
@@ -2637,10 +2637,12 @@ def yieldfile(fn , bufsz ) :
|
|
2637
2637
|
def hashcopy(
|
2638
2638
|
fin ,
|
2639
2639
|
fout ,
|
2640
|
-
|
2641
|
-
max_sz
|
2640
|
+
hashobj ,
|
2641
|
+
max_sz ,
|
2642
|
+
slp ,
|
2642
2643
|
) :
|
2643
|
-
|
2644
|
+
if not hashobj:
|
2645
|
+
hashobj = hashlib.sha512()
|
2644
2646
|
tlen = 0
|
2645
2647
|
for buf in fin:
|
2646
2648
|
tlen += len(buf)
|
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.
|
5
|
-
S_BUILD_DT = "2024-10-
|
4
|
+
S_VERSION = "2.4"
|
5
|
+
S_BUILD_DT = "2024-10-16"
|
6
6
|
|
7
7
|
"""
|
8
8
|
u2c.py: upload to copyparty
|
@@ -62,6 +62,9 @@ else:
|
|
62
62
|
|
63
63
|
unicode = str
|
64
64
|
|
65
|
+
|
66
|
+
WTF8 = "replace" if PY2 else "surrogateescape"
|
67
|
+
|
65
68
|
VT100 = platform.system() != "Windows"
|
66
69
|
|
67
70
|
|
@@ -228,7 +231,7 @@ class File(object):
|
|
228
231
|
self.lmod = lmod # type: float
|
229
232
|
|
230
233
|
self.abs = os.path.join(top, rel) # type: bytes
|
231
|
-
self.name = self.rel.split(b"/")[-1].decode("utf-8",
|
234
|
+
self.name = self.rel.split(b"/")[-1].decode("utf-8", WTF8) # type: str
|
232
235
|
|
233
236
|
# set by get_hashlist
|
234
237
|
self.cids = [] # type: list[tuple[str, int, int]] # [ hash, ofs, sz ]
|
@@ -267,10 +270,41 @@ class FileSlice(object):
|
|
267
270
|
raise Exception(9)
|
268
271
|
tlen += clen
|
269
272
|
|
270
|
-
self.len = tlen
|
273
|
+
self.len = self.tlen = tlen
|
271
274
|
self.cdr = self.car + self.len
|
272
275
|
self.ofs = 0 # type: int
|
273
|
-
|
276
|
+
|
277
|
+
self.f = None
|
278
|
+
self.seek = self._seek0
|
279
|
+
self.read = self._read0
|
280
|
+
|
281
|
+
def subchunk(self, maxsz, nth):
|
282
|
+
if self.tlen <= maxsz:
|
283
|
+
return -1
|
284
|
+
|
285
|
+
if not nth:
|
286
|
+
self.car0 = self.car
|
287
|
+
self.cdr0 = self.cdr
|
288
|
+
|
289
|
+
self.car = self.car0 + maxsz * nth
|
290
|
+
if self.car >= self.cdr0:
|
291
|
+
return -2
|
292
|
+
|
293
|
+
self.cdr = self.car + min(self.cdr0 - self.car, maxsz)
|
294
|
+
self.len = self.cdr - self.car
|
295
|
+
self.seek(0)
|
296
|
+
return nth
|
297
|
+
|
298
|
+
def unsub(self):
|
299
|
+
self.car = self.car0
|
300
|
+
self.cdr = self.cdr0
|
301
|
+
self.len = self.tlen
|
302
|
+
|
303
|
+
def _open(self):
|
304
|
+
self.seek = self._seek
|
305
|
+
self.read = self._read
|
306
|
+
|
307
|
+
self.f = open(self.file.abs, "rb", 512 * 1024)
|
274
308
|
self.f.seek(self.car)
|
275
309
|
|
276
310
|
# https://stackoverflow.com/questions/4359495/what-is-exactly-a-file-like-object-in-python
|
@@ -282,10 +316,14 @@ class FileSlice(object):
|
|
282
316
|
except:
|
283
317
|
pass # py27 probably
|
284
318
|
|
319
|
+
def close(self, *a, **ka):
|
320
|
+
return # until _open
|
321
|
+
|
285
322
|
def tell(self):
|
286
323
|
return self.ofs
|
287
324
|
|
288
|
-
def
|
325
|
+
def _seek(self, ofs, wh=0):
|
326
|
+
|
289
327
|
if wh == 1:
|
290
328
|
ofs = self.ofs + ofs
|
291
329
|
elif wh == 2:
|
@@ -299,12 +337,21 @@ class FileSlice(object):
|
|
299
337
|
self.ofs = ofs
|
300
338
|
self.f.seek(self.car + ofs)
|
301
339
|
|
302
|
-
def
|
340
|
+
def _read(self, sz):
|
341
|
+
|
303
342
|
sz = min(sz, self.len - self.ofs)
|
304
343
|
ret = self.f.read(sz)
|
305
344
|
self.ofs += len(ret)
|
306
345
|
return ret
|
307
346
|
|
347
|
+
def _seek0(self, ofs, wh=0):
|
348
|
+
self._open()
|
349
|
+
return self.seek(ofs, wh)
|
350
|
+
|
351
|
+
def _read0(self, sz):
|
352
|
+
self._open()
|
353
|
+
return self.read(sz)
|
354
|
+
|
308
355
|
|
309
356
|
class MTHash(object):
|
310
357
|
def __init__(self, cores):
|
@@ -557,13 +604,17 @@ def walkdir(err, top, excl, seen):
|
|
557
604
|
for ap, inf in sorted(statdir(err, top)):
|
558
605
|
if excl.match(ap):
|
559
606
|
continue
|
560
|
-
yield ap, inf
|
561
607
|
if stat.S_ISDIR(inf.st_mode):
|
608
|
+
yield ap, inf
|
562
609
|
try:
|
563
610
|
for x in walkdir(err, ap, excl, seen):
|
564
611
|
yield x
|
565
612
|
except Exception as ex:
|
566
613
|
err.append((ap, str(ex)))
|
614
|
+
elif stat.S_ISREG(inf.st_mode):
|
615
|
+
yield ap, inf
|
616
|
+
else:
|
617
|
+
err.append((ap, "irregular filetype 0%o" % (inf.st_mode,)))
|
567
618
|
|
568
619
|
|
569
620
|
def walkdirs(err, tops, excl):
|
@@ -609,11 +660,12 @@ def walkdirs(err, tops, excl):
|
|
609
660
|
|
610
661
|
# mostly from copyparty/util.py
|
611
662
|
def quotep(btxt):
|
663
|
+
# type: (bytes) -> bytes
|
612
664
|
quot1 = quote(btxt, safe=b"/")
|
613
665
|
if not PY2:
|
614
666
|
quot1 = quot1.encode("ascii")
|
615
667
|
|
616
|
-
return quot1.replace(b" ", b"
|
668
|
+
return quot1.replace(b" ", b"%20") # type: ignore
|
617
669
|
|
618
670
|
|
619
671
|
# from copyparty/util.py
|
@@ -641,7 +693,7 @@ def up2k_chunksize(filesize):
|
|
641
693
|
while True:
|
642
694
|
for mul in [1, 2]:
|
643
695
|
nchunks = math.ceil(filesize * 1.0 / chunksize)
|
644
|
-
if nchunks <= 256 or (chunksize >= 32 * 1024 * 1024 and nchunks
|
696
|
+
if nchunks <= 256 or (chunksize >= 32 * 1024 * 1024 and nchunks <= 4096):
|
645
697
|
return chunksize
|
646
698
|
|
647
699
|
chunksize += stepsize
|
@@ -720,7 +772,7 @@ def handshake(ar, file, search):
|
|
720
772
|
url = file.url
|
721
773
|
else:
|
722
774
|
if b"/" in file.rel:
|
723
|
-
url = quotep(file.rel.rsplit(b"/", 1)[0]).decode("utf-8"
|
775
|
+
url = quotep(file.rel.rsplit(b"/", 1)[0]).decode("utf-8")
|
724
776
|
else:
|
725
777
|
url = ""
|
726
778
|
url = ar.vtop + url
|
@@ -766,15 +818,15 @@ def handshake(ar, file, search):
|
|
766
818
|
if search:
|
767
819
|
return r["hits"], False
|
768
820
|
|
769
|
-
file.url = r["purl"]
|
821
|
+
file.url = quotep(r["purl"].encode("utf-8", WTF8)).decode("utf-8")
|
770
822
|
file.name = r["name"]
|
771
823
|
file.wark = r["wark"]
|
772
824
|
|
773
825
|
return r["hash"], r["sprs"]
|
774
826
|
|
775
827
|
|
776
|
-
def upload(fsl, stats):
|
777
|
-
# type: (FileSlice, str) -> None
|
828
|
+
def upload(fsl, stats, maxsz):
|
829
|
+
# type: (FileSlice, str, int) -> None
|
778
830
|
"""upload a range of file data, defined by one or more `cid` (chunk-hash)"""
|
779
831
|
|
780
832
|
ctxt = fsl.cids[0]
|
@@ -792,21 +844,33 @@ def upload(fsl, stats):
|
|
792
844
|
if stats:
|
793
845
|
headers["X-Up2k-Stat"] = stats
|
794
846
|
|
847
|
+
nsub = 0
|
795
848
|
try:
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
849
|
+
while nsub != -1:
|
850
|
+
nsub = fsl.subchunk(maxsz, nsub)
|
851
|
+
if nsub == -2:
|
852
|
+
return
|
853
|
+
if nsub >= 0:
|
854
|
+
headers["X-Up2k-Subc"] = str(maxsz * nsub)
|
855
|
+
headers.pop(CLEN, None)
|
856
|
+
nsub += 1
|
857
|
+
|
858
|
+
sc, txt = web.req("POST", fsl.file.url, headers, fsl, MO)
|
859
|
+
|
860
|
+
if sc == 400:
|
861
|
+
if (
|
862
|
+
"already being written" in txt
|
863
|
+
or "already got that" in txt
|
864
|
+
or "only sibling chunks" in txt
|
865
|
+
):
|
866
|
+
fsl.file.nojoin = 1
|
867
|
+
|
868
|
+
if sc >= 400:
|
869
|
+
raise Exception("http %s: %s" % (sc, txt))
|
808
870
|
finally:
|
809
871
|
fsl.f.close()
|
872
|
+
if nsub != -1:
|
873
|
+
fsl.unsub()
|
810
874
|
|
811
875
|
|
812
876
|
class Ctl(object):
|
@@ -938,7 +1002,7 @@ class Ctl(object):
|
|
938
1002
|
print(" %d up %s" % (ncs - nc, cid))
|
939
1003
|
stats = "%d/0/0/%d" % (nf, self.nfiles - nf)
|
940
1004
|
fslice = FileSlice(file, [cid])
|
941
|
-
upload(fslice, stats)
|
1005
|
+
upload(fslice, stats, self.ar.szm)
|
942
1006
|
|
943
1007
|
print(" ok!")
|
944
1008
|
if file.recheck:
|
@@ -1057,7 +1121,7 @@ class Ctl(object):
|
|
1057
1121
|
print(" ls ~{0}".format(srd))
|
1058
1122
|
zt = (
|
1059
1123
|
self.ar.vtop,
|
1060
|
-
quotep(rd.replace(b"\\", b"/")).decode("utf-8"
|
1124
|
+
quotep(rd.replace(b"\\", b"/")).decode("utf-8"),
|
1061
1125
|
)
|
1062
1126
|
sc, txt = web.req("GET", "%s%s?ls<&dots" % zt, {})
|
1063
1127
|
if sc >= 400:
|
@@ -1066,7 +1130,7 @@ class Ctl(object):
|
|
1066
1130
|
j = json.loads(txt)
|
1067
1131
|
for f in j["dirs"] + j["files"]:
|
1068
1132
|
rfn = f["href"].split("?")[0].rstrip("/")
|
1069
|
-
ls[unquote(rfn.encode("utf-8",
|
1133
|
+
ls[unquote(rfn.encode("utf-8", WTF8))] = f
|
1070
1134
|
except Exception as ex:
|
1071
1135
|
print(" mkdir ~{0} ({1})".format(srd, ex))
|
1072
1136
|
|
@@ -1080,7 +1144,7 @@ class Ctl(object):
|
|
1080
1144
|
lnodes = [x.split(b"/")[-1] for x in zls]
|
1081
1145
|
bnames = [x for x in ls if x not in lnodes and x != b".hist"]
|
1082
1146
|
vpath = self.ar.url.split("://")[-1].split("/", 1)[-1]
|
1083
|
-
names = [x.decode("utf-8",
|
1147
|
+
names = [x.decode("utf-8", WTF8) for x in bnames]
|
1084
1148
|
locs = [vpath + srd + "/" + x for x in names]
|
1085
1149
|
while locs:
|
1086
1150
|
req = locs
|
@@ -1286,7 +1350,7 @@ class Ctl(object):
|
|
1286
1350
|
self._check_if_done()
|
1287
1351
|
continue
|
1288
1352
|
|
1289
|
-
njoin =
|
1353
|
+
njoin = self.ar.sz // chunksz
|
1290
1354
|
cs = hs[:]
|
1291
1355
|
while cs:
|
1292
1356
|
fsl = FileSlice(file, cs[:1])
|
@@ -1338,7 +1402,7 @@ class Ctl(object):
|
|
1338
1402
|
)
|
1339
1403
|
|
1340
1404
|
try:
|
1341
|
-
upload(fsl, stats)
|
1405
|
+
upload(fsl, stats, self.ar.szm)
|
1342
1406
|
except Exception as ex:
|
1343
1407
|
t = "upload failed, retrying: %s #%s+%d (%s)\n"
|
1344
1408
|
eprint(t % (file.name, cids[0][:8], len(cids) - 1, ex))
|
@@ -1427,6 +1491,7 @@ source file/folder selection uses rsync syntax, meaning that:
|
|
1427
1491
|
ap.add_argument("-j", type=int, metavar="CONNS", default=2, help="parallel connections")
|
1428
1492
|
ap.add_argument("-J", type=int, metavar="CORES", default=hcores, help="num cpu-cores to use for hashing; set 0 or 1 for single-core hashing")
|
1429
1493
|
ap.add_argument("--sz", type=int, metavar="MiB", default=64, help="try to make each POST this big")
|
1494
|
+
ap.add_argument("--szm", type=int, metavar="MiB", default=96, help="max size of each POST (default is cloudflare max)")
|
1430
1495
|
ap.add_argument("-nh", action="store_true", help="disable hashing while uploading")
|
1431
1496
|
ap.add_argument("-ns", action="store_true", help="no status panel (for slow consoles and macos)")
|
1432
1497
|
ap.add_argument("--cd", type=float, metavar="SEC", default=5, help="delay before reattempting a failed handshake/upload")
|
@@ -1454,6 +1519,9 @@ source file/folder selection uses rsync syntax, meaning that:
|
|
1454
1519
|
if ar.dr:
|
1455
1520
|
ar.ow = True
|
1456
1521
|
|
1522
|
+
ar.sz *= 1024 * 1024
|
1523
|
+
ar.szm *= 1024 * 1024
|
1524
|
+
|
1457
1525
|
ar.x = "|".join(ar.x or [])
|
1458
1526
|
|
1459
1527
|
setattr(ar, "wlist", ar.url == "-")
|
copyparty/web/baguettebox.js.gz
CHANGED
Binary file
|
copyparty/web/browser.js.gz
CHANGED
Binary file
|
copyparty/web/ui.css.gz
CHANGED
Binary file
|
copyparty/web/up2k.js.gz
CHANGED
Binary file
|
copyparty/web/util.js.gz
CHANGED
Binary file
|
copyparty/web/w.hash.js.gz
CHANGED
Binary file
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.15.
|
3
|
+
Version: 1.15.8
|
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
|
@@ -273,7 +273,7 @@ also see [comparison to similar software](./docs/versus.md)
|
|
273
273
|
* upload
|
274
274
|
* ☑ basic: plain multipart, ie6 support
|
275
275
|
* ☑ [up2k](#uploading): js, resumable, multithreaded
|
276
|
-
* **no filesize limit!**
|
276
|
+
* **no filesize limit!** even on Cloudflare
|
277
277
|
* ☑ stash: simple PUT filedropper
|
278
278
|
* ☑ filename randomizer
|
279
279
|
* ☑ write-only folders
|
@@ -708,7 +708,7 @@ up2k has several advantages:
|
|
708
708
|
* uploads resume if you reboot your browser or pc, just upload the same files again
|
709
709
|
* server detects any corruption; the client reuploads affected chunks
|
710
710
|
* the client doesn't upload anything that already exists on the server
|
711
|
-
* no filesize limit
|
711
|
+
* no filesize limit, even when a proxy limits the request size (for example Cloudflare)
|
712
712
|
* much higher speeds than ftp/scp/tarpipe on some internet connections (mainly american ones) thanks to parallel connections
|
713
713
|
* the last-modified timestamp of the file is preserved
|
714
714
|
|
@@ -744,6 +744,8 @@ note that since up2k has to read each file twice, `[🎈] bup` can *theoreticall
|
|
744
744
|
|
745
745
|
if you are resuming a massive upload and want to skip hashing the files which already finished, you can enable `turbo` in the `[⚙️] config` tab, but please read the tooltip on that button
|
746
746
|
|
747
|
+
if the server is behind a proxy which imposes a request-size limit, you can configure up2k to sneak below the limit with server-option `--u2sz` (the default is 96 MiB to support Cloudflare)
|
748
|
+
|
747
749
|
|
748
750
|
### file-search
|
749
751
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
copyparty/__init__.py,sha256=Chqw7uXX4r_-a2p6-xthrrqVHFI4aZdW45sWU7UvqeE,2597
|
2
|
-
copyparty/__main__.py,sha256=
|
3
|
-
copyparty/__version__.py,sha256=
|
4
|
-
copyparty/authsrv.py,sha256
|
2
|
+
copyparty/__main__.py,sha256=oTzWyuZotTwi-dHpD41mVp_EW2mPSWDMKP8o_nxp9Yk,110180
|
3
|
+
copyparty/__version__.py,sha256=V6jLFRwMXDp4buHI89fYcQoINvqpP0XYLPVMn_QHEDg,258
|
4
|
+
copyparty/authsrv.py,sha256=-lImrFH6pm3gcI76vZiFfEgrQx3_STYTSS2soYX5y1Y,98711
|
5
5
|
copyparty/broker_mp.py,sha256=jsHUM2BSfRVRyZT869iPCqYEHSqedk6VkwvygZwbEZE,4017
|
6
6
|
copyparty/broker_mpw.py,sha256=PYFgQfssOCfdI6qayW1ZjO1j1-7oez094muhYMbPOz0,3339
|
7
7
|
copyparty/broker_thr.py,sha256=MXrwjusP0z1LPURUhi5jx_TL3jrXhYcDrJPDSKu6EEU,1705
|
@@ -11,9 +11,9 @@ copyparty/cfg.py,sha256=33nLatBUmzRFKQ4KpoQei3ZY6EqRrlaHpQnvCNFXcHI,10112
|
|
11
11
|
copyparty/dxml.py,sha256=lZpg-kn-kQsXRtNY1n6fRaS-b7uXzMCyv8ovKnhZcZc,1548
|
12
12
|
copyparty/fsutil.py,sha256=5CshJWO7CflfaRRNOb3JxghUH7W5rmS_HWNmKfx42MM,4538
|
13
13
|
copyparty/ftpd.py,sha256=G_h1urfIikzfCWGXnW9p-rioWdNM_Je6vWYq0-QSbC8,17580
|
14
|
-
copyparty/httpcli.py,sha256=
|
14
|
+
copyparty/httpcli.py,sha256=xhM8unCDyo7Gj3hmLdhncXUCgKHgb6a300bolO1WF4U,194152
|
15
15
|
copyparty/httpconn.py,sha256=mQSgljh0Q-jyWjF4tQLrHbRKRe9WKl19kGqsGMsJpWo,6880
|
16
|
-
copyparty/httpsrv.py,sha256=
|
16
|
+
copyparty/httpsrv.py,sha256=d_UiGnQKniBoEV68lNFgnYm-byda7uj56mFf-YC7piI,17223
|
17
17
|
copyparty/ico.py,sha256=eWSxEae4wOCfheHl-m-wchYvFRAR_97kJDb4NGaB-Z8,3561
|
18
18
|
copyparty/mdns.py,sha256=vC078llnL1v0pvL3mnwacuStFHPJUQuxo9Opj-IbHL4,18155
|
19
19
|
copyparty/metrics.py,sha256=aV09nntEmKMIyde8xoPtj1ehDOQVQOHchRF4uMMNzqM,8855
|
@@ -31,8 +31,8 @@ copyparty/tftpd.py,sha256=jZbf2JpeJmkuQWJErmAPG-dKhtYNvIUHbkAgodSXw9Y,13582
|
|
31
31
|
copyparty/th_cli.py,sha256=o6FMkerYvAXS455z3DUossVztu_nzFlYSQhs6qN6Jt8,4636
|
32
32
|
copyparty/th_srv.py,sha256=hI9wY1E_9N9Cgqvtr8zADeVqqiLGTiTdAnYAA7WFvJw,29346
|
33
33
|
copyparty/u2idx.py,sha256=JjgqwgJBNj6sTn4PJfuqM3VEHqlmoyGC5bk4_92K2h0,13414
|
34
|
-
copyparty/up2k.py,sha256=
|
35
|
-
copyparty/util.py,sha256=
|
34
|
+
copyparty/up2k.py,sha256=Zn33f8EkFpAnK_RTDyVNA97CKlS9MS3k5mnnvYh1w1k,165351
|
35
|
+
copyparty/util.py,sha256=7_-17F94TsLw644xLT8FfO_fH0DwViD54-grej3f8sY,92379
|
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
|
@@ -54,10 +54,10 @@ copyparty/stolen/ifaddr/__init__.py,sha256=vpREjAyPubr5s1NJi91icXV3q1o4DrKAvHABw
|
|
54
54
|
copyparty/stolen/ifaddr/_posix.py,sha256=-67NdfGrCktfQPakT2fLbjl2U00QMvyBGkSvrUuTOrU,2626
|
55
55
|
copyparty/stolen/ifaddr/_shared.py,sha256=uNC4SdEIgdSLKvuUzsf1aM-H1Xrc_9mpLoOT43YukGs,6206
|
56
56
|
copyparty/stolen/ifaddr/_win32.py,sha256=EE-QyoBgeB7lYQ6z62VjXNaRozaYfCkaJBHGNA8QtZM,4026
|
57
|
-
copyparty/web/baguettebox.js.gz,sha256=
|
57
|
+
copyparty/web/baguettebox.js.gz,sha256=YIaxFDsubJfGIdzzxA-cL6GwJVmpWZyaPhW9hHcOIIw,7964
|
58
58
|
copyparty/web/browser.css.gz,sha256=4bAS9Xkl2fflhaxRSRSVoYQcpXsg1mCWxsYjId7phbU,11610
|
59
59
|
copyparty/web/browser.html,sha256=ISpfvWEawufJCYZIqvuXiyUgiXgjmOTtScz4zrEaypI,4870
|
60
|
-
copyparty/web/browser.js.gz,sha256=
|
60
|
+
copyparty/web/browser.js.gz,sha256=7rubbEoqFlNn7FPF0mz3L2LQeWuPzw95MYpIaZmcczE,84985
|
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
|
@@ -79,13 +79,13 @@ copyparty/web/splash.html,sha256=pUbsso_W3Q7bso8fy8qxh-fHDrrLm39mBBTIlTeH63w,523
|
|
79
79
|
copyparty/web/splash.js.gz,sha256=Xoccku-2vE3tABo-88q3Cl4koHs_AE76T8QvMy4u6T8,2540
|
80
80
|
copyparty/web/svcs.html,sha256=P5YZimYLeQMT0uz6u3clQSNZRc5Zs0Ok-ffcbcGSYuc,11762
|
81
81
|
copyparty/web/svcs.js.gz,sha256=k81ZvZ3I-f4fMHKrNGGOgOlvXnCBz0mVjD-8mieoWCA,520
|
82
|
-
copyparty/web/ui.css.gz,sha256=
|
83
|
-
copyparty/web/up2k.js.gz,sha256=
|
84
|
-
copyparty/web/util.js.gz,sha256=
|
85
|
-
copyparty/web/w.hash.js.gz,sha256=
|
82
|
+
copyparty/web/ui.css.gz,sha256=wloSacrHgP722hy4XiOvVY2GI9-V4zvfvzu84LLWS_o,2779
|
83
|
+
copyparty/web/up2k.js.gz,sha256=iMaZ2joij8ndkA2iFCTri6MnYRxXzQbRu9uwAnxZBj8,23096
|
84
|
+
copyparty/web/util.js.gz,sha256=NvjPYhIa0-C_NhUyW-Ra-XinUCRjj8G3pYq1zJHYWEk,14805
|
85
|
+
copyparty/web/w.hash.js.gz,sha256=5weFAxkcfHhu90tUKnVnTu04U_PQjhzUVRfvir199I0,1093
|
86
86
|
copyparty/web/a/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
87
87
|
copyparty/web/a/partyfuse.py,sha256=fa9bBYNJHvtWpNVjQyyFzx6JOK7MJL1u0zj80PBYQKs,27960
|
88
|
-
copyparty/web/a/u2c.py,sha256=
|
88
|
+
copyparty/web/a/u2c.py,sha256=QA0t_k422uLRh8Kt2eQIpcGYaaUZA1CGz_S-z23Yk-4,49343
|
89
89
|
copyparty/web/a/webdav-cfg.bat,sha256=Y4NoGZlksAIg4cBMb7KdJrpKC6Nx97onaTl6yMjaimk,1449
|
90
90
|
copyparty/web/dd/2.png,sha256=gJ14XFPzaw95L6z92fSq9eMPikSQyu-03P1lgiGe0_I,258
|
91
91
|
copyparty/web/dd/3.png,sha256=4lho8Koz5tV7jJ4ODo6GMTScZfkqsT05yp48EDFIlyg,252
|
@@ -106,9 +106,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
|
|
106
106
|
copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
|
107
107
|
copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
|
108
108
|
copyparty/web/deps/sha512.hw.js.gz,sha256=vqoXeracj-99Z5MfY3jK2N4WiSzYQdfjy0RnUlQDhSU,8110
|
109
|
-
copyparty-1.15.
|
110
|
-
copyparty-1.15.
|
111
|
-
copyparty-1.15.
|
112
|
-
copyparty-1.15.
|
113
|
-
copyparty-1.15.
|
114
|
-
copyparty-1.15.
|
109
|
+
copyparty-1.15.8.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
|
110
|
+
copyparty-1.15.8.dist-info/METADATA,sha256=R5tW0crtzFZ4vKzGny4gQ6-IOERgBgj7LZtP14Huung,138915
|
111
|
+
copyparty-1.15.8.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
112
|
+
copyparty-1.15.8.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
|
113
|
+
copyparty-1.15.8.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
|
114
|
+
copyparty-1.15.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|