copyparty 1.15.0__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/__init__.py +1 -0
- copyparty/__main__.py +21 -12
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +84 -8
- copyparty/broker_mp.py +6 -2
- copyparty/broker_mpw.py +6 -2
- copyparty/broker_thr.py +5 -9
- copyparty/broker_util.py +13 -1
- copyparty/cfg.py +1 -0
- copyparty/fsutil.py +0 -3
- copyparty/httpcli.py +145 -70
- copyparty/httpconn.py +0 -1
- copyparty/httpsrv.py +3 -5
- copyparty/svchub.py +67 -5
- copyparty/th_srv.py +15 -7
- copyparty/u2idx.py +0 -3
- copyparty/up2k.py +223 -100
- copyparty/util.py +116 -63
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/splash.css.gz +0 -0
- copyparty/web/splash.html +12 -0
- copyparty/web/splash.js.gz +0 -0
- {copyparty-1.15.0.dist-info → copyparty-1.15.2.dist-info}/METADATA +15 -3
- {copyparty-1.15.0.dist-info → copyparty-1.15.2.dist-info}/RECORD +29 -29
- {copyparty-1.15.0.dist-info → copyparty-1.15.2.dist-info}/WHEEL +1 -1
- {copyparty-1.15.0.dist-info → copyparty-1.15.2.dist-info}/LICENSE +0 -0
- {copyparty-1.15.0.dist-info → copyparty-1.15.2.dist-info}/entry_points.txt +0 -0
- {copyparty-1.15.0.dist-info → copyparty-1.15.2.dist-info}/top_level.txt +0 -0
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
|
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
|
-
|
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 =
|
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
|
-
|
471
|
-
|
472
|
-
"
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
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
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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(
|
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
|
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
|
-
|
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
|
+
]
|
copyparty/web/browser.css.gz
CHANGED
Binary file
|
copyparty/web/browser.js.gz
CHANGED
Binary file
|
copyparty/web/splash.css.gz
CHANGED
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>
|
copyparty/web/splash.js.gz
CHANGED
Binary file
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.15.
|
3
|
+
Version: 1.15.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
|
+

|
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=
|
2
|
-
copyparty/__main__.py,sha256=
|
3
|
-
copyparty/__version__.py,sha256=
|
4
|
-
copyparty/authsrv.py,sha256=
|
5
|
-
copyparty/broker_mp.py,sha256=
|
6
|
-
copyparty/broker_mpw.py,sha256=
|
7
|
-
copyparty/broker_thr.py,sha256=
|
8
|
-
copyparty/broker_util.py,sha256=
|
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=
|
10
|
+
copyparty/cfg.py,sha256=f4Jzpv202CE_689GYEtyL02yLVlqLKiCnEq6Tua41uk,9996
|
11
11
|
copyparty/dxml.py,sha256=lZpg-kn-kQsXRtNY1n6fRaS-b7uXzMCyv8ovKnhZcZc,1548
|
12
|
-
copyparty/fsutil.py,sha256=
|
12
|
+
copyparty/fsutil.py,sha256=5CshJWO7CflfaRRNOb3JxghUH7W5rmS_HWNmKfx42MM,4538
|
13
13
|
copyparty/ftpd.py,sha256=1vD-KTy07xfEEEk1dx37pUYModpNO2gIhVXvFUr205M,17497
|
14
|
-
copyparty/httpcli.py,sha256=
|
15
|
-
copyparty/httpconn.py,sha256=
|
16
|
-
copyparty/httpsrv.py,sha256=
|
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=
|
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=
|
33
|
-
copyparty/u2idx.py,sha256=
|
34
|
-
copyparty/up2k.py,sha256=
|
35
|
-
copyparty/util.py,sha256
|
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
|
@@ -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=4dS8-r4si84ca71l98672ahnRI86Aq95MU-bc5knykk,7962
|
58
|
-
copyparty/web/browser.css.gz,sha256
|
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=
|
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=
|
78
|
-
copyparty/web/splash.html,sha256=
|
79
|
-
copyparty/web/splash.js.gz,sha256=
|
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.
|
109
|
-
copyparty-1.15.
|
110
|
-
copyparty-1.15.
|
111
|
-
copyparty-1.15.
|
112
|
-
copyparty-1.15.
|
113
|
-
copyparty-1.15.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|