copyparty 1.11.2__py3-none-any.whl → 1.12.0__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 +0 -1
- copyparty/__main__.py +24 -31
- copyparty/__version__.py +3 -3
- copyparty/authsrv.py +19 -0
- copyparty/httpcli.py +83 -12
- copyparty/mtag.py +1 -2
- copyparty/sutil.py +3 -1
- copyparty/svchub.py +5 -0
- copyparty/th_cli.py +1 -1
- copyparty/th_srv.py +47 -5
- copyparty/web/baguettebox.js.gz +0 -0
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/deps/busy.mp3.gz +0 -0
- copyparty/web/deps/marked.js.gz +0 -0
- copyparty/web/md.css.gz +0 -0
- copyparty/web/md.js.gz +0 -0
- copyparty/web/md2.css.gz +0 -0
- copyparty/web/md2.js.gz +0 -0
- copyparty/web/mde.css.gz +0 -0
- copyparty/web/mde.js.gz +0 -0
- copyparty/web/msg.css.gz +0 -0
- copyparty/web/splash.css.gz +0 -0
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/ui.css.gz +0 -0
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.11.2.dist-info → copyparty-1.12.0.dist-info}/METADATA +25 -7
- {copyparty-1.11.2.dist-info → copyparty-1.12.0.dist-info}/RECORD +33 -32
- {copyparty-1.11.2.dist-info → copyparty-1.12.0.dist-info}/LICENSE +0 -0
- {copyparty-1.11.2.dist-info → copyparty-1.12.0.dist-info}/WHEEL +0 -0
- {copyparty-1.11.2.dist-info → copyparty-1.12.0.dist-info}/entry_points.txt +0 -0
- {copyparty-1.11.2.dist-info → copyparty-1.12.0.dist-info}/top_level.txt +0 -0
copyparty/__init__.py
CHANGED
copyparty/__main__.py
CHANGED
@@ -151,7 +151,8 @@ def warn(msg ) :
|
|
151
151
|
|
152
152
|
|
153
153
|
def init_E(EE ) :
|
154
|
-
#
|
154
|
+
# some cpython alternatives (such as pyoxidizer) can
|
155
|
+
# __init__ several times, so do expensive stuff here
|
155
156
|
|
156
157
|
E = EE # pylint: disable=redefined-outer-name
|
157
158
|
|
@@ -184,34 +185,9 @@ def init_E(EE ) :
|
|
184
185
|
|
185
186
|
raise Exception("could not find a writable path for config")
|
186
187
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
import tempfile
|
191
|
-
from importlib.resources import open_binary
|
192
|
-
|
193
|
-
td = tempfile.TemporaryDirectory(prefix="")
|
194
|
-
atexit.register(td.cleanup)
|
195
|
-
tdn = td.name
|
196
|
-
|
197
|
-
with open_binary("copyparty", "z.tar") as tgz:
|
198
|
-
with tarfile.open(fileobj=tgz) as tf:
|
199
|
-
try:
|
200
|
-
tf.extractall(tdn, filter="tar")
|
201
|
-
except TypeError:
|
202
|
-
tf.extractall(tdn) # nosec (archive is safe)
|
203
|
-
|
204
|
-
return tdn
|
205
|
-
|
206
|
-
try:
|
207
|
-
E.mod = os.path.dirname(os.path.realpath(__file__))
|
208
|
-
if E.mod.endswith("__init__"):
|
209
|
-
E.mod = os.path.dirname(E.mod)
|
210
|
-
except:
|
211
|
-
if not E.ox:
|
212
|
-
raise
|
213
|
-
|
214
|
-
E.mod = _unpack()
|
188
|
+
E.mod = os.path.dirname(os.path.realpath(__file__))
|
189
|
+
if E.mod.endswith("__init__"):
|
190
|
+
E.mod = os.path.dirname(E.mod)
|
215
191
|
|
216
192
|
if sys.platform == "win32":
|
217
193
|
bdir = os.environ.get("APPDATA") or os.environ.get("TEMP") or "."
|
@@ -268,6 +244,19 @@ def get_fk_salt() :
|
|
268
244
|
return ret.decode("utf-8")
|
269
245
|
|
270
246
|
|
247
|
+
def get_dk_salt() :
|
248
|
+
fp = os.path.join(E.cfg, "dk-salt.txt")
|
249
|
+
try:
|
250
|
+
with open(fp, "rb") as f:
|
251
|
+
ret = f.read().strip()
|
252
|
+
except:
|
253
|
+
ret = base64.b64encode(os.urandom(30))
|
254
|
+
with open(fp, "wb") as f:
|
255
|
+
f.write(ret + b"\n")
|
256
|
+
|
257
|
+
return ret.decode("utf-8")
|
258
|
+
|
259
|
+
|
271
260
|
def get_ah_salt() :
|
272
261
|
fp = os.path.join(E.cfg, "ah-salt.txt")
|
273
262
|
try:
|
@@ -1125,13 +1114,14 @@ def add_safety(ap):
|
|
1125
1114
|
ap2.add_argument("--acam", metavar="V[,V]", type=u, default="GET,HEAD", help="Access-Control-Allow-Methods; list of methods to accept from offsite ('*' behaves like \033[33m--acao\033[0m's description)")
|
1126
1115
|
|
1127
1116
|
|
1128
|
-
def add_salt(ap, fk_salt, ah_salt):
|
1117
|
+
def add_salt(ap, fk_salt, dk_salt, ah_salt):
|
1129
1118
|
ap2 = ap.add_argument_group('salting options')
|
1130
1119
|
ap2.add_argument("--ah-alg", metavar="ALG", type=u, default="none", help="account-pw hashing algorithm; one of these, best to worst: \033[32margon2 scrypt sha2 none\033[0m (each optionally followed by alg-specific comma-sep. config)")
|
1131
1120
|
ap2.add_argument("--ah-salt", metavar="SALT", type=u, default=ah_salt, help="account-pw salt; ignored if \033[33m--ah-alg\033[0m is none (default)")
|
1132
1121
|
ap2.add_argument("--ah-gen", metavar="PW", type=u, default="", help="generate hashed password for \033[33mPW\033[0m, or read passwords from STDIN if \033[33mPW\033[0m is [\033[32m-\033[0m]")
|
1133
1122
|
ap2.add_argument("--ah-cli", action="store_true", help="launch an interactive shell which hashes passwords without ever storing or displaying the original passwords")
|
1134
1123
|
ap2.add_argument("--fk-salt", metavar="SALT", type=u, default=fk_salt, help="per-file accesskey salt; used to generate unpredictable URLs for hidden files")
|
1124
|
+
ap2.add_argument("--dk-salt", metavar="SALT", type=u, default=dk_salt, help="per-directory accesskey salt; used to generate unpredictable URLs to share folders with users who only have the 'get' permission")
|
1135
1125
|
ap2.add_argument("--warksalt", metavar="SALT", type=u, default="hunter2", help="up2k file-hash salt; serves no purpose, no reason to change this (but delete all databases if you do)")
|
1136
1126
|
|
1137
1127
|
|
@@ -1197,6 +1187,8 @@ def add_thumbnail(ap):
|
|
1197
1187
|
|
1198
1188
|
def add_transcoding(ap):
|
1199
1189
|
ap2 = ap.add_argument_group('transcoding options')
|
1190
|
+
ap2.add_argument("--q-opus", metavar="KBPS", type=int, default=128, help="target bitrate for transcoding to opus; set 0 to disable")
|
1191
|
+
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")
|
1200
1192
|
ap2.add_argument("--no-acode", action="store_true", help="disable audio transcoding")
|
1201
1193
|
ap2.add_argument("--no-bacode", action="store_true", help="disable batch audio transcoding by folder download (zip/tar)")
|
1202
1194
|
ap2.add_argument("--ac-maxage", metavar="SEC", type=int, default=86400, help="delete cached transcode output after \033[33mSEC\033[0m seconds")
|
@@ -1313,6 +1305,7 @@ def run_argparse(
|
|
1313
1305
|
cert_path = os.path.join(E.cfg, "cert.pem")
|
1314
1306
|
|
1315
1307
|
fk_salt = get_fk_salt()
|
1308
|
+
dk_salt = get_dk_salt()
|
1316
1309
|
ah_salt = get_ah_salt()
|
1317
1310
|
|
1318
1311
|
# alpine peaks at 5 threads for some reason,
|
@@ -1344,7 +1337,7 @@ def run_argparse(
|
|
1344
1337
|
add_tftp(ap)
|
1345
1338
|
add_smb(ap)
|
1346
1339
|
add_safety(ap)
|
1347
|
-
add_salt(ap, fk_salt, ah_salt)
|
1340
|
+
add_salt(ap, fk_salt, dk_salt, ah_salt)
|
1348
1341
|
add_optouts(ap)
|
1349
1342
|
add_shutdown(ap)
|
1350
1343
|
add_yolo(ap)
|
copyparty/__version__.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
VERSION = (1,
|
4
|
-
CODENAME = "
|
5
|
-
BUILD_DT = (2024,
|
3
|
+
VERSION = (1, 12, 0)
|
4
|
+
CODENAME = "locksmith"
|
5
|
+
BUILD_DT = (2024, 4, 6)
|
6
6
|
|
7
7
|
S_VERSION = ".".join(map(str, VERSION))
|
8
8
|
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
copyparty/authsrv.py
CHANGED
@@ -548,7 +548,12 @@ class VFS(object):
|
|
548
548
|
# no vfs nodes in the list of real inodes
|
549
549
|
real = [x for x in real if x[0] not in self.nodes]
|
550
550
|
|
551
|
+
dbv = self.dbv or self
|
551
552
|
for name, vn2 in sorted(self.nodes.items()):
|
553
|
+
if vn2.dbv == dbv and self.flags.get("dk"):
|
554
|
+
virt_vis[name] = vn2
|
555
|
+
continue
|
556
|
+
|
552
557
|
ok = False
|
553
558
|
zx = vn2.axs
|
554
559
|
axs = [zx.uread, zx.uwrite, zx.umove, zx.udel, zx.uget]
|
@@ -1674,6 +1679,20 @@ class AuthSrv(object):
|
|
1674
1679
|
vol.flags["fk"] = int(fk) if fk is not True else 8
|
1675
1680
|
have_fk = True
|
1676
1681
|
|
1682
|
+
dk = vol.flags.get("dk")
|
1683
|
+
dks = vol.flags.get("dks")
|
1684
|
+
dky = vol.flags.get("dky")
|
1685
|
+
if dks is not None and dky is not None:
|
1686
|
+
t = "WARNING: volume /%s has both dks and dky enabled; this is too yolo and not permitted"
|
1687
|
+
raise Exception(t % (vol.vpath,))
|
1688
|
+
|
1689
|
+
if dks and not dk:
|
1690
|
+
dk = dks
|
1691
|
+
if dky and not dk:
|
1692
|
+
dk = dky
|
1693
|
+
if dk:
|
1694
|
+
vol.flags["dk"] = int(dk) if dk is not True else 8
|
1695
|
+
|
1677
1696
|
if have_fk and re.match(r"^[0-9\.]+$", self.args.fk_salt):
|
1678
1697
|
self.log("filekey salt: {}".format(self.args.fk_salt))
|
1679
1698
|
|
copyparty/httpcli.py
CHANGED
@@ -1962,7 +1962,12 @@ class HttpCli(object):
|
|
1962
1962
|
|
1963
1963
|
v = self.uparam[k]
|
1964
1964
|
|
1965
|
-
|
1965
|
+
if self._use_dirkey():
|
1966
|
+
vn = self.vn
|
1967
|
+
rem = self.rem
|
1968
|
+
else:
|
1969
|
+
vn, rem = self.asrv.vfs.get(self.vpath, self.uname, True, False)
|
1970
|
+
|
1966
1971
|
zs = self.parser.require("files", 1024 * 1024)
|
1967
1972
|
if not zs:
|
1968
1973
|
raise Pebkac(422, "need files list")
|
@@ -2441,7 +2446,7 @@ class HttpCli(object):
|
|
2441
2446
|
self.log("user not allowed to overwrite with ?replace")
|
2442
2447
|
elif bos.path.exists(abspath):
|
2443
2448
|
try:
|
2444
|
-
|
2449
|
+
wunlink(self.log, abspath, vfs.flags)
|
2445
2450
|
t = "overwriting file with new upload: %s"
|
2446
2451
|
except:
|
2447
2452
|
t = "toctou while deleting for ?replace: %s"
|
@@ -2866,6 +2871,30 @@ class HttpCli(object):
|
|
2866
2871
|
|
2867
2872
|
return file_lastmod, True
|
2868
2873
|
|
2874
|
+
def _use_dirkey(self, ap = "") :
|
2875
|
+
if self.can_read or not self.can_get:
|
2876
|
+
return False
|
2877
|
+
|
2878
|
+
if self.vn.flags.get("dky"):
|
2879
|
+
return True
|
2880
|
+
|
2881
|
+
req = self.uparam.get("k") or ""
|
2882
|
+
if not req:
|
2883
|
+
return False
|
2884
|
+
|
2885
|
+
dk_len = self.vn.flags.get("dk")
|
2886
|
+
if not dk_len:
|
2887
|
+
return False
|
2888
|
+
|
2889
|
+
ap = ap or self.vn.canonical(self.rem)
|
2890
|
+
zs = self.gen_fk(2, self.args.dk_salt, ap, 0, 0)[:dk_len]
|
2891
|
+
if req == zs:
|
2892
|
+
return True
|
2893
|
+
|
2894
|
+
t = "wrong dirkey, want %s, got %s\n vp: %s\n ap: %s"
|
2895
|
+
self.log(t % (zs, req, self.req, ap), 6)
|
2896
|
+
return False
|
2897
|
+
|
2869
2898
|
def _expand(self, txt , phs ) :
|
2870
2899
|
for ph in phs:
|
2871
2900
|
if ph.startswith("hdr."):
|
@@ -3144,7 +3173,7 @@ class HttpCli(object):
|
|
3144
3173
|
# for f in fgen: print(repr({k: f[k] for k in ["vp", "ap"]}))
|
3145
3174
|
cfmt = ""
|
3146
3175
|
if self.thumbcli and not self.args.no_bacode:
|
3147
|
-
for zs in ("opus", "w", "j"):
|
3176
|
+
for zs in ("opus", "mp3", "w", "j"):
|
3148
3177
|
if zs in self.ouparam or uarg == zs:
|
3149
3178
|
cfmt = zs
|
3150
3179
|
|
@@ -3553,7 +3582,7 @@ class HttpCli(object):
|
|
3553
3582
|
|
3554
3583
|
dst = dst[len(top) + 1 :]
|
3555
3584
|
|
3556
|
-
ret = self.gen_tree(top, dst)
|
3585
|
+
ret = self.gen_tree(top, dst, self.uparam.get("k", ""))
|
3557
3586
|
if self.is_vproxied:
|
3558
3587
|
parents = self.args.R.split("/")
|
3559
3588
|
for parent in reversed(parents):
|
@@ -3563,18 +3592,25 @@ class HttpCli(object):
|
|
3563
3592
|
self.reply(zs.encode("utf-8"), mime="application/json")
|
3564
3593
|
return True
|
3565
3594
|
|
3566
|
-
def gen_tree(self, top , target ) :
|
3595
|
+
def gen_tree(self, top , target , dk ) :
|
3567
3596
|
ret = {}
|
3568
3597
|
excl = None
|
3569
3598
|
if target:
|
3570
3599
|
excl, target = (target.split("/", 1) + [""])[:2]
|
3571
|
-
sub = self.gen_tree("/".join([top, excl]).strip("/"), target)
|
3600
|
+
sub = self.gen_tree("/".join([top, excl]).strip("/"), target, dk)
|
3572
3601
|
ret["k" + quotep(excl)] = sub
|
3573
3602
|
|
3574
3603
|
vfs = self.asrv.vfs
|
3604
|
+
dk_sz = False
|
3605
|
+
if dk:
|
3606
|
+
vn, rem = vfs.get(top, self.uname, False, False)
|
3607
|
+
if vn.flags.get("dks") and self._use_dirkey(vn.canonical(rem)):
|
3608
|
+
dk_sz = vn.flags.get("dk")
|
3609
|
+
|
3575
3610
|
dots = False
|
3611
|
+
fsroot = ""
|
3576
3612
|
try:
|
3577
|
-
vn, rem = vfs.get(top, self.uname,
|
3613
|
+
vn, rem = vfs.get(top, self.uname, not dk_sz, False)
|
3578
3614
|
fsroot, vfs_ls, vfs_virt = vn.ls(
|
3579
3615
|
rem,
|
3580
3616
|
self.uname,
|
@@ -3582,7 +3618,9 @@ class HttpCli(object):
|
|
3582
3618
|
[[True, False], [False, True]],
|
3583
3619
|
)
|
3584
3620
|
dots = self.uname in vn.axs.udot
|
3621
|
+
dk_sz = vn.flags.get("dk")
|
3585
3622
|
except:
|
3623
|
+
dk_sz = None
|
3586
3624
|
vfs_ls = []
|
3587
3625
|
vfs_virt = {}
|
3588
3626
|
for v in self.rvol:
|
@@ -3597,6 +3635,14 @@ class HttpCli(object):
|
|
3597
3635
|
|
3598
3636
|
dirs = [quotep(x) for x in dirs if x != excl]
|
3599
3637
|
|
3638
|
+
if dk_sz and fsroot:
|
3639
|
+
kdirs = []
|
3640
|
+
for dn in dirs:
|
3641
|
+
ap = os.path.join(fsroot, dn)
|
3642
|
+
zs = self.gen_fk(2, self.args.dk_salt, ap, 0, 0)[:dk_sz]
|
3643
|
+
kdirs.append(dn + "?k=" + zs)
|
3644
|
+
dirs = kdirs
|
3645
|
+
|
3600
3646
|
for x in vfs_virt:
|
3601
3647
|
if x != excl:
|
3602
3648
|
try:
|
@@ -3861,6 +3907,7 @@ class HttpCli(object):
|
|
3861
3907
|
self.out_headers["X-Robots-Tag"] = "noindex, nofollow"
|
3862
3908
|
|
3863
3909
|
is_dir = stat.S_ISDIR(st.st_mode)
|
3910
|
+
is_dk = False
|
3864
3911
|
fk_pass = False
|
3865
3912
|
icur = None
|
3866
3913
|
if is_dir and (e2t or e2d):
|
@@ -3869,7 +3916,7 @@ class HttpCli(object):
|
|
3869
3916
|
icur = idx.get_cur(dbv.realpath)
|
3870
3917
|
|
3871
3918
|
th_fmt = self.uparam.get("th")
|
3872
|
-
if self.can_read:
|
3919
|
+
if self.can_read or (self.can_get and vn.flags.get("dk")):
|
3873
3920
|
if th_fmt is not None:
|
3874
3921
|
nothumb = "dthumb" in dbv.flags
|
3875
3922
|
if is_dir:
|
@@ -3975,8 +4022,11 @@ class HttpCli(object):
|
|
3975
4022
|
|
3976
4023
|
return self.tx_file(abspath)
|
3977
4024
|
|
3978
|
-
elif is_dir and not self.can_read
|
3979
|
-
|
4025
|
+
elif is_dir and not self.can_read:
|
4026
|
+
if self._use_dirkey(abspath):
|
4027
|
+
is_dk = True
|
4028
|
+
elif not self.can_write:
|
4029
|
+
return self.tx_404(True)
|
3980
4030
|
|
3981
4031
|
srv_info = []
|
3982
4032
|
|
@@ -3998,7 +4048,7 @@ class HttpCli(object):
|
|
3998
4048
|
srv_infot = "</span> // <span>".join(srv_info)
|
3999
4049
|
|
4000
4050
|
perms = []
|
4001
|
-
if self.can_read:
|
4051
|
+
if self.can_read or is_dk:
|
4002
4052
|
perms.append("read")
|
4003
4053
|
if self.can_write:
|
4004
4054
|
perms.append("write")
|
@@ -4126,7 +4176,7 @@ class HttpCli(object):
|
|
4126
4176
|
if not self.conn.hsrv.prism:
|
4127
4177
|
j2a["no_prism"] = True
|
4128
4178
|
|
4129
|
-
if not self.can_read:
|
4179
|
+
if not self.can_read and not is_dk:
|
4130
4180
|
if is_ls:
|
4131
4181
|
return self.tx_ls(ls_ret)
|
4132
4182
|
|
@@ -4179,8 +4229,15 @@ class HttpCli(object):
|
|
4179
4229
|
):
|
4180
4230
|
ls_names = exclude_dotfiles(ls_names)
|
4181
4231
|
|
4232
|
+
add_dk = vf.get("dk")
|
4182
4233
|
add_fk = vf.get("fk")
|
4183
4234
|
fk_alg = 2 if "fka" in vf else 1
|
4235
|
+
if add_dk:
|
4236
|
+
if vf.get("dky"):
|
4237
|
+
add_dk = False
|
4238
|
+
else:
|
4239
|
+
zs = self.gen_fk(2, self.args.dk_salt, abspath, 0, 0)[:add_dk]
|
4240
|
+
ls_ret["dk"] = cgv["dk"] = zs
|
4184
4241
|
|
4185
4242
|
dirs = []
|
4186
4243
|
files = []
|
@@ -4208,6 +4265,12 @@ class HttpCli(object):
|
|
4208
4265
|
href += "/"
|
4209
4266
|
if self.args.no_zip:
|
4210
4267
|
margin = "DIR"
|
4268
|
+
elif add_dk:
|
4269
|
+
zs = absreal(fspath)
|
4270
|
+
margin = '<a href="%s?k=%s&zip" rel="nofollow">zip</a>' % (
|
4271
|
+
quotep(href),
|
4272
|
+
self.gen_fk(2, self.args.dk_salt, zs, 0, 0)[:add_dk],
|
4273
|
+
)
|
4211
4274
|
else:
|
4212
4275
|
margin = '<a href="%s?zip" rel="nofollow">zip</a>' % (quotep(href),)
|
4213
4276
|
elif fn in hist:
|
@@ -4248,6 +4311,11 @@ class HttpCli(object):
|
|
4248
4311
|
0 if ANYWIN else inf.st_ino,
|
4249
4312
|
)[:add_fk],
|
4250
4313
|
)
|
4314
|
+
elif add_dk and is_dir:
|
4315
|
+
href = "%s?k=%s" % (
|
4316
|
+
quotep(href),
|
4317
|
+
self.gen_fk(2, self.args.dk_salt, fspath, 0, 0)[:add_dk],
|
4318
|
+
)
|
4251
4319
|
else:
|
4252
4320
|
href = quotep(href)
|
4253
4321
|
|
@@ -4266,6 +4334,9 @@ class HttpCli(object):
|
|
4266
4334
|
files.append(item)
|
4267
4335
|
item["rd"] = rem
|
4268
4336
|
|
4337
|
+
if is_dk and not vf.get("dks"):
|
4338
|
+
dirs = []
|
4339
|
+
|
4269
4340
|
if (
|
4270
4341
|
self.cookies.get("idxh") == "y"
|
4271
4342
|
and "ls" not in self.uparam
|
copyparty/mtag.py
CHANGED
@@ -545,8 +545,7 @@ class MTag(object):
|
|
545
545
|
pypath = str(os.pathsep.join(zsl))
|
546
546
|
env["PYTHONPATH"] = pypath
|
547
547
|
except:
|
548
|
-
|
549
|
-
raise
|
548
|
+
raise # might be expected outside cpython
|
550
549
|
|
551
550
|
ret = {}
|
552
551
|
for tagname, parser in sorted(parsers.items(), key=lambda x: (x[1].pri, x[0])):
|
copyparty/sutil.py
CHANGED
@@ -75,7 +75,9 @@ def enthumb(
|
|
75
75
|
) :
|
76
76
|
rem = f["vp"]
|
77
77
|
ext = rem.rsplit(".", 1)[-1].lower()
|
78
|
-
if fmt == "
|
78
|
+
if (fmt == "mp3" and ext == "mp3") or (
|
79
|
+
fmt == "opus" and ext in "aac|m4a|mp3|ogg|opus|wma".split("|")
|
80
|
+
):
|
79
81
|
raise Exception()
|
80
82
|
|
81
83
|
vp = vjoin(vtop, rem.split("/", 1)[1])
|
copyparty/svchub.py
CHANGED
@@ -270,6 +270,11 @@ class SvcHub(object):
|
|
270
270
|
if want_ff and ANYWIN:
|
271
271
|
self.log("thumb", "download FFmpeg to fix it:\033[0m " + FFMPEG_URL, 3)
|
272
272
|
|
273
|
+
if not args.no_acode:
|
274
|
+
if not re.match("^(0|[qv][0-9]|[0-9]{2,3}k)$", args.q_mp3.lower()):
|
275
|
+
t = "invalid mp3 transcoding quality [%s] specified; only supports [0] to disable, a CBR value such as [192k], or a CQ/CRF value such as [v2]"
|
276
|
+
raise Exception(t % (args.q_mp3,))
|
277
|
+
|
273
278
|
args.th_poke = min(args.th_poke, args.th_maxage, args.ac_maxage)
|
274
279
|
|
275
280
|
zms = ""
|
copyparty/th_cli.py
CHANGED
copyparty/th_srv.py
CHANGED
@@ -106,7 +106,7 @@ def thumb_path(histpath , rem , mtime , fmt , ffa ) :
|
|
106
106
|
h = hashlib.sha512(afsenc(fn)).digest()
|
107
107
|
fn = base64.urlsafe_b64encode(h).decode("ascii")[:24]
|
108
108
|
|
109
|
-
if fmt in ("opus", "caf"):
|
109
|
+
if fmt in ("opus", "caf", "mp3"):
|
110
110
|
cat = "ac"
|
111
111
|
else:
|
112
112
|
fc = fmt[:1]
|
@@ -304,6 +304,8 @@ class ThumbSrv(object):
|
|
304
304
|
elif lib == "ff" and ext in self.fmt_ffa:
|
305
305
|
if tpath.endswith(".opus") or tpath.endswith(".caf"):
|
306
306
|
funs.append(self.conv_opus)
|
307
|
+
elif tpath.endswith(".mp3"):
|
308
|
+
funs.append(self.conv_mp3)
|
307
309
|
elif tpath.endswith(".png"):
|
308
310
|
funs.append(self.conv_waves)
|
309
311
|
png_ok = True
|
@@ -634,8 +636,47 @@ class ThumbSrv(object):
|
|
634
636
|
cmd += [fsenc(tpath)]
|
635
637
|
self._run_ff(cmd, vn)
|
636
638
|
|
639
|
+
def conv_mp3(self, abspath , tpath , fmt , vn ) :
|
640
|
+
quality = self.args.q_mp3.lower()
|
641
|
+
if self.args.no_acode or not quality:
|
642
|
+
raise Exception("disabled in server config")
|
643
|
+
|
644
|
+
self.wait4ram(0.2, tpath)
|
645
|
+
ret, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
646
|
+
if "ac" not in ret:
|
647
|
+
raise Exception("not audio")
|
648
|
+
|
649
|
+
if quality.endswith("k"):
|
650
|
+
qk = b"-b:a"
|
651
|
+
qv = quality.encode("ascii")
|
652
|
+
else:
|
653
|
+
qk = b"-q:a"
|
654
|
+
qv = quality[1:].encode("ascii")
|
655
|
+
|
656
|
+
# extremely conservative choices for output format
|
657
|
+
# (always 2ch 44k1) because if a device is old enough
|
658
|
+
# to not support opus then it's probably also super picky
|
659
|
+
|
660
|
+
# fmt: off
|
661
|
+
cmd = [
|
662
|
+
b"ffmpeg",
|
663
|
+
b"-nostdin",
|
664
|
+
b"-v", b"error",
|
665
|
+
b"-hide_banner",
|
666
|
+
b"-i", fsenc(abspath),
|
667
|
+
b"-map_metadata", b"-1",
|
668
|
+
b"-map", b"0:a:0",
|
669
|
+
b"-ar", b"44100",
|
670
|
+
b"-ac", b"2",
|
671
|
+
b"-c:a", b"libmp3lame",
|
672
|
+
qk, qv,
|
673
|
+
fsenc(tpath)
|
674
|
+
]
|
675
|
+
# fmt: on
|
676
|
+
self._run_ff(cmd, vn, oom=300)
|
677
|
+
|
637
678
|
def conv_opus(self, abspath , tpath , fmt , vn ) :
|
638
|
-
if self.args.no_acode:
|
679
|
+
if self.args.no_acode or not self.args.q_opus:
|
639
680
|
raise Exception("disabled in server config")
|
640
681
|
|
641
682
|
self.wait4ram(0.2, tpath)
|
@@ -659,6 +700,7 @@ class ThumbSrv(object):
|
|
659
700
|
pass
|
660
701
|
|
661
702
|
caf_src = abspath if src_opus else tmp_opus
|
703
|
+
bq = ("%dk" % (self.args.q_opus,)).encode("ascii")
|
662
704
|
|
663
705
|
if not want_caf or not src_opus:
|
664
706
|
# fmt: off
|
@@ -671,7 +713,7 @@ class ThumbSrv(object):
|
|
671
713
|
b"-map_metadata", b"-1",
|
672
714
|
b"-map", b"0:a:0",
|
673
715
|
b"-c:a", b"libopus",
|
674
|
-
b"-b:a",
|
716
|
+
b"-b:a", bq,
|
675
717
|
fsenc(tmp_opus)
|
676
718
|
]
|
677
719
|
# fmt: on
|
@@ -694,7 +736,7 @@ class ThumbSrv(object):
|
|
694
736
|
b"-map_metadata", b"-1",
|
695
737
|
b"-ac", b"2",
|
696
738
|
b"-c:a", b"libopus",
|
697
|
-
b"-b:a",
|
739
|
+
b"-b:a", bq,
|
698
740
|
b"-f", b"caf",
|
699
741
|
fsenc(tpath)
|
700
742
|
]
|
@@ -768,7 +810,7 @@ class ThumbSrv(object):
|
|
768
810
|
|
769
811
|
def _clean(self, cat , thumbpath ) :
|
770
812
|
# self.log("cln {}".format(thumbpath))
|
771
|
-
exts = ["jpg", "webp", "png"] if cat == "th" else ["opus", "caf"]
|
813
|
+
exts = ["jpg", "webp", "png"] if cat == "th" else ["opus", "caf", "mp3"]
|
772
814
|
maxage = getattr(self.args, cat + "_maxage")
|
773
815
|
now = time.time()
|
774
816
|
prev_b64 = None
|
copyparty/web/baguettebox.js.gz
CHANGED
Binary file
|
copyparty/web/browser.css.gz
CHANGED
Binary file
|
copyparty/web/browser.js.gz
CHANGED
Binary file
|
Binary file
|
copyparty/web/deps/marked.js.gz
CHANGED
Binary file
|
copyparty/web/md.css.gz
CHANGED
Binary file
|
copyparty/web/md.js.gz
CHANGED
Binary file
|
copyparty/web/md2.css.gz
CHANGED
Binary file
|
copyparty/web/md2.js.gz
CHANGED
Binary file
|
copyparty/web/mde.css.gz
CHANGED
Binary file
|
copyparty/web/mde.js.gz
CHANGED
Binary file
|
copyparty/web/msg.css.gz
CHANGED
Binary file
|
copyparty/web/splash.css.gz
CHANGED
Binary file
|
copyparty/web/splash.js.gz
CHANGED
Binary file
|
copyparty/web/ui.css.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.1
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.12.0
|
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
|
@@ -148,6 +148,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
148
148
|
* [gotchas](#gotchas) - behavior that might be unexpected
|
149
149
|
* [cors](#cors) - cross-site request config
|
150
150
|
* [filekeys](#filekeys) - prevent filename bruteforcing
|
151
|
+
* [dirkeys](#dirkeys) - share specific folders in a volume
|
151
152
|
* [password hashing](#password-hashing) - you can hash passwords
|
152
153
|
* [https](#https) - both HTTP and HTTPS are accepted
|
153
154
|
* [recovering from crashes](#recovering-from-crashes)
|
@@ -253,7 +254,7 @@ firewall-cmd --reload
|
|
253
254
|
* browser
|
254
255
|
* ☑ [navpane](#navpane) (directory tree sidebar)
|
255
256
|
* ☑ file manager (cut/paste, delete, [batch-rename](#batch-rename))
|
256
|
-
* ☑ audio player (with [OS media controls](https://user-images.githubusercontent.com/241032/215347492-b4250797-6c90-4e09-9a4c-721edf2fb15c.png) and opus transcoding)
|
257
|
+
* ☑ audio player (with [OS media controls](https://user-images.githubusercontent.com/241032/215347492-b4250797-6c90-4e09-9a4c-721edf2fb15c.png) and opus/mp3 transcoding)
|
257
258
|
* ☑ image gallery with webm player
|
258
259
|
* ☑ textfile browser with syntax hilighting
|
259
260
|
* ☑ [thumbnails](#thumbnails)
|
@@ -641,7 +642,7 @@ you can also zip a selection of files or folders by clicking them in the browser
|
|
641
642
|
|
642
643
|

|
643
644
|
|
644
|
-
cool trick: download a folder by appending url-params `?tar&opus` to transcode all audio files (except aac|m4a|mp3|ogg|opus|wma) to opus before they're added to the archive
|
645
|
+
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
|
645
646
|
* 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
|
646
647
|
* and url-params `&j` / `&w` produce jpeg/webm thumbnails/spectrograms instead of the original audio/video/images
|
647
648
|
* can also be used to pregenerate thumbnails; combine with `--th-maxage=9999999` or `--th-clean=0`
|
@@ -832,9 +833,9 @@ open the `[🎺]` media-player-settings tab to configure it,
|
|
832
833
|
* `[loop]` keeps looping the folder
|
833
834
|
* `[next]` plays into the next folder
|
834
835
|
* "transcode":
|
835
|
-
* `[flac]` converts `flac` and `wav` files into opus
|
836
|
-
* `[aac]` converts `aac` and `m4a` files into opus
|
837
|
-
* `[oth]` converts all other known formats into opus
|
836
|
+
* `[flac]` converts `flac` and `wav` files into opus (if supported by browser) or mp3
|
837
|
+
* `[aac]` converts `aac` and `m4a` files into opus (if supported by browser) or mp3
|
838
|
+
* `[oth]` converts all other known formats into opus (if supported by browser) or mp3
|
838
839
|
* `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`
|
839
840
|
* "tint" reduces the contrast of the playback bar
|
840
841
|
|
@@ -1893,12 +1894,29 @@ cors can be configured with `--acao` and `--acam`, or the protections entirely d
|
|
1893
1894
|
|
1894
1895
|
prevent filename bruteforcing
|
1895
1896
|
|
1896
|
-
volflag `
|
1897
|
+
volflag `fk` generates filekeys (per-file accesskeys) for all files; users which have full read-access (permission `r`) will then see URLs with the correct filekey `?k=...` appended to the end, and `g` users must provide that URL including the correct key to avoid a 404
|
1897
1898
|
|
1898
1899
|
by default, filekeys are generated based on salt (`--fk-salt`) + filesystem-path + file-size + inode (if not windows); add volflag `fka` to generate slightly weaker filekeys which will not be invalidated if the file is edited (only salt + path)
|
1899
1900
|
|
1900
1901
|
permissions `wG` (write + upget) lets users upload files and receive their own filekeys, still without being able to see other uploads
|
1901
1902
|
|
1903
|
+
### dirkeys
|
1904
|
+
|
1905
|
+
share specific folders in a volume without giving away full read-access to the rest -- the visitor only needs the `g` (get) permission to view the link
|
1906
|
+
|
1907
|
+
volflag `dk` generates dirkeys (per-directory accesskeys) for all folders, granting read-access to that folder; by default only that folder itself, no subfolders
|
1908
|
+
|
1909
|
+
volflag `dky` disables the actual key-check, meaning anyone can see the contents of a folder where they have `g` access, but not its subdirectories
|
1910
|
+
|
1911
|
+
* `dk` + `dky` gives the same behavior as if all users with `g` access have full read-access, but subfolders are hidden files (their names start with a dot), so `dky` is an alternative to renaming all the folders for that purpose, maybe just for some users
|
1912
|
+
|
1913
|
+
volflag `dks` lets people enter subfolders as well, and also enables download-as-zip/tar
|
1914
|
+
|
1915
|
+
dirkeys are generated based on another salt (`--dk-salt`) + filesystem-path and have a few limitations:
|
1916
|
+
* the key does not change if the contents of the folder is modified
|
1917
|
+
* if you need a new dirkey, either change the salt or rename the folder
|
1918
|
+
* linking to a textfile (so it opens in the textfile viewer) is not possible if recipient doesn't have read-access
|
1919
|
+
|
1902
1920
|
|
1903
1921
|
## password hashing
|
1904
1922
|
|
@@ -1,7 +1,7 @@
|
|
1
|
-
copyparty/__init__.py,sha256=
|
2
|
-
copyparty/__main__.py,sha256=
|
3
|
-
copyparty/__version__.py,sha256=
|
4
|
-
copyparty/authsrv.py,sha256=
|
1
|
+
copyparty/__init__.py,sha256=fUINM1abqDGzCCH_JcXdOnLdKOV-SrTI2Xo2QgQW2P4,1703
|
2
|
+
copyparty/__main__.py,sha256=e9hqYxGdNJDzebB6wcUoc0J_MJ7QjDFOd8U0lHMg6so,94463
|
3
|
+
copyparty/__version__.py,sha256=jARmXj1ooaOsD7tTXj7PQut35A5JlMrdGV1wNuozsHs,250
|
4
|
+
copyparty/authsrv.py,sha256=qHC_sSExXnd4LLKdSKQM5RA_Xrvl2zdNT_TZRJhA454,84436
|
5
5
|
copyparty/broker_mp.py,sha256=4mEZC5tiHUazJMgYuwInNo2dxS7jrbzrGb1qs2UBt9k,3948
|
6
6
|
copyparty/broker_mpw.py,sha256=4ZI7bJYOwUibeAJVv9_FPGNmHrr9eOtkj_Kz0JEppTU,3197
|
7
7
|
copyparty/broker_thr.py,sha256=eKr--HJGig5zqvNGwH9UoBG9Nvi9mT2axrRmJwknd0s,1759
|
@@ -11,25 +11,25 @@ copyparty/cfg.py,sha256=CQKSyixyhxeM2narwbOfjUsjn0DZ_VYXbdMSa_tv4gw,9189
|
|
11
11
|
copyparty/dxml.py,sha256=lZpg-kn-kQsXRtNY1n6fRaS-b7uXzMCyv8ovKnhZcZc,1548
|
12
12
|
copyparty/fsutil.py,sha256=c4fTvmclKbVABNsjU4rGddsjCgRwi9YExAyo-06ATc8,3932
|
13
13
|
copyparty/ftpd.py,sha256=OIExjfqOEw-Y_ygez6cIZUQec4SFOmoxEH_WOVvw-aE,15961
|
14
|
-
copyparty/httpcli.py,sha256=
|
14
|
+
copyparty/httpcli.py,sha256=oxMEY8rzE-f6LN2JlzvqY-dc7v85swecpFffvHjEqck,148735
|
15
15
|
copyparty/httpconn.py,sha256=gLOURB2Nb1w6n2ihGBspEnzEfUND9Osa4klzYuAbgzI,6829
|
16
16
|
copyparty/httpsrv.py,sha256=af6LdApfj-Q4mWC5mVQjhnyrFzNy8_bXK3VUe0xKkeY,16368
|
17
17
|
copyparty/ico.py,sha256=AYHdK6NlYBfBgafVYXia3jHQ9XHZdUL1D8WftLMAzIU,3545
|
18
18
|
copyparty/mdns.py,sha256=CcraggbDxTT1ntYzD8Ebgqmw5Q4HkyZcfh5ymtCV_ak,17469
|
19
19
|
copyparty/metrics.py,sha256=OqXFkAuoVhayGAGd_Sv-OQ9SVmdXYV8M7CxitkzE3lo,8854
|
20
|
-
copyparty/mtag.py,sha256=
|
20
|
+
copyparty/mtag.py,sha256=wiXd26ZSYgOu4lkRDn4KLaqo6H2V7cpqUMepTHTCfKE,16851
|
21
21
|
copyparty/multicast.py,sha256=4N_NQVIrGSF5ZNWYdpAfYHoZefkSowN3w1GfWAxhTL0,12291
|
22
22
|
copyparty/pwhash.py,sha256=D82y8emnwpHDQq7Cr8lNuppHshbNA9ptcR2XsGOOk6E,3937
|
23
23
|
copyparty/smbd.py,sha256=iACj5pbiKsX7bVu20BK3ebPQLB_qA7WS2l-ytrSfT3Y,14054
|
24
24
|
copyparty/ssdp.py,sha256=H6ZftXttydcnBxcg2-Prm4P-XiybgT3xiJRUXU1pbrE,6343
|
25
25
|
copyparty/star.py,sha256=K4NuzyfT4956uoW6GJSQ2II-JsSV57apQZwRZ4mjFoo,3790
|
26
|
-
copyparty/sutil.py,sha256=
|
27
|
-
copyparty/svchub.py,sha256=
|
26
|
+
copyparty/sutil.py,sha256=_G4TM0YFa1vXzhRypHJ88QBdZWtYgDbom4CZjGvGIwc,3074
|
27
|
+
copyparty/svchub.py,sha256=mf101Y51z4H86DYjJ0YD1LOWzygTKdHWevUNaQUOGR8,31701
|
28
28
|
copyparty/szip.py,sha256=631TsEwGKV22yAnusJtvE-9fGFWr61HPGBinu-jk1QA,8591
|
29
29
|
copyparty/tcpsrv.py,sha256=2LGUqOBAIrsmL-1pwrbsPXR71gutHccqRp-hjzt91Us,17289
|
30
30
|
copyparty/tftpd.py,sha256=7EHAZ9LnjAXupwRNIENJ2eA8Q0lFynnwwbziV3fyzns,13157
|
31
|
-
copyparty/th_cli.py,sha256=
|
32
|
-
copyparty/th_srv.py,sha256=
|
31
|
+
copyparty/th_cli.py,sha256=eSW7sBiaZAsh_XffXFzb035CTSbS3J3Q0G-BMzQGuSY,4385
|
32
|
+
copyparty/th_srv.py,sha256=X_nQB7YlwPuj5_4v2jVGh7H2UtNgf_lDAD8aBjcmJag,27161
|
33
33
|
copyparty/u2idx.py,sha256=JBEqKX1ZM8GIvQrDYb5VQ_5QiFNFsjWF6H9drHlPVEY,12709
|
34
34
|
copyparty/up2k.py,sha256=834EGhamt6oXmdHdHS9BxG2LiHm4Q2Ax3NpUv8DbMr4,140723
|
35
35
|
copyparty/util.py,sha256=sClrlGCL-sGVSYAuMAFR-SNXIIj5T2Tl12y6XLR_ikE,81016
|
@@ -54,31 +54,31 @@ copyparty/stolen/ifaddr/__init__.py,sha256=_BUN7eM5oD2Jgib6B22tEFSb20fD9urNPPaAl
|
|
54
54
|
copyparty/stolen/ifaddr/_posix.py,sha256=-67NdfGrCktfQPakT2fLbjl2U00QMvyBGkSvrUuTOrU,2626
|
55
55
|
copyparty/stolen/ifaddr/_shared.py,sha256=cJACl8cOxQ-HSYphZTzKMAjAx_TAFyJwUPjfD102Xqw,6111
|
56
56
|
copyparty/stolen/ifaddr/_win32.py,sha256=EE-QyoBgeB7lYQ6z62VjXNaRozaYfCkaJBHGNA8QtZM,4026
|
57
|
-
copyparty/web/baguettebox.js.gz,sha256=
|
58
|
-
copyparty/web/browser.css.gz,sha256=
|
57
|
+
copyparty/web/baguettebox.js.gz,sha256=zf2x1n512edRty2zjP6L3-N_jpb2pMWDOjuLFynNyM8,7669
|
58
|
+
copyparty/web/browser.css.gz,sha256=dvCrWbY4MswsS5tmDR50eaWk1rhsAecos3x8iKd3v6w,11434
|
59
59
|
copyparty/web/browser.html,sha256=uAejLJd11rV_tQx3h2nHnJ1XY6zn1JV-meIAv74Lc8o,4873
|
60
|
-
copyparty/web/browser.js.gz,sha256=
|
60
|
+
copyparty/web/browser.js.gz,sha256=hzmgXRCwVPg7rxWnOCGcSsAmOYFjVpgztWU0Yo4639g,67267
|
61
61
|
copyparty/web/browser2.html,sha256=ciQlgr9GWuIapdsRBFNRvRFvN5T_5n920LqDMbsj5-g,1605
|
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
|
-
copyparty/web/md.css.gz,sha256=
|
64
|
+
copyparty/web/md.css.gz,sha256=UZpN0J7ubVM05CZkbZYkQRJeGgJt_GNDEzKTGSQd8h4,2032
|
65
65
|
copyparty/web/md.html,sha256=qnJpj_5-MoVYr9j5Rhy7dS40wctqdWbjELmNa-J9cUY,4110
|
66
|
-
copyparty/web/md.js.gz,sha256=
|
67
|
-
copyparty/web/md2.css.gz,sha256=
|
68
|
-
copyparty/web/md2.js.gz,sha256=
|
69
|
-
copyparty/web/mde.css.gz,sha256=
|
66
|
+
copyparty/web/md.js.gz,sha256=AHRQ3a-PZq_UiGh4CjNwXRllJCvA0IqqYmeHhFWhCig,4179
|
67
|
+
copyparty/web/md2.css.gz,sha256=uIVHKScThdbcfhXNSHgKZnALYpxbnXC-WuEzOJ20Lpc,699
|
68
|
+
copyparty/web/md2.js.gz,sha256=8xLixaTfTXC808538OOSLhp9AqKowYaunjDeBsbiBEw,8350
|
69
|
+
copyparty/web/mde.css.gz,sha256=2SkAEDKIRPqywNJ8t_heQaeBQ_R73Rf-pQI_bDoKF6o,942
|
70
70
|
copyparty/web/mde.html,sha256=FMMq4ySXoOrQV5E836KmQCry3COOhMu0DSstAdJZL_g,1678
|
71
|
-
copyparty/web/mde.js.gz,sha256=
|
72
|
-
copyparty/web/msg.css.gz,sha256=
|
71
|
+
copyparty/web/mde.js.gz,sha256=kN2eUSvr4mFuksfK4-4LimJmWdwsao39Sea2lWtu8L0,2224
|
72
|
+
copyparty/web/msg.css.gz,sha256=u90fXYAVrMD-jqwf5XFVC1ptSpSHZUe8Mez6PX101P8,300
|
73
73
|
copyparty/web/msg.html,sha256=XDg51WLO7RruZnoFnKpeJ33k47-tBHP3bR7l55Jwre4,896
|
74
|
-
copyparty/web/splash.css.gz,sha256=
|
74
|
+
copyparty/web/splash.css.gz,sha256=1IV-0cAs-RNCnvHu-wbSVzKMCkHIblManOLB8d3xcqk,949
|
75
75
|
copyparty/web/splash.html,sha256=KFAvTnofyVURBQyTnrYwuDdEKN_iGtvdjicn5b33tL8,3822
|
76
|
-
copyparty/web/splash.js.gz,sha256=
|
76
|
+
copyparty/web/splash.js.gz,sha256=2R8UYlAN8WpIABg8clgWckWqgD8nKtz3eGZFu2y1g88,1420
|
77
77
|
copyparty/web/svcs.html,sha256=s7vUSrCrELC3iTemksodRBhQpssO7s4xW1vA-CX6vU8,11702
|
78
78
|
copyparty/web/svcs.js.gz,sha256=k81ZvZ3I-f4fMHKrNGGOgOlvXnCBz0mVjD-8mieoWCA,520
|
79
|
-
copyparty/web/ui.css.gz,sha256=
|
80
|
-
copyparty/web/up2k.js.gz,sha256=
|
81
|
-
copyparty/web/util.js.gz,sha256
|
79
|
+
copyparty/web/ui.css.gz,sha256=skuzZHqTU0ag5hButpQmKI9wM7ro-UJ2PnpTodTWYF4,2616
|
80
|
+
copyparty/web/up2k.js.gz,sha256=ZuxLQW8mJSvLu_Aa8fDT3F9rptuAzNDmaOLd0MeMrd8,22114
|
81
|
+
copyparty/web/util.js.gz,sha256=Ag3zWWzbHoWYQtRnn8frhl3PcUzTIjSOUQiSxQWp1M8,14330
|
82
82
|
copyparty/web/w.hash.js.gz,sha256=__hBMd5oZWfTrb8ZCJNT21isoSqyrxKE6qdaKGQVAhc,1060
|
83
83
|
copyparty/web/a/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
84
84
|
copyparty/web/a/partyfuse.py,sha256=MuRkaSuYsdfWfBFMOkbPwDXqSvNTw3sd7QhhlKCDZ8I,32311
|
@@ -90,9 +90,10 @@ copyparty/web/dd/4.png,sha256=fIwEVmtZNZtloZuVEKPKnkx3SELwRJmB3US61y7t2lI,248
|
|
90
90
|
copyparty/web/dd/5.png,sha256=Lfpu8-yOlhONuoMbygloKqQVPXSm9gjxH2gUYn5QQAE,250
|
91
91
|
copyparty/web/dd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
92
92
|
copyparty/web/deps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
93
|
+
copyparty/web/deps/busy.mp3.gz,sha256=EVphk1_HYyRKJmtpeK99vbAstF7ub1f9ndu020H8PQ8,106
|
93
94
|
copyparty/web/deps/easymde.css.gz,sha256=vWxfueI64rPikuqFj69wJBtGisqf93AheQtOZqgUI_c,3041
|
94
95
|
copyparty/web/deps/easymde.js.gz,sha256=1FykpDM7_FiL4EeZAg4Qcggjoo4PE_MBTgRcBWvjD90,77000
|
95
|
-
copyparty/web/deps/marked.js.gz,sha256=
|
96
|
+
copyparty/web/deps/marked.js.gz,sha256=ltXpV8Z7mOCygryr9tkSw1Ydo2lV0CSLxOOsTqGUDiw,22422
|
96
97
|
copyparty/web/deps/mini-fa.css.gz,sha256=CTPrNaH8OTVmxajrGP88E2MkjadY9_81TBVnd9sw9Y8,572
|
97
98
|
copyparty/web/deps/mini-fa.woff,sha256=L9DNncV2TIyvsrspMbJouvnnt7F068Hbn7YZYvN76AU,2784
|
98
99
|
copyparty/web/deps/prism.css.gz,sha256=Z_A6rJ3MN5KWnjvXaV787aTW_5DT-xjFd0YZ7_W-Krk,1468
|
@@ -101,9 +102,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
|
|
101
102
|
copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
|
102
103
|
copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
|
103
104
|
copyparty/web/deps/sha512.hw.js.gz,sha256=vqoXeracj-99Z5MfY3jK2N4WiSzYQdfjy0RnUlQDhSU,8110
|
104
|
-
copyparty-1.
|
105
|
-
copyparty-1.
|
106
|
-
copyparty-1.
|
107
|
-
copyparty-1.
|
108
|
-
copyparty-1.
|
109
|
-
copyparty-1.
|
105
|
+
copyparty-1.12.0.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
|
106
|
+
copyparty-1.12.0.dist-info/METADATA,sha256=aJEMFtvG-b_ofuoUiX9c9oD8ilOhsZyafcf7eeGpfAU,117598
|
107
|
+
copyparty-1.12.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
108
|
+
copyparty-1.12.0.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
|
109
|
+
copyparty-1.12.0.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
|
110
|
+
copyparty-1.12.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|