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/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.18"
5
- S_BUILD_DT = "2024-06-01"
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 = None, a = None):
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, cid):
138
+ def __init__(self, file, cids):
134
139
  # type: (File, str) -> None
135
140
 
136
- self.car, self.len = file.kchunks[cid]
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 [{0}] ...".format(hn), end="")
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(" {0}".format(url))
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
- file.kchunks[k] = [v1, v2]
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
- r = req_ses.post(url, headers=headers, json=req)
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(file, cid, pw, stats):
640
- # type: (File, str, str, str) -> None
641
- """upload one specific chunk, `cid` (a chunk-hash)"""
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": cid,
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=f)
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
- f.f.close()
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[tuple[File, str]]
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
- upload(file, cid, self.ar.a, stats)
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 {0} duplicate files".format(len(self.recheck)))
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 {0} duplicate files".format(len(self.recheck)))
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
- kw = "uploaded" if file.up_b else " found"
1064
- print("{0} {1}".format(kw, upath))
1065
- for cid in hs:
1066
- self.q_upload.put([file, cid])
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
- task = self.q_upload.get()
1071
- if not task:
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
- self.t0_up = self.t0_up or time.time()
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(file, cid, self.ar.a, stats)
1167
+ upload(fsl, self.ar.a, stats)
1092
1168
  except Exception as ex:
1093
- t = "upload failed, retrying: {0} #{1} ({2})\n"
1094
- eprint(t.format(file.name, cid[:8], ex))
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 = file.kchunks[cid][1]
1100
- file.ucids = [x for x in file.ucids if x != cid]
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, cid]
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
Binary file
Binary file
Binary file
copyparty/web/svcs.html CHANGED
@@ -64,16 +64,7 @@
64
64
  </div>
65
65
 
66
66
  <div class="os lin">
67
- <pre>
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>&nbsp;</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
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: copyparty
3
- Version: 1.13.3
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.3.1 ; extra == 'tftpd'
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
- * unaffected by cloudflare's max-upload-size (100 MiB)
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/2 is recommended for now
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, *very fast* on small files
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