copyparty 1.13.1__py3-none-any.whl → 1.13.3__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 +52 -8
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +16 -4
- copyparty/broker_mp.py +2 -5
- copyparty/cfg.py +1 -0
- copyparty/httpcli.py +12 -8
- copyparty/httpsrv.py +1 -4
- copyparty/mdns.py +23 -2
- copyparty/mtag.py +74 -3
- copyparty/smbd.py +1 -1
- copyparty/ssdp.py +22 -2
- copyparty/star.py +4 -4
- copyparty/sutil.py +12 -6
- copyparty/svchub.py +9 -2
- copyparty/szip.py +4 -4
- copyparty/th_cli.py +5 -0
- copyparty/th_srv.py +51 -13
- copyparty/up2k.py +16 -10
- copyparty/util.py +54 -14
- copyparty/web/a/u2c.py +26 -9
- copyparty/web/baguettebox.js.gz +0 -0
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/deps/marked.js.gz +0 -0
- copyparty/web/md2.js.gz +0 -0
- copyparty/web/splash.js.gz +0 -0
- {copyparty-1.13.1.dist-info → copyparty-1.13.3.dist-info}/METADATA +7 -4
- {copyparty-1.13.1.dist-info → copyparty-1.13.3.dist-info}/RECORD +32 -32
- {copyparty-1.13.1.dist-info → copyparty-1.13.3.dist-info}/LICENSE +0 -0
- {copyparty-1.13.1.dist-info → copyparty-1.13.3.dist-info}/WHEEL +0 -0
- {copyparty-1.13.1.dist-info → copyparty-1.13.3.dist-info}/entry_points.txt +0 -0
- {copyparty-1.13.1.dist-info → copyparty-1.13.3.dist-info}/top_level.txt +0 -0
copyparty/szip.py
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
from __future__ import print_function, unicode_literals
|
3
3
|
|
4
|
-
import argparse
|
5
4
|
import calendar
|
6
5
|
import stat
|
7
6
|
import time
|
8
7
|
import zlib
|
9
8
|
|
9
|
+
from .authsrv import AuthSrv
|
10
10
|
from .bos import bos
|
11
11
|
from .sutil import StreamArc, errdesc
|
12
12
|
from .util import min_ex, sanitize_fn, spack, sunpack, yieldfile
|
@@ -213,13 +213,13 @@ class StreamZip(StreamArc):
|
|
213
213
|
def __init__(
|
214
214
|
self,
|
215
215
|
log ,
|
216
|
-
|
216
|
+
asrv ,
|
217
217
|
fgen ,
|
218
218
|
utf8 = False,
|
219
219
|
pre_crc = False,
|
220
220
|
**kwargs
|
221
221
|
) :
|
222
|
-
super(StreamZip, self).__init__(log,
|
222
|
+
super(StreamZip, self).__init__(log, asrv, fgen)
|
223
223
|
|
224
224
|
self.utf8 = utf8
|
225
225
|
self.pre_crc = pre_crc
|
@@ -296,7 +296,7 @@ class StreamZip(StreamArc):
|
|
296
296
|
mbuf = b""
|
297
297
|
|
298
298
|
if errors:
|
299
|
-
errf, txt = errdesc(errors)
|
299
|
+
errf, txt = errdesc(self.asrv.vfs, errors)
|
300
300
|
self.log("\n".join(([repr(errf)] + txt[1:])))
|
301
301
|
for x in self.ser(errf):
|
302
302
|
yield x
|
copyparty/th_cli.py
CHANGED
@@ -104,6 +104,11 @@ class ThumbCli(object):
|
|
104
104
|
|
105
105
|
fmt = sfmt
|
106
106
|
|
107
|
+
elif fmt[:1] == "p" and not is_au:
|
108
|
+
t = "cannot thumbnail [%s]: png only allowed for waveforms"
|
109
|
+
self.log(t % (rem), 6)
|
110
|
+
return None
|
111
|
+
|
107
112
|
histpath = self.asrv.vfs.histtab.get(ptop)
|
108
113
|
if not histpath:
|
109
114
|
self.log("no histpath for [{}]".format(ptop))
|
copyparty/th_srv.py
CHANGED
@@ -15,7 +15,7 @@ from queue import Queue
|
|
15
15
|
from .__init__ import ANYWIN, TYPE_CHECKING
|
16
16
|
from .authsrv import VFS
|
17
17
|
from .bos import bos
|
18
|
-
from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, ffprobe
|
18
|
+
from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, au_unpk, ffprobe
|
19
19
|
from .util import BytesIO # type: ignore
|
20
20
|
from .util import (
|
21
21
|
FFMPEG_URL,
|
@@ -294,6 +294,12 @@ class ThumbSrv(object):
|
|
294
294
|
ext = abspath.split(".")[-1].lower()
|
295
295
|
png_ok = False
|
296
296
|
funs = []
|
297
|
+
|
298
|
+
if ext in self.args.au_unpk:
|
299
|
+
ap_unpk = au_unpk(self.log, self.args.au_unpk, abspath, vn)
|
300
|
+
else:
|
301
|
+
ap_unpk = abspath
|
302
|
+
|
297
303
|
if not bos.path.exists(tpath):
|
298
304
|
for lib in self.args.th_dec:
|
299
305
|
if lib == "pil" and ext in self.fmt_pil:
|
@@ -313,9 +319,6 @@ class ThumbSrv(object):
|
|
313
319
|
else:
|
314
320
|
funs.append(self.conv_spec)
|
315
321
|
|
316
|
-
if not png_ok and tpath.endswith(".png"):
|
317
|
-
raise Pebkac(400, "png only allowed for waveforms")
|
318
|
-
|
319
322
|
tdir, tfn = os.path.split(tpath)
|
320
323
|
ttpath = os.path.join(tdir, "w", tfn)
|
321
324
|
try:
|
@@ -325,7 +328,10 @@ class ThumbSrv(object):
|
|
325
328
|
|
326
329
|
for fun in funs:
|
327
330
|
try:
|
328
|
-
|
331
|
+
if not png_ok and tpath.endswith(".png"):
|
332
|
+
raise Exception("png only allowed for waveforms")
|
333
|
+
|
334
|
+
fun(ap_unpk, ttpath, fmt, vn)
|
329
335
|
break
|
330
336
|
except Exception as ex:
|
331
337
|
msg = "{} could not create thumbnail of {}\n{}"
|
@@ -343,6 +349,9 @@ class ThumbSrv(object):
|
|
343
349
|
except:
|
344
350
|
pass
|
345
351
|
|
352
|
+
if abspath != ap_unpk:
|
353
|
+
wunlink(self.log, ap_unpk, vn.flags)
|
354
|
+
|
346
355
|
try:
|
347
356
|
wrename(self.log, ttpath, tpath, vn.flags)
|
348
357
|
except:
|
@@ -581,6 +590,25 @@ class ThumbSrv(object):
|
|
581
590
|
cmd += [fsenc(tpath)]
|
582
591
|
self._run_ff(cmd, vn)
|
583
592
|
|
593
|
+
if "pngquant" in vn.flags:
|
594
|
+
wtpath = tpath + ".png"
|
595
|
+
cmd = [
|
596
|
+
b"pngquant",
|
597
|
+
b"--strip",
|
598
|
+
b"--nofs",
|
599
|
+
b"--output",
|
600
|
+
fsenc(wtpath),
|
601
|
+
fsenc(tpath),
|
602
|
+
]
|
603
|
+
ret = runcmd(cmd, timeout=vn.flags["convt"], nice=True, oom=400)[0]
|
604
|
+
if ret:
|
605
|
+
try:
|
606
|
+
wunlink(self.log, wtpath, vn.flags)
|
607
|
+
except:
|
608
|
+
pass
|
609
|
+
else:
|
610
|
+
wrename(self.log, wtpath, tpath, vn.flags)
|
611
|
+
|
584
612
|
def conv_spec(self, abspath , tpath , fmt , vn ) :
|
585
613
|
ret, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
586
614
|
if "ac" not in ret:
|
@@ -643,8 +671,8 @@ class ThumbSrv(object):
|
|
643
671
|
raise Exception("disabled in server config")
|
644
672
|
|
645
673
|
self.wait4ram(0.2, tpath)
|
646
|
-
|
647
|
-
if "ac" not in
|
674
|
+
tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
675
|
+
if "ac" not in tags:
|
648
676
|
raise Exception("not audio")
|
649
677
|
|
650
678
|
if quality.endswith("k"):
|
@@ -665,7 +693,7 @@ class ThumbSrv(object):
|
|
665
693
|
b"-v", b"error",
|
666
694
|
b"-hide_banner",
|
667
695
|
b"-i", fsenc(abspath),
|
668
|
-
|
696
|
+
] + self.big_tags(rawtags) + [
|
669
697
|
b"-map", b"0:a:0",
|
670
698
|
b"-ar", b"44100",
|
671
699
|
b"-ac", b"2",
|
@@ -681,16 +709,16 @@ class ThumbSrv(object):
|
|
681
709
|
raise Exception("disabled in server config")
|
682
710
|
|
683
711
|
self.wait4ram(0.2, tpath)
|
684
|
-
|
685
|
-
if "ac" not in
|
712
|
+
tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
713
|
+
if "ac" not in tags:
|
686
714
|
raise Exception("not audio")
|
687
715
|
|
688
716
|
try:
|
689
|
-
dur =
|
717
|
+
dur = tags[".dur"][1]
|
690
718
|
except:
|
691
719
|
dur = 0
|
692
720
|
|
693
|
-
src_opus = abspath.lower().endswith(".opus") or
|
721
|
+
src_opus = abspath.lower().endswith(".opus") or tags["ac"][1] == "opus"
|
694
722
|
want_caf = tpath.endswith(".caf")
|
695
723
|
tmp_opus = tpath
|
696
724
|
if want_caf:
|
@@ -711,7 +739,7 @@ class ThumbSrv(object):
|
|
711
739
|
b"-v", b"error",
|
712
740
|
b"-hide_banner",
|
713
741
|
b"-i", fsenc(abspath),
|
714
|
-
|
742
|
+
] + self.big_tags(rawtags) + [
|
715
743
|
b"-map", b"0:a:0",
|
716
744
|
b"-c:a", b"libopus",
|
717
745
|
b"-b:a", bq,
|
@@ -768,6 +796,16 @@ class ThumbSrv(object):
|
|
768
796
|
except:
|
769
797
|
pass
|
770
798
|
|
799
|
+
def big_tags(self, raw_tags ) :
|
800
|
+
ret = []
|
801
|
+
for k, vs in raw_tags.items():
|
802
|
+
for v in vs:
|
803
|
+
if len(str(v)) >= 1024:
|
804
|
+
bv = k.encode("utf-8", "replace")
|
805
|
+
ret += [b"-metadata", bv + b"="]
|
806
|
+
break
|
807
|
+
return ret
|
808
|
+
|
771
809
|
def poke(self, tdir ) :
|
772
810
|
if not self.poke_cd.poke(tdir):
|
773
811
|
return
|
copyparty/up2k.py
CHANGED
@@ -10,7 +10,6 @@ import math
|
|
10
10
|
import os
|
11
11
|
import re
|
12
12
|
import shutil
|
13
|
-
import signal
|
14
13
|
import stat
|
15
14
|
import subprocess as sp
|
16
15
|
import tempfile
|
@@ -29,6 +28,7 @@ from .fsutil import Fstab
|
|
29
28
|
from .mtag import MParser, MTag
|
30
29
|
from .util import (
|
31
30
|
HAVE_SQLITE3,
|
31
|
+
VF_CAREFUL,
|
32
32
|
SYMTIME,
|
33
33
|
Daemon,
|
34
34
|
MTHash,
|
@@ -88,9 +88,6 @@ CV_EXTS = set(zsg.split(","))
|
|
88
88
|
HINT_HISTPATH = "you could try moving the database to another location (preferably an SSD or NVME drive) using either the --hist argument (global option for all volumes), or the hist volflag (just for this volume)"
|
89
89
|
|
90
90
|
|
91
|
-
VF_CAREFUL = {"mv_re_t": 5, "rm_re_t": 5, "mv_re_r": 0.1, "rm_re_r": 0.1}
|
92
|
-
|
93
|
-
|
94
91
|
class Dbw(object):
|
95
92
|
def __init__(self, c , n , t ) :
|
96
93
|
self.c = c
|
@@ -458,7 +455,13 @@ class Up2k(object):
|
|
458
455
|
# important; not deferred by db_act
|
459
456
|
timeout = self._check_lifetimes()
|
460
457
|
|
461
|
-
|
458
|
+
try:
|
459
|
+
timeout = min(timeout, now + self._check_xiu())
|
460
|
+
except Exception as ex:
|
461
|
+
if "closed cursor" in str(ex):
|
462
|
+
self.log("sched_rescan: lost db")
|
463
|
+
return
|
464
|
+
raise
|
462
465
|
|
463
466
|
with self.mutex:
|
464
467
|
for vp, vol in sorted(self.asrv.vfs.all_vols.items()):
|
@@ -703,9 +706,9 @@ class Up2k(object):
|
|
703
706
|
try:
|
704
707
|
bos.makedirs(vol.realpath) # gonna happen at snap anyways
|
705
708
|
dir_is_empty(self.log_func, not self.args.no_scandir, vol.realpath)
|
706
|
-
except:
|
709
|
+
except Exception as ex:
|
707
710
|
self.volstate[vol.vpath] = "OFFLINE (cannot access folder)"
|
708
|
-
self.log("cannot access "
|
711
|
+
self.log("cannot access %s: %r" % (vol.realpath, ex), c=1)
|
709
712
|
continue
|
710
713
|
|
711
714
|
if scan_vols and vol.vpath not in scan_vols:
|
@@ -1036,8 +1039,11 @@ class Up2k(object):
|
|
1036
1039
|
return None
|
1037
1040
|
|
1038
1041
|
def _verify_db_cache(self, cur , vpath ) :
|
1039
|
-
# check if
|
1040
|
-
|
1042
|
+
# check if list of intersecting volumes changed since last use; drop caches if so
|
1043
|
+
prefix = (vpath + "/").lstrip("/")
|
1044
|
+
zsl = [x for x in self.asrv.vfs.all_vols if x.startswith(prefix)]
|
1045
|
+
zsl = [x[len(prefix) :] for x in zsl]
|
1046
|
+
zsl.sort()
|
1041
1047
|
zb = hashlib.sha1("\n".join(zsl).encode("utf-8", "replace")).digest()
|
1042
1048
|
vcfg = base64.urlsafe_b64encode(zb[:18]).decode("ascii")
|
1043
1049
|
|
@@ -1647,7 +1653,7 @@ class Up2k(object):
|
|
1647
1653
|
|
1648
1654
|
if e2vp and rewark:
|
1649
1655
|
self.hub.retcode = 1
|
1650
|
-
|
1656
|
+
Daemon(self.hub.sigterm)
|
1651
1657
|
raise Exception("{} files have incorrect hashes".format(len(rewark)))
|
1652
1658
|
|
1653
1659
|
if not e2vu or not rewark:
|
copyparty/util.py
CHANGED
@@ -337,6 +337,21 @@ APPLESAN_TXT = r"/(__MACOS|Icon\r\r)|/\.(_|DS_Store|AppleDouble|LSOverride|Docum
|
|
337
337
|
APPLESAN_RE = re.compile(APPLESAN_TXT)
|
338
338
|
|
339
339
|
|
340
|
+
HUMANSIZE_UNITS = ("B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB")
|
341
|
+
|
342
|
+
UNHUMANIZE_UNITS = {
|
343
|
+
"b": 1,
|
344
|
+
"k": 1024,
|
345
|
+
"m": 1024 * 1024,
|
346
|
+
"g": 1024 * 1024 * 1024,
|
347
|
+
"t": 1024 * 1024 * 1024 * 1024,
|
348
|
+
"p": 1024 * 1024 * 1024 * 1024 * 1024,
|
349
|
+
"e": 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
|
350
|
+
}
|
351
|
+
|
352
|
+
VF_CAREFUL = {"mv_re_t": 5, "rm_re_t": 5, "mv_re_r": 0.1, "rm_re_r": 0.1}
|
353
|
+
|
354
|
+
|
340
355
|
pybin = sys.executable or ""
|
341
356
|
if EXE:
|
342
357
|
pybin = ""
|
@@ -442,13 +457,22 @@ class Daemon(threading.Thread):
|
|
442
457
|
r = True,
|
443
458
|
ka = None,
|
444
459
|
) :
|
445
|
-
threading.Thread.__init__(
|
446
|
-
|
447
|
-
|
460
|
+
threading.Thread.__init__(self, name=name)
|
461
|
+
self.a = a or ()
|
462
|
+
self.ka = ka or {}
|
463
|
+
self.fun = target
|
448
464
|
self.daemon = True
|
449
465
|
if r:
|
450
466
|
self.start()
|
451
467
|
|
468
|
+
def run(self):
|
469
|
+
if not ANYWIN and not PY2:
|
470
|
+
signal.pthread_sigmask(
|
471
|
+
signal.SIG_BLOCK, [signal.SIGINT, signal.SIGTERM, signal.SIGUSR1]
|
472
|
+
)
|
473
|
+
|
474
|
+
self.fun(*self.a, **self.ka)
|
475
|
+
|
452
476
|
|
453
477
|
class Netdev(object):
|
454
478
|
def __init__(self, ip , idx , name , desc ):
|
@@ -843,6 +867,7 @@ class ProgressPrinter(threading.Thread):
|
|
843
867
|
self.start()
|
844
868
|
|
845
869
|
def run(self) :
|
870
|
+
sigblock()
|
846
871
|
tp = 0
|
847
872
|
msg = None
|
848
873
|
no_stdout = self.args.q
|
@@ -1287,6 +1312,15 @@ def log_thrs(log , ival , name ) :
|
|
1287
1312
|
log(name, "\033[0m \033[33m".join(tv), 3)
|
1288
1313
|
|
1289
1314
|
|
1315
|
+
def sigblock():
|
1316
|
+
if ANYWIN or PY2:
|
1317
|
+
return
|
1318
|
+
|
1319
|
+
signal.pthread_sigmask(
|
1320
|
+
signal.SIG_BLOCK, [signal.SIGINT, signal.SIGTERM, signal.SIGUSR1]
|
1321
|
+
)
|
1322
|
+
|
1323
|
+
|
1290
1324
|
def vol_san(vols , txt ) :
|
1291
1325
|
txt0 = txt
|
1292
1326
|
for vol in vols:
|
@@ -1308,10 +1342,11 @@ def vol_san(vols , txt ) :
|
|
1308
1342
|
|
1309
1343
|
def min_ex(max_lines = 8, reverse = False) :
|
1310
1344
|
et, ev, tb = sys.exc_info()
|
1311
|
-
stb = traceback.extract_tb(tb)
|
1345
|
+
stb = traceback.extract_tb(tb) if tb else traceback.extract_stack()[:-1]
|
1312
1346
|
fmt = "%s @ %d <%s>: %s"
|
1313
1347
|
ex = [fmt % (fp.split(os.sep)[-1], ln, fun, txt) for fp, ln, fun, txt in stb]
|
1314
|
-
|
1348
|
+
if et or ev or tb:
|
1349
|
+
ex.append("[%s] %s" % (et.__name__ if et else "(anonymous)", ev))
|
1315
1350
|
return "\n".join(ex[-max_lines:][:: -1 if reverse else 1])
|
1316
1351
|
|
1317
1352
|
|
@@ -1764,7 +1799,7 @@ def gencookie(k , v , r , tls , dur = 0, txt = "") :
|
|
1764
1799
|
|
1765
1800
|
|
1766
1801
|
def humansize(sz , terse = False) :
|
1767
|
-
for unit in
|
1802
|
+
for unit in HUMANSIZE_UNITS:
|
1768
1803
|
if sz < 1024:
|
1769
1804
|
break
|
1770
1805
|
|
@@ -1785,12 +1820,7 @@ def unhumanize(sz ) :
|
|
1785
1820
|
pass
|
1786
1821
|
|
1787
1822
|
mc = sz[-1:].lower()
|
1788
|
-
mi =
|
1789
|
-
"k": 1024,
|
1790
|
-
"m": 1024 * 1024,
|
1791
|
-
"g": 1024 * 1024 * 1024,
|
1792
|
-
"t": 1024 * 1024 * 1024 * 1024,
|
1793
|
-
}.get(mc, 1)
|
1823
|
+
mi = UNHUMANIZE_UNITS.get(mc, 1)
|
1794
1824
|
return int(float(sz[:-1]) * mi)
|
1795
1825
|
|
1796
1826
|
|
@@ -2473,6 +2503,7 @@ def sendfile_py(
|
|
2473
2503
|
s ,
|
2474
2504
|
bufsz ,
|
2475
2505
|
slp ,
|
2506
|
+
use_poll ,
|
2476
2507
|
) :
|
2477
2508
|
remains = upper - lower
|
2478
2509
|
f.seek(lower)
|
@@ -2501,22 +2532,31 @@ def sendfile_kern(
|
|
2501
2532
|
s ,
|
2502
2533
|
bufsz ,
|
2503
2534
|
slp ,
|
2535
|
+
use_poll ,
|
2504
2536
|
) :
|
2505
2537
|
out_fd = s.fileno()
|
2506
2538
|
in_fd = f.fileno()
|
2507
2539
|
ofs = lower
|
2508
2540
|
stuck = 0.0
|
2541
|
+
if use_poll:
|
2542
|
+
poll = select.poll()
|
2543
|
+
poll.register(out_fd, select.POLLOUT)
|
2544
|
+
|
2509
2545
|
while ofs < upper:
|
2510
2546
|
stuck = stuck or time.time()
|
2511
2547
|
try:
|
2512
2548
|
req = min(2 ** 30, upper - ofs)
|
2513
|
-
|
2549
|
+
if use_poll:
|
2550
|
+
poll.poll(10000)
|
2551
|
+
else:
|
2552
|
+
select.select([], [out_fd], [], 10)
|
2514
2553
|
n = os.sendfile(out_fd, in_fd, ofs, req)
|
2515
2554
|
stuck = 0
|
2516
2555
|
except OSError as ex:
|
2517
2556
|
# client stopped reading; do another select
|
2518
2557
|
d = time.time() - stuck
|
2519
2558
|
if d < 3600 and ex.errno == errno.EWOULDBLOCK:
|
2559
|
+
time.sleep(0.02)
|
2520
2560
|
continue
|
2521
2561
|
|
2522
2562
|
n = 0
|
@@ -2660,7 +2700,7 @@ def unescape_cookie(orig ) :
|
|
2660
2700
|
|
2661
2701
|
def guess_mime(url , fallback = "application/octet-stream") :
|
2662
2702
|
try:
|
2663
|
-
|
2703
|
+
ext = url.rsplit(".", 1)[1].lower()
|
2664
2704
|
except:
|
2665
2705
|
return fallback
|
2666
2706
|
|
copyparty/web/a/u2c.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
from __future__ import print_function, unicode_literals
|
3
3
|
|
4
|
-
S_VERSION = "1.
|
5
|
-
S_BUILD_DT = "2024-
|
4
|
+
S_VERSION = "1.18"
|
5
|
+
S_BUILD_DT = "2024-06-01"
|
6
6
|
|
7
7
|
"""
|
8
8
|
u2c.py: upload to copyparty
|
@@ -79,12 +79,21 @@ req_ses = requests.Session()
|
|
79
79
|
|
80
80
|
|
81
81
|
class Daemon(threading.Thread):
|
82
|
-
def __init__(self, target, name=None, a=None):
|
83
|
-
|
84
|
-
|
82
|
+
def __init__(self, target, name = None, a = None):
|
83
|
+
threading.Thread.__init__(self, name=name)
|
84
|
+
self.a = a or ()
|
85
|
+
self.fun = target
|
85
86
|
self.daemon = True
|
86
87
|
self.start()
|
87
88
|
|
89
|
+
def run(self):
|
90
|
+
try:
|
91
|
+
signal.pthread_sigmask(signal.SIG_BLOCK, [signal.SIGINT, signal.SIGTERM])
|
92
|
+
except:
|
93
|
+
pass
|
94
|
+
|
95
|
+
self.fun(*self.a)
|
96
|
+
|
88
97
|
|
89
98
|
class File(object):
|
90
99
|
"""an up2k upload task; represents a single file"""
|
@@ -1135,7 +1144,7 @@ source file/folder selection uses rsync syntax, meaning that:
|
|
1135
1144
|
ap.add_argument("url", type=unicode, help="server url, including destination folder")
|
1136
1145
|
ap.add_argument("files", type=unicode, nargs="+", help="files and/or folders to process")
|
1137
1146
|
ap.add_argument("-v", action="store_true", help="verbose")
|
1138
|
-
ap.add_argument("-a", metavar="
|
1147
|
+
ap.add_argument("-a", metavar="PASSWD", help="password or $filepath")
|
1139
1148
|
ap.add_argument("-s", action="store_true", help="file-search (disables upload)")
|
1140
1149
|
ap.add_argument("-x", type=unicode, metavar="REGEX", default="", help="skip file if filesystem-abspath matches REGEX, example: '.*/\\.hist/.*'")
|
1141
1150
|
ap.add_argument("--ok", action="store_true", help="continue even if some local files are inaccessible")
|
@@ -1153,8 +1162,8 @@ source file/folder selection uses rsync syntax, meaning that:
|
|
1153
1162
|
ap.add_argument("--drd", action="store_true", help="delete remote files during upload instead of afterwards; reduces peak disk space usage, but will reupload instead of detecting renames")
|
1154
1163
|
|
1155
1164
|
ap = app.add_argument_group("performance tweaks")
|
1156
|
-
ap.add_argument("-j", type=int, metavar="
|
1157
|
-
ap.add_argument("-J", type=int, metavar="
|
1165
|
+
ap.add_argument("-j", type=int, metavar="CONNS", default=2, help="parallel connections")
|
1166
|
+
ap.add_argument("-J", type=int, metavar="CORES", default=hcores, help="num cpu-cores to use for hashing; set 0 or 1 for single-core hashing")
|
1158
1167
|
ap.add_argument("-nh", action="store_true", help="disable hashing while uploading")
|
1159
1168
|
ap.add_argument("-ns", action="store_true", help="no status panel (for slow consoles and macos)")
|
1160
1169
|
ap.add_argument("--cd", type=float, metavar="SEC", default=5, help="delay before reattempting a failed handshake/upload")
|
@@ -1162,7 +1171,7 @@ source file/folder selection uses rsync syntax, meaning that:
|
|
1162
1171
|
ap.add_argument("-z", action="store_true", help="ZOOMIN' (skip uploading files if they exist at the destination with the ~same last-modified timestamp, so same as yolo / turbo with date-chk but even faster)")
|
1163
1172
|
|
1164
1173
|
ap = app.add_argument_group("tls")
|
1165
|
-
ap.add_argument("-te", metavar="
|
1174
|
+
ap.add_argument("-te", metavar="PATH", help="path to ca.pem or cert.pem to expect/verify")
|
1166
1175
|
ap.add_argument("-td", action="store_true", help="disable certificate check")
|
1167
1176
|
# fmt: on
|
1168
1177
|
|
@@ -1199,6 +1208,14 @@ source file/folder selection uses rsync syntax, meaning that:
|
|
1199
1208
|
ar.url = ar.url.rstrip("/") + "/"
|
1200
1209
|
if "://" not in ar.url:
|
1201
1210
|
ar.url = "http://" + ar.url
|
1211
|
+
|
1212
|
+
if "https://" in ar.url.lower():
|
1213
|
+
try:
|
1214
|
+
import ssl, zipfile
|
1215
|
+
except:
|
1216
|
+
t = "ERROR: https is not available for some reason; please use http"
|
1217
|
+
print("\n\n %s\n\n" % (t,))
|
1218
|
+
raise
|
1202
1219
|
|
1203
1220
|
if ar.a and ar.a.startswith("$"):
|
1204
1221
|
fn = ar.a[1:]
|
copyparty/web/baguettebox.js.gz
CHANGED
Binary file
|
copyparty/web/browser.css.gz
CHANGED
Binary file
|
copyparty/web/browser.js.gz
CHANGED
Binary file
|
copyparty/web/deps/marked.js.gz
CHANGED
Binary file
|
copyparty/web/md2.js.gz
CHANGED
Binary file
|
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.13.
|
3
|
+
Version: 1.13.3
|
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
|
@@ -284,6 +284,7 @@ also see [comparison to similar software](./docs/versus.md)
|
|
284
284
|
* ☑ ...of videos using FFmpeg
|
285
285
|
* ☑ ...of audio (spectrograms) using FFmpeg
|
286
286
|
* ☑ cache eviction (max-age; maybe max-size eventually)
|
287
|
+
* ☑ multilingual UI (english, norwegian, [add your own](./docs/rice/#translations)))
|
287
288
|
* ☑ SPA (browse while uploading)
|
288
289
|
* server indexing
|
289
290
|
* ☑ [locate files by contents](#file-search)
|
@@ -466,7 +467,7 @@ configuring accounts/volumes with arguments:
|
|
466
467
|
`-v .::r,usr1,usr2:rw,usr3,usr4` = usr1/2 read-only, 3/4 read-write
|
467
468
|
|
468
469
|
permissions:
|
469
|
-
* `r` (read): browse folder contents, download files, download as zip/tar
|
470
|
+
* `r` (read): browse folder contents, download files, download as zip/tar, see filekeys/dirkeys
|
470
471
|
* `w` (write): upload files, move files *into* this folder
|
471
472
|
* `m` (move): move files/folders *from* this folder
|
472
473
|
* `d` (delete): delete files/folders
|
@@ -668,7 +669,7 @@ you can also zip a selection of files or folders by clicking them in the browser
|
|
668
669
|
|
669
670
|
cool trick: download a folder by appending url-params `?tar&opus` or `?tar&mp3` to transcode all audio files (except aac|m4a|mp3|ogg|opus|wma) to opus/mp3 before they're added to the archive
|
670
671
|
* super useful if you're 5 minutes away from takeoff and realize you don't have any music on your phone but your server only has flac files and downloading those will burn through all your data + there wouldn't be enough time anyways
|
671
|
-
* and url-params `&j` / `&w` produce jpeg/webm thumbnails/spectrograms instead of the original audio/video/images
|
672
|
+
* and url-params `&j` / `&w` produce jpeg/webm thumbnails/spectrograms instead of the original audio/video/images (`&p` for audio waveforms)
|
672
673
|
* can also be used to pregenerate thumbnails; combine with `--th-maxage=9999999` or `--th-clean=0`
|
673
674
|
|
674
675
|
|
@@ -964,6 +965,8 @@ using arguments or config files, or a mix of both:
|
|
964
965
|
|
965
966
|
**NB:** as humongous as this readme is, there is also a lot of undocumented features. Run copyparty with `--help` to see all available global options; all of those can be used in the `[global]` section of config files, and everything listed in `--help-flags` can be used in volumes as volflags.
|
966
967
|
* if running in docker/podman, try this: `docker run --rm -it copyparty/ac --help`
|
968
|
+
* or see this (probably outdated): https://ocv.me/copyparty/helptext.html
|
969
|
+
* or if you prefer plaintext, https://ocv.me/copyparty/helptext.txt
|
967
970
|
|
968
971
|
|
969
972
|
## zeroconf
|
@@ -1140,7 +1143,7 @@ tweaking the ui
|
|
1140
1143
|
* to sort in music order (album, track, artist, title) with filename as fallback, you could `--sort tags/Cirle,tags/.tn,tags/Artist,tags/Title,href`
|
1141
1144
|
* to sort by upload date, first enable showing the upload date in the listing with `-e2d -mte +.up_at` and then `--sort tags/.up_at`
|
1142
1145
|
|
1143
|
-
see [./docs/rice](./docs/rice) for more, including how to add stuff (css/`<meta>`/...) to the html `<head>` tag
|
1146
|
+
see [./docs/rice](./docs/rice) for more, including how to add stuff (css/`<meta>`/...) to the html `<head>` tag, or to add your own translation
|
1144
1147
|
|
1145
1148
|
|
1146
1149
|
## opengraph
|