copyparty 1.16.9__py3-none-any.whl → 1.16.11__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 +19 -5
- copyparty/__version__.py +2 -2
- copyparty/cfg.py +1 -0
- copyparty/httpcli.py +17 -1
- copyparty/th_cli.py +6 -2
- copyparty/th_srv.py +100 -41
- copyparty/util.py +19 -10
- copyparty/web/a/u2c.py +50 -3
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/md.js.gz +0 -0
- copyparty/web/md2.js.gz +0 -0
- copyparty/web/svcs.js.gz +0 -0
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.16.9.dist-info → copyparty-1.16.11.dist-info}/METADATA +17 -1
- {copyparty-1.16.9.dist-info → copyparty-1.16.11.dist-info}/RECORD +21 -21
- {copyparty-1.16.9.dist-info → copyparty-1.16.11.dist-info}/LICENSE +0 -0
- {copyparty-1.16.9.dist-info → copyparty-1.16.11.dist-info}/WHEEL +0 -0
- {copyparty-1.16.9.dist-info → copyparty-1.16.11.dist-info}/entry_points.txt +0 -0
- {copyparty-1.16.9.dist-info → copyparty-1.16.11.dist-info}/top_level.txt +0 -0
copyparty/__main__.py
CHANGED
@@ -767,11 +767,22 @@ def get_sects():
|
|
767
767
|
values for --urlform:
|
768
768
|
\033[36mstash\033[35m dumps the data to file and returns length + checksum
|
769
769
|
\033[36msave,get\033[35m dumps to file and returns the page like a GET
|
770
|
-
\033[36mprint
|
771
|
-
|
770
|
+
\033[36mprint \033[35m prints the data to log and returns an error
|
771
|
+
\033[36mprint,xm \033[35m prints the data to log and returns --xm output
|
772
|
+
\033[36mprint,get\033[35m prints the data to log and returns GET\033[0m
|
772
773
|
|
773
|
-
note that the \033[35m--xm\033[0m hook will only run if \033[35m--urlform\033[0m
|
774
|
-
|
774
|
+
note that the \033[35m--xm\033[0m hook will only run if \033[35m--urlform\033[0m is
|
775
|
+
either \033[36mprint\033[0m or \033[36mprint,get\033[0m or the default \033[36mprint,xm\033[0m
|
776
|
+
|
777
|
+
if an \033[35m--xm\033[0m hook returns text, then
|
778
|
+
the response code will be HTTP 202;
|
779
|
+
http/get responses will be HTTP 200
|
780
|
+
|
781
|
+
if there are multiple \033[35m--xm\033[0m hooks defined, then
|
782
|
+
the first hook that produced output is returned
|
783
|
+
|
784
|
+
if there are no \033[35m--xm\033[0m hooks defined, then the default
|
785
|
+
\033[36mprint,xm\033[0m behaves like \033[36mprint,get\033[0m (returning html)
|
775
786
|
"""
|
776
787
|
),
|
777
788
|
],
|
@@ -952,7 +963,7 @@ def add_general(ap, nc, srvname):
|
|
952
963
|
ap2.add_argument("-v", metavar="VOL", type=u, action="append", help="add volume, \033[33mSRC\033[0m:\033[33mDST\033[0m:\033[33mFLAG\033[0m; examples [\033[32m.::r\033[0m], [\033[32m/mnt/nas/music:/music:r:aed\033[0m], see --help-accounts")
|
953
964
|
ap2.add_argument("--grp", metavar="G:N,N", type=u, action="append", help="add group, \033[33mNAME\033[0m:\033[33mUSER1\033[0m,\033[33mUSER2\033[0m,\033[33m...\033[0m; example [\033[32madmins:ed,foo,bar\033[0m]")
|
954
965
|
ap2.add_argument("-ed", action="store_true", help="enable the ?dots url parameter / client option which allows clients to see dotfiles / hidden files (volflag=dots)")
|
955
|
-
ap2.add_argument("--urlform", metavar="MODE", type=u, default="print,
|
966
|
+
ap2.add_argument("--urlform", metavar="MODE", type=u, default="print,xm", help="how to handle url-form POSTs; see \033[33m--help-urlform\033[0m")
|
956
967
|
ap2.add_argument("--wintitle", metavar="TXT", type=u, default="cpp @ $pub", help="server terminal title, for example [\033[32m$ip-10.1.2.\033[0m] or [\033[32m$ip-]")
|
957
968
|
ap2.add_argument("--name", metavar="TXT", type=u, default=srvname, help="server name (displayed topleft in browser and in mDNS)")
|
958
969
|
ap2.add_argument("--mime", metavar="EXT=MIME", type=u, action="append", help="map file \033[33mEXT\033[0mension to \033[33mMIME\033[0mtype, for example [\033[32mjpg=image/jpeg\033[0m]")
|
@@ -1325,6 +1336,7 @@ def add_admin(ap):
|
|
1325
1336
|
ap2.add_argument("--no-ups-page", action="store_true", help="disable ?ru (list of recent uploads)")
|
1326
1337
|
ap2.add_argument("--no-up-list", action="store_true", help="don't show list of incoming files in controlpanel")
|
1327
1338
|
ap2.add_argument("--dl-list", metavar="LVL", type=int, default=2, help="who can see active downloads in the controlpanel? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=everyone")
|
1339
|
+
ap2.add_argument("--ups-who", metavar="LVL", type=int, default=2, help="who can see recent uploads on the ?ru page? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=everyone (volflag=ups_who)")
|
1328
1340
|
ap2.add_argument("--ups-when", action="store_true", help="let everyone see upload timestamps on the ?ru page, not just admins")
|
1329
1341
|
|
1330
1342
|
|
@@ -1365,6 +1377,8 @@ def add_transcoding(ap):
|
|
1365
1377
|
ap2 = ap.add_argument_group('transcoding options')
|
1366
1378
|
ap2.add_argument("--q-opus", metavar="KBPS", type=int, default=128, help="target bitrate for transcoding to opus; set 0 to disable")
|
1367
1379
|
ap2.add_argument("--q-mp3", metavar="QUALITY", type=u, default="q2", help="target quality for transcoding to mp3, for example [\033[32m192k\033[0m] (CBR) or [\033[32mq0\033[0m] (CQ/CRF, q0=maxquality, q9=smallest); set 0 to disable")
|
1380
|
+
ap2.add_argument("--no-caf", action="store_true", help="disable transcoding to caf-opus (affects iOS v12~v17), will use mp3 instead")
|
1381
|
+
ap2.add_argument("--no-owa", action="store_true", help="disable transcoding to webm-opus (iOS v18 and later), will use mp3 instead")
|
1368
1382
|
ap2.add_argument("--no-acode", action="store_true", help="disable audio transcoding")
|
1369
1383
|
ap2.add_argument("--no-bacode", action="store_true", help="disable batch audio transcoding by folder download (zip/tar)")
|
1370
1384
|
ap2.add_argument("--ac-maxage", metavar="SEC", type=int, default=86400, help="delete cached transcode output after \033[33mSEC\033[0m seconds")
|
copyparty/__version__.py
CHANGED
copyparty/cfg.py
CHANGED
copyparty/httpcli.py
CHANGED
@@ -1891,6 +1891,9 @@ class HttpCli(object):
|
|
1891
1891
|
if "stash" in opt:
|
1892
1892
|
return self.handle_stash(False)
|
1893
1893
|
|
1894
|
+
xm = []
|
1895
|
+
xm_rsp = {}
|
1896
|
+
|
1894
1897
|
if "save" in opt:
|
1895
1898
|
post_sz, _, _, _, _, path, _ = self.dump_to_file(False)
|
1896
1899
|
self.log("urlform: %d bytes, %r" % (post_sz, path))
|
@@ -1913,7 +1916,7 @@ class HttpCli(object):
|
|
1913
1916
|
plain = plain[4:]
|
1914
1917
|
xm = self.vn.flags.get("xm")
|
1915
1918
|
if xm:
|
1916
|
-
runhook(
|
1919
|
+
xm_rsp = runhook(
|
1917
1920
|
self.log,
|
1918
1921
|
self.conn.hsrv.broker,
|
1919
1922
|
None,
|
@@ -1937,6 +1940,13 @@ class HttpCli(object):
|
|
1937
1940
|
except Exception as ex:
|
1938
1941
|
self.log(repr(ex))
|
1939
1942
|
|
1943
|
+
if "xm" in opt:
|
1944
|
+
if xm:
|
1945
|
+
self.loud_reply(xm_rsp.get("stdout") or "", status=202)
|
1946
|
+
return True
|
1947
|
+
else:
|
1948
|
+
return self.handle_get()
|
1949
|
+
|
1940
1950
|
if "get" in opt:
|
1941
1951
|
return self.handle_get()
|
1942
1952
|
|
@@ -5120,6 +5130,12 @@ class HttpCli(object):
|
|
5120
5130
|
adm = "*" in vol.axs.uadmin or self.uname in vol.axs.uadmin
|
5121
5131
|
dots = "*" in vol.axs.udot or self.uname in vol.axs.udot
|
5122
5132
|
|
5133
|
+
lvl = int(vol.flags["ups_who"])
|
5134
|
+
if not lvl:
|
5135
|
+
continue
|
5136
|
+
elif lvl == 1 and not adm:
|
5137
|
+
continue
|
5138
|
+
|
5123
5139
|
n = 1000
|
5124
5140
|
q = "select sz, rd, fn, ip, at from up where at>0 order by at desc"
|
5125
5141
|
for sz, rd, fn, ip, at in cur.execute(q):
|
copyparty/th_cli.py
CHANGED
@@ -6,7 +6,7 @@ import os
|
|
6
6
|
from .__init__ import TYPE_CHECKING
|
7
7
|
from .authsrv import VFS
|
8
8
|
from .bos import bos
|
9
|
-
from .th_srv import HAVE_WEBP, thumb_path
|
9
|
+
from .th_srv import EXTS_AC, HAVE_WEBP, thumb_path
|
10
10
|
from .util import Cooldown
|
11
11
|
|
12
12
|
if TYPE_CHECKING:
|
@@ -54,13 +54,17 @@ class ThumbCli(object):
|
|
54
54
|
if is_vid and "dvthumb" in dbv.flags:
|
55
55
|
return None
|
56
56
|
|
57
|
-
want_opus = fmt in
|
57
|
+
want_opus = fmt in EXTS_AC
|
58
58
|
is_au = ext in self.fmt_ffa
|
59
59
|
is_vau = want_opus and ext in self.fmt_ffv
|
60
60
|
if is_au or is_vau:
|
61
61
|
if want_opus:
|
62
62
|
if self.args.no_acode:
|
63
63
|
return None
|
64
|
+
elif fmt == "caf" and self.args.no_caf:
|
65
|
+
fmt = "mp3"
|
66
|
+
elif fmt == "owa" and self.args.no_owa:
|
67
|
+
fmt = "mp3"
|
64
68
|
else:
|
65
69
|
if "dathumb" in dbv.flags:
|
66
70
|
return None
|
copyparty/th_srv.py
CHANGED
@@ -43,6 +43,9 @@ HAVE_HEIF = False
|
|
43
43
|
HAVE_AVIF = False
|
44
44
|
HAVE_WEBP = False
|
45
45
|
|
46
|
+
EXTS_TH = set(["jpg", "webp", "png"])
|
47
|
+
EXTS_AC = set(["opus", "owa", "caf", "mp3"])
|
48
|
+
|
46
49
|
try:
|
47
50
|
if os.environ.get("PRTY_NO_PIL"):
|
48
51
|
raise Exception()
|
@@ -136,7 +139,7 @@ def thumb_path(histpath , rem , mtime , fmt , ffa ) :
|
|
136
139
|
h = hashlib.sha512(afsenc(fn)).digest()
|
137
140
|
fn = ub64enc(h).decode("ascii")[:24]
|
138
141
|
|
139
|
-
if fmt in
|
142
|
+
if fmt in EXTS_AC:
|
140
143
|
cat = "ac"
|
141
144
|
else:
|
142
145
|
fc = fmt[:1]
|
@@ -331,9 +334,10 @@ class ThumbSrv(object):
|
|
331
334
|
ap_unpk = abspath
|
332
335
|
|
333
336
|
if not bos.path.exists(tpath):
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
+
tex = tpath.rsplit(".", 1)[-1]
|
338
|
+
want_mp3 = tex == "mp3"
|
339
|
+
want_opus = tex in ("opus", "owa", "caf")
|
340
|
+
want_png = tex == "png"
|
337
341
|
want_au = want_mp3 or want_opus
|
338
342
|
for lib in self.args.th_dec:
|
339
343
|
can_au = lib == "ff" and (
|
@@ -750,47 +754,102 @@ class ThumbSrv(object):
|
|
750
754
|
if "ac" not in tags:
|
751
755
|
raise Exception("not audio")
|
752
756
|
|
757
|
+
sq = "%dk" % (self.args.q_opus,)
|
758
|
+
bq = sq.encode("ascii")
|
759
|
+
if tags["ac"][1] == "opus":
|
760
|
+
enc = "-c:a copy"
|
761
|
+
else:
|
762
|
+
enc = "-c:a libopus -b:a " + sq
|
763
|
+
|
764
|
+
fun = self._conv_caf if fmt == "caf" else self._conv_owa
|
765
|
+
|
766
|
+
fun(abspath, tpath, tags, rawtags, enc, bq, vn)
|
767
|
+
|
768
|
+
def _conv_owa(
|
769
|
+
self,
|
770
|
+
abspath ,
|
771
|
+
tpath ,
|
772
|
+
tags ,
|
773
|
+
rawtags ,
|
774
|
+
enc ,
|
775
|
+
bq ,
|
776
|
+
vn ,
|
777
|
+
) :
|
778
|
+
if tpath.endswith(".owa"):
|
779
|
+
container = b"webm"
|
780
|
+
tagset = [b"-map_metadata", b"-1"]
|
781
|
+
else:
|
782
|
+
container = b"opus"
|
783
|
+
tagset = self.big_tags(rawtags)
|
784
|
+
|
785
|
+
self.log("conv2 %s [%s]" % (container, enc), 6)
|
786
|
+
benc = enc.encode("ascii").split(b" ")
|
787
|
+
|
788
|
+
# fmt: off
|
789
|
+
cmd = [
|
790
|
+
b"ffmpeg",
|
791
|
+
b"-nostdin",
|
792
|
+
b"-v", b"error",
|
793
|
+
b"-hide_banner",
|
794
|
+
b"-i", fsenc(abspath),
|
795
|
+
] + tagset + [
|
796
|
+
b"-map", b"0:a:0",
|
797
|
+
] + benc + [
|
798
|
+
b"-f", container,
|
799
|
+
fsenc(tpath)
|
800
|
+
]
|
801
|
+
# fmt: on
|
802
|
+
self._run_ff(cmd, vn, oom=300)
|
803
|
+
|
804
|
+
def _conv_caf(
|
805
|
+
self,
|
806
|
+
abspath ,
|
807
|
+
tpath ,
|
808
|
+
tags ,
|
809
|
+
rawtags ,
|
810
|
+
enc ,
|
811
|
+
bq ,
|
812
|
+
vn ,
|
813
|
+
) :
|
814
|
+
tmp_opus = tpath + ".opus"
|
815
|
+
try:
|
816
|
+
wunlink(self.log, tmp_opus, vn.flags)
|
817
|
+
except:
|
818
|
+
pass
|
819
|
+
|
753
820
|
try:
|
754
821
|
dur = tags[".dur"][1]
|
755
822
|
except:
|
756
823
|
dur = 0
|
757
824
|
|
758
|
-
|
759
|
-
|
760
|
-
tmp_opus = tpath
|
761
|
-
if want_caf:
|
762
|
-
tmp_opus = tpath + ".opus"
|
763
|
-
try:
|
764
|
-
wunlink(self.log, tmp_opus, vn.flags)
|
765
|
-
except:
|
766
|
-
pass
|
767
|
-
|
768
|
-
caf_src = abspath if src_opus else tmp_opus
|
769
|
-
bq = ("%dk" % (self.args.q_opus,)).encode("ascii")
|
825
|
+
self.log("conv2 caf-tmp [%s]" % (enc,), 6)
|
826
|
+
benc = enc.encode("ascii").split(b" ")
|
770
827
|
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
self._run_ff(cmd, vn, oom=300)
|
828
|
+
# fmt: off
|
829
|
+
cmd = [
|
830
|
+
b"ffmpeg",
|
831
|
+
b"-nostdin",
|
832
|
+
b"-v", b"error",
|
833
|
+
b"-hide_banner",
|
834
|
+
b"-i", fsenc(abspath),
|
835
|
+
b"-map_metadata", b"-1",
|
836
|
+
b"-map", b"0:a:0",
|
837
|
+
] + benc + [
|
838
|
+
b"-f", b"opus",
|
839
|
+
fsenc(tmp_opus)
|
840
|
+
]
|
841
|
+
# fmt: on
|
842
|
+
self._run_ff(cmd, vn, oom=300)
|
787
843
|
|
788
844
|
# iOS fails to play some "insufficiently complex" files
|
789
845
|
# (average file shorter than 8 seconds), so of course we
|
790
846
|
# fix that by mixing in some inaudible pink noise :^)
|
791
847
|
# 6.3 sec seems like the cutoff so lets do 7, and
|
792
848
|
# 7 sec of psyqui-musou.opus @ 3:50 is 174 KiB
|
793
|
-
|
849
|
+
sz = bos.path.getsize(tmp_opus)
|
850
|
+
if dur < 20 or sz < 256 * 1024:
|
851
|
+
zs = bq.decode("ascii")
|
852
|
+
self.log("conv2 caf-transcode; dur=%d sz=%d q=%s" % (dur, sz, zs), 6)
|
794
853
|
# fmt: off
|
795
854
|
cmd = [
|
796
855
|
b"ffmpeg",
|
@@ -809,15 +868,16 @@ class ThumbSrv(object):
|
|
809
868
|
# fmt: on
|
810
869
|
self._run_ff(cmd, vn, oom=300)
|
811
870
|
|
812
|
-
|
871
|
+
else:
|
813
872
|
# simple remux should be safe
|
873
|
+
self.log("conv2 caf-remux; dur=%d sz=%d" % (dur, sz), 6)
|
814
874
|
# fmt: off
|
815
875
|
cmd = [
|
816
876
|
b"ffmpeg",
|
817
877
|
b"-nostdin",
|
818
878
|
b"-v", b"error",
|
819
879
|
b"-hide_banner",
|
820
|
-
b"-i", fsenc(
|
880
|
+
b"-i", fsenc(tmp_opus),
|
821
881
|
b"-map_metadata", b"-1",
|
822
882
|
b"-map", b"0:a:0",
|
823
883
|
b"-c:a", b"copy",
|
@@ -827,11 +887,10 @@ class ThumbSrv(object):
|
|
827
887
|
# fmt: on
|
828
888
|
self._run_ff(cmd, vn, oom=300)
|
829
889
|
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
pass
|
890
|
+
try:
|
891
|
+
wunlink(self.log, tmp_opus, vn.flags)
|
892
|
+
except:
|
893
|
+
pass
|
835
894
|
|
836
895
|
def big_tags(self, raw_tags ) :
|
837
896
|
ret = []
|
@@ -887,7 +946,7 @@ class ThumbSrv(object):
|
|
887
946
|
|
888
947
|
def _clean(self, cat , thumbpath ) :
|
889
948
|
# self.log("cln {}".format(thumbpath))
|
890
|
-
exts =
|
949
|
+
exts = EXTS_TH if cat == "th" else EXTS_AC
|
891
950
|
maxage = getattr(self.args, cat + "_maxage")
|
892
951
|
now = time.time()
|
893
952
|
prev_b64 = None
|
copyparty/util.py
CHANGED
@@ -222,6 +222,7 @@ URL_BUG = URL_PRJ + "/issues/new?labels=bug&template=bug_report.md"
|
|
222
222
|
HTTPCODE = {
|
223
223
|
200: "OK",
|
224
224
|
201: "Created",
|
225
|
+
202: "Accepted",
|
225
226
|
204: "No Content",
|
226
227
|
206: "Partial Content",
|
227
228
|
207: "Multi-Status",
|
@@ -309,6 +310,7 @@ DAV_ALLPROPS = set(DAV_ALLPROP_L)
|
|
309
310
|
|
310
311
|
MIMES = {
|
311
312
|
"opus": "audio/ogg; codecs=opus",
|
313
|
+
"owa": "audio/webm; codecs=opus",
|
312
314
|
}
|
313
315
|
|
314
316
|
|
@@ -3391,7 +3393,7 @@ def _zmq_hook(
|
|
3391
3393
|
msg ,
|
3392
3394
|
wait ,
|
3393
3395
|
sp_ka ,
|
3394
|
-
)
|
3396
|
+
) :
|
3395
3397
|
import zmq
|
3396
3398
|
|
3397
3399
|
try:
|
@@ -3402,6 +3404,7 @@ def _zmq_hook(
|
|
3402
3404
|
mtx = ZMQ["mtx"]
|
3403
3405
|
|
3404
3406
|
ret = ""
|
3407
|
+
nret = 0
|
3405
3408
|
t0 = time.time()
|
3406
3409
|
if verbose and log:
|
3407
3410
|
log("hook(%s) %r entering zmq-main-lock" % (src, cmd), 6)
|
@@ -3428,18 +3431,21 @@ def _zmq_hook(
|
|
3428
3431
|
|
3429
3432
|
if mode == "pub":
|
3430
3433
|
sck = ctx.socket(zmq.PUB)
|
3434
|
+
sck.setsockopt(zmq.LINGER, 0)
|
3431
3435
|
sck.bind(uri)
|
3432
3436
|
time.sleep(1) # give clients time to connect; avoids losing first msg
|
3433
3437
|
elif mode == "push":
|
3434
3438
|
sck = ctx.socket(zmq.PUSH)
|
3435
|
-
sck.bind(uri)
|
3436
3439
|
if timeout:
|
3437
3440
|
sck.SNDTIMEO = int(timeout * 1000)
|
3441
|
+
sck.setsockopt(zmq.LINGER, 0)
|
3442
|
+
sck.bind(uri)
|
3438
3443
|
elif mode == "req":
|
3439
3444
|
sck = ctx.socket(zmq.REQ)
|
3440
|
-
sck.connect(uri)
|
3441
3445
|
if timeout:
|
3442
3446
|
sck.RCVTIMEO = int(timeout * 1000)
|
3447
|
+
sck.setsockopt(zmq.LINGER, 0)
|
3448
|
+
sck.connect(uri)
|
3443
3449
|
else:
|
3444
3450
|
raise Exception()
|
3445
3451
|
|
@@ -3460,6 +3466,10 @@ def _zmq_hook(
|
|
3460
3466
|
log("hook(%s) %r awaiting ack from req" % (src, cmd), 6)
|
3461
3467
|
try:
|
3462
3468
|
ret = sck.recv().decode("utf-8", "replace")
|
3469
|
+
if ret.startswith("return "):
|
3470
|
+
m = re.search("^return ([0-9]+)", ret[:12])
|
3471
|
+
if m:
|
3472
|
+
nret = int(m.group(1))
|
3463
3473
|
except:
|
3464
3474
|
sck.close()
|
3465
3475
|
del ZMQ[cmd] # bad state; must reset
|
@@ -3473,7 +3483,7 @@ def _zmq_hook(
|
|
3473
3483
|
if wait > 0:
|
3474
3484
|
time.sleep(wait)
|
3475
3485
|
|
3476
|
-
return ret
|
3486
|
+
return nret, ret
|
3477
3487
|
|
3478
3488
|
|
3479
3489
|
def _runhook(
|
@@ -3520,12 +3530,9 @@ def _runhook(
|
|
3520
3530
|
arg = txt or ap
|
3521
3531
|
|
3522
3532
|
if acmd[0].startswith("zmq:"):
|
3523
|
-
zs =
|
3524
|
-
|
3525
|
-
|
3526
|
-
except Exception as ex:
|
3527
|
-
if log:
|
3528
|
-
log("zeromq failed: %r" % (ex,))
|
3533
|
+
zi, zs = _zmq_hook(log, verbose, src, acmd[0][4:].lower(), arg, wait, sp_ka)
|
3534
|
+
if zi:
|
3535
|
+
raise Exception("zmq says %d" % (zi,))
|
3529
3536
|
return {"rc": 0, "stdout": zs}
|
3530
3537
|
|
3531
3538
|
acmd += [arg]
|
@@ -3606,6 +3613,8 @@ def runhook(
|
|
3606
3613
|
elif k in ret:
|
3607
3614
|
if k == "rc" and v:
|
3608
3615
|
ret[k] = v
|
3616
|
+
elif k == "stdout" and v and not ret[k]:
|
3617
|
+
ret[k] = v
|
3609
3618
|
else:
|
3610
3619
|
ret[k] = v
|
3611
3620
|
except Exception as ex:
|
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.
|
5
|
-
S_BUILD_DT = "2025-01-
|
4
|
+
S_VERSION = "2.9"
|
5
|
+
S_BUILD_DT = "2025-01-27"
|
6
6
|
|
7
7
|
"""
|
8
8
|
u2c.py: upload to copyparty
|
@@ -234,6 +234,10 @@ CLEN = "Content-Length"
|
|
234
234
|
|
235
235
|
web = None # type: HCli
|
236
236
|
|
237
|
+
links = [] # type: list[str]
|
238
|
+
linkmtx = threading.Lock()
|
239
|
+
linkfile = None
|
240
|
+
|
237
241
|
|
238
242
|
class File(object):
|
239
243
|
"""an up2k upload task; represents a single file"""
|
@@ -759,6 +763,29 @@ def get_hashlist(file, pcb, mth):
|
|
759
763
|
file.kchunks[k] = [v1, v2]
|
760
764
|
|
761
765
|
|
766
|
+
def printlink(ar, purl, name, fk):
|
767
|
+
if not name:
|
768
|
+
url = purl # srch
|
769
|
+
else:
|
770
|
+
name = quotep(name.encode("utf-8", WTF8)).decode("utf-8")
|
771
|
+
if fk:
|
772
|
+
url = "%s%s?k=%s" % (purl, name, fk)
|
773
|
+
else:
|
774
|
+
url = "%s%s" % (purl, name)
|
775
|
+
|
776
|
+
url = "%s/%s" % (ar.burl, url.lstrip("/"))
|
777
|
+
|
778
|
+
with linkmtx:
|
779
|
+
if ar.u:
|
780
|
+
links.append(url)
|
781
|
+
if ar.ud:
|
782
|
+
print(url)
|
783
|
+
if linkfile:
|
784
|
+
zs = "%s\n" % (url,)
|
785
|
+
zb = zs.encode("utf-8", "replace")
|
786
|
+
linkfile.write(zb)
|
787
|
+
|
788
|
+
|
762
789
|
def handshake(ar, file, search):
|
763
790
|
# type: (argparse.Namespace, File, bool) -> tuple[list[str], bool]
|
764
791
|
"""
|
@@ -830,12 +857,17 @@ def handshake(ar, file, search):
|
|
830
857
|
raise Exception(txt)
|
831
858
|
|
832
859
|
if search:
|
860
|
+
if ar.uon and r["hits"]:
|
861
|
+
printlink(ar, r["hits"][0]["rp"], "", "")
|
833
862
|
return r["hits"], False
|
834
863
|
|
835
864
|
file.url = quotep(r["purl"].encode("utf-8", WTF8)).decode("utf-8")
|
836
865
|
file.name = r["name"]
|
837
866
|
file.wark = r["wark"]
|
838
867
|
|
868
|
+
if ar.uon and not r["hash"]:
|
869
|
+
printlink(ar, file.url, r["name"], r.get("fk"))
|
870
|
+
|
839
871
|
return r["hash"], r["sprs"]
|
840
872
|
|
841
873
|
|
@@ -1470,7 +1502,7 @@ class APF(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFor
|
|
1470
1502
|
|
1471
1503
|
|
1472
1504
|
def main():
|
1473
|
-
global web
|
1505
|
+
global web, linkfile
|
1474
1506
|
|
1475
1507
|
time.strptime("19970815", "%Y%m%d") # python#7980
|
1476
1508
|
"".encode("idna") # python#29288
|
@@ -1507,6 +1539,11 @@ source file/folder selection uses rsync syntax, meaning that:
|
|
1507
1539
|
ap.add_argument("--spd", action="store_true", help="print speeds for each file")
|
1508
1540
|
ap.add_argument("--version", action="store_true", help="show version and exit")
|
1509
1541
|
|
1542
|
+
ap = app.add_argument_group("print links")
|
1543
|
+
ap.add_argument("-u", action="store_true", help="print list of download-links after all uploads finished")
|
1544
|
+
ap.add_argument("-ud", action="store_true", help="print download-link after each upload finishes")
|
1545
|
+
ap.add_argument("-uf", type=unicode, metavar="PATH", help="print list of download-links to file")
|
1546
|
+
|
1510
1547
|
ap = app.add_argument_group("compatibility")
|
1511
1548
|
ap.add_argument("--cls", action="store_true", help="clear screen before start")
|
1512
1549
|
ap.add_argument("--rh", type=int, metavar="TRIES", default=0, help="resolve server hostname before upload (good for buggy networks, but TLS certs will break)")
|
@@ -1592,6 +1629,10 @@ source file/folder selection uses rsync syntax, meaning that:
|
|
1592
1629
|
ar.x = "|".join(ar.x or [])
|
1593
1630
|
|
1594
1631
|
setattr(ar, "wlist", ar.url == "-")
|
1632
|
+
setattr(ar, "uon", ar.u or ar.ud or ar.uf)
|
1633
|
+
|
1634
|
+
if ar.uf:
|
1635
|
+
linkfile = open(ar.uf, "wb")
|
1595
1636
|
|
1596
1637
|
for k in "dl dr drd wlist".split():
|
1597
1638
|
errs = []
|
@@ -1654,6 +1695,12 @@ source file/folder selection uses rsync syntax, meaning that:
|
|
1654
1695
|
ar.z = True
|
1655
1696
|
ctl = Ctl(ar, ctl.stats)
|
1656
1697
|
|
1698
|
+
if links:
|
1699
|
+
print()
|
1700
|
+
print("\n".join(links))
|
1701
|
+
if linkfile:
|
1702
|
+
linkfile.close()
|
1703
|
+
|
1657
1704
|
if ctl.errs:
|
1658
1705
|
print("WARNING: %d errors" % (ctl.errs))
|
1659
1706
|
|
copyparty/web/browser.css.gz
CHANGED
Binary file
|
copyparty/web/browser.js.gz
CHANGED
Binary file
|
copyparty/web/md.js.gz
CHANGED
Binary file
|
copyparty/web/md2.js.gz
CHANGED
Binary file
|
copyparty/web/svcs.js.gz
CHANGED
Binary file
|
copyparty/web/up2k.js.gz
CHANGED
Binary file
|
copyparty/web/util.js.gz
CHANGED
Binary file
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.16.
|
3
|
+
Version: 1.16.11
|
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
|
@@ -410,10 +410,19 @@ same order here too
|
|
410
410
|
* iPhones: the volume control doesn't work because [apple doesn't want it to](https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html#//apple_ref/doc/uid/TP40009523-CH5-SW11)
|
411
411
|
* `AudioContext` will probably never be a viable workaround as apple introduces new issues faster than they fix current ones
|
412
412
|
|
413
|
+
* iPhones: music volume goes on a rollercoaster during song changes
|
414
|
+
* nothing I can do about it because `AudioContext` is still broken in safari
|
415
|
+
|
413
416
|
* iPhones: the preload feature (in the media-player-options tab) can cause a tiny audio glitch 20sec before the end of each song, but disabling it may cause worse iOS bugs to appear instead
|
414
417
|
* just a hunch, but disabling preloading may cause playback to stop entirely, or possibly mess with bluetooth speakers
|
415
418
|
* tried to add a tooltip regarding this but looks like apple broke my tooltips
|
416
419
|
|
420
|
+
* iPhones: preloaded awo files make safari log MEDIA_ERR_NETWORK errors as playback starts, but the song plays just fine so eh whatever
|
421
|
+
* awo, opus-weba, is apple's new take on opus support, replacing opus-caf which was technically limited to cbr opus
|
422
|
+
|
423
|
+
* iPhones: preloading another awo file may cause playback to stop
|
424
|
+
* can be somewhat mitigated with `mp.au.play()` in `mp.onpreload` but that can hit a race condition in safari that starts playing the same audio object twice in parallel...
|
425
|
+
|
417
426
|
* Windows: folders cannot be accessed if the name ends with `.`
|
418
427
|
* python or windows bug
|
419
428
|
|
@@ -945,6 +954,8 @@ will show uploader IP and upload-time if the visitor has the admin permission
|
|
945
954
|
|
946
955
|
* global-option `--ups-when` makes upload-time visible to all users, and not just admins
|
947
956
|
|
957
|
+
* global-option `--ups-who` (volflag `ups_who`) specifies who gets access (0=nobody, 1=admins, 2=everyone), default=2
|
958
|
+
|
948
959
|
note that the [🧯 unpost](#unpost) feature is better suited for viewing *your own* recent uploads, as it includes the option to undo/delete them
|
949
960
|
|
950
961
|
|
@@ -984,6 +995,11 @@ open the `[🎺]` media-player-settings tab to configure it,
|
|
984
995
|
* `[aac]` converts `aac` and `m4a` files into opus (if supported by browser) or mp3
|
985
996
|
* `[oth]` converts all other known formats into opus (if supported by browser) or mp3
|
986
997
|
* `aac|ac3|aif|aiff|alac|alaw|amr|ape|au|dfpwm|dts|flac|gsm|it|m4a|mo3|mod|mp2|mp3|mpc|mptm|mt2|mulaw|ogg|okt|opus|ra|s3m|tak|tta|ulaw|wav|wma|wv|xm|xpk`
|
998
|
+
* "transcode to":
|
999
|
+
* `[opus]` produces an `opus` whenever transcoding is necessary (the best choice on Android and PCs)
|
1000
|
+
* `[awo]` is `opus` in a `weba` file, good for iPhones (iOS 17.5 and newer) but Apple is still fixing some state-confusion bugs as of iOS 18.2.1
|
1001
|
+
* `[caf]` is `opus` in a `caf` file, good for iPhones (iOS 11 through 17), technically unsupported by Apple but works for the mos tpart
|
1002
|
+
* `[mp3]` -- the myth, the legend, the undying master of mediocre sound quality that definitely works everywhere
|
987
1003
|
* "tint" reduces the contrast of the playback bar
|
988
1004
|
|
989
1005
|
|
@@ -1,17 +1,17 @@
|
|
1
1
|
copyparty/__init__.py,sha256=VR6ZZhB9IxaK5TDXDTBM_OIP5ydkrdbaEnstktLM__s,2649
|
2
|
-
copyparty/__main__.py,sha256=
|
3
|
-
copyparty/__version__.py,sha256=
|
2
|
+
copyparty/__main__.py,sha256=5igJxZ_H08z51qaF_YrWsRp1SNzV810RHvVq38lHrtI,115336
|
3
|
+
copyparty/__version__.py,sha256=3EfC4asnk6XDUfAe5nBClbNInAuJvogNSfslz0AxznQ,252
|
4
4
|
copyparty/authsrv.py,sha256=uADnPkHiTH0UcBUr-w5hdUare6p-74O7cJQaL9lkpws,104513
|
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=
|
10
|
+
copyparty/cfg.py,sha256=5QJPyuCUoQn2svd-4zzFmC0As13b08qenNJhPF5YkOU,10638
|
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=
|
14
|
+
copyparty/httpcli.py,sha256=yXpUvHcU3AKwQLEyvC-p-8mWdcuDJSueWg7J2gHzMCs,216276
|
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
|
@@ -28,11 +28,11 @@ copyparty/svchub.py,sha256=mgZ2UndrMqWuUywecsCgVz488WoS0EFHR94VvWZjeT0,41454
|
|
28
28
|
copyparty/szip.py,sha256=HFtnwOiBgx0HMLUf-h_T84zSlRijPxmhRo5PM613kRA,8602
|
29
29
|
copyparty/tcpsrv.py,sha256=2q18dGR8jnezA4SMfUXa-wrGRGX3nHIwkxkWvkTzF2A,19889
|
30
30
|
copyparty/tftpd.py,sha256=PXgG4rTmiaU_TavSyZWD5cFphdfChs9YvNY21qfExt8,13611
|
31
|
-
copyparty/th_cli.py,sha256=
|
32
|
-
copyparty/th_srv.py,sha256=
|
31
|
+
copyparty/th_cli.py,sha256=PxDAmUvO_8Vm5edXiWtsCft0Fw69QL9rCHf9zLmUNeA,4800
|
32
|
+
copyparty/th_srv.py,sha256=tHbh_Ve3v8tYclWH2thLs5oFufeXgJi1duUMveKIx9k,30725
|
33
33
|
copyparty/u2idx.py,sha256=G6MDbD4I_sJSOwaNFZ6XLTQhnEDrB12pVKuKhzQ_leE,13676
|
34
34
|
copyparty/up2k.py,sha256=alObSL1EVyPedi_24s7DBFlBzA0ZMkr9_jOtzdRV3vk,175427
|
35
|
-
copyparty/util.py,sha256=
|
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
|
38
38
|
copyparty/bos/path.py,sha256=yEjCq2ki9CvxA5sCT8pS0keEXwugs0ZeUyUhdBziOCI,777
|
@@ -55,17 +55,17 @@ 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=_amC3ipOrXKEFz8DsVP-JEl49VjMQYiKyF78eWfG-uk,7965
|
58
|
-
copyparty/web/browser.css.gz,sha256=
|
58
|
+
copyparty/web/browser.css.gz,sha256=fs6dyfIin4XNWlw3d6dOInZaW0U33-hZKMwRCB3ge-Y,11645
|
59
59
|
copyparty/web/browser.html,sha256=dekrZQ6w8ciB-QPlp-mjcuzUVKlsCYcvvi6efmXRfQE,4822
|
60
|
-
copyparty/web/browser.js.gz,sha256=
|
60
|
+
copyparty/web/browser.js.gz,sha256=CKdT13IN5XDOExgTnCFYMgxP2Fr9mdjRFIQv1OZQkEM,91199
|
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
|
64
64
|
copyparty/web/md.css.gz,sha256=UZpN0J7ubVM05CZkbZYkQRJeGgJt_GNDEzKTGSQd8h4,2032
|
65
65
|
copyparty/web/md.html,sha256=hz-xJVfKtaeTUQn3tGh7ebIMvLbOjVKkrMhsCTr3lGM,4200
|
66
|
-
copyparty/web/md.js.gz,sha256=
|
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=
|
68
|
+
copyparty/web/md2.js.gz,sha256=z3JASmIT5PVEWgTpHpc4uBkZsbqgtSF4Nt0w2b_8Mek,8355
|
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
|
@@ -81,14 +81,14 @@ copyparty/web/splash.css.gz,sha256=S8_A7JJl71xACRBYGzafeaD82OacW6Fa7oKPiNyrhAs,1
|
|
81
81
|
copyparty/web/splash.html,sha256=QEnTH9QZXFmAuyVtgqOuuHKBtIdi7uclpRqe0ZMewj4,6249
|
82
82
|
copyparty/web/splash.js.gz,sha256=4VqNznN10-bT33IJm3VWzBEJ1s08XZyxFB1TYPUkuAo,2739
|
83
83
|
copyparty/web/svcs.html,sha256=dnE1fG15zOpq7u0GYt8ij6BUv_LTwsiipFeneVYlMsM,14140
|
84
|
-
copyparty/web/svcs.js.gz,sha256=
|
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=
|
87
|
-
copyparty/web/util.js.gz,sha256=
|
86
|
+
copyparty/web/up2k.js.gz,sha256=N4idIqOefXurVh4ZhN6lv9nFPX_azLwE41vhXT2qBkI,23833
|
87
|
+
copyparty/web/util.js.gz,sha256=amPEbzFzNKmkM40jeGYB3h7wpgpHNSxHX19vcNAH8MI,15087
|
88
88
|
copyparty/web/w.hash.js.gz,sha256=l3GpSJD6mcU-1CRWkIj7PybgbjlfSr8oeO3vortIrQk,1105
|
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=
|
91
|
+
copyparty/web/a/u2c.py,sha256=8F-uVfu0ceXRehbNLjjRfcBKStP5Lm_vJ_JAhEM6zgk,52994
|
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.
|
113
|
-
copyparty-1.16.
|
114
|
-
copyparty-1.16.
|
115
|
-
copyparty-1.16.
|
116
|
-
copyparty-1.16.
|
117
|
-
copyparty-1.16.
|
112
|
+
copyparty-1.16.11.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
|
113
|
+
copyparty-1.16.11.dist-info/METADATA,sha256=Rm11Nqji9bckgX12wuhgCBYYbYRzX_RKei8H1GIW17A,146927
|
114
|
+
copyparty-1.16.11.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
115
|
+
copyparty-1.16.11.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
|
116
|
+
copyparty-1.16.11.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
|
117
|
+
copyparty-1.16.11.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|