copyparty 1.13.3__py3-none-any.whl → 1.13.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- copyparty/__main__.py +80 -41
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +68 -9
- copyparty/broker_util.py +3 -3
- copyparty/cert.py +10 -6
- copyparty/cfg.py +3 -0
- copyparty/ftpd.py +44 -7
- copyparty/httpcli.py +224 -96
- copyparty/httpsrv.py +3 -3
- copyparty/mdns.py +1 -1
- copyparty/mtag.py +3 -0
- copyparty/smbd.py +1 -1
- copyparty/ssdp.py +1 -1
- copyparty/svchub.py +4 -2
- copyparty/tcpsrv.py +4 -1
- copyparty/tftpd.py +5 -10
- copyparty/th_cli.py +3 -2
- copyparty/th_srv.py +16 -8
- copyparty/up2k.py +124 -40
- copyparty/util.py +45 -15
- copyparty/web/a/u2c.py +117 -38
- copyparty/web/baguettebox.js.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/deps/marked.js.gz +0 -0
- copyparty/web/svcs.html +11 -10
- 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.13.3.dist-info → copyparty-1.13.5.dist-info}/METADATA +45 -10
- {copyparty-1.13.3.dist-info → copyparty-1.13.5.dist-info}/RECORD +35 -35
- {copyparty-1.13.3.dist-info → copyparty-1.13.5.dist-info}/WHEEL +1 -1
- {copyparty-1.13.3.dist-info → copyparty-1.13.5.dist-info}/LICENSE +0 -0
- {copyparty-1.13.3.dist-info → copyparty-1.13.5.dist-info}/entry_points.txt +0 -0
- {copyparty-1.13.3.dist-info → copyparty-1.13.5.dist-info}/top_level.txt +0 -0
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.
|
5
|
-
S_BUILD_DT = "2024-
|
4
|
+
S_VERSION = "1.20"
|
5
|
+
S_BUILD_DT = "2024-07-22"
|
6
6
|
|
7
7
|
"""
|
8
8
|
u2c.py: upload to copyparty
|
@@ -20,6 +20,7 @@ import sys
|
|
20
20
|
import stat
|
21
21
|
import math
|
22
22
|
import time
|
23
|
+
import json
|
23
24
|
import atexit
|
24
25
|
import signal
|
25
26
|
import socket
|
@@ -79,7 +80,7 @@ req_ses = requests.Session()
|
|
79
80
|
|
80
81
|
|
81
82
|
class Daemon(threading.Thread):
|
82
|
-
def __init__(self, target, name
|
83
|
+
def __init__(self, target, name=None, a=None):
|
83
84
|
threading.Thread.__init__(self, name=name)
|
84
85
|
self.a = a or ()
|
85
86
|
self.fun = target
|
@@ -110,18 +111,22 @@ class File(object):
|
|
110
111
|
# set by get_hashlist
|
111
112
|
self.cids = [] # type: list[tuple[str, int, int]] # [ hash, ofs, sz ]
|
112
113
|
self.kchunks = {} # type: dict[str, tuple[int, int]] # hash: [ ofs, sz ]
|
114
|
+
self.t_hash = 0.0 # type: float
|
113
115
|
|
114
116
|
# set by handshake
|
115
117
|
self.recheck = False # duplicate; redo handshake after all files done
|
116
118
|
self.ucids = [] # type: list[str] # chunks which need to be uploaded
|
117
119
|
self.wark = "" # type: str
|
118
120
|
self.url = "" # type: str
|
119
|
-
self.nhs = 0
|
121
|
+
self.nhs = 0 # type: int
|
120
122
|
|
121
123
|
# set by upload
|
124
|
+
self.t0_up = 0.0 # type: float
|
125
|
+
self.t1_up = 0.0 # type: float
|
126
|
+
self.nojoin = 0 # type: int
|
122
127
|
self.up_b = 0 # type: int
|
123
128
|
self.up_c = 0 # type: int
|
124
|
-
self.cd = 0
|
129
|
+
self.cd = 0 # type: int
|
125
130
|
|
126
131
|
# t = "size({}) lmod({}) top({}) rel({}) abs({}) name({})\n"
|
127
132
|
# eprint(t.format(self.size, self.lmod, self.top, self.rel, self.abs, self.name))
|
@@ -130,10 +135,20 @@ class File(object):
|
|
130
135
|
class FileSlice(object):
|
131
136
|
"""file-like object providing a fixed window into a file"""
|
132
137
|
|
133
|
-
def __init__(self, file,
|
138
|
+
def __init__(self, file, cids):
|
134
139
|
# type: (File, str) -> None
|
135
140
|
|
136
|
-
self.
|
141
|
+
self.file = file
|
142
|
+
self.cids = cids
|
143
|
+
|
144
|
+
self.car, tlen = file.kchunks[cids[0]]
|
145
|
+
for cid in cids[1:]:
|
146
|
+
ofs, clen = file.kchunks[cid]
|
147
|
+
if ofs != self.car + tlen:
|
148
|
+
raise Exception(9)
|
149
|
+
tlen += clen
|
150
|
+
|
151
|
+
self.len = tlen
|
137
152
|
self.cdr = self.car + self.len
|
138
153
|
self.ofs = 0 # type: int
|
139
154
|
self.f = open(file.abs, "rb", 512 * 1024)
|
@@ -357,7 +372,7 @@ def undns(url):
|
|
357
372
|
usp = urlsplit(url)
|
358
373
|
hn = usp.hostname
|
359
374
|
gai = None
|
360
|
-
eprint("resolving host [
|
375
|
+
eprint("resolving host [%s] ..." % (hn,))
|
361
376
|
try:
|
362
377
|
gai = socket.getaddrinfo(hn, None)
|
363
378
|
hn = gai[0][4][0]
|
@@ -375,7 +390,7 @@ def undns(url):
|
|
375
390
|
|
376
391
|
usp = usp._replace(netloc=hn)
|
377
392
|
url = urlunsplit(usp)
|
378
|
-
eprint("
|
393
|
+
eprint(" %s\n" % (url,))
|
379
394
|
return url
|
380
395
|
|
381
396
|
|
@@ -518,6 +533,8 @@ def get_hashlist(file, pcb, mth):
|
|
518
533
|
file_ofs = 0
|
519
534
|
ret = []
|
520
535
|
with open(file.abs, "rb", 512 * 1024) as f:
|
536
|
+
t0 = time.time()
|
537
|
+
|
521
538
|
if mth and file.size >= 1024 * 512:
|
522
539
|
ret = mth.hash(f, file.size, chunk_sz, pcb, file)
|
523
540
|
file_rem = 0
|
@@ -544,10 +561,12 @@ def get_hashlist(file, pcb, mth):
|
|
544
561
|
if pcb:
|
545
562
|
pcb(file, file_ofs)
|
546
563
|
|
564
|
+
file.t_hash = time.time() - t0
|
547
565
|
file.cids = ret
|
548
566
|
file.kchunks = {}
|
549
567
|
for k, v1, v2 in ret:
|
550
|
-
|
568
|
+
if k not in file.kchunks:
|
569
|
+
file.kchunks[k] = [v1, v2]
|
551
570
|
|
552
571
|
|
553
572
|
def handshake(ar, file, search):
|
@@ -589,7 +608,8 @@ def handshake(ar, file, search):
|
|
589
608
|
sc = 600
|
590
609
|
txt = ""
|
591
610
|
try:
|
592
|
-
|
611
|
+
zs = json.dumps(req, separators=(",\n", ": "))
|
612
|
+
r = req_ses.post(url, headers=headers, data=zs)
|
593
613
|
sc = r.status_code
|
594
614
|
txt = r.text
|
595
615
|
if sc < 400:
|
@@ -636,13 +656,13 @@ def handshake(ar, file, search):
|
|
636
656
|
return r["hash"], r["sprs"]
|
637
657
|
|
638
658
|
|
639
|
-
def upload(
|
640
|
-
# type: (
|
641
|
-
"""upload
|
659
|
+
def upload(fsl, pw, stats):
|
660
|
+
# type: (FileSlice, str, str) -> None
|
661
|
+
"""upload a range of file data, defined by one or more `cid` (chunk-hash)"""
|
642
662
|
|
643
663
|
headers = {
|
644
|
-
"X-Up2k-Hash":
|
645
|
-
"X-Up2k-Wark": file.wark,
|
664
|
+
"X-Up2k-Hash": ",".join(fsl.cids),
|
665
|
+
"X-Up2k-Wark": fsl.file.wark,
|
646
666
|
"Content-Type": "application/octet-stream",
|
647
667
|
}
|
648
668
|
|
@@ -652,15 +672,24 @@ def upload(file, cid, pw, stats):
|
|
652
672
|
if pw:
|
653
673
|
headers["Cookie"] = "=".join(["cppwd", pw])
|
654
674
|
|
655
|
-
f = FileSlice(file, cid)
|
656
675
|
try:
|
657
|
-
r = req_ses.post(file.url, headers=headers, data=
|
676
|
+
r = req_ses.post(fsl.file.url, headers=headers, data=fsl)
|
677
|
+
|
678
|
+
if r.status_code == 400:
|
679
|
+
txt = r.text
|
680
|
+
if (
|
681
|
+
"already being written" in txt
|
682
|
+
or "already got that" in txt
|
683
|
+
or "only sibling chunks" in txt
|
684
|
+
):
|
685
|
+
fsl.file.nojoin = 1
|
686
|
+
|
658
687
|
if not r:
|
659
688
|
raise Exception(repr(r))
|
660
689
|
|
661
690
|
_ = r.content
|
662
691
|
finally:
|
663
|
-
|
692
|
+
fsl.f.close()
|
664
693
|
|
665
694
|
|
666
695
|
class Ctl(object):
|
@@ -724,6 +753,8 @@ class Ctl(object):
|
|
724
753
|
if ar.safe:
|
725
754
|
self._safe()
|
726
755
|
else:
|
756
|
+
self.at_hash = 0.0
|
757
|
+
self.at_up = 0.0
|
727
758
|
self.hash_f = 0
|
728
759
|
self.hash_c = 0
|
729
760
|
self.hash_b = 0
|
@@ -743,7 +774,7 @@ class Ctl(object):
|
|
743
774
|
|
744
775
|
self.mutex = threading.Lock()
|
745
776
|
self.q_handshake = Queue() # type: Queue[File]
|
746
|
-
self.q_upload = Queue() # type: Queue[
|
777
|
+
self.q_upload = Queue() # type: Queue[FileSlice]
|
747
778
|
|
748
779
|
self.st_hash = [None, "(idle, starting...)"] # type: tuple[File, int]
|
749
780
|
self.st_up = [None, "(idle, starting...)"] # type: tuple[File, int]
|
@@ -788,7 +819,8 @@ class Ctl(object):
|
|
788
819
|
for nc, cid in enumerate(hs):
|
789
820
|
print(" {0} up {1}".format(ncs - nc, cid))
|
790
821
|
stats = "{0}/0/0/{1}".format(nf, self.nfiles - nf)
|
791
|
-
|
822
|
+
fslice = FileSlice(file, [cid])
|
823
|
+
upload(fslice, self.ar.a, stats)
|
792
824
|
|
793
825
|
print(" ok!")
|
794
826
|
if file.recheck:
|
@@ -797,7 +829,7 @@ class Ctl(object):
|
|
797
829
|
if not self.recheck:
|
798
830
|
return
|
799
831
|
|
800
|
-
eprint("finalizing
|
832
|
+
eprint("finalizing %d duplicate files\n" % (len(self.recheck),))
|
801
833
|
for file in self.recheck:
|
802
834
|
handshake(self.ar, file, search)
|
803
835
|
|
@@ -871,10 +903,16 @@ class Ctl(object):
|
|
871
903
|
t = "{0} eta @ {1}/s, {2}, {3}# left".format(self.eta, spd, sleft, nleft)
|
872
904
|
eprint(txt + "\033]0;{0}\033\\\r{0}{1}".format(t, tail))
|
873
905
|
|
906
|
+
spd = humansize(self.hash_b / self.at_hash)
|
907
|
+
eprint("\nhasher: %.2f sec, %s/s\n" % (self.at_hash, spd))
|
908
|
+
if self.up_b and self.at_up:
|
909
|
+
spd = humansize(self.up_b / self.at_up)
|
910
|
+
eprint("upload: %.2f sec, %s/s\n" % (self.at_up, spd))
|
911
|
+
|
874
912
|
if not self.recheck:
|
875
913
|
return
|
876
914
|
|
877
|
-
eprint("finalizing
|
915
|
+
eprint("finalizing %d duplicate files\n" % (len(self.recheck),))
|
878
916
|
for file in self.recheck:
|
879
917
|
handshake(self.ar, file, False)
|
880
918
|
|
@@ -1060,21 +1098,60 @@ class Ctl(object):
|
|
1060
1098
|
self.handshaker_busy -= 1
|
1061
1099
|
|
1062
1100
|
if not hs:
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1101
|
+
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
|
+
|
1106
|
+
if self.ar.spd:
|
1107
|
+
if VT100:
|
1108
|
+
c1 = "\033[36m"
|
1109
|
+
c2 = "\033[0m"
|
1110
|
+
else:
|
1111
|
+
c1 = c2 = ""
|
1112
|
+
|
1113
|
+
spd_h = humansize(file.size / file.t_hash, True)
|
1114
|
+
if file.up_b:
|
1115
|
+
spd_u = humansize(file.size / t_up, True)
|
1116
|
+
|
1117
|
+
t = "uploaded %s %s(h:%.2fs,%s/s,up:%.2fs,%s/s)%s"
|
1118
|
+
print(t % (upath, c1, file.t_hash, spd_h, t_up, spd_u, c2))
|
1119
|
+
else:
|
1120
|
+
t = " found %s %s(%.2fs,%s/s)%s"
|
1121
|
+
print(t % (upath, c1, file.t_hash, spd_h, c2))
|
1122
|
+
else:
|
1123
|
+
kw = "uploaded" if file.up_b else " found"
|
1124
|
+
print("{0} {1}".format(kw, upath))
|
1125
|
+
|
1126
|
+
cs = hs[:]
|
1127
|
+
while cs:
|
1128
|
+
fsl = FileSlice(file, cs[:1])
|
1129
|
+
try:
|
1130
|
+
if file.nojoin:
|
1131
|
+
raise Exception()
|
1132
|
+
for n in range(2, min(len(cs), self.ar.sz) + 1):
|
1133
|
+
fsl = FileSlice(file, cs[:n])
|
1134
|
+
except:
|
1135
|
+
pass
|
1136
|
+
cs = cs[len(fsl.cids) :]
|
1137
|
+
self.q_upload.put(fsl)
|
1067
1138
|
|
1068
1139
|
def uploader(self):
|
1069
1140
|
while True:
|
1070
|
-
|
1071
|
-
if not
|
1141
|
+
fsl = self.q_upload.get()
|
1142
|
+
if not fsl:
|
1072
1143
|
self.st_up = [None, "(finished)"]
|
1073
1144
|
break
|
1074
1145
|
|
1146
|
+
file = fsl.file
|
1147
|
+
cids = fsl.cids
|
1148
|
+
|
1075
1149
|
with self.mutex:
|
1076
1150
|
self.uploader_busy += 1
|
1077
|
-
|
1151
|
+
if not file.t0_up:
|
1152
|
+
file.t0_up = time.time()
|
1153
|
+
if not self.t0_up:
|
1154
|
+
self.t0_up = file.t0_up
|
1078
1155
|
|
1079
1156
|
stats = "%d/%d/%d/%d %d/%d %s" % (
|
1080
1157
|
self.up_f,
|
@@ -1086,22 +1163,22 @@ class Ctl(object):
|
|
1086
1163
|
self.eta,
|
1087
1164
|
)
|
1088
1165
|
|
1089
|
-
file, cid = task
|
1090
1166
|
try:
|
1091
|
-
upload(
|
1167
|
+
upload(fsl, self.ar.a, stats)
|
1092
1168
|
except Exception as ex:
|
1093
|
-
t = "upload failed, retrying:
|
1094
|
-
eprint(t
|
1169
|
+
t = "upload failed, retrying: %s #%s+%d (%s)\n"
|
1170
|
+
eprint(t % (file.name, cids[0][:8], len(cids) - 1, ex))
|
1095
1171
|
file.cd = time.time() + self.ar.cd
|
1096
1172
|
# handshake will fix it
|
1097
1173
|
|
1098
1174
|
with self.mutex:
|
1099
|
-
sz =
|
1100
|
-
file.ucids = [x for x in file.ucids if x
|
1175
|
+
sz = fsl.len
|
1176
|
+
file.ucids = [x for x in file.ucids if x not in cids]
|
1101
1177
|
if not file.ucids:
|
1178
|
+
file.t1_up = time.time()
|
1102
1179
|
self.q_handshake.put(file)
|
1103
1180
|
|
1104
|
-
self.st_up = [file,
|
1181
|
+
self.st_up = [file, cids[0]]
|
1105
1182
|
file.up_b += sz
|
1106
1183
|
self.up_b += sz
|
1107
1184
|
self.up_br += sz
|
@@ -1150,6 +1227,7 @@ source file/folder selection uses rsync syntax, meaning that:
|
|
1150
1227
|
ap.add_argument("--ok", action="store_true", help="continue even if some local files are inaccessible")
|
1151
1228
|
ap.add_argument("--touch", action="store_true", help="if last-modified timestamps differ, push local to server (need write+delete perms)")
|
1152
1229
|
ap.add_argument("--ow", action="store_true", help="overwrite existing files instead of autorenaming")
|
1230
|
+
ap.add_argument("--spd", action="store_true", help="print speeds for each file")
|
1153
1231
|
ap.add_argument("--version", action="store_true", help="show version and exit")
|
1154
1232
|
|
1155
1233
|
ap = app.add_argument_group("compatibility")
|
@@ -1164,6 +1242,7 @@ source file/folder selection uses rsync syntax, meaning that:
|
|
1164
1242
|
ap = app.add_argument_group("performance tweaks")
|
1165
1243
|
ap.add_argument("-j", type=int, metavar="CONNS", default=2, help="parallel connections")
|
1166
1244
|
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")
|
1245
|
+
ap.add_argument("--sz", type=int, metavar="MiB", default=64, help="try to make each POST this big")
|
1167
1246
|
ap.add_argument("-nh", action="store_true", help="disable hashing while uploading")
|
1168
1247
|
ap.add_argument("-ns", action="store_true", help="no status panel (for slow consoles and macos)")
|
1169
1248
|
ap.add_argument("--cd", type=float, metavar="SEC", default=5, help="delay before reattempting a failed handshake/upload")
|
@@ -1208,7 +1287,7 @@ source file/folder selection uses rsync syntax, meaning that:
|
|
1208
1287
|
ar.url = ar.url.rstrip("/") + "/"
|
1209
1288
|
if "://" not in ar.url:
|
1210
1289
|
ar.url = "http://" + ar.url
|
1211
|
-
|
1290
|
+
|
1212
1291
|
if "https://" in ar.url.lower():
|
1213
1292
|
try:
|
1214
1293
|
import ssl, zipfile
|
copyparty/web/baguettebox.js.gz
CHANGED
Binary file
|
copyparty/web/browser.js.gz
CHANGED
Binary file
|
copyparty/web/deps/marked.js.gz
CHANGED
Binary file
|
copyparty/web/svcs.html
CHANGED
@@ -64,16 +64,7 @@
|
|
64
64
|
</div>
|
65
65
|
|
66
66
|
<div class="os lin">
|
67
|
-
<
|
68
|
-
yum install davfs2
|
69
|
-
{% if accs %}printf '%s\n' <b>{{ pw }}</b> k | {% endif %}mount -t davfs -ouid=1000 http{{ s }}://{{ ep }}/{{ rvp }} <b>mp</b>
|
70
|
-
</pre>
|
71
|
-
<p>make it automount on boot:</p>
|
72
|
-
<pre>
|
73
|
-
printf '%s\n' "http{{ s }}://{{ ep }}/{{ rvp }} <b>{{ pw }}</b> k" >> /etc/davfs2/secrets
|
74
|
-
printf '%s\n' "http{{ s }}://{{ ep }}/{{ rvp }} <b>mp</b> davfs rw,user,uid=1000,noauto 0 0" >> /etc/fstab
|
75
|
-
</pre>
|
76
|
-
<p>or you can use rclone instead, which is much slower but doesn't require root (plus it keeps lastmodified on upload):</p>
|
67
|
+
<p>rclone (v1.63 or later) is recommended:</p>
|
77
68
|
<pre>
|
78
69
|
rclone config create {{ aname }}-dav webdav url=http{{ s }}://{{ rip }}{{ hport }} vendor=owncloud pacer_min_sleep=0.01ms{% if accs %} user=k pass=<b>{{ pw }}</b>{% endif %}
|
79
70
|
rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-dav:{{ rvp }} <b>mp</b>
|
@@ -85,6 +76,16 @@
|
|
85
76
|
<li>running <code>rclone mount</code> as root? add <code>--allow-other</code></li>
|
86
77
|
<li>old version of rclone? replace all <code>=</code> with <code> </code> (space)</li>
|
87
78
|
</ul>
|
79
|
+
<p>alternatively use davfs2 (requires root, is slower, forgets lastmodified-timestamp on upload):</p>
|
80
|
+
<pre>
|
81
|
+
yum install davfs2
|
82
|
+
{% if accs %}printf '%s\n' <b>{{ pw }}</b> k | {% endif %}mount -t davfs -ouid=1000 http{{ s }}://{{ ep }}/{{ rvp }} <b>mp</b>
|
83
|
+
</pre>
|
84
|
+
<p>make davfs2 automount on boot:</p>
|
85
|
+
<pre>
|
86
|
+
printf '%s\n' "http{{ s }}://{{ ep }}/{{ rvp }} <b>{{ pw }}</b> k" >> /etc/davfs2/secrets
|
87
|
+
printf '%s\n' "http{{ s }}://{{ ep }}/{{ rvp }} <b>mp</b> davfs rw,user,uid=1000,noauto 0 0" >> /etc/fstab
|
88
|
+
</pre>
|
88
89
|
<p>or the emergency alternative (gnome/gui-only):</p>
|
89
90
|
<!-- gnome-bug: ignores vp -->
|
90
91
|
<pre>
|
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.13.
|
3
|
+
Version: 1.13.5
|
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
|
@@ -46,7 +46,7 @@ Requires-Dist: pyopenssl ; extra == 'ftps'
|
|
46
46
|
Provides-Extra: pwhash
|
47
47
|
Requires-Dist: argon2-cffi ; extra == 'pwhash'
|
48
48
|
Provides-Extra: tftpd
|
49
|
-
Requires-Dist: partftpy >=0.
|
49
|
+
Requires-Dist: partftpy >=0.4.0 ; extra == 'tftpd'
|
50
50
|
Provides-Extra: thumbnails
|
51
51
|
Requires-Dist: Pillow ; extra == 'thumbnails'
|
52
52
|
Provides-Extra: thumbnails2
|
@@ -137,6 +137,8 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
137
137
|
* [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
|
138
138
|
* [real-ip](#real-ip) - teaching copyparty how to see client IPs
|
139
139
|
* [prometheus](#prometheus) - metrics/stats can be enabled
|
140
|
+
* [other extremely specific features](#other-extremely-specific-features) - you'll never find a use for these
|
141
|
+
* [custom mimetypes](#custom-mimetypes) - change the association of a file extension
|
140
142
|
* [packages](#packages) - the party might be closer than you think
|
141
143
|
* [arch package](#arch-package) - now [available on aur](https://aur.archlinux.org/packages/copyparty) maintained by [@icxes](https://github.com/icxes)
|
142
144
|
* [fedora package](#fedora-package) - does not exist yet
|
@@ -261,7 +263,7 @@ also see [comparison to similar software](./docs/versus.md)
|
|
261
263
|
* upload
|
262
264
|
* ☑ basic: plain multipart, ie6 support
|
263
265
|
* ☑ [up2k](#uploading): js, resumable, multithreaded
|
264
|
-
*
|
266
|
+
* **no filesize limit!** ...unless you use Cloudflare, then it's 383.9 GiB
|
265
267
|
* ☑ stash: simple PUT filedropper
|
266
268
|
* ☑ filename randomizer
|
267
269
|
* ☑ write-only folders
|
@@ -277,6 +279,7 @@ also see [comparison to similar software](./docs/versus.md)
|
|
277
279
|
* ☑ [navpane](#navpane) (directory tree sidebar)
|
278
280
|
* ☑ file manager (cut/paste, delete, [batch-rename](#batch-rename))
|
279
281
|
* ☑ audio player (with [OS media controls](https://user-images.githubusercontent.com/241032/215347492-b4250797-6c90-4e09-9a4c-721edf2fb15c.png) and opus/mp3 transcoding)
|
282
|
+
* ☑ play video files as audio (converted on server)
|
280
283
|
* ☑ image gallery with webm player
|
281
284
|
* ☑ textfile browser with syntax hilighting
|
282
285
|
* ☑ [thumbnails](#thumbnails)
|
@@ -627,6 +630,7 @@ it does static images with Pillow / pyvips / FFmpeg, and uses FFmpeg for video f
|
|
627
630
|
audio files are covnerted into spectrograms using FFmpeg unless you `--no-athumb` (and some FFmpeg builds may need `--th-ff-swr`)
|
628
631
|
|
629
632
|
images with the following names (see `--th-covers`) become the thumbnail of the folder they're in: `folder.png`, `folder.jpg`, `cover.png`, `cover.jpg`
|
633
|
+
* the order is significant, so if both `cover.png` and `folder.jpg` exist in a folder, it will pick the first matching `--th-covers` entry (`folder.jpg`)
|
630
634
|
* and, if you enable [file indexing](#file-indexing), it will also try those names as dotfiles (`.folder.jpg` and so), and then fallback on the first picture in the folder (if it has any pictures at all)
|
631
635
|
|
632
636
|
in the grid/thumbnail view, if the audio player panel is open, songs will start playing when clicked
|
@@ -634,6 +638,7 @@ in the grid/thumbnail view, if the audio player panel is open, songs will start
|
|
634
638
|
|
635
639
|
enabling `multiselect` lets you click files to select them, and then shift-click another file for range-select
|
636
640
|
* `multiselect` is mostly intended for phones/tablets, but the `sel` option in the `[⚙️] settings` tab is better suited for desktop use, allowing selection by CTRL-clicking and range-selection with SHIFT-click, all without affecting regular clicking
|
641
|
+
* the `sel` option can be made default globally with `--gsel` or per-volume with volflag `gsel`
|
637
642
|
|
638
643
|
|
639
644
|
## zip downloads
|
@@ -696,6 +701,7 @@ up2k has several advantages:
|
|
696
701
|
* uploads resume if you reboot your browser or pc, just upload the same files again
|
697
702
|
* server detects any corruption; the client reuploads affected chunks
|
698
703
|
* the client doesn't upload anything that already exists on the server
|
704
|
+
* no filesize limit unless imposed by a proxy, for example Cloudflare, which blocks uploads over 383.9 GiB
|
699
705
|
* much higher speeds than ftp/scp/tarpipe on some internet connections (mainly american ones) thanks to parallel connections
|
700
706
|
* the last-modified timestamp of the file is preserved
|
701
707
|
|
@@ -763,7 +769,7 @@ uploads can be given a lifetime, afer which they expire / self-destruct
|
|
763
769
|
|
764
770
|
the feature must be enabled per-volume with the `lifetime` [upload rule](#upload-rules) which sets the upper limit for how long a file gets to stay on the server
|
765
771
|
|
766
|
-
clients can specify a shorter expiration time using the [up2k ui](#uploading) -- the relevant options become visible upon navigating into a folder with `lifetimes` enabled -- or by using the `life` [upload modifier](#write)
|
772
|
+
clients can specify a shorter expiration time using the [up2k ui](#uploading) -- the relevant options become visible upon navigating into a folder with `lifetimes` enabled -- or by using the `life` [upload modifier](./docs/devnotes.md#write)
|
767
773
|
|
768
774
|
specifying a custom expiration time client-side will affect the timespan in which unposts are permitted, so keep an eye on the estimates in the up2k ui
|
769
775
|
|
@@ -850,6 +856,7 @@ some hilights:
|
|
850
856
|
* OS integration; control playback from your phone's lockscreen ([windows](https://user-images.githubusercontent.com/241032/233213022-298a98ba-721a-4cf1-a3d4-f62634bc53d5.png) // [iOS](https://user-images.githubusercontent.com/241032/142711926-0700be6c-3e31-47b3-9928-53722221f722.png) // [android](https://user-images.githubusercontent.com/241032/233212311-a7368590-08c7-4f9f-a1af-48ccf3f36fad.png))
|
851
857
|
* shows the audio waveform in the seekbar
|
852
858
|
* not perfectly gapless but can get really close (see settings + eq below); good enough to enjoy gapless albums as intended
|
859
|
+
* videos can be played as audio, without wasting bandwidth on the video
|
853
860
|
|
854
861
|
click the `play` link next to an audio file, or copy the link target to [share it](https://a.ocv.me/pub/demo/music/Ubiktune%20-%20SOUNDSHOCK%202%20-%20FM%20FUNK%20TERRROR!!/#af-1fbfba61&t=18) (optionally with a timestamp to start playing from, like that example does)
|
855
862
|
|
@@ -931,6 +938,8 @@ see [./srv/expand/](./srv/expand/) for usage and examples
|
|
931
938
|
|
932
939
|
* files named `.prologue.html` / `.epilogue.html` will be rendered before/after directory listings unless `--no-logues`
|
933
940
|
|
941
|
+
* files named `descript.ion` / `DESCRIPT.ION` are parsed and displayed in the file listing, or as the epilogue if nonstandard
|
942
|
+
|
934
943
|
* files named `README.md` / `readme.md` will be rendered after directory listings unless `--no-readme` (but `.epilogue.html` takes precedence)
|
935
944
|
|
936
945
|
* `README.md` and `*logue.html` can contain placeholder values which are replaced server-side before embedding into directory listings; see `--help-exp`
|
@@ -1035,7 +1044,7 @@ some recommended FTP / FTPS clients; `wark` = example password:
|
|
1035
1044
|
|
1036
1045
|
## webdav server
|
1037
1046
|
|
1038
|
-
with read-write support, supports winXP and later, macos, nautilus/gvfs
|
1047
|
+
with read-write support, supports winXP and later, macos, nautilus/gvfs ... a greay way to [access copyparty straight from the file explorer in your OS](#mount-as-drive)
|
1039
1048
|
|
1040
1049
|
click the [connect](http://127.0.0.1:3923/?hc) button in the control-panel to see connection instructions for windows, linux, macos
|
1041
1050
|
|
@@ -1499,8 +1508,9 @@ you can either:
|
|
1499
1508
|
* or do location-based proxying, using `--rp-loc=/stuff` to tell copyparty where it is mounted -- has a slight performance cost and higher chance of bugs
|
1500
1509
|
* if copyparty says `incorrect --rp-loc or webserver config; expected vpath starting with [...]` it's likely because the webserver is stripping away the proxy location from the request URLs -- see the `ProxyPass` in the apache example below
|
1501
1510
|
|
1502
|
-
some reverse proxies (such as [Caddy](https://caddyserver.com/)) can automatically obtain a valid https/tls certificate for you, and some support HTTP/2 and QUIC which could be a nice speed boost
|
1503
|
-
* **warning:** nginx-QUIC is still experimental and can make uploads much slower, so HTTP/
|
1511
|
+
some reverse proxies (such as [Caddy](https://caddyserver.com/)) can automatically obtain a valid https/tls certificate for you, and some support HTTP/2 and QUIC which *could* be a nice speed boost, depending on a lot of factors
|
1512
|
+
* **warning:** nginx-QUIC (HTTP/3) is still experimental and can make uploads much slower, so HTTP/1.1 is recommended for now
|
1513
|
+
* depending on server/client, HTTP/1.1 can also be 5x faster than HTTP/2
|
1504
1514
|
|
1505
1515
|
example webserver configs:
|
1506
1516
|
|
@@ -1580,6 +1590,28 @@ the following options are available to disable some of the metrics:
|
|
1580
1590
|
note: the following metrics are counted incorrectly if multiprocessing is enabled with `-j`: `cpp_http_conns`, `cpp_http_reqs`, `cpp_sus_reqs`, `cpp_active_bans`, `cpp_total_bans`
|
1581
1591
|
|
1582
1592
|
|
1593
|
+
## other extremely specific features
|
1594
|
+
|
1595
|
+
you'll never find a use for these:
|
1596
|
+
|
1597
|
+
|
1598
|
+
### custom mimetypes
|
1599
|
+
|
1600
|
+
change the association of a file extension
|
1601
|
+
|
1602
|
+
using commandline args, you can do something like `--mime gif=image/jif` and `--mime ts=text/x.typescript` (can be specified multiple times)
|
1603
|
+
|
1604
|
+
in a config-file, this is the same as:
|
1605
|
+
|
1606
|
+
```yaml
|
1607
|
+
[global]
|
1608
|
+
mime: gif=image/jif
|
1609
|
+
mime: ts=text/x.typescript
|
1610
|
+
```
|
1611
|
+
|
1612
|
+
run copyparty with `--mimes` to list all the default mappings
|
1613
|
+
|
1614
|
+
|
1583
1615
|
# packages
|
1584
1616
|
|
1585
1617
|
the party might be closer than you think
|
@@ -1823,12 +1855,14 @@ alternatively, some alternatives roughly sorted by speed (unreproducible benchma
|
|
1823
1855
|
* [rclone-http](./docs/rclone.md) (26s), read-only
|
1824
1856
|
* [partyfuse.py](./bin/#partyfusepy) (35s), read-only
|
1825
1857
|
* [rclone-ftp](./docs/rclone.md) (47s), read/WRITE
|
1826
|
-
* davfs2 (103s), read/WRITE
|
1858
|
+
* davfs2 (103s), read/WRITE
|
1827
1859
|
* [win10-webdav](#webdav-server) (138s), read/WRITE
|
1828
1860
|
* [win10-smb2](#smb-server) (387s), read/WRITE
|
1829
1861
|
|
1830
1862
|
most clients will fail to mount the root of a copyparty server unless there is a root volume (so you get the admin-panel instead of a browser when accessing it) -- in that case, mount a specific volume instead
|
1831
1863
|
|
1864
|
+
if you have volumes that are accessible without a password, then some webdav clients (such as davfs2) require the global-option `--dav-auth` to access any password-protected areas
|
1865
|
+
|
1832
1866
|
|
1833
1867
|
# android app
|
1834
1868
|
|
@@ -1857,6 +1891,7 @@ defaults are usually fine - expect `8 GiB/s` download, `1 GiB/s` upload
|
|
1857
1891
|
|
1858
1892
|
below are some tweaks roughly ordered by usefulness:
|
1859
1893
|
|
1894
|
+
* disabling HTTP/2 and HTTP/3 can make uploads 5x faster, depending on server/client software
|
1860
1895
|
* `-q` disables logging and can help a bunch, even when combined with `-lo` to redirect logs to file
|
1861
1896
|
* `--hist` pointing to a fast location (ssd) will make directory listings and searches faster when `-e2d` or `-e2t` is set
|
1862
1897
|
* and also makes thumbnails load faster, regardless of e2d/e2t
|
@@ -1972,7 +2007,7 @@ volflag `dk` generates dirkeys (per-directory accesskeys) for all folders, grant
|
|
1972
2007
|
|
1973
2008
|
volflag `dky` disables the actual key-check, meaning anyone can see the contents of a folder where they have `g` access, but not its subdirectories
|
1974
2009
|
|
1975
|
-
* `dk` + `dky` gives the same behavior as if all users with `g` access have full read-access, but subfolders are hidden files (their names start with a dot), so `dky` is an alternative to renaming all the folders for that purpose, maybe just for some users
|
2010
|
+
* `dk` + `dky` gives the same behavior as if all users with `g` access have full read-access, but subfolders are hidden files (as if their names start with a dot), so `dky` is an alternative to renaming all the folders for that purpose, maybe just for some users
|
1976
2011
|
|
1977
2012
|
volflag `dks` lets people enter subfolders as well, and also enables download-as-zip/tar
|
1978
2013
|
|
@@ -1997,7 +2032,7 @@ the default configs take about 0.4 sec and 256 MiB RAM to process a new password
|
|
1997
2032
|
|
1998
2033
|
both HTTP and HTTPS are accepted by default, but letting a [reverse proxy](#reverse-proxy) handle the https/tls/ssl would be better (probably more secure by default)
|
1999
2034
|
|
2000
|
-
copyparty doesn't speak HTTP/2 or QUIC, so using a reverse proxy would solve that as well
|
2035
|
+
copyparty doesn't speak HTTP/2 or QUIC, so using a reverse proxy would solve that as well -- but note that HTTP/1 is usually faster than both HTTP/2 and HTTP/3
|
2001
2036
|
|
2002
2037
|
if [cfssl](https://github.com/cloudflare/cfssl/releases/latest) is installed, copyparty will automatically create a CA and server-cert on startup
|
2003
2038
|
* the certs are written to `--crt-dir` for distribution, see `--help` for the other `--crt` options
|