copyparty 1.15.1__py3-none-any.whl → 1.15.2__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/util.py CHANGED
@@ -3,7 +3,7 @@ from __future__ import print_function, unicode_literals
3
3
 
4
4
  import argparse
5
5
  import base64
6
- import contextlib
6
+ import binascii
7
7
  import errno
8
8
  import hashlib
9
9
  import hmac
@@ -30,13 +30,10 @@ from collections import Counter
30
30
  from ipaddress import IPv4Address, IPv4Network, IPv6Address, IPv6Network
31
31
  from queue import Queue
32
32
 
33
- from .__init__ import ANYWIN, EXE, MACOS, PY2, TYPE_CHECKING, VT100, WINDOWS
33
+ from .__init__ import ANYWIN, EXE, MACOS, PY2, PY36, TYPE_CHECKING, VT100, WINDOWS
34
34
  from .__version__ import S_BUILD_DT, S_VERSION
35
35
  from .stolen import surrogateescape
36
36
 
37
- ub64dec = base64.urlsafe_b64decode
38
- ub64enc = base64.urlsafe_b64encode
39
-
40
37
  try:
41
38
  from datetime import datetime, timezone
42
39
 
@@ -64,7 +61,7 @@ if PY2:
64
61
 
65
62
 
66
63
  if sys.version_info >= (3, 7) or (
67
- sys.version_info >= (3, 6) and platform.python_implementation() == "CPython"
64
+ PY36 and platform.python_implementation() == "CPython"
68
65
  ):
69
66
  ODict = dict
70
67
  else:
@@ -143,12 +140,8 @@ except ImportError:
143
140
 
144
141
  if not PY2:
145
142
  from io import BytesIO
146
- from urllib.parse import quote_from_bytes as quote
147
- from urllib.parse import unquote_to_bytes as unquote
148
143
  else:
149
144
  from StringIO import StringIO as BytesIO # type: ignore
150
- from urllib import quote # type: ignore # pylint: disable=no-name-in-module
151
- from urllib import unquote # type: ignore # pylint: disable=no-name-in-module
152
145
 
153
146
 
154
147
  try:
@@ -195,7 +188,7 @@ else:
195
188
  FS_ENCODING = sys.getfilesystemencoding()
196
189
 
197
190
 
198
- SYMTIME = sys.version_info > (3, 6) and os.utime in os.supports_follow_symlinks
191
+ SYMTIME = PY36 and os.utime in os.supports_follow_symlinks
199
192
 
200
193
  META_NOBOTS = '<meta name="robots" content="noindex, nofollow">\n'
201
194
 
@@ -317,7 +310,7 @@ MAGIC_MAP = {"jpeg": "jpg"}
317
310
 
318
311
  DEF_EXP = "self.ip self.ua self.uname self.host cfg.name cfg.logout vf.scan vf.thsize hdr.cf_ipcountry srv.itime srv.htime"
319
312
 
320
- DEF_MTE = "circle,album,.tn,artist,title,.bpm,key,.dur,.q,.vq,.aq,vc,ac,fmt,res,.fps,ahash,vhash"
313
+ DEF_MTE = ".files,circle,album,.tn,artist,title,.bpm,key,.dur,.q,.vq,.aq,vc,ac,fmt,res,.fps,ahash,vhash"
321
314
 
322
315
  DEF_MTH = ".vq,.aq,vc,ac,fmt,res,.fps"
323
316
 
@@ -419,7 +412,6 @@ def py_desc() :
419
412
 
420
413
 
421
414
  def _sqlite_ver() :
422
- assert sqlite3 # type: ignore
423
415
  try:
424
416
  co = sqlite3.connect(":memory:")
425
417
  cur = co.cursor()
@@ -467,17 +459,36 @@ VERSIONS = (
467
459
  )
468
460
 
469
461
 
470
- _ = (mp, BytesIO, quote, unquote, SQLITE_VER, JINJA_VER, PYFTPD_VER, PARTFTPY_VER)
471
- __all__ = [
472
- "mp",
473
- "BytesIO",
474
- "quote",
475
- "unquote",
476
- "SQLITE_VER",
477
- "JINJA_VER",
478
- "PYFTPD_VER",
479
- "PARTFTPY_VER",
480
- ]
462
+ try:
463
+ _b64_enc_tl = bytes.maketrans(b"+/", b"-_")
464
+ _b64_dec_tl = bytes.maketrans(b"-_", b"+/")
465
+
466
+ def ub64enc(bs ) :
467
+ x = binascii.b2a_base64(bs, newline=False)
468
+ return x.translate(_b64_enc_tl)
469
+
470
+ def ub64dec(bs ) :
471
+ bs = bs.translate(_b64_dec_tl)
472
+ return binascii.a2b_base64(bs)
473
+
474
+ def b64enc(bs ) :
475
+ return binascii.b2a_base64(bs, newline=False)
476
+
477
+ def b64dec(bs ) :
478
+ return binascii.a2b_base64(bs)
479
+
480
+ zb = b">>>????"
481
+ zb2 = base64.urlsafe_b64encode(zb)
482
+ if zb2 != ub64enc(zb) or zb != ub64dec(zb2):
483
+ raise Exception("bad smoke")
484
+
485
+ except Exception as ex:
486
+ ub64enc = base64.urlsafe_b64encode # type: ignore
487
+ ub64dec = base64.urlsafe_b64decode # type: ignore
488
+ b64enc = base64.b64encode # type: ignore
489
+ b64dec = base64.b64decode # type: ignore
490
+ if not PY36:
491
+ print("using fallback base64 codec due to %r" % (ex,))
481
492
 
482
493
 
483
494
  class Daemon(threading.Thread):
@@ -1009,7 +1020,6 @@ class MTHash(object):
1009
1020
  if self.stop:
1010
1021
  return nch, "", ofs0, chunk_sz
1011
1022
 
1012
- assert f
1013
1023
  hashobj = hashlib.sha512()
1014
1024
  while chunk_rem > 0:
1015
1025
  with self.imutex:
@@ -1024,7 +1034,7 @@ class MTHash(object):
1024
1034
  ofs += len(buf)
1025
1035
 
1026
1036
  bdig = hashobj.digest()[:33]
1027
- udig = base64.urlsafe_b64encode(bdig).decode("utf-8")
1037
+ udig = ub64enc(bdig).decode("ascii")
1028
1038
  return nch, udig, ofs0, chunk_sz
1029
1039
 
1030
1040
 
@@ -1050,7 +1060,7 @@ class HMaccas(object):
1050
1060
  self.cache = {}
1051
1061
 
1052
1062
  zb = hmac.new(self.key, msg, hashlib.sha512).digest()
1053
- zs = base64.urlsafe_b64encode(zb)[: self.retlen].decode("utf-8")
1063
+ zs = ub64enc(zb)[: self.retlen].decode("ascii")
1054
1064
  self.cache[msg] = zs
1055
1065
  return zs
1056
1066
 
@@ -1381,18 +1391,13 @@ def min_ex(max_lines = 8, reverse = False) :
1381
1391
  return "\n".join(ex[-max_lines:][:: -1 if reverse else 1])
1382
1392
 
1383
1393
 
1384
- @contextlib.contextmanager
1385
- def ren_open(
1386
- fname , *args , **kwargs
1387
- ) :
1394
+ def ren_open(fname , *args , **kwargs ) :
1388
1395
  fun = kwargs.pop("fun", open)
1389
1396
  fdir = kwargs.pop("fdir", None)
1390
1397
  suffix = kwargs.pop("suffix", None)
1391
1398
 
1392
1399
  if fname == os.devnull:
1393
- with fun(fname, *args, **kwargs) as f:
1394
- yield {"orz": (f, fname)}
1395
- return
1400
+ return fun(fname, *args, **kwargs), fname
1396
1401
 
1397
1402
  if suffix:
1398
1403
  ext = fname.split(".")[-1]
@@ -1414,6 +1419,7 @@ def ren_open(
1414
1419
  asciified = False
1415
1420
  b64 = ""
1416
1421
  while True:
1422
+ f = None
1417
1423
  try:
1418
1424
  if fdir:
1419
1425
  fpath = os.path.join(fdir, fname)
@@ -1425,19 +1431,19 @@ def ren_open(
1425
1431
  fname += suffix
1426
1432
  ext += suffix
1427
1433
 
1428
- with fun(fsenc(fpath), *args, **kwargs) as f:
1429
- if b64:
1430
- assert fdir
1431
- fp2 = "fn-trunc.%s.txt" % (b64,)
1432
- fp2 = os.path.join(fdir, fp2)
1433
- with open(fsenc(fp2), "wb") as f2:
1434
- f2.write(orig_name.encode("utf-8"))
1434
+ f = fun(fsenc(fpath), *args, **kwargs)
1435
+ if b64:
1436
+ fp2 = "fn-trunc.%s.txt" % (b64,)
1437
+ fp2 = os.path.join(fdir, fp2)
1438
+ with open(fsenc(fp2), "wb") as f2:
1439
+ f2.write(orig_name.encode("utf-8"))
1435
1440
 
1436
- yield {"orz": (f, fname)}
1437
- return
1441
+ return f, fname
1438
1442
 
1439
1443
  except OSError as ex_:
1440
1444
  ex = ex_
1445
+ if f:
1446
+ f.close()
1441
1447
 
1442
1448
  # EPERM: android13
1443
1449
  if ex.errno in (errno.EINVAL, errno.EPERM) and not asciified:
@@ -1458,8 +1464,7 @@ def ren_open(
1458
1464
 
1459
1465
  if not b64:
1460
1466
  zs = ("%s\n%s" % (orig_name, suffix)).encode("utf-8", "replace")
1461
- zs = hashlib.sha512(zs).digest()[:12]
1462
- b64 = base64.urlsafe_b64encode(zs).decode("utf-8")
1467
+ b64 = ub64enc(hashlib.sha512(zs).digest()[:12]).decode("ascii")
1463
1468
 
1464
1469
  badlen = len(fname)
1465
1470
  while len(fname) >= badlen:
@@ -1692,7 +1697,6 @@ class MultipartParser(object):
1692
1697
  returns the value of the next field in the multipart body,
1693
1698
  raises if the field name is not as expected
1694
1699
  """
1695
- assert self.gen
1696
1700
  p_field, p_fname, p_data = next(self.gen)
1697
1701
  if p_field != field_name:
1698
1702
  raise WrongPostKey(field_name, p_field, p_fname, p_data)
@@ -1701,7 +1705,6 @@ class MultipartParser(object):
1701
1705
 
1702
1706
  def drop(self) :
1703
1707
  """discards the remaining multipart body"""
1704
- assert self.gen
1705
1708
  for _, _, data in self.gen:
1706
1709
  for _ in data:
1707
1710
  pass
@@ -1765,9 +1768,8 @@ def rand_name(fdir , fn , rnd ) :
1765
1768
 
1766
1769
  nc = rnd + extra
1767
1770
  nb = (6 + 6 * nc) // 8
1768
- zb = os.urandom(nb)
1769
- zb = base64.urlsafe_b64encode(zb)
1770
- fn = zb[:nc].decode("utf-8") + ext
1771
+ zb = ub64enc(os.urandom(nb))
1772
+ fn = zb[:nc].decode("ascii") + ext
1771
1773
  ok = not os.path.exists(fsenc(os.path.join(fdir, fn)))
1772
1774
 
1773
1775
  return fn
@@ -1780,7 +1782,7 @@ def gen_filekey(alg , salt , fspath , fsize , inode ) :
1780
1782
  zs = "%s %s" % (salt, fspath)
1781
1783
 
1782
1784
  zb = zs.encode("utf-8", "replace")
1783
- return base64.urlsafe_b64encode(hashlib.sha512(zb).digest()).decode("ascii")
1785
+ return ub64enc(hashlib.sha512(zb).digest()).decode("ascii")
1784
1786
 
1785
1787
 
1786
1788
  def gen_filekey_dbg(
@@ -1794,7 +1796,6 @@ def gen_filekey_dbg(
1794
1796
  ) :
1795
1797
  ret = gen_filekey(alg, salt, fspath, fsize, inode)
1796
1798
 
1797
- assert log_ptn
1798
1799
  if log_ptn.search(fspath):
1799
1800
  try:
1800
1801
  import inspect
@@ -2053,6 +2054,8 @@ def html_bescape(s , quot = False, crlf = False) :
2053
2054
 
2054
2055
  def _quotep2(txt ) :
2055
2056
  """url quoter which deals with bytes correctly"""
2057
+ if not txt:
2058
+ return ""
2056
2059
  btxt = w8enc(txt)
2057
2060
  quot = quote(btxt, safe=b"/")
2058
2061
  return w8dec(quot.replace(b" ", b"+")) # type: ignore
@@ -2060,18 +2063,61 @@ def _quotep2(txt ) :
2060
2063
 
2061
2064
  def _quotep3(txt ) :
2062
2065
  """url quoter which deals with bytes correctly"""
2066
+ if not txt:
2067
+ return ""
2063
2068
  btxt = w8enc(txt)
2064
2069
  quot = quote(btxt, safe=b"/").encode("utf-8")
2065
2070
  return w8dec(quot.replace(b" ", b"+"))
2066
2071
 
2067
2072
 
2068
- quotep = _quotep3 if not PY2 else _quotep2
2073
+ if not PY2:
2074
+ _uqsb = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.-~/"
2075
+ _uqtl = {
2076
+ n: ("%%%02X" % (n,) if n not in _uqsb else chr(n)).encode("utf-8")
2077
+ for n in range(256)
2078
+ }
2079
+ _uqtl[b" "] = b"+"
2080
+
2081
+ def _quotep3b(txt ) :
2082
+ """url quoter which deals with bytes correctly"""
2083
+ if not txt:
2084
+ return ""
2085
+ btxt = w8enc(txt)
2086
+ if btxt.rstrip(_uqsb):
2087
+ lut = _uqtl
2088
+ btxt = b"".join([lut[ch] for ch in btxt])
2089
+ return w8dec(btxt)
2090
+
2091
+ quotep = _quotep3b
2092
+
2093
+ _hexd = "0123456789ABCDEFabcdef"
2094
+ _hex2b = {(a + b).encode(): bytes.fromhex(a + b) for a in _hexd for b in _hexd}
2095
+
2096
+ def unquote(btxt ) :
2097
+ h2b = _hex2b
2098
+ parts = iter(btxt.split(b"%"))
2099
+ ret = [next(parts)]
2100
+ for item in parts:
2101
+ c = h2b.get(item[:2])
2102
+ if c is None:
2103
+ ret.append(b"%")
2104
+ ret.append(item)
2105
+ else:
2106
+ ret.append(c)
2107
+ ret.append(item[2:])
2108
+ return b"".join(ret)
2109
+
2110
+ from urllib.parse import quote_from_bytes as quote
2111
+ else:
2112
+ from urllib import quote # type: ignore # pylint: disable=no-name-in-module
2113
+ from urllib import unquote # type: ignore # pylint: disable=no-name-in-module
2114
+
2115
+ quotep = _quotep2
2069
2116
 
2070
2117
 
2071
2118
  def unquotep(txt ) :
2072
2119
  """url unquoter which deals with bytes correctly"""
2073
2120
  btxt = w8enc(txt)
2074
- # btxt = btxt.replace(b"+", b" ")
2075
2121
  unq2 = unquote(btxt)
2076
2122
  return w8dec(unq2)
2077
2123
 
@@ -2217,12 +2263,12 @@ w8enc = _w8enc3 if not PY2 else _w8enc2
2217
2263
 
2218
2264
  def w8b64dec(txt ) :
2219
2265
  """decodes base64(filesystem-bytes) to wtf8"""
2220
- return w8dec(base64.urlsafe_b64decode(txt.encode("ascii")))
2266
+ return w8dec(ub64dec(txt.encode("ascii")))
2221
2267
 
2222
2268
 
2223
2269
  def w8b64enc(txt ) :
2224
2270
  """encodes wtf8 to base64(filesystem-bytes)"""
2225
- return base64.urlsafe_b64encode(w8enc(txt)).decode("ascii")
2271
+ return ub64enc(w8enc(txt)).decode("ascii")
2226
2272
 
2227
2273
 
2228
2274
  if not PY2 and WINDOWS:
@@ -2380,7 +2426,6 @@ def wunlink(log , abspath , flags ) :
2380
2426
  def get_df(abspath ) :
2381
2427
  try:
2382
2428
  # some fuses misbehave
2383
- assert ctypes
2384
2429
  if ANYWIN:
2385
2430
  bfree = ctypes.c_ulonglong(0)
2386
2431
  ctypes.windll.kernel32.GetDiskFreeSpaceExW( # type: ignore
@@ -2598,8 +2643,7 @@ def hashcopy(
2598
2643
  if slp:
2599
2644
  time.sleep(slp)
2600
2645
 
2601
- digest = hashobj.digest()[:33]
2602
- digest_b64 = base64.urlsafe_b64encode(digest).decode("utf-8")
2646
+ digest_b64 = ub64enc(hashobj.digest()[:33]).decode("ascii")
2603
2647
 
2604
2648
  return tlen, hashobj.hexdigest(), digest_b64
2605
2649
 
@@ -2839,7 +2883,6 @@ def getalive(pids , pgid ) :
2839
2883
  alive.append(pid)
2840
2884
  else:
2841
2885
  # windows doesn't have pgroups; assume
2842
- assert psutil
2843
2886
  psutil.Process(pid)
2844
2887
  alive.append(pid)
2845
2888
  except:
@@ -2857,7 +2900,6 @@ def killtree(root ) :
2857
2900
  pgid = 0
2858
2901
 
2859
2902
  if HAVE_PSUTIL:
2860
- assert psutil
2861
2903
  pids = [root]
2862
2904
  parent = psutil.Process(root)
2863
2905
  for child in parent.children(recursive=True):
@@ -3270,7 +3312,6 @@ def runhook(
3270
3312
  at ,
3271
3313
  txt ,
3272
3314
  ) :
3273
- assert broker or up2k
3274
3315
  asrv = (broker or up2k).asrv
3275
3316
  args = (broker or up2k).args
3276
3317
  vp = vp.replace("\\", "/")
@@ -3464,7 +3505,6 @@ def termsize() :
3464
3505
  def hidedir(dp) :
3465
3506
  if ANYWIN:
3466
3507
  try:
3467
- assert ctypes
3468
3508
  k32 = ctypes.WinDLL("kernel32")
3469
3509
  attrs = k32.GetFileAttributesW(dp)
3470
3510
  if attrs >= 0:
@@ -3500,3 +3540,16 @@ class WrongPostKey(Pebkac):
3500
3540
  self.got = got
3501
3541
  self.fname = fname
3502
3542
  self.datagen = datagen
3543
+
3544
+
3545
+ _ = (mp, BytesIO, quote, unquote, SQLITE_VER, JINJA_VER, PYFTPD_VER, PARTFTPY_VER)
3546
+ __all__ = [
3547
+ "mp",
3548
+ "BytesIO",
3549
+ "quote",
3550
+ "unquote",
3551
+ "SQLITE_VER",
3552
+ "JINJA_VER",
3553
+ "PYFTPD_VER",
3554
+ "PARTFTPY_VER",
3555
+ ]
Binary file
Binary file
copyparty/web/splash.html CHANGED
@@ -60,6 +60,18 @@
60
60
  </div>
61
61
  {%- endif %}
62
62
 
63
+ {%- if ups %}
64
+ <h1 id="aa">incoming files:</h1>
65
+ <table class="vols">
66
+ <thead><tr><th>%</th><th>speed</th><th>eta</th><th>idle</th><th>dir</th><th>file</th></tr></thead>
67
+ <tbody>
68
+ {% for u in ups %}
69
+ <tr><td>{{ u[0] }}</td><td>{{ u[1] }}</td><td>{{ u[2] }}</td><td>{{ u[3] }}</td><td><a href="{{ u[4] }}">{{ u[5]|e }}</a></td><td>{{ u[6]|e }}</td></tr>
70
+ {% endfor %}
71
+ </tbody>
72
+ </table>
73
+ {%- endif %}
74
+
63
75
  {%- if rvol %}
64
76
  <h1 id="f">you can browse:</h1>
65
77
  <ul>
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: copyparty
3
- Version: 1.15.1
3
+ Version: 1.15.2
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
@@ -52,7 +52,7 @@ Requires-Dist: Pillow; extra == "thumbnails"
52
52
  Provides-Extra: thumbnails2
53
53
  Requires-Dist: pyvips; extra == "thumbnails2"
54
54
 
55
- <img src="docs/logo.svg" width="250" align="right"/>
55
+ <img src="https://github.com/9001/copyparty/raw/hovudstraum/docs/logo.svg" width="250" align="right"/>
56
56
 
57
57
  ### 💾🎉 copyparty
58
58
 
@@ -97,6 +97,7 @@ turn almost any device into a file server with resumable uploads/downloads using
97
97
  * [unpost](#unpost) - undo/delete accidental uploads
98
98
  * [self-destruct](#self-destruct) - uploads can be given a lifetime
99
99
  * [race the beam](#race-the-beam) - download files while they're still uploading ([demo video](http://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm))
100
+ * [incoming files](#incoming-files) - the control-panel shows the ETA for all incoming files
100
101
  * [file manager](#file-manager) - cut/paste, rename, and delete files/folders (if you have permission)
101
102
  * [shares](#shares) - share a file or folder by creating a temporary link
102
103
  * [batch rename](#batch-rename) - select some files and press `F2` to bring up the rename UI
@@ -294,7 +295,7 @@ also see [comparison to similar software](./docs/versus.md)
294
295
  * ☑ ...of videos using FFmpeg
295
296
  * ☑ ...of audio (spectrograms) using FFmpeg
296
297
  * ☑ cache eviction (max-age; maybe max-size eventually)
297
- * ☑ multilingual UI (english, norwegian, [add your own](./docs/rice/#translations)))
298
+ * ☑ multilingual UI (english, norwegian, chinese, [add your own](./docs/rice/#translations)))
298
299
  * ☑ SPA (browse while uploading)
299
300
  * server indexing
300
301
  * ☑ [locate files by contents](#file-search)
@@ -785,6 +786,13 @@ download files while they're still uploading ([demo video](http://a.ocv.me/pub/g
785
786
  requires the file to be uploaded using up2k (which is the default drag-and-drop uploader), alternatively the command-line program
786
787
 
787
788
 
789
+ ### incoming files
790
+
791
+ the control-panel shows the ETA for all incoming files , but only for files being uploaded into volumes where you have read-access
792
+
793
+ ![copyparty-cpanel-upload-eta-or8](https://github.com/user-attachments/assets/fd275ffa-698c-4fca-a307-4d2181269a6a)
794
+
795
+
788
796
  ## file manager
789
797
 
790
798
  cut/paste, rename, and delete files/folders (if you have permission)
@@ -1603,6 +1611,8 @@ you can either:
1603
1611
  * 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
1604
1612
  * 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
1605
1613
 
1614
+ when running behind a reverse-proxy (this includes services like cloudflare), it is important to configure real-ip correctly, as many features rely on knowing the client's IP. Look out for red and yellow log messages which explain how to do this. But basically, set `--xff-hdr` to the name of the http header to read the IP from (usually `x-forwarded-for`, but cloudflare uses `cf-connecting-ip`), and then `--xff-src` to the IP of the reverse-proxy so copyparty will trust the xff-hdr. Note that `--rp-loc` in particular will not work at all unless you do this
1615
+
1606
1616
  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
1607
1617
  * **warning:** nginx-QUIC (HTTP/3) is still experimental and can make uploads much slower, so HTTP/1.1 is recommended for now
1608
1618
  * depending on server/client, HTTP/1.1 can also be 5x faster than HTTP/2
@@ -1933,6 +1943,7 @@ interact with copyparty using non-browser clients
1933
1943
  * [rclone](https://rclone.org/) as client can give ~5x performance, see [./docs/rclone.md](docs/rclone.md)
1934
1944
 
1935
1945
  * sharex (screenshot utility): see [./contrib/sharex.sxcu](contrib/#sharexsxcu)
1946
+ * and for screenshots on linux, see [./contrib/flameshot.sh](./contrib/flameshot.sh)
1936
1947
 
1937
1948
  * contextlet (web browser integration); see [contrib contextlet](contrib/#send-to-cppcontextletjson)
1938
1949
 
@@ -2011,6 +2022,7 @@ below are some tweaks roughly ordered by usefulness:
2011
2022
  * and also makes thumbnails load faster, regardless of e2d/e2t
2012
2023
  * `--dedup` enables deduplication and thus avoids writing to the HDD if someone uploads a dupe
2013
2024
  * `--safe-dedup 1` makes deduplication much faster during upload by skipping verification of file contents; safe if there is no other software editing/moving the files in the volumes
2025
+ * `--no-dirsz` shows the size of folder inodes instead of the total size of the contents, giving about 30% faster folder listings
2014
2026
  * `--no-hash .` when indexing a network-disk if you don't care about the actual filehashes and only want the names/tags searchable
2015
2027
  * if your volumes are on a network-disk such as NFS / SMB / s3, specifying larger values for `--iobuf` and/or `--s-rd-sz` and/or `--s-wr-sz` may help; try setting all of them to `524288` or `1048576` or `4194304`
2016
2028
  * `--no-htp --hash-mt=0 --mtag-mt=1 --th-mt=1` minimizes the number of threads; can help in some eccentric environments (like the vscode debugger)
@@ -1,19 +1,19 @@
1
- copyparty/__init__.py,sha256=fUINM1abqDGzCCH_JcXdOnLdKOV-SrTI2Xo2QgQW2P4,1703
2
- copyparty/__main__.py,sha256=M8o_4CGNuteh7j_3iiFN_FTu0IIHKrjRScZOBFKqXis,109083
3
- copyparty/__version__.py,sha256=BCJou8sLX4iTHVA4RubRxWxlC8xKh9t-i3XAUnB-aq8,256
4
- copyparty/authsrv.py,sha256=pX-Dax35ClxftNnN50UslvZZbPTFI73v-EFLGSqtAA4,98516
5
- copyparty/broker_mp.py,sha256=krsUWduBULlZ1LnroHhWLGnUBWYtWc_kohZXgHsE458,4004
6
- copyparty/broker_mpw.py,sha256=9kb_3BYUduwtmluuqdm0OhY6T4DwpUSX2xW7jo6veQ8,3326
7
- copyparty/broker_thr.py,sha256=BErWJkpd1bnRCcUHowAPNHCfo3CSqVmD-5yVRKjIIhU,1800
8
- copyparty/broker_util.py,sha256=w0E-GhoOgq8ow7mEWi3GOyqraux6VG9yk1tif1yo0jc,1474
1
+ copyparty/__init__.py,sha256=uoy7oFFwJdcw8jvMh7yfIjZsM7BZ2BWW35K6gJ1YB7M,1736
2
+ copyparty/__main__.py,sha256=XD-zB_tg-b7zdSV1zpJH4PQ9U2rszc_eaYQsZOmizDM,109518
3
+ copyparty/__version__.py,sha256=TCm_pC-aG7cSTDvOAnDjzktx8G1xp7uh6fSiGOyx3RI,257
4
+ copyparty/authsrv.py,sha256=Uw_RaCJIsXUzjYih4R2VLQOkDfUePLfidiW5ERvCtMo,98670
5
+ copyparty/broker_mp.py,sha256=jsHUM2BSfRVRyZT869iPCqYEHSqedk6VkwvygZwbEZE,4017
6
+ copyparty/broker_mpw.py,sha256=PYFgQfssOCfdI6qayW1ZjO1j1-7oez094muhYMbPOz0,3339
7
+ copyparty/broker_thr.py,sha256=MXrwjusP0z1LPURUhi5jx_TL3jrXhYcDrJPDSKu6EEU,1705
8
+ copyparty/broker_util.py,sha256=76mfnFOpX1gUUvtjm8UQI7jpTIaVINX10QonM-B7ggc,1680
9
9
  copyparty/cert.py,sha256=kRFkMwBUCV_Vo7BYweD-yJ7Hpp5BCpaXneyBWxlu1PM,7759
10
- copyparty/cfg.py,sha256=6cj2xJnBa9vRubM5U_mkA87zG2Ug11vnyk2hYz0XfxI,9965
10
+ copyparty/cfg.py,sha256=f4Jzpv202CE_689GYEtyL02yLVlqLKiCnEq6Tua41uk,9996
11
11
  copyparty/dxml.py,sha256=lZpg-kn-kQsXRtNY1n6fRaS-b7uXzMCyv8ovKnhZcZc,1548
12
- copyparty/fsutil.py,sha256=hnEHgySI43-XJJKbI8n6t1A6oVHzR_nYdsBcAwtreBk,4610
12
+ copyparty/fsutil.py,sha256=5CshJWO7CflfaRRNOb3JxghUH7W5rmS_HWNmKfx42MM,4538
13
13
  copyparty/ftpd.py,sha256=1vD-KTy07xfEEEk1dx37pUYModpNO2gIhVXvFUr205M,17497
14
- copyparty/httpcli.py,sha256=T-bISKn73-zt-q3C4dbRDgfB1tspze7dhM3gUY0ztX0,183570
15
- copyparty/httpconn.py,sha256=mwIDup85cBowIfJOse8rla5bqTz7nf-ChgfR-5-V0JM,6938
16
- copyparty/httpsrv.py,sha256=8_1Ivg3eco7HJDjqL_rUB58IOUaUnoXGhO62bOMXLBk,17242
14
+ copyparty/httpcli.py,sha256=TsX_ZIqkPh07VNvkCT9pNW3n2sL1StKIxov_jC-fch0,185931
15
+ copyparty/httpconn.py,sha256=tpVXyi_bwGigMeUACQ8fbXBdzALxpht4DI6l1HGaQMk,6903
16
+ copyparty/httpsrv.py,sha256=iQ8Zc1Qm0NfK1sApkZAIw94NmheFujcdk154aQqV7I4,17207
17
17
  copyparty/ico.py,sha256=eWSxEae4wOCfheHl-m-wchYvFRAR_97kJDb4NGaB-Z8,3561
18
18
  copyparty/mdns.py,sha256=vC078llnL1v0pvL3mnwacuStFHPJUQuxo9Opj-IbHL4,18155
19
19
  copyparty/metrics.py,sha256=O8qiPNDxNjub_PI8C8Qu9rBQ_z0J1mnKonqkcTeAtf4,8845
@@ -24,15 +24,15 @@ copyparty/smbd.py,sha256=8zkC9BjVtGiKXMLajbdakxoKeFzACdM75SW0_SvqXJA,14490
24
24
  copyparty/ssdp.py,sha256=8iyF5sqIjATJLWcAtnJa8eadHosOn0CP4ywltzJ7bVY,7023
25
25
  copyparty/star.py,sha256=tV5BbX6AiQ7N4UU8DYtSTckNYeoeey4DBqq4LjfymbY,3818
26
26
  copyparty/sutil.py,sha256=JTMrQwcWH85hXB_cKG206eDZ967WZDGaP00AWvl_gB0,3214
27
- copyparty/svchub.py,sha256=mhELkl2XCvaTrxLcXbfGSPbRtXA1wE66ap73wEWcvFI,40087
27
+ copyparty/svchub.py,sha256=qLG2CE8VHro7I6FnYMwiij0LcBC5lJw-hiucBag3NHY,39939
28
28
  copyparty/szip.py,sha256=tor4yjdHhEL4Ox-Xg7-cuUFrMO0IwQD29aRX5Cp8MYs,8605
29
29
  copyparty/tcpsrv.py,sha256=jM_Za64O8LEMfMrU4irJluIJZrU494e2b759r_KhaUQ,19881
30
30
  copyparty/tftpd.py,sha256=jZbf2JpeJmkuQWJErmAPG-dKhtYNvIUHbkAgodSXw9Y,13582
31
31
  copyparty/th_cli.py,sha256=o6FMkerYvAXS455z3DUossVztu_nzFlYSQhs6qN6Jt8,4636
32
- copyparty/th_srv.py,sha256=27IftjIXUQzRRiUytt-CgXkybEoP3HHHoXaDAvxEmLo,29217
33
- copyparty/u2idx.py,sha256=t4mzjj2GDrkjIHt0RM68y1EgT5qOBoz6mkYgjMbqA38,13526
34
- copyparty/up2k.py,sha256=USxHI5IN-9DQp_P7Yl4683yxlvaWTslZT90h6sfqpuk,159853
35
- copyparty/util.py,sha256=qkwrCRqDI7iCiO3X2RQ1LdGVblnkIQe1YTuJPlF27u4,88666
32
+ copyparty/th_srv.py,sha256=hI9wY1E_9N9Cgqvtr8zADeVqqiLGTiTdAnYAA7WFvJw,29346
33
+ copyparty/u2idx.py,sha256=uATbv6yluwa1KmZ3q6rD3XREuWktuL6KgKqK2sixtVU,13413
34
+ copyparty/up2k.py,sha256=MjKHd_5F8riP1SgQjl7NT6OueqcMb_ggNbSh07RZCAk,163265
35
+ copyparty/util.py,sha256=-6WJb535Daxj-kyLqaM5DMKTQXm0q_k0ydewE-Dy1wg,89932
36
36
  copyparty/bos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
37
  copyparty/bos/bos.py,sha256=Wb7eWsXJgR5AFlBR9ZOyKrLTwy-Kct9RrGiOu4Jo37Y,1622
38
38
  copyparty/bos/path.py,sha256=yEjCq2ki9CvxA5sCT8pS0keEXwugs0ZeUyUhdBziOCI,777
@@ -57,7 +57,7 @@ copyparty/stolen/ifaddr/_win32.py,sha256=EE-QyoBgeB7lYQ6z62VjXNaRozaYfCkaJBHGNA8
57
57
  copyparty/web/baguettebox.js.gz,sha256=4dS8-r4si84ca71l98672ahnRI86Aq95MU-bc5knykk,7962
58
58
  copyparty/web/browser.css.gz,sha256=TM9gYp-MFSjj_2kV-mKrLFit0Ayz2e7AxI4L0LJmrBE,11608
59
59
  copyparty/web/browser.html,sha256=vvfWiu_aOFRar8u5lridMRKQSPF4R0YkA41zrsh82Qs,4878
60
- copyparty/web/browser.js.gz,sha256=EZhEAuKeZH9qO1gfRzyARG-PFmP7rFtQfHRr0ZnUEYs,84762
60
+ copyparty/web/browser.js.gz,sha256=toOG_luoeV_Ep1P9Z4muqTxpMTBVi2XXdAwkHajBzig,84654
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
@@ -74,9 +74,9 @@ copyparty/web/msg.html,sha256=w9CM3hkLLGJX9fWEaG4gSbTOPe2GcPqW8BpSCDiFzOI,977
74
74
  copyparty/web/shares.css.gz,sha256=m-nRqTGPiU3ohZxvGaROzFr98F_jmohQnjieqEAyjBo,496
75
75
  copyparty/web/shares.html,sha256=d--9tyg6u3JzszpEtMmU5S4XdUF_mfUAhzCvwl-XAXw,2384
76
76
  copyparty/web/shares.js.gz,sha256=296fTZV4sW7CxT-YNnDufUZL-aIy4E4r8q-XtSy6bHs,652
77
- copyparty/web/splash.css.gz,sha256=4DOtEKBWyaDKel7fdnwvnc9FrKlkht-ec7R2nRlruPU,1023
78
- copyparty/web/splash.html,sha256=dAo4KXKmXUMGcIwetZkFtVxk-mCMNkscD36BxLwRdow,4804
79
- copyparty/web/splash.js.gz,sha256=pxEHaRDpxTnW6WdRWpKlRux8jtI7B5RImRjUVs9gdQQ,2582
77
+ copyparty/web/splash.css.gz,sha256=RjdNoIT5BSxXRFu0ldMUH4ghRNUMCTs2mGKzstrpI6o,1033
78
+ copyparty/web/splash.html,sha256=bwrUygrBbxfF2eISVkT60tJtMMToh1_9LBBb1DQDPv4,5235
79
+ copyparty/web/splash.js.gz,sha256=K45-vh2sjjlAi4s27S5DwMESsyG8pHJTzvIL0ljLgNI,2627
80
80
  copyparty/web/svcs.html,sha256=v0C3cOFWXYlvp3GEifz1Qj0W3MD8JANT3WTON05GZ9o,11797
81
81
  copyparty/web/svcs.js.gz,sha256=k81ZvZ3I-f4fMHKrNGGOgOlvXnCBz0mVjD-8mieoWCA,520
82
82
  copyparty/web/ui.css.gz,sha256=ae1JosPYS8d2F9e_b95bTwa7qYwk8Ur_UhoVpRYEp0Y,2658
@@ -105,9 +105,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
105
105
  copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
106
106
  copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
107
107
  copyparty/web/deps/sha512.hw.js.gz,sha256=vqoXeracj-99Z5MfY3jK2N4WiSzYQdfjy0RnUlQDhSU,8110
108
- copyparty-1.15.1.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
109
- copyparty-1.15.1.dist-info/METADATA,sha256=sfkSpMV_LX0ITSPAYWbMeaYs8Zz1LC0ZSuv5GSpTjLw,134165
110
- copyparty-1.15.1.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
111
- copyparty-1.15.1.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
112
- copyparty-1.15.1.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
113
- copyparty-1.15.1.dist-info/RECORD,,
108
+ copyparty-1.15.2.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
109
+ copyparty-1.15.2.dist-info/METADATA,sha256=JBTM_vTMv_pes8JJKgblNMDuBoLqY74PvUqf7Dtda-s,135376
110
+ copyparty-1.15.2.dist-info/WHEEL,sha256=5Mi1sN9lKoFv_gxcPtisEVrJZihrm_beibeg5R6xb4I,91
111
+ copyparty-1.15.2.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
112
+ copyparty-1.15.2.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
113
+ copyparty-1.15.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (74.1.2)
2
+ Generator: setuptools (75.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5