copyparty 1.16.13__py3-none-any.whl → 1.16.15__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 CHANGED
@@ -1031,6 +1031,7 @@ def add_upload(ap):
1031
1031
  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")
1032
1032
  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)")
1033
1033
  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]")
1034
+ ap2.add_argument("--u2ow", metavar="NUM", type=int, default=0, help="web-client: default setting for when to overwrite existing files; [\033[32m0\033[0m]=never, [\033[32m1\033[0m]=if-client-newer, [\033[32m2\033[0m]=always (volflag=u2ow)")
1034
1035
  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")
1035
1036
  ap2.add_argument("--write-uplog", action="store_true", help="write POST reports to textfiles in working-directory")
1036
1037
 
@@ -1261,7 +1262,7 @@ def add_optouts(ap):
1261
1262
  ap2.add_argument("--no-tarcmp", action="store_true", help="disable download as compressed tar (?tar=gz, ?tar=bz2, ?tar=xz, ?tar=gz:9, ...)")
1262
1263
  ap2.add_argument("--no-lifetime", action="store_true", help="do not allow clients (or server config) to schedule an upload to be deleted after a given time")
1263
1264
  ap2.add_argument("--no-pipe", action="store_true", help="disable race-the-beam (lockstep download of files which are currently being uploaded) (volflag=nopipe)")
1264
- ap2.add_argument("--no-db-ip", action="store_true", help="do not write uploader IPs into the database")
1265
+ ap2.add_argument("--no-db-ip", action="store_true", help="do not write uploader-IP into the database; will also disable unpost, you may want \033[32m--forget-ip\033[0m instead (volflag=no_db_ip)")
1265
1266
 
1266
1267
 
1267
1268
  def add_safety(ap):
@@ -1411,6 +1412,7 @@ def add_db_general(ap, hcores):
1411
1412
  ap2.add_argument("--no-dhash", action="store_true", help="disable rescan acceleration; do full database integrity check -- makes the db ~5%% smaller and bootup/rescans 3~10x slower")
1412
1413
  ap2.add_argument("--re-dhash", action="store_true", help="force a cache rebuild on startup; enable this once if it gets out of sync (should never be necessary)")
1413
1414
  ap2.add_argument("--no-forget", action="store_true", help="never forget indexed files, even when deleted from disk -- makes it impossible to ever upload the same file twice -- only useful for offloading uploads to a cloud service or something (volflag=noforget)")
1415
+ ap2.add_argument("--forget-ip", metavar="MIN", type=int, default=0, help="remove uploader-IP from database (and make unpost impossible) \033[33mMIN\033[0m minutes after upload, for GDPR reasons. Default [\033[32m0\033[0m] is never-forget. [\033[32m1440\033[0m]=day, [\033[32m10080\033[0m]=week, [\033[32m43200\033[0m]=month. (volflag=forget_ip)")
1414
1416
  ap2.add_argument("--dbd", metavar="PROFILE", default="wal", help="database durability profile; sets the tradeoff between robustness and speed, see \033[33m--help-dbd\033[0m (volflag=dbd)")
1415
1417
  ap2.add_argument("--xlink", action="store_true", help="on upload: check all volumes for dupes, not just the target volume (probably buggy, not recommended) (volflag=xlink)")
1416
1418
  ap2.add_argument("--hash-mt", metavar="CORES", type=int, default=hcores, help="num cpu cores to use for file hashing; set 0 or 1 for single-core hashing")
copyparty/__version__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 16, 13)
3
+ VERSION = (1, 16, 15)
4
4
  CODENAME = "COPYparty"
5
- BUILD_DT = (2025, 2, 13)
5
+ BUILD_DT = (2025, 2, 25)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
copyparty/authsrv.py CHANGED
@@ -1382,8 +1382,16 @@ class AuthSrv(object):
1382
1382
  name = name.lower()
1383
1383
 
1384
1384
  # volflags are snake_case, but a leading dash is the removal operator
1385
- if name not in flagdescs and "-" in name[1:]:
1386
- name = name[:1] + name[1:].replace("-", "_")
1385
+ stripped = name.lstrip("-")
1386
+ zi = len(name) - len(stripped)
1387
+ if zi > 1:
1388
+ t = "WARNING: the config for volume [/%s] specified a volflag with multiple leading hyphens (%s); use one hyphen to remove, or zero hyphens to add a flag. Will now enable flag [%s]"
1389
+ self.log(t % (vpath, name, stripped), 3)
1390
+ name = stripped
1391
+ zi = 0
1392
+
1393
+ if stripped not in flagdescs and "-" in stripped:
1394
+ name = ("-" * zi) + stripped.replace("-", "_")
1387
1395
 
1388
1396
  desc = flagdescs.get(name.lstrip("-"), "?").replace("\n", " ")
1389
1397
 
@@ -1569,6 +1577,11 @@ class AuthSrv(object):
1569
1577
  for vol in vfs.all_vols.values():
1570
1578
  unknown_flags = set()
1571
1579
  for k, v in vol.flags.items():
1580
+ stripped = k.lstrip("-")
1581
+ if k != stripped and stripped not in vol.flags:
1582
+ t = "WARNING: the config for volume [/%s] tried to remove volflag [%s] by specifying [%s] but that volflag was not already set"
1583
+ self.log(t % (vol.vpath, stripped, k), 3)
1584
+ k = stripped
1572
1585
  if k not in flagdescs and k not in k_ign:
1573
1586
  unknown_flags.add(k)
1574
1587
  if unknown_flags:
@@ -1936,11 +1949,8 @@ class AuthSrv(object):
1936
1949
  if vf not in vol.flags:
1937
1950
  vol.flags[vf] = getattr(self.args, ga)
1938
1951
 
1939
- for k in ("nrand",):
1940
- if k not in vol.flags:
1941
- vol.flags[k] = getattr(self.args, k)
1942
-
1943
- for k in ("nrand", "u2abort", "ups_who", "zip_who"):
1952
+ zs = "forget_ip nrand u2abort u2ow ups_who zip_who"
1953
+ for k in zs.split():
1944
1954
  if k in vol.flags:
1945
1955
  vol.flags[k] = int(vol.flags[k])
1946
1956
 
@@ -2415,6 +2425,7 @@ class AuthSrv(object):
2415
2425
  "u2j": self.args.u2j,
2416
2426
  "u2sz": self.args.u2sz,
2417
2427
  "u2ts": vf["u2ts"],
2428
+ "u2ow": vf["u2ow"],
2418
2429
  "frand": bool(vf.get("rand")),
2419
2430
  "lifetime": vn.js_ls["lifetime"],
2420
2431
  "u2sort": self.args.u2sort,
copyparty/cfg.py CHANGED
@@ -43,6 +43,7 @@ def vf_bmap() :
43
43
  "gsel",
44
44
  "hardlink",
45
45
  "magic",
46
+ "no_db_ip",
46
47
  "no_sb_md",
47
48
  "no_sb_lg",
48
49
  "nsort",
@@ -73,6 +74,7 @@ def vf_vmap() :
73
74
  }
74
75
  for k in (
75
76
  "dbd",
77
+ "forget_ip",
76
78
  "hsortn",
77
79
  "html_head",
78
80
  "lg_sbf",
@@ -80,6 +82,7 @@ def vf_vmap() :
80
82
  "lg_sba",
81
83
  "md_sba",
82
84
  "nrand",
85
+ "u2ow",
83
86
  "og_desc",
84
87
  "og_site",
85
88
  "og_th",
@@ -156,7 +159,8 @@ flagcats = {
156
159
  "daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files",
157
160
  "nosub": "forces all uploads into the top folder of the vfs",
158
161
  "magic": "enables filetype detection for nameless uploads",
159
- "gz": "allows server-side gzip of uploads with ?gz (also c,xz)",
162
+ "gz": "allows server-side gzip compression of uploads with ?gz",
163
+ "xz": "allows server-side lzma compression of uploads with ?xz",
160
164
  "pk": "forces server-side compression, optional arg: xz,9",
161
165
  },
162
166
  "upload rules": {
@@ -167,6 +171,7 @@ flagcats = {
167
171
  "medialinks": "return medialinks for non-up2k uploads (not hotlinks)",
168
172
  "rand": "force randomized filenames, 9 chars long by default",
169
173
  "nrand=N": "randomized filenames are N chars long",
174
+ "u2ow=N": "overwrite existing files? 0=no 1=if-older 2=always",
170
175
  "u2ts=fc": "[f]orce [c]lient-last-modified or [u]pload-time",
171
176
  "u2abort=1": "allow aborting unfinished uploads? 0=no 1=strict 2=ip-chk 3=acct-chk",
172
177
  "sz=1k-3m": "allow filesizes between 1 KiB and 3MiB",
@@ -197,6 +202,8 @@ flagcats = {
197
202
  "nohash=\\.iso$": "skips hashing file contents if path matches *.iso",
198
203
  "noidx=\\.iso$": "fully ignores the contents at paths matching *.iso",
199
204
  "noforget": "don't forget files when deleted from disk",
205
+ "forget_ip=43200": "forget uploader-IP after 30 days (GDPR)",
206
+ "no_db_ip": "never store uploader-IP in the db; disables unpost",
200
207
  "fat32": "avoid excessive reindexing on android sdcardfs",
201
208
  "dbd=[acid|swal|wal|yolo]": "database speed-durability tradeoff",
202
209
  "xlink": "cross-volume dupe detection / linking (dangerous)",
copyparty/httpcli.py CHANGED
@@ -148,6 +148,8 @@ RE_HSAFE = re.compile(r"[\x00-\x1f<>\"'&]") # search always much faster
148
148
  RE_HOST = re.compile(r"[^][0-9a-zA-Z.:_-]") # search faster <=17ch
149
149
  RE_MHOST = re.compile(r"^[][0-9a-zA-Z.:_-]+$") # match faster >=18ch
150
150
  RE_K = re.compile(r"[^0-9a-zA-Z_-]") # search faster <=17ch
151
+ RE_HR = re.compile(r"[<>\"'&]")
152
+ RE_MDV = re.compile(r"(.*)\.([0-9]+\.[0-9]{3})(\.[Mm][Dd])$")
151
153
 
152
154
  UPARAM_CC_OK = set("doc move tree".split())
153
155
 
@@ -1803,7 +1805,8 @@ class HttpCli(object):
1803
1805
  dst = unquotep(dst)
1804
1806
 
1805
1807
  # overwrite=True is default; rfc4918 9.8.4
1806
- overwrite = self.headers.get("overwrite", "").lower() != "f"
1808
+ zs = self.headers.get("overwrite", "").lower()
1809
+ overwrite = zs not in ["f", "false"]
1807
1810
 
1808
1811
  try:
1809
1812
  fun = self._cp if self.mode == "COPY" else self._mv
@@ -5946,7 +5949,7 @@ class HttpCli(object):
5946
5949
  # [num-backups, most-recent, hist-path]
5947
5950
  hist = {}
5948
5951
  histdir = os.path.join(fsroot, ".hist")
5949
- ptn = re.compile(r"(.*)\.([0-9]+\.[0-9]{3})(\.[^\.]+)$")
5952
+ ptn = RE_MDV
5950
5953
  try:
5951
5954
  for hfn in bos.listdir(histdir):
5952
5955
  m = ptn.match(hfn)
@@ -5979,6 +5982,7 @@ class HttpCli(object):
5979
5982
 
5980
5983
  dirs = []
5981
5984
  files = []
5985
+ ptn_hr = RE_HR
5982
5986
  for fn in ls_names:
5983
5987
  base = ""
5984
5988
  href = fn
@@ -6033,11 +6037,13 @@ class HttpCli(object):
6033
6037
  zd.second,
6034
6038
  )
6035
6039
 
6036
- try:
6037
- ext = "---" if is_dir else fn.rsplit(".", 1)[1]
6040
+ if is_dir:
6041
+ ext = "---"
6042
+ elif "." in fn:
6043
+ ext = ptn_hr.sub("@", fn.rsplit(".", 1)[1])
6038
6044
  if len(ext) > 16:
6039
6045
  ext = ext[:16]
6040
- except:
6046
+ else:
6041
6047
  ext = "%"
6042
6048
 
6043
6049
  if add_fk and not is_dir:
copyparty/up2k.py CHANGED
@@ -553,6 +553,7 @@ class Up2k(object):
553
553
  else:
554
554
  # important; not deferred by db_act
555
555
  timeout = self._check_lifetimes()
556
+ timeout = min(self._check_forget_ip(), timeout)
556
557
  try:
557
558
  if self.args.shr:
558
559
  timeout = min(self._check_shares(), timeout)
@@ -613,6 +614,43 @@ class Up2k(object):
613
614
  for v in vols:
614
615
  volage[v] = now
615
616
 
617
+ def _check_forget_ip(self) :
618
+ now = time.time()
619
+ timeout = now + 9001
620
+ for vp, vol in sorted(self.vfs.all_vols.items()):
621
+ maxage = vol.flags["forget_ip"]
622
+ if not maxage:
623
+ continue
624
+
625
+ cur = self.cur.get(vol.realpath)
626
+ if not cur:
627
+ continue
628
+
629
+ cutoff = now - maxage * 60
630
+
631
+ for _ in range(2):
632
+ q = "select ip, at from up where ip > '' order by +at limit 1"
633
+ hits = cur.execute(q).fetchall()
634
+ if not hits:
635
+ break
636
+
637
+ remains = hits[0][1] - cutoff
638
+ if remains > 0:
639
+ timeout = min(timeout, now + remains)
640
+ break
641
+
642
+ q = "update up set ip = '' where ip > '' and at <= %d"
643
+ cur.execute(q % (cutoff,))
644
+ zi = cur.rowcount
645
+ cur.connection.commit()
646
+
647
+ t = "forget-ip(%d) removed %d IPs from db [/%s]"
648
+ self.log(t % (maxage, zi, vol.vpath))
649
+
650
+ timeout = min(timeout, now + 900)
651
+
652
+ return timeout
653
+
616
654
  def _check_lifetimes(self) :
617
655
  now = time.time()
618
656
  timeout = now + 9001
@@ -1074,7 +1112,7 @@ class Up2k(object):
1074
1112
  ft = "\033[0;32m{}{:.0}"
1075
1113
  ff = "\033[0;35m{}{:.0}"
1076
1114
  fv = "\033[0;36m{}:\033[90m{}"
1077
- zs = "html_head mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot"
1115
+ zs = "ext_th_d html_head mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot"
1078
1116
  fx = set(zs.split())
1079
1117
  fd = vf_bmap()
1080
1118
  fd.update(vf_cmap())
@@ -3319,7 +3357,17 @@ class Up2k(object):
3319
3357
  return fname
3320
3358
 
3321
3359
  fp = djoin(fdir, fname)
3322
- if job.get("replace") and bos.path.exists(fp):
3360
+
3361
+ ow = job.get("replace") and bos.path.exists(fp)
3362
+ if ow and "mt" in str(job["replace"]).lower():
3363
+ mts = bos.stat(fp).st_mtime
3364
+ mtc = job["lmod"]
3365
+ if mtc < mts:
3366
+ t = "will not overwrite; server %d sec newer than client; %d > %d %r"
3367
+ self.log(t % (mts - mtc, mts, mtc, fp))
3368
+ ow = False
3369
+
3370
+ if ow:
3323
3371
  self.log("replacing existing file at %r" % (fp,))
3324
3372
  cur = None
3325
3373
  ptop = job["ptop"]
@@ -3770,7 +3818,7 @@ class Up2k(object):
3770
3818
  db_ip = ""
3771
3819
  else:
3772
3820
  # plugins may expect this to look like an actual IP
3773
- db_ip = "1.1.1.1" if self.args.no_db_ip else ip
3821
+ db_ip = "1.1.1.1" if "no_db_ip" in vflags else ip
3774
3822
 
3775
3823
  sql = "insert into up values (?,?,?,?,?,?,?)"
3776
3824
  v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 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 = "2.9"
5
- S_BUILD_DT = "2025-01-27"
4
+ S_VERSION = "2.10"
5
+ S_BUILD_DT = "2025-02-19"
6
6
 
7
7
  """
8
8
  u2c.py: upload to copyparty
@@ -805,7 +805,9 @@ def handshake(ar, file, search):
805
805
  else:
806
806
  if ar.touch:
807
807
  req["umod"] = True
808
- if ar.ow:
808
+ if ar.owo:
809
+ req["replace"] = "mt"
810
+ elif ar.ow:
809
811
  req["replace"] = True
810
812
 
811
813
  file.recheck = False
@@ -1536,6 +1538,7 @@ source file/folder selection uses rsync syntax, meaning that:
1536
1538
  ap.add_argument("--ok", action="store_true", help="continue even if some local files are inaccessible")
1537
1539
  ap.add_argument("--touch", action="store_true", help="if last-modified timestamps differ, push local to server (need write+delete perms)")
1538
1540
  ap.add_argument("--ow", action="store_true", help="overwrite existing files instead of autorenaming")
1541
+ ap.add_argument("--owo", action="store_true", help="overwrite existing files if server-file is older")
1539
1542
  ap.add_argument("--spd", action="store_true", help="print speeds for each file")
1540
1543
  ap.add_argument("--version", action="store_true", help="show version and exit")
1541
1544
 
Binary file
Binary file
copyparty/web/md2.js.gz CHANGED
Binary file
copyparty/web/up2k.js.gz CHANGED
Binary file
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: copyparty
3
- Version: 1.16.13
3
+ Version: 1.16.15
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
@@ -151,9 +151,11 @@ turn almost any device into a file server with resumable uploads/downloads using
151
151
  * [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
152
152
  * [real-ip](#real-ip) - teaching copyparty how to see client IPs
153
153
  * [reverse-proxy performance](#reverse-proxy-performance)
154
+ * [permanent cloudflare tunnel](#permanent-cloudflare-tunnel) - if you have a domain and want to get your copyparty online real quick
154
155
  * [prometheus](#prometheus) - metrics/stats can be enabled
155
156
  * [other extremely specific features](#other-extremely-specific-features) - you'll never find a use for these
156
157
  * [custom mimetypes](#custom-mimetypes) - change the association of a file extension
158
+ * [GDPR compliance](#GDPR-compliance) - imagine using copyparty professionally...
157
159
  * [feature chickenbits](#feature-chickenbits) - buggy feature? rip it out
158
160
  * [packages](#packages) - the party might be closer than you think
159
161
  * [arch package](#arch-package) - now [available on aur](https://aur.archlinux.org/packages/copyparty) maintained by [@icxes](https://github.com/icxes)
@@ -216,8 +218,8 @@ enable thumbnails (images/audio/video), media indexing, and audio transcoding by
216
218
  * **MacOS:** `port install py-Pillow ffmpeg`
217
219
  * **MacOS** (alternative): `brew install pillow ffmpeg`
218
220
  * **Windows:** `python -m pip install --user -U Pillow`
219
- * install python and ffmpeg manually; do not use `winget` or `Microsoft Store` (it breaks $PATH)
220
- * copyparty.exe comes with `Pillow` and only needs `ffmpeg`
221
+ * install [python](https://www.python.org/downloads/windows/) and [ffmpeg](#optional-dependencies) manually; do not use `winget` or `Microsoft Store` (it breaks $PATH)
222
+ * copyparty.exe comes with `Pillow` and only needs [ffmpeg](#optional-dependencies) for mediatags/videothumbs
221
223
  * see [optional dependencies](#optional-dependencies) to enable even more features
222
224
 
223
225
  running copyparty without arguments (for example doubleclicking it on Windows) will give everyone read/write access to the current folder; you may want [accounts and volumes](#accounts-and-volumes)
@@ -240,6 +242,8 @@ first download [cloudflared](https://developers.cloudflare.com/cloudflare-one/co
240
242
 
241
243
  as the tunnel starts, it will show a URL which you can share to let anyone browse your stash or upload files to you
242
244
 
245
+ but if you have a domain, then you probably want to skip the random autogenerated URL and instead make a [permanent cloudflare tunnel](#permanent-cloudflare-tunnel)
246
+
243
247
  since people will be connecting through cloudflare, run copyparty with `--xff-hdr cf-connecting-ip` to detect client IPs correctly
244
248
 
245
249
 
@@ -281,6 +285,7 @@ also see [comparison to similar software](./docs/versus.md)
281
285
  * ☑ [upnp / zeroconf / mdns / ssdp](#zeroconf)
282
286
  * ☑ [event hooks](#event-hooks) / script runner
283
287
  * ☑ [reverse-proxy support](https://github.com/9001/copyparty#reverse-proxy)
288
+ * ☑ cross-platform (Windows, Linux, Macos, Android, FreeBSD, arm32/arm64, ppc64le, s390x, risc-v/riscv64)
284
289
  * upload
285
290
  * ☑ basic: plain multipart, ie6 support
286
291
  * ☑ [up2k](#uploading): js, resumable, multithreaded
@@ -458,6 +463,9 @@ upgrade notes
458
463
 
459
464
  "frequently" asked questions
460
465
 
466
+ * can I change the 🌲 spinning pine-tree loading animation?
467
+ * [yeah...](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#boring-loader-spinner) :-(
468
+
461
469
  * is it possible to block read-access to folders unless you know the exact URL for a particular file inside?
462
470
  * yes, using the [`g` permission](#accounts-and-volumes), see the examples there
463
471
  * you can also do this with linux filesystem permissions; `chmod 111 music` will make it possible to access files and folders inside the `music` folder but not list the immediate contents -- also works with other software, not just copyparty
@@ -480,6 +488,14 @@ upgrade notes
480
488
  * copyparty seems to think I am using http, even though the URL is https
481
489
  * your reverse-proxy is not sending the `X-Forwarded-Proto: https` header; this could be because your reverse-proxy itself is confused. Ensure that none of the intermediates (such as cloudflare) are terminating https before the traffic hits your entrypoint
482
490
 
491
+ * thumbnails are broken (you get a colorful square which says the filetype instead)
492
+ * you need to install `FFmpeg` or `Pillow`; see [thumbnails](#thumbnails)
493
+
494
+ * thumbnails are broken (some images appear, but other files just get a blank box, and/or the broken-image placeholder)
495
+ * probably due to a reverse-proxy messing with the request URLs and stripping the query parameters (`?th=w`), so check your URL rewrite rules
496
+ * could also be due to incorrect caching settings in reverse-proxies and/or CDNs, so make sure that nothing is set to ignore the query string
497
+ * could also be due to misbehaving privacy-related browser extensions, so try to disable those
498
+
483
499
  * i want to learn python and/or programming and am considering looking at the copyparty source code in that occasion
484
500
  * ```bash
485
501
  _| _ __ _ _|_
@@ -710,6 +726,7 @@ press `g` or `田` to toggle grid-view instead of the file listing and `t` togg
710
726
  it does static images with Pillow / pyvips / FFmpeg, and uses FFmpeg for video files, so you may want to `--no-thumb` or maybe just `--no-vthumb` depending on how dangerous your users are
711
727
  * pyvips is 3x faster than Pillow, Pillow is 3x faster than FFmpeg
712
728
  * disable thumbnails for specific volumes with volflag `dthumb` for all, or `dvthumb` / `dathumb` / `dithumb` for video/audio/images only
729
+ * for installing FFmpeg on windows, see [optional dependencies](#optional-dependencies)
713
730
 
714
731
  audio files are converted into spectrograms using FFmpeg unless you `--no-athumb` (and some FFmpeg builds may need `--th-ff-swr`)
715
732
 
@@ -821,8 +838,11 @@ the up2k UI is the epitome of polished intuitive experiences:
821
838
  * "parallel uploads" specifies how many chunks to upload at the same time
822
839
  * `[🏃]` analysis of other files should continue while one is uploading
823
840
  * `[🥔]` shows a simpler UI for faster uploads from slow devices
841
+ * `[🛡️]` decides when to overwrite existing files on the server
842
+ * `🛡️` = never (generate a new filename instead)
843
+ * `🕒` = overwrite if the server-file is older
844
+ * `♻️` = always overwrite if the files are different
824
845
  * `[🎲]` generate random filenames during upload
825
- * `[📅]` preserve last-modified timestamps; server times will match yours
826
846
  * `[🔎]` switch between upload and [file-search](#file-search) mode
827
847
  * ignore `[🔎]` if you add files by dragging them into the browser
828
848
 
@@ -2039,6 +2059,26 @@ in summary, `haproxy > caddy > traefik > nginx > apache > lighttpd`, and use uds
2039
2059
  * if these results are bullshit because my config exampels are bad, please submit corrections!
2040
2060
 
2041
2061
 
2062
+ ## permanent cloudflare tunnel
2063
+
2064
+ if you have a domain and want to get your copyparty online real quick, either from your home-PC behind a CGNAT or from a server without an existing [reverse-proxy](#reverse-proxy) setup, one approach is to create a [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/) (formerly "Argo Tunnel")
2065
+
2066
+ I'd recommend making a `Locally-managed tunnel` for more control, but if you prefer to make a `Remotely-managed tunnel` then this is currently how:
2067
+
2068
+ * `cloudflare dashboard` » `zero trust` » `networks` » `tunnels` » `create a tunnel` » `cloudflared` » choose a cool `subdomain` and leave the `path` blank, and use `service type` = `http` and `URL` = `127.0.0.1:3923`
2069
+
2070
+ * and if you want to just run the tunnel without installing it, skip the `cloudflared service install BASE64` step and instead do `cloudflared --no-autoupdate tunnel run --token BASE64`
2071
+
2072
+ NOTE: since people will be connecting through cloudflare, as mentioned in [real-ip](#real-ip) you should run copyparty with `--xff-hdr cf-connecting-ip` to detect client IPs correctly
2073
+
2074
+ config file example:
2075
+
2076
+ ```yaml
2077
+ [global]
2078
+ xff-hdr: cf-connecting-ip
2079
+ ```
2080
+
2081
+
2042
2082
  ## prometheus
2043
2083
 
2044
2084
  metrics/stats can be enabled at URL `/.cpr/metrics` for grafana / prometheus / etc (openmetrics 1.0.0)
@@ -2125,6 +2165,18 @@ in a config file, this is the same as:
2125
2165
  run copyparty with `--mimes` to list all the default mappings
2126
2166
 
2127
2167
 
2168
+ ### GDPR compliance
2169
+
2170
+ imagine using copyparty professionally... **TINLA/IANAL; EU laws are hella confusing**
2171
+
2172
+ * remember to disable logging, or configure logrotation to an acceptable timeframe with `-lo cpp-%Y-%m%d.txt.xz` or similar
2173
+
2174
+ * if running with the database enabled (recommended), then have it forget uploader-IPs after some time using `--forget-ip 43200`
2175
+ * don't set it too low; [unposting](#unpost) a file is no longer possible after this takes effect
2176
+
2177
+ * if you actually *are* a lawyer then I'm open for feedback, would be fun
2178
+
2179
+
2128
2180
  ### feature chickenbits
2129
2181
 
2130
2182
  buggy feature? rip it out by setting any of the following environment variables to disable its associated bell or whistle,
@@ -2312,6 +2364,7 @@ quick summary of more eccentric web-browsers trying to view a directory index:
2312
2364
  | **ie4** and **netscape** 4.0 | can browse, upload with `?b=u`, auth with `&pw=wark` |
2313
2365
  | **ncsa mosaic** 2.7 | does not get a pass, [pic1](https://user-images.githubusercontent.com/241032/174189227-ae816026-cf6f-4be5-a26e-1b3b072c1b2f.png) - [pic2](https://user-images.githubusercontent.com/241032/174189225-5651c059-5152-46e9-ac26-7e98e497901b.png) |
2314
2366
  | **SerenityOS** (7e98457) | hits a page fault, works with `?b=u`, file upload not-impl |
2367
+ | **sony psp** 5.50 | can browse, upload/mkdir/msg (thx dwarf) [screenshot](https://github.com/user-attachments/assets/9d21f020-1110-4652-abeb-6fc09c533d4f) |
2315
2368
  | **nintendo 3ds** | can browse, upload, view thumbnails (thx bnjmn) |
2316
2369
 
2317
2370
  <p align="center"><img src="https://github.com/user-attachments/assets/88deab3d-6cad-4017-8841-2f041472b853" /></p>
@@ -2642,6 +2695,8 @@ enable [smb](#smb-server) support (**not** recommended): `impacket==0.12.0`
2642
2695
 
2643
2696
  `pyvips` gives higher quality thumbnails than `Pillow` and is 320% faster, using 270% more ram: `sudo apt install libvips42 && python3 -m pip install --user -U pyvips`
2644
2697
 
2698
+ to install FFmpeg on Windows, grab [a recent build](https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z) -- you need `ffmpeg.exe` and `ffprobe.exe` from inside the `bin` folder; copy them into `C:\Windows\System32` or any other folder that's in your `%PATH%`
2699
+
2645
2700
 
2646
2701
  ### dependency chickenbits
2647
2702
 
@@ -1,17 +1,17 @@
1
1
  copyparty/__init__.py,sha256=VR6ZZhB9IxaK5TDXDTBM_OIP5ydkrdbaEnstktLM__s,2649
2
- copyparty/__main__.py,sha256=Tcvi7uIGA37yGVXJbbhn-Cr0OBtt81aRF2Or0ZHXz6U,116449
3
- copyparty/__version__.py,sha256=Kh8nmhFwgLz9KpLvVPB-drOjIKb4uSXTYfW7c0xGr3E,252
4
- copyparty/authsrv.py,sha256=-oy1PtcMONLcrMllSY2aPuyHWQmE0Ot_EQTX7UJ5tuc,107035
2
+ copyparty/__main__.py,sha256=JDjMsCiOMMBPs8HR8vpgEJMgnxq-qjX-hcgxKHF9WX4,117137
3
+ copyparty/__version__.py,sha256=HKXFw8jbvkFpMUWxHYEZL8rEZ2F2g8Y3xZcoqydWK0o,252
4
+ copyparty/authsrv.py,sha256=EqlE2YztBcZHF5MHStZawA8ZKSlm-ePkrhsen90lKE8,107740
5
5
  copyparty/broker_mp.py,sha256=QdOXXvV2Xn6J0CysEqyY3GZbqxQMyWnTpnba-a5lMc0,4987
6
6
  copyparty/broker_mpw.py,sha256=PpSS4SK3pItlpfD8OwVr3QmJEPKlUgaf2nuMOozixgU,3347
7
7
  copyparty/broker_thr.py,sha256=fjoYtpSscUA7-nMl4r1n2R7UK3J9lrvLS3rUZ-iJzKQ,1721
8
8
  copyparty/broker_util.py,sha256=76mfnFOpX1gUUvtjm8UQI7jpTIaVINX10QonM-B7ggc,1680
9
9
  copyparty/cert.py,sha256=0ZAPeXeMR164vWn9GQU3JDKooYXEq_NOQkDeg543ivg,8009
10
- copyparty/cfg.py,sha256=-Cbva1shfXQVGFl8Xbo-bkUYjkpU6amPZpTyPJcE7M0,12926
10
+ copyparty/cfg.py,sha256=Z04AuNy-viZiiVJ8fLf3RULgH4w8ZidjsAVj4cmJKlA,13272
11
11
  copyparty/dxml.py,sha256=vu5uZQtwvwoqnFHbULs2Zh_y2DETu0T-ENpMZ1i2CV4,2505
12
12
  copyparty/fsutil.py,sha256=IVOFG8zBQPMQDDv7RIStSJHwHiAnVNROZS37O5k465A,4524
13
13
  copyparty/ftpd.py,sha256=T97SFS7JFtvRLbJX8C4fJSYwe13vhN3-E6emtlVmqLA,17608
14
- copyparty/httpcli.py,sha256=Dr776ra7_wTCghymLOymy_Bpv8A15Qmgt9ERe-8kZ5A,219131
14
+ copyparty/httpcli.py,sha256=DT2ZV-Hrxg1itfmeTkkHIctJHdG8nxtnIZJ8Whufxxo,219291
15
15
  copyparty/httpconn.py,sha256=mQSgljh0Q-jyWjF4tQLrHbRKRe9WKl19kGqsGMsJpWo,6880
16
16
  copyparty/httpsrv.py,sha256=pxH_Eh8ElBLvOEDejgpP9Bvk65HNEou-03aYIcgXhrs,18090
17
17
  copyparty/ico.py,sha256=eWSxEae4wOCfheHl-m-wchYvFRAR_97kJDb4NGaB-Z8,3561
@@ -31,7 +31,7 @@ copyparty/tftpd.py,sha256=PXgG4rTmiaU_TavSyZWD5cFphdfChs9YvNY21qfExt8,13611
31
31
  copyparty/th_cli.py,sha256=PxDAmUvO_8Vm5edXiWtsCft0Fw69QL9rCHf9zLmUNeA,4800
32
32
  copyparty/th_srv.py,sha256=tHbh_Ve3v8tYclWH2thLs5oFufeXgJi1duUMveKIx9k,30725
33
33
  copyparty/u2idx.py,sha256=G6MDbD4I_sJSOwaNFZ6XLTQhnEDrB12pVKuKhzQ_leE,13676
34
- copyparty/up2k.py,sha256=yli2ALT61o1sPta4ckJu4v7hwUrV7IAJSsJo1-OEZWY,175423
34
+ copyparty/up2k.py,sha256=flkVZkHy7cfRZh7DSbTMbr4jfw7poZDOX3gFu5vyFxM,176973
35
35
  copyparty/util.py,sha256=Y_znSn3hBNYaaduwcCB7mmBYsi6vv9CYC1zJ9rq9yeQ,99435
36
36
  copyparty/bos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
37
  copyparty/bos/bos.py,sha256=Wb7eWsXJgR5AFlBR9ZOyKrLTwy-Kct9RrGiOu4Jo37Y,1622
@@ -55,9 +55,9 @@ copyparty/stolen/ifaddr/_posix.py,sha256=-67NdfGrCktfQPakT2fLbjl2U00QMvyBGkSvrUu
55
55
  copyparty/stolen/ifaddr/_shared.py,sha256=uNC4SdEIgdSLKvuUzsf1aM-H1Xrc_9mpLoOT43YukGs,6206
56
56
  copyparty/stolen/ifaddr/_win32.py,sha256=EE-QyoBgeB7lYQ6z62VjXNaRozaYfCkaJBHGNA8QtZM,4026
57
57
  copyparty/web/baguettebox.js.gz,sha256=r2c_hOZV_RTyl4CqWWX14FDWP8nnDVwGkDl4Sfk0rU4,8239
58
- copyparty/web/browser.css.gz,sha256=A44DddZBf-PEEMOj-u5YF_JrSk5DZYf-8f_J5jqC2is,11651
58
+ copyparty/web/browser.css.gz,sha256=_HiFW5vPUusWadoqdY8ZihuWizY9UECAc5nIamBPRi4,11654
59
59
  copyparty/web/browser.html,sha256=auvhLVE_t0aIN0q-nk0zOWFqITgDhroMAAviBNLoFfc,4788
60
- copyparty/web/browser.js.gz,sha256=Ec5tzZutoI8daxzHdfRfDP7L9ovDTY10j2g-hkeRlmA,91598
60
+ copyparty/web/browser.js.gz,sha256=dZ0sELnKXMnfQ_hrYb19xZ0SE9wFlYbDOCy-TEnFono,92091
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
@@ -65,7 +65,7 @@ copyparty/web/md.css.gz,sha256=UZpN0J7ubVM05CZkbZYkQRJeGgJt_GNDEzKTGSQd8h4,2032
65
65
  copyparty/web/md.html,sha256=hz-xJVfKtaeTUQn3tGh7ebIMvLbOjVKkrMhsCTr3lGM,4200
66
66
  copyparty/web/md.js.gz,sha256=m5dpIskZZ8FD9X-L0EWpoZP-yLOo-PCWJ7j4AFtHVRI,4163
67
67
  copyparty/web/md2.css.gz,sha256=uIVHKScThdbcfhXNSHgKZnALYpxbnXC-WuEzOJ20Lpc,699
68
- copyparty/web/md2.js.gz,sha256=z3JASmIT5PVEWgTpHpc4uBkZsbqgtSF4Nt0w2b_8Mek,8355
68
+ copyparty/web/md2.js.gz,sha256=42GJAAMaAa6dnBAYJfUlz4yCtSzYe5d-W-B7zOfiLW0,8372
69
69
  copyparty/web/mde.css.gz,sha256=2SkAEDKIRPqywNJ8t_heQaeBQ_R73Rf-pQI_bDoKF6o,942
70
70
  copyparty/web/mde.html,sha256=fRGUlnNhK6ra8P4jskQLUw6Aaef6g6Heh4EEgYhJkxU,1770
71
71
  copyparty/web/mde.js.gz,sha256=kN2eUSvr4mFuksfK4-4LimJmWdwsao39Sea2lWtu8L0,2224
@@ -83,12 +83,12 @@ copyparty/web/splash.js.gz,sha256=4VqNznN10-bT33IJm3VWzBEJ1s08XZyxFB1TYPUkuAo,27
83
83
  copyparty/web/svcs.html,sha256=dnE1fG15zOpq7u0GYt8ij6BUv_LTwsiipFeneVYlMsM,14140
84
84
  copyparty/web/svcs.js.gz,sha256=lMXEP9W-VlXyANlva4q0ASSxvvHYlE2CrmxGgZXZop0,713
85
85
  copyparty/web/ui.css.gz,sha256=0sHIwGsL3_xH8Uu6N0Ag3bKBTjf-e_yfFbKynEZXAnk,2800
86
- copyparty/web/up2k.js.gz,sha256=N4idIqOefXurVh4ZhN6lv9nFPX_azLwE41vhXT2qBkI,23833
86
+ copyparty/web/up2k.js.gz,sha256=1bw_NV3mm8crU0ZyXMJiDwBHc4hheAsPig4GXFmGm6Y,24066
87
87
  copyparty/web/util.js.gz,sha256=wD3tP5j1iE5Uj5AvLW5zZbQJXDIFDlqgBTGdXeRVqo0,15110
88
- copyparty/web/w.hash.js.gz,sha256=l3GpSJD6mcU-1CRWkIj7PybgbjlfSr8oeO3vortIrQk,1105
88
+ copyparty/web/w.hash.js.gz,sha256=JhJagnqIkcKng_hs6otEgzcuQE7keToG_r5dd2o3EfU,1108
89
89
  copyparty/web/a/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
90
  copyparty/web/a/partyfuse.py,sha256=9p5Hpg_IBiSimv7j9kmPhCGpy-FLXSRUOYnLjJ5JifU,28049
91
- copyparty/web/a/u2c.py,sha256=8F-uVfu0ceXRehbNLjjRfcBKStP5Lm_vJ_JAhEM6zgk,52994
91
+ copyparty/web/a/u2c.py,sha256=oRKEApPuaXiOkmUyA-WGCDjtWsn8FirSW6nCJtx9sgk,53157
92
92
  copyparty/web/a/webdav-cfg.bat,sha256=Y4NoGZlksAIg4cBMb7KdJrpKC6Nx97onaTl6yMjaimk,1449
93
93
  copyparty/web/dd/2.png,sha256=gJ14XFPzaw95L6z92fSq9eMPikSQyu-03P1lgiGe0_I,258
94
94
  copyparty/web/dd/3.png,sha256=4lho8Koz5tV7jJ4ODo6GMTScZfkqsT05yp48EDFIlyg,252
@@ -109,9 +109,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
109
109
  copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
110
110
  copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
111
111
  copyparty/web/deps/sha512.hw.js.gz,sha256=UAed2_ocklZCnIzcSYz2h4P1ycztlCLj-ewsRTud2lU,7939
112
- copyparty-1.16.13.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
113
- copyparty-1.16.13.dist-info/METADATA,sha256=hm4IR2Xqc3bnZLZuEE3MPuNqwi73-IXzd_WqnVo_2NI,154166
114
- copyparty-1.16.13.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
115
- copyparty-1.16.13.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
116
- copyparty-1.16.13.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
117
- copyparty-1.16.13.dist-info/RECORD,,
112
+ copyparty-1.16.15.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
113
+ copyparty-1.16.15.dist-info/METADATA,sha256=x6aMlu6u9GN0-GrOf4OZPk4Ew-dy-2iqOcR48EXUIxs,158045
114
+ copyparty-1.16.15.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
115
+ copyparty-1.16.15.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
116
+ copyparty-1.16.15.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
117
+ copyparty-1.16.15.dist-info/RECORD,,