copyparty 1.13.0__py3-none-any.whl → 1.13.1__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 +27 -2
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +14 -2
- copyparty/cfg.py +15 -2
- copyparty/fsutil.py +25 -5
- copyparty/httpcli.py +263 -19
- copyparty/metrics.py +1 -1
- copyparty/svchub.py +5 -1
- copyparty/tcpsrv.py +6 -0
- copyparty/u2idx.py +18 -3
- copyparty/up2k.py +13 -1
- copyparty/util.py +12 -0
- copyparty/web/baguettebox.js.gz +0 -0
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.html +1 -1
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/md.html +1 -1
- copyparty/web/mde.html +1 -1
- copyparty/web/msg.html +1 -1
- copyparty/web/splash.html +2 -1
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/svcs.html +1 -1
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.13.0.dist-info → copyparty-1.13.1.dist-info}/METADATA +50 -6
- {copyparty-1.13.0.dist-info → copyparty-1.13.1.dist-info}/RECORD +29 -29
- {copyparty-1.13.0.dist-info → copyparty-1.13.1.dist-info}/LICENSE +0 -0
- {copyparty-1.13.0.dist-info → copyparty-1.13.1.dist-info}/WHEEL +0 -0
- {copyparty-1.13.0.dist-info → copyparty-1.13.1.dist-info}/entry_points.txt +0 -0
- {copyparty-1.13.0.dist-info → copyparty-1.13.1.dist-info}/top_level.txt +0 -0
copyparty/__main__.py
CHANGED
@@ -854,6 +854,7 @@ def add_fs(ap):
|
|
854
854
|
ap2.add_argument("--rm-retry", metavar="T/R", type=u, default=rm_re_def, help="if a file cannot be deleted because it is busy, continue trying for \033[33mT\033[0m seconds, retry every \033[33mR\033[0m seconds; disable with 0/0 (volflag=rm_retry)")
|
855
855
|
ap2.add_argument("--mv-retry", metavar="T/R", type=u, default=rm_re_def, help="if a file cannot be renamed because it is busy, continue trying for \033[33mT\033[0m seconds, retry every \033[33mR\033[0m seconds; disable with 0/0 (volflag=mv_retry)")
|
856
856
|
ap2.add_argument("--iobuf", metavar="BYTES", type=int, default=256*1024, help="file I/O buffer-size; if your volumes are on a network drive, try increasing to \033[32m524288\033[0m or even \033[32m4194304\033[0m (and let me know if that improves your performance)")
|
857
|
+
ap2.add_argument("--mtab-age", metavar="SEC", type=int, default=60, help="rebuild mountpoint cache every \033[33mSEC\033[0m to keep track of sparse-files support; keep low on servers with removable media")
|
857
858
|
|
858
859
|
|
859
860
|
def add_upload(ap):
|
@@ -1250,19 +1251,38 @@ def add_txt(ap):
|
|
1250
1251
|
ap2.add_argument("--exp-lg", metavar="V,V,V", type=u, default=DEF_EXP, help="comma/space-separated list of placeholders to expand in prologue/epilogue files (volflag=exp_lg)")
|
1251
1252
|
|
1252
1253
|
|
1254
|
+
def add_og(ap):
|
1255
|
+
ap2 = ap.add_argument_group('og / open graph / discord-embed options')
|
1256
|
+
ap2.add_argument("--og", action="store_true", help="disable hotlinking and return an html document instead; this is required by open-graph, but can also be useful on its own (volflag=og)")
|
1257
|
+
ap2.add_argument("--og-ua", metavar="RE", type=u, default="", help="only disable hotlinking / engage OG behavior if the useragent matches regex \033[33mRE\033[0m (volflag=og_ua)")
|
1258
|
+
ap2.add_argument("--og-tpl", metavar="PATH", type=u, default="", help="do not return the regular copyparty html, but instead load the jinja2 template at \033[33mPATH\033[0m (if path contains 'EXT' then EXT will be replaced with the requested file's extension) (volflag=og_tpl)")
|
1259
|
+
ap2.add_argument("--og-no-head", action="store_true", help="do not automatically add OG entries into <head> (useful if you're doing this yourself in a template or such) (volflag=og_no_head)")
|
1260
|
+
ap2.add_argument("--og-th", metavar="FMT", type=u, default="jf3", help="thumbnail format; j=jpeg, jf=jpeg-uncropped, jf3=jpeg-uncropped-large, w=webm, ... (volflag=og_th)")
|
1261
|
+
ap2.add_argument("--og-title", metavar="TXT", type=u, default="", help="fallback title if there is nothing in the \033[33m-e2t\033[0m database (volflag=og_title)")
|
1262
|
+
ap2.add_argument("--og-title-a", metavar="T", type=u, default="🎵 {{ artist }} - {{ title }}", help="audio title format; takes any metadata key (volflag=og_title_a)")
|
1263
|
+
ap2.add_argument("--og-title-v", metavar="T", type=u, default="{{ title }}", help="video title format; takes any metadata key (volflag=og_title_v)")
|
1264
|
+
ap2.add_argument("--og-title-i", metavar="T", type=u, default="{{ title }}", help="image title format; takes any metadata key (volflag=og_title_i)")
|
1265
|
+
ap2.add_argument("--og-s-title", action="store_true", help="force default title; do not read from tags (volflag=og_s_title)")
|
1266
|
+
ap2.add_argument("--og-desc", metavar="TXT", type=u, default="", help="description text; same for all files, disable with [\033[32m-\033[0m] (volflag=og_desc)")
|
1267
|
+
ap2.add_argument("--og-site", metavar="TXT", type=u, default="", help="sitename; defaults to \033[33m--name\033[0m, disable with [\033[32m-\033[0m] (volflag=og_site)")
|
1268
|
+
ap2.add_argument("--tcolor", metavar="RGB", type=u, default="333", help="accent color (3 or 6 hex digits); may also affect safari and/or android-chrome (volflag=tcolor)")
|
1269
|
+
ap2.add_argument("--uqe", action="store_true", help="query-string parceling; translate a request for \033[33m/foo/.uqe/BASE64\033[0m into \033[33m/foo?TEXT\033[0m, or \033[33m/foo/?TEXT\033[0m if the first character in \033[33mTEXT\033[0m is a slash. Automatically enabled for \033[33m--og\033[0m")
|
1270
|
+
|
1271
|
+
|
1253
1272
|
def add_ui(ap, retry):
|
1254
1273
|
ap2 = ap.add_argument_group('ui options')
|
1255
1274
|
ap2.add_argument("--grid", action="store_true", help="show grid/thumbnails by default (volflag=grid)")
|
1256
1275
|
ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language; one of the following: \033[32meng nor\033[0m")
|
1257
1276
|
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use (0..7)")
|
1258
1277
|
ap2.add_argument("--themes", metavar="NUM", type=int, default=8, help="number of themes installed")
|
1278
|
+
ap2.add_argument("--au-vol", metavar="0-100", type=int, default=50, choices=range(0, 101), help="default audio/video volume percent")
|
1259
1279
|
ap2.add_argument("--sort", metavar="C,C,C", type=u, default="href", help="default sort order, comma-separated column IDs (see header tooltips), prefix with '-' for descending. Examples: \033[32mhref -href ext sz ts tags/Album tags/.tn\033[0m (volflag=sort)")
|
1260
1280
|
ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files matching \033[33mREGEX\033[0m in file list. Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
|
1261
1281
|
ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable")
|
1262
1282
|
ap2.add_argument("--mpmc", metavar="URL", type=u, default="", help="change the mediaplayer-toggle mouse cursor; URL to a folder with {2..5}.png inside (or disable with [\033[32m.\033[0m])")
|
1263
1283
|
ap2.add_argument("--js-browser", metavar="L", type=u, help="URL to additional JS to include")
|
1264
1284
|
ap2.add_argument("--css-browser", metavar="L", type=u, help="URL to additional CSS to include")
|
1265
|
-
ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages")
|
1285
|
+
ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages; can be @PATH to send the contents of a file at PATH, and/or begin with %% to render as jinja2 template (volflag=html_head)")
|
1266
1286
|
ap2.add_argument("--ih", action="store_true", help="if a folder contains index.html, show that instead of the directory listing by default (can be changed in the client settings UI, or add ?v to URL for override)")
|
1267
1287
|
ap2.add_argument("--textfiles", metavar="CSV", type=u, default="txt,nfo,diz,cue,readme", help="file extensions to present as plaintext")
|
1268
1288
|
ap2.add_argument("--txt-max", metavar="KiB", type=int, default=64, help="max size of embedded textfiles on ?doc= (anything bigger will be lazy-loaded by JS)")
|
@@ -1350,6 +1370,7 @@ def run_argparse(
|
|
1350
1370
|
add_hooks(ap)
|
1351
1371
|
add_stats(ap)
|
1352
1372
|
add_txt(ap)
|
1373
|
+
add_og(ap)
|
1353
1374
|
add_ui(ap, retry)
|
1354
1375
|
add_admin(ap)
|
1355
1376
|
add_logging(ap)
|
@@ -1384,12 +1405,16 @@ def run_argparse(
|
|
1384
1405
|
return ret
|
1385
1406
|
|
1386
1407
|
|
1387
|
-
def main(argv = None) :
|
1408
|
+
def main(argv = None, rsrc = None) :
|
1388
1409
|
time.strptime("19970815", "%Y%m%d") # python#7980
|
1389
1410
|
if WINDOWS:
|
1390
1411
|
os.system("rem") # enables colors
|
1391
1412
|
|
1392
1413
|
init_E(E)
|
1414
|
+
|
1415
|
+
if rsrc: # pyz
|
1416
|
+
E.mod = rsrc
|
1417
|
+
|
1393
1418
|
if argv is None:
|
1394
1419
|
argv = sys.argv
|
1395
1420
|
|
copyparty/__version__.py
CHANGED
copyparty/authsrv.py
CHANGED
@@ -1435,6 +1435,7 @@ class AuthSrv(object):
|
|
1435
1435
|
elif "" not in mount:
|
1436
1436
|
# there's volumes but no root; make root inaccessible
|
1437
1437
|
vfs = VFS(self.log_func, "", "", AXS(), {})
|
1438
|
+
vfs.flags["tcolor"] = self.args.tcolor
|
1438
1439
|
vfs.flags["d2d"] = True
|
1439
1440
|
|
1440
1441
|
maxdepth = 0
|
@@ -1720,7 +1721,11 @@ class AuthSrv(object):
|
|
1720
1721
|
if self.args.e2d or "e2ds" in vol.flags:
|
1721
1722
|
vol.flags["e2d"] = True
|
1722
1723
|
|
1723
|
-
for ga, vf in [
|
1724
|
+
for ga, vf in [
|
1725
|
+
["no_hash", "nohash"],
|
1726
|
+
["no_idx", "noidx"],
|
1727
|
+
["og_ua", "og_ua"],
|
1728
|
+
]:
|
1724
1729
|
if vf in vol.flags:
|
1725
1730
|
ptn = re.compile(vol.flags.pop(vf))
|
1726
1731
|
else:
|
@@ -1766,6 +1771,13 @@ class AuthSrv(object):
|
|
1766
1771
|
t = 'volume "/%s" has invalid %stry [%s]'
|
1767
1772
|
raise Exception(t % (vol.vpath, k, vol.flags.get(k + "try")))
|
1768
1773
|
|
1774
|
+
if vol.flags.get("og"):
|
1775
|
+
self.args.uqe = True
|
1776
|
+
|
1777
|
+
zs = str(vol.flags.get("tcolor", "")).lstrip("#")
|
1778
|
+
if len(zs) == 3: # fc5 => ffcc55
|
1779
|
+
vol.flags["tcolor"] = "".join([x * 2 for x in zs])
|
1780
|
+
|
1769
1781
|
for k1, k2 in IMPLICATIONS:
|
1770
1782
|
if k1 in vol.flags:
|
1771
1783
|
vol.flags[k2] = True
|
@@ -2403,7 +2415,7 @@ def expand_config_file(
|
|
2403
2415
|
if not cnames:
|
2404
2416
|
t = "warning: tried to read config-files from folder '%s' but it does not contain any "
|
2405
2417
|
if names:
|
2406
|
-
t += ".conf files; the following files were ignored: %s"
|
2418
|
+
t += ".conf files; the following files/subfolders were ignored: %s"
|
2407
2419
|
t = t % (fp, ", ".join(names[:8]))
|
2408
2420
|
else:
|
2409
2421
|
t += "files at all"
|
copyparty/cfg.py
CHANGED
@@ -39,6 +39,9 @@ def vf_bmap() :
|
|
39
39
|
"magic",
|
40
40
|
"no_sb_md",
|
41
41
|
"no_sb_lg",
|
42
|
+
"og",
|
43
|
+
"og_no_head",
|
44
|
+
"og_s_title",
|
42
45
|
"rand",
|
43
46
|
"xdev",
|
44
47
|
"xlink",
|
@@ -61,12 +64,23 @@ def vf_vmap() :
|
|
61
64
|
}
|
62
65
|
for k in (
|
63
66
|
"dbd",
|
67
|
+
"html_head",
|
64
68
|
"lg_sbf",
|
65
69
|
"md_sbf",
|
66
70
|
"nrand",
|
71
|
+
"og_desc",
|
72
|
+
"og_site",
|
73
|
+
"og_th",
|
74
|
+
"og_title",
|
75
|
+
"og_title_a",
|
76
|
+
"og_title_v",
|
77
|
+
"og_title_i",
|
78
|
+
"og_tpl",
|
79
|
+
"og_ua",
|
67
80
|
"mv_retry",
|
68
81
|
"rm_retry",
|
69
82
|
"sort",
|
83
|
+
"tcolor",
|
70
84
|
"unlist",
|
71
85
|
"u2abort",
|
72
86
|
"u2ts",
|
@@ -81,7 +95,6 @@ def vf_cmap() :
|
|
81
95
|
for k in (
|
82
96
|
"exp_lg",
|
83
97
|
"exp_md",
|
84
|
-
"html_head",
|
85
98
|
"mte",
|
86
99
|
"mth",
|
87
100
|
"mtp",
|
@@ -201,7 +214,7 @@ flagcats = {
|
|
201
214
|
"grid": "show grid/thumbnails by default",
|
202
215
|
"sort": "default sort order",
|
203
216
|
"unlist": "dont list files matching REGEX",
|
204
|
-
"html_head=TXT": "includes TXT in the <head
|
217
|
+
"html_head=TXT": "includes TXT in the <head>, or @PATH for file at PATH",
|
205
218
|
"robots": "allows indexing by search engines (default)",
|
206
219
|
"norobots": "kindly asks search engines to leave",
|
207
220
|
"no_sb_md": "disable js sandbox for markdown files",
|
copyparty/fsutil.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
from __future__ import print_function, unicode_literals
|
3
3
|
|
4
|
+
import argparse
|
4
5
|
import os
|
5
6
|
import re
|
6
7
|
import time
|
@@ -11,20 +12,26 @@ from .bos import bos
|
|
11
12
|
from .util import chkcmd, min_ex
|
12
13
|
|
13
14
|
class Fstab(object):
|
14
|
-
def __init__(self, log ):
|
15
|
+
def __init__(self, log , args ):
|
15
16
|
self.log_func = log
|
16
17
|
|
18
|
+
self.warned = False
|
17
19
|
self.trusted = False
|
18
20
|
self.tab = None
|
21
|
+
self.oldtab = None
|
22
|
+
self.srctab = "a"
|
19
23
|
self.cache = {}
|
20
24
|
self.age = 0.0
|
25
|
+
self.maxage = args.mtab_age
|
21
26
|
|
22
27
|
def log(self, msg , c = 0) :
|
23
28
|
self.log_func("fstab", msg, c)
|
24
29
|
|
25
30
|
def get(self, path ) :
|
26
|
-
|
27
|
-
|
31
|
+
now = time.time()
|
32
|
+
if now - self.age > self.maxage or len(self.cache) > 9000:
|
33
|
+
self.age = now
|
34
|
+
self.oldtab = self.tab or self.oldtab
|
28
35
|
self.tab = None
|
29
36
|
self.cache = {}
|
30
37
|
|
@@ -69,7 +76,7 @@ class Fstab(object):
|
|
69
76
|
self.trusted = False
|
70
77
|
|
71
78
|
def build_tab(self) :
|
72
|
-
self.log("
|
79
|
+
self.log("inspecting mtab for changes")
|
73
80
|
|
74
81
|
sptn = r"^.*? on (.*) type ([^ ]+) \(.*"
|
75
82
|
if MACOS:
|
@@ -78,6 +85,7 @@ class Fstab(object):
|
|
78
85
|
ptn = re.compile(sptn)
|
79
86
|
so, _ = chkcmd(["mount"])
|
80
87
|
tab1 = []
|
88
|
+
atab = []
|
81
89
|
for ln in so.split("\n"):
|
82
90
|
m = ptn.match(ln)
|
83
91
|
if not m:
|
@@ -85,6 +93,15 @@ class Fstab(object):
|
|
85
93
|
|
86
94
|
zs1, zs2 = m.groups()
|
87
95
|
tab1.append((str(zs1), str(zs2)))
|
96
|
+
atab.append(ln)
|
97
|
+
|
98
|
+
# keep empirically-correct values if mounttab unchanged
|
99
|
+
srctab = "\n".join(sorted(atab))
|
100
|
+
if srctab == self.srctab:
|
101
|
+
self.tab = self.oldtab
|
102
|
+
return
|
103
|
+
|
104
|
+
self.log("mtab has changed; reevaluating support for sparse files")
|
88
105
|
|
89
106
|
tab1.sort(key=lambda x: (len(x[0]), x[0]))
|
90
107
|
path1, fs1 = tab1[0]
|
@@ -93,6 +110,7 @@ class Fstab(object):
|
|
93
110
|
tab.add(fs, path.lstrip("/"))
|
94
111
|
|
95
112
|
self.tab = tab
|
113
|
+
self.srctab = srctab
|
96
114
|
|
97
115
|
def relabel(self, path , nval ) :
|
98
116
|
assert self.tab
|
@@ -127,7 +145,9 @@ class Fstab(object):
|
|
127
145
|
self.trusted = True
|
128
146
|
except:
|
129
147
|
# prisonparty or other restrictive environment
|
130
|
-
|
148
|
+
if not self.warned:
|
149
|
+
self.warned = True
|
150
|
+
self.log("failed to build tab:\n{}".format(min_ex()), 3)
|
131
151
|
self.build_fallback()
|
132
152
|
|
133
153
|
assert self.tab
|
copyparty/httpcli.py
CHANGED
@@ -84,6 +84,9 @@ from .util import (
|
|
84
84
|
sanitize_vpath,
|
85
85
|
sendfile_kern,
|
86
86
|
sendfile_py,
|
87
|
+
ub64dec,
|
88
|
+
ub64enc,
|
89
|
+
ujoin,
|
87
90
|
undot,
|
88
91
|
unescape_cookie,
|
89
92
|
unquotep,
|
@@ -213,6 +216,13 @@ class HttpCli(object):
|
|
213
216
|
ka["favico"] = self.args.favico
|
214
217
|
ka["s_name"] = self.args.bname
|
215
218
|
ka["s_doctitle"] = self.args.doctitle
|
219
|
+
ka["tcolor"] = self.vn.flags["tcolor"]
|
220
|
+
|
221
|
+
zso = self.vn.flags.get("html_head")
|
222
|
+
if zso:
|
223
|
+
ka["this"] = self
|
224
|
+
self._build_html_head(zso, ka)
|
225
|
+
|
216
226
|
ka["html_head"] = self.html_head
|
217
227
|
return tpl.render(**ka) # type: ignore
|
218
228
|
|
@@ -360,6 +370,21 @@ class HttpCli(object):
|
|
360
370
|
if "&" in self.req and "?" not in self.req:
|
361
371
|
self.hint = "did you mean '?' instead of '&'"
|
362
372
|
|
373
|
+
if self.args.uqe and "/.uqe/" in self.req:
|
374
|
+
try:
|
375
|
+
vpath, query = self.req.split("?")[0].split("/.uqe/")
|
376
|
+
query = query.split("/")[0] # discard trailing junk
|
377
|
+
# (usually a "filename" to trick discord into behaving)
|
378
|
+
query = ub64dec(query.encode("utf-8")).decode("utf-8", "replace")
|
379
|
+
if query.startswith("/"):
|
380
|
+
self.req = "%s/?%s" % (vpath, query[1:])
|
381
|
+
else:
|
382
|
+
self.req = "%s?%s" % (vpath, query)
|
383
|
+
except Exception as ex:
|
384
|
+
t = "bad uqe in request [%s]: %r" % (self.req, ex)
|
385
|
+
self.loud_reply(t, status=400)
|
386
|
+
return False
|
387
|
+
|
363
388
|
# split req into vpath + uparam
|
364
389
|
uparam = {}
|
365
390
|
if "?" not in self.req:
|
@@ -426,7 +451,8 @@ class HttpCli(object):
|
|
426
451
|
cookie_pw = ""
|
427
452
|
|
428
453
|
if len(uparam) > 10 or len(cookies) > 50:
|
429
|
-
|
454
|
+
self.loud_reply("u wot m8", status=400)
|
455
|
+
return False
|
430
456
|
|
431
457
|
self.uparam = uparam
|
432
458
|
self.cookies = cookies
|
@@ -714,6 +740,31 @@ class HttpCli(object):
|
|
714
740
|
or ("; Trident/" in self.ua and not k304)
|
715
741
|
)
|
716
742
|
|
743
|
+
def _build_html_head(self, maybe_html , kv ) :
|
744
|
+
html = str(maybe_html)
|
745
|
+
is_jinja = html[:2] in "%@%"
|
746
|
+
if is_jinja:
|
747
|
+
html = html.replace("%", "", 1)
|
748
|
+
|
749
|
+
if html.startswith("@"):
|
750
|
+
with open(html[1:], "rb") as f:
|
751
|
+
html = f.read().decode("utf-8")
|
752
|
+
|
753
|
+
if html.startswith("%"):
|
754
|
+
html = html[1:]
|
755
|
+
is_jinja = True
|
756
|
+
|
757
|
+
if is_jinja:
|
758
|
+
print("applying jinja")
|
759
|
+
with self.conn.hsrv.mutex:
|
760
|
+
if html not in self.conn.hsrv.j2:
|
761
|
+
j2env = jinja2.Environment()
|
762
|
+
tpl = j2env.from_string(html)
|
763
|
+
self.conn.hsrv.j2[html] = tpl
|
764
|
+
html = self.conn.hsrv.j2[html].render(**kv)
|
765
|
+
|
766
|
+
self.html_head += html + "\n"
|
767
|
+
|
717
768
|
def send_headers(
|
718
769
|
self,
|
719
770
|
length ,
|
@@ -2247,6 +2298,10 @@ class HttpCli(object):
|
|
2247
2298
|
def handle_login(self) :
|
2248
2299
|
assert self.parser
|
2249
2300
|
pwd = self.parser.require("cppwd", 64)
|
2301
|
+
try:
|
2302
|
+
uhash = self.parser.require("uhash", 256)
|
2303
|
+
except:
|
2304
|
+
uhash = ""
|
2250
2305
|
self.parser.drop()
|
2251
2306
|
|
2252
2307
|
self.out_headerlist = [
|
@@ -2259,6 +2314,11 @@ class HttpCli(object):
|
|
2259
2314
|
|
2260
2315
|
dst += self.ourlq()
|
2261
2316
|
|
2317
|
+
uhash = uhash.lstrip("#")
|
2318
|
+
if uhash not in ("", "-"):
|
2319
|
+
dst += "&" if "?" in dst else "?"
|
2320
|
+
dst += "_=1#" + html_escape(uhash, True, True)
|
2321
|
+
|
2262
2322
|
msg = self.get_pwd_cookie(pwd)
|
2263
2323
|
html = self.j2s("msg", h1=msg, h2='<a href="' + dst + '">ack</a>', redir=dst)
|
2264
2324
|
self.reply(html.encode("utf-8"))
|
@@ -3184,7 +3244,7 @@ class HttpCli(object):
|
|
3184
3244
|
data_end = file_size
|
3185
3245
|
break
|
3186
3246
|
|
3187
|
-
if num_need != len(job["need"]):
|
3247
|
+
if num_need != len(job["need"]) and data_end - lower < 8 * M:
|
3188
3248
|
num_need = len(job["need"])
|
3189
3249
|
data_end = 0
|
3190
3250
|
for cid in job["hash"]:
|
@@ -3480,7 +3540,6 @@ class HttpCli(object):
|
|
3480
3540
|
targs = {
|
3481
3541
|
"r": self.args.SR if self.is_vproxied else "",
|
3482
3542
|
"ts": self.conn.hsrv.cachebuster(),
|
3483
|
-
"html_head": self.html_head,
|
3484
3543
|
"edit": "edit" in self.uparam,
|
3485
3544
|
"title": html_escape(self.vpath, crlf=True),
|
3486
3545
|
"lastmod": int(ts_md * 1000),
|
@@ -3491,6 +3550,13 @@ class HttpCli(object):
|
|
3491
3550
|
"md": boundary,
|
3492
3551
|
"arg_base": arg_base,
|
3493
3552
|
}
|
3553
|
+
|
3554
|
+
zfv = self.vn.flags.get("html_head")
|
3555
|
+
if zfv:
|
3556
|
+
targs["this"] = self
|
3557
|
+
self._build_html_head(zfv, targs)
|
3558
|
+
|
3559
|
+
targs["html_head"] = self.html_head
|
3494
3560
|
zs = template.render(**targs).encode("utf-8", "replace")
|
3495
3561
|
html = zs.split(boundary.encode("utf-8"))
|
3496
3562
|
if len(html) != 2:
|
@@ -3606,8 +3672,6 @@ class HttpCli(object):
|
|
3606
3672
|
self.reply(zb, mime="text/plain; charset=utf-8")
|
3607
3673
|
return True
|
3608
3674
|
|
3609
|
-
self.html_head += self.vn.flags.get("html_head", "")
|
3610
|
-
|
3611
3675
|
html = self.j2s(
|
3612
3676
|
"splash",
|
3613
3677
|
this=self,
|
@@ -3652,7 +3716,7 @@ class HttpCli(object):
|
|
3652
3716
|
return True
|
3653
3717
|
|
3654
3718
|
def set_cfg_reset(self) :
|
3655
|
-
for k in ("k304", "js", "idxh", "cppwd", "cppws"):
|
3719
|
+
for k in ("k304", "js", "idxh", "dots", "cppwd", "cppws"):
|
3656
3720
|
cookie = gencookie(k, "x", self.args.R, False)
|
3657
3721
|
self.out_headerlist.append(("Set-Cookie", cookie))
|
3658
3722
|
|
@@ -3863,7 +3927,7 @@ class HttpCli(object):
|
|
3863
3927
|
allvols = [x for x in allvols if "e2d" in x.flags]
|
3864
3928
|
|
3865
3929
|
for vol in allvols:
|
3866
|
-
cur = idx.get_cur(vol
|
3930
|
+
cur = idx.get_cur(vol)
|
3867
3931
|
if not cur:
|
3868
3932
|
continue
|
3869
3933
|
|
@@ -4078,7 +4142,17 @@ class HttpCli(object):
|
|
4078
4142
|
e2d = "e2d" in vn.flags
|
4079
4143
|
e2t = "e2t" in vn.flags
|
4080
4144
|
|
4081
|
-
|
4145
|
+
add_og = "og" in vn.flags
|
4146
|
+
if add_og:
|
4147
|
+
if "th" in self.uparam or "raw" in self.uparam:
|
4148
|
+
og_ua = add_og = False
|
4149
|
+
elif self.args.og_ua:
|
4150
|
+
og_ua = add_og = self.args.og_ua.search(self.ua)
|
4151
|
+
else:
|
4152
|
+
og_ua = False
|
4153
|
+
add_og = True
|
4154
|
+
og_fn = ""
|
4155
|
+
|
4082
4156
|
if "b" in self.uparam:
|
4083
4157
|
self.out_headers["X-Robots-Tag"] = "noindex, nofollow"
|
4084
4158
|
|
@@ -4086,13 +4160,15 @@ class HttpCli(object):
|
|
4086
4160
|
is_dk = False
|
4087
4161
|
fk_pass = False
|
4088
4162
|
icur = None
|
4089
|
-
if
|
4163
|
+
if (e2t or e2d) and (is_dir or add_og):
|
4090
4164
|
idx = self.conn.get_u2idx()
|
4091
4165
|
if idx and hasattr(idx, "p_end"):
|
4092
|
-
icur = idx.get_cur(dbv
|
4166
|
+
icur = idx.get_cur(dbv)
|
4093
4167
|
|
4094
4168
|
th_fmt = self.uparam.get("th")
|
4095
|
-
if self.can_read or (
|
4169
|
+
if self.can_read or (
|
4170
|
+
self.can_get and (vn.flags.get("dk") or "fk" not in vn.flags)
|
4171
|
+
):
|
4096
4172
|
if th_fmt is not None:
|
4097
4173
|
nothumb = "dthumb" in dbv.flags
|
4098
4174
|
if is_dir:
|
@@ -4139,7 +4215,7 @@ class HttpCli(object):
|
|
4139
4215
|
elif self.can_write and th_fmt is not None:
|
4140
4216
|
return self.tx_svg("upload\nonly")
|
4141
4217
|
|
4142
|
-
|
4218
|
+
if not self.can_read and self.can_get and self.avn:
|
4143
4219
|
axs = self.avn.axs
|
4144
4220
|
if self.uname not in axs.uhtml:
|
4145
4221
|
pass
|
@@ -4185,6 +4261,17 @@ class HttpCli(object):
|
|
4185
4261
|
self.log(t % (correct, got, self.req, abspath), 6)
|
4186
4262
|
return self.tx_404()
|
4187
4263
|
|
4264
|
+
if add_og:
|
4265
|
+
if og_ua or self.host not in self.headers.get("referer", ""):
|
4266
|
+
self.vpath, og_fn = vsplit(self.vpath)
|
4267
|
+
vpath = self.vpath
|
4268
|
+
vn, rem = self.asrv.vfs.get(self.vpath, self.uname, False, False)
|
4269
|
+
abspath = vn.dcanonical(rem)
|
4270
|
+
dbv, vrem = vn.get_dbv(rem)
|
4271
|
+
is_dir = stat.S_ISDIR(st.st_mode)
|
4272
|
+
is_dk = True
|
4273
|
+
vpnodes.pop()
|
4274
|
+
|
4188
4275
|
if (
|
4189
4276
|
(abspath.endswith(".md") or self.can_delete)
|
4190
4277
|
and "nohtml" not in vn.flags
|
@@ -4196,9 +4283,10 @@ class HttpCli(object):
|
|
4196
4283
|
):
|
4197
4284
|
return self.tx_md(vn, abspath)
|
4198
4285
|
|
4199
|
-
|
4200
|
-
|
4201
|
-
|
4286
|
+
if not add_og or not og_fn:
|
4287
|
+
return self.tx_file(
|
4288
|
+
abspath, None if st.st_size or "nopipe" in vn.flags else vn.realpath
|
4289
|
+
)
|
4202
4290
|
|
4203
4291
|
elif is_dir and not self.can_read:
|
4204
4292
|
if self._use_dirkey(abspath):
|
@@ -4245,7 +4333,11 @@ class HttpCli(object):
|
|
4245
4333
|
is_ls = "ls" in self.uparam
|
4246
4334
|
is_js = self.args.force_js or self.cookies.get("js") == "y"
|
4247
4335
|
|
4248
|
-
if
|
4336
|
+
if (
|
4337
|
+
not is_ls
|
4338
|
+
and not add_og
|
4339
|
+
and (self.ua.startswith("curl/") or self.ua.startswith("fetch"))
|
4340
|
+
):
|
4249
4341
|
self.uparam["ls"] = "v"
|
4250
4342
|
is_ls = True
|
4251
4343
|
|
@@ -4319,6 +4411,7 @@ class HttpCli(object):
|
|
4319
4411
|
"dsort": vf["sort"],
|
4320
4412
|
"dcrop": vf["crop"],
|
4321
4413
|
"dth3x": vf["th3x"],
|
4414
|
+
"dvol": self.args.au_vol,
|
4322
4415
|
"themes": self.args.themes,
|
4323
4416
|
"turbolvl": self.args.turbo,
|
4324
4417
|
"u2j": self.args.u2j,
|
@@ -4370,7 +4463,7 @@ class HttpCli(object):
|
|
4370
4463
|
|
4371
4464
|
for k in ["zip", "tar"]:
|
4372
4465
|
v = self.uparam.get(k)
|
4373
|
-
if v is not None:
|
4466
|
+
if v is not None and (not add_og or not og_fn):
|
4374
4467
|
return self.tx_zip(k, v, self.vpath, vn, rem, [])
|
4375
4468
|
|
4376
4469
|
fsroot, vfs_ls, vfs_virt = vn.ls(
|
@@ -4384,6 +4477,10 @@ class HttpCli(object):
|
|
4384
4477
|
ls_names = [x[0] for x in vfs_ls]
|
4385
4478
|
ls_names.extend(list(vfs_virt.keys()))
|
4386
4479
|
|
4480
|
+
if add_og and og_fn and not self.can_read:
|
4481
|
+
ls_names = [og_fn]
|
4482
|
+
is_js = True
|
4483
|
+
|
4387
4484
|
# check for old versions of files,
|
4388
4485
|
# [num-backups, most-recent, hist-path]
|
4389
4486
|
hist = {}
|
@@ -4445,12 +4542,14 @@ class HttpCli(object):
|
|
4445
4542
|
margin = "DIR"
|
4446
4543
|
elif add_dk:
|
4447
4544
|
zs = absreal(fspath)
|
4448
|
-
margin = '<a href="%s?k=%s&zip" rel="nofollow">zip</a>' % (
|
4545
|
+
margin = '<a href="%s?k=%s&zip=crc" rel="nofollow">zip</a>' % (
|
4449
4546
|
quotep(href),
|
4450
4547
|
self.gen_fk(2, self.args.dk_salt, zs, 0, 0)[:add_dk],
|
4451
4548
|
)
|
4452
4549
|
else:
|
4453
|
-
margin = '<a href="%s?zip" rel="nofollow">zip</a>' % (
|
4550
|
+
margin = '<a href="%s?zip=crc" rel="nofollow">zip</a>' % (
|
4551
|
+
quotep(href),
|
4552
|
+
)
|
4454
4553
|
elif fn in hist:
|
4455
4554
|
margin = '<a href="%s.hist/%s">#%s</a>' % (
|
4456
4555
|
base,
|
@@ -4594,6 +4693,9 @@ class HttpCli(object):
|
|
4594
4693
|
else:
|
4595
4694
|
taglist = list(tagset)
|
4596
4695
|
|
4696
|
+
if not files and not dirs and not readme and not logues[0] and not logues[1]:
|
4697
|
+
logues[1] = "this folder is empty"
|
4698
|
+
|
4597
4699
|
if is_ls:
|
4598
4700
|
ls_ret["dirs"] = dirs
|
4599
4701
|
ls_ret["files"] = files
|
@@ -4645,6 +4747,148 @@ class HttpCli(object):
|
|
4645
4747
|
if "mth" in vn.flags:
|
4646
4748
|
j2a["def_hcols"] = list(vn.flags["mth"])
|
4647
4749
|
|
4750
|
+
if add_og and "raw" not in self.uparam:
|
4751
|
+
j2a["this"] = self
|
4752
|
+
cgv["og_fn"] = og_fn
|
4753
|
+
if og_fn and vn.flags.get("og_tpl"):
|
4754
|
+
tpl = vn.flags["og_tpl"]
|
4755
|
+
if "EXT" in tpl:
|
4756
|
+
zs = og_fn.split(".")[-1].lower()
|
4757
|
+
tpl2 = tpl.replace("EXT", zs)
|
4758
|
+
if os.path.exists(tpl2):
|
4759
|
+
tpl = tpl2
|
4760
|
+
with self.conn.hsrv.mutex:
|
4761
|
+
if tpl not in self.conn.hsrv.j2:
|
4762
|
+
tdir, tname = os.path.split(tpl)
|
4763
|
+
j2env = jinja2.Environment()
|
4764
|
+
j2env.loader = jinja2.FileSystemLoader(tdir)
|
4765
|
+
self.conn.hsrv.j2[tpl] = j2env.get_template(tname)
|
4766
|
+
thumb = ""
|
4767
|
+
is_pic = is_vid = is_au = False
|
4768
|
+
covernames = self.args.th_coversd
|
4769
|
+
for fn in ls_names:
|
4770
|
+
if fn.lower() in covernames:
|
4771
|
+
thumb = fn
|
4772
|
+
break
|
4773
|
+
if og_fn:
|
4774
|
+
ext = og_fn.split(".")[-1].lower()
|
4775
|
+
if ext in self.thumbcli.thumbable:
|
4776
|
+
is_pic = (
|
4777
|
+
ext in self.thumbcli.fmt_pil
|
4778
|
+
or ext in self.thumbcli.fmt_vips
|
4779
|
+
or ext in self.thumbcli.fmt_ffi
|
4780
|
+
)
|
4781
|
+
is_vid = ext in self.thumbcli.fmt_ffv
|
4782
|
+
is_au = ext in self.thumbcli.fmt_ffa
|
4783
|
+
if not thumb or not is_au:
|
4784
|
+
thumb = og_fn
|
4785
|
+
file = next((x for x in files if x["name"] == og_fn), None)
|
4786
|
+
else:
|
4787
|
+
file = None
|
4788
|
+
|
4789
|
+
url_base = "%s://%s/%s" % (
|
4790
|
+
"https" if self.is_https else "http",
|
4791
|
+
self.host,
|
4792
|
+
self.args.RS + quotep(vpath),
|
4793
|
+
)
|
4794
|
+
j2a["og_is_pic"] = is_pic
|
4795
|
+
j2a["og_is_vid"] = is_vid
|
4796
|
+
j2a["og_is_au"] = is_au
|
4797
|
+
if thumb:
|
4798
|
+
fmt = vn.flags.get("og_th", "j")
|
4799
|
+
th_base = ujoin(url_base, quotep(thumb))
|
4800
|
+
query = "th=%s&cache" % (fmt,)
|
4801
|
+
query = ub64enc(query.encode("utf-8")).decode("utf-8")
|
4802
|
+
# discord looks at file extension, not content-type...
|
4803
|
+
query += "/a.jpg" if "j" in fmt else "/a.webp"
|
4804
|
+
j2a["og_thumb"] = "%s/.uqe/%s" % (th_base, query)
|
4805
|
+
|
4806
|
+
j2a["og_fn"] = og_fn
|
4807
|
+
j2a["og_file"] = file
|
4808
|
+
if og_fn:
|
4809
|
+
og_fn_q = quotep(og_fn)
|
4810
|
+
query = ub64enc(b"raw").decode("utf-8")
|
4811
|
+
if "." in og_fn:
|
4812
|
+
query += "/a.%s" % (og_fn.split(".")[-1])
|
4813
|
+
|
4814
|
+
j2a["og_url"] = ujoin(url_base, og_fn_q)
|
4815
|
+
j2a["og_raw"] = j2a["og_url"] + "/.uqe/" + query
|
4816
|
+
else:
|
4817
|
+
j2a["og_url"] = j2a["og_raw"] = url_base
|
4818
|
+
|
4819
|
+
if not vn.flags.get("og_no_head"):
|
4820
|
+
ogh = {"twitter:card": "summary"}
|
4821
|
+
|
4822
|
+
title = str(vn.flags.get("og_title") or "")
|
4823
|
+
|
4824
|
+
if thumb:
|
4825
|
+
ogh["og:image"] = j2a["og_thumb"]
|
4826
|
+
|
4827
|
+
zso = vn.flags.get("og_desc") or ""
|
4828
|
+
if zso != "-":
|
4829
|
+
ogh["og:description"] = str(zso)
|
4830
|
+
|
4831
|
+
zs = vn.flags.get("og_site") or self.args.name
|
4832
|
+
if zs not in ("", "-"):
|
4833
|
+
ogh["og:site_name"] = zs
|
4834
|
+
|
4835
|
+
tagmap = {}
|
4836
|
+
if is_au:
|
4837
|
+
title = str(vn.flags.get("og_title_a") or "")
|
4838
|
+
ogh["og:type"] = "music.song"
|
4839
|
+
ogh["og:audio"] = j2a["og_raw"]
|
4840
|
+
tagmap = {
|
4841
|
+
"artist": "og:music:musician",
|
4842
|
+
"album": "og:music:album",
|
4843
|
+
".dur": "og:music:duration",
|
4844
|
+
}
|
4845
|
+
elif is_vid:
|
4846
|
+
title = str(vn.flags.get("og_title_v") or "")
|
4847
|
+
ogh["og:type"] = "video.other"
|
4848
|
+
ogh["og:video"] = j2a["og_raw"]
|
4849
|
+
tagmap = {
|
4850
|
+
"title": "og:title",
|
4851
|
+
".dur": "og:video:duration",
|
4852
|
+
}
|
4853
|
+
elif is_pic:
|
4854
|
+
title = str(vn.flags.get("og_title_i") or "")
|
4855
|
+
ogh["twitter:card"] = "summary_large_image"
|
4856
|
+
ogh["twitter:image"] = ogh["og:image"] = j2a["og_raw"]
|
4857
|
+
|
4858
|
+
try:
|
4859
|
+
for k, v in file["tags"].items():
|
4860
|
+
zs = "{{ %s }}" % (k,)
|
4861
|
+
title = title.replace(zs, str(v))
|
4862
|
+
except:
|
4863
|
+
pass
|
4864
|
+
title = re.sub(r"\{\{ [^}]+ \}\}", "", title)
|
4865
|
+
while title.startswith(" - "):
|
4866
|
+
title = title[3:]
|
4867
|
+
while title.endswith(" - "):
|
4868
|
+
title = title[:3]
|
4869
|
+
|
4870
|
+
if vn.flags.get("og_s_title") or not title:
|
4871
|
+
title = str(vn.flags.get("og_title") or "")
|
4872
|
+
|
4873
|
+
for tag, hname in tagmap.items():
|
4874
|
+
try:
|
4875
|
+
v = file["tags"][tag]
|
4876
|
+
if not v:
|
4877
|
+
continue
|
4878
|
+
ogh[hname] = int(v) if tag == ".dur" else v
|
4879
|
+
except:
|
4880
|
+
pass
|
4881
|
+
|
4882
|
+
ogh["og:title"] = title
|
4883
|
+
|
4884
|
+
oghs = [
|
4885
|
+
'\t<meta property="%s" content="%s">'
|
4886
|
+
% (k, html_escape(str(v), True, True))
|
4887
|
+
for k, v in ogh.items()
|
4888
|
+
]
|
4889
|
+
zs = self.html_head + "\n%s\n" % ("\n".join(oghs),)
|
4890
|
+
self.html_head = zs.replace("\n\n", "\n")
|
4891
|
+
|
4648
4892
|
html = self.j2s(tpl, **j2a)
|
4649
4893
|
self.reply(html.encode("utf-8", "replace"))
|
4650
4894
|
return True
|
copyparty/metrics.py
CHANGED
copyparty/svchub.py
CHANGED
@@ -520,7 +520,7 @@ class SvcHub(object):
|
|
520
520
|
al.exp_md = odfusion(exp, al.exp_md.replace(" ", ","))
|
521
521
|
al.exp_lg = odfusion(exp, al.exp_lg.replace(" ", ","))
|
522
522
|
|
523
|
-
for k in ["no_hash", "no_idx"]:
|
523
|
+
for k in ["no_hash", "no_idx", "og_ua"]:
|
524
524
|
ptn = getattr(self.args, k)
|
525
525
|
if ptn:
|
526
526
|
setattr(self.args, k, re.compile(ptn))
|
@@ -551,6 +551,10 @@ class SvcHub(object):
|
|
551
551
|
except:
|
552
552
|
raise Exception("invalid --mv-retry [%s]" % (self.args.mv_retry,))
|
553
553
|
|
554
|
+
al.tcolor = al.tcolor.lstrip("#")
|
555
|
+
if len(al.tcolor) == 3: # fc5 => ffcc55
|
556
|
+
al.tcolor = "".join([x * 2 for x in al.tcolor])
|
557
|
+
|
554
558
|
return True
|
555
559
|
|
556
560
|
def _ipa2re(self, txt) :
|
copyparty/tcpsrv.py
CHANGED
@@ -460,6 +460,12 @@ class TcpSrv(object):
|
|
460
460
|
sys.stderr.flush()
|
461
461
|
|
462
462
|
def _qr(self, t1 , t2 ) :
|
463
|
+
t2c = {zs: zli for zs, zli in t2.items() if zs in ("127.0.0.1", "::1")}
|
464
|
+
t2b = {zs: zli for zs, zli in t2.items() if ":" in zs and zs not in t2c}
|
465
|
+
t2 = {zs: zli for zs, zli in t2.items() if zs not in t2b and zs not in t2c}
|
466
|
+
t2.update(t2b) # first ipv4, then ipv6...
|
467
|
+
t2.update(t2c) # ...and finally localhost
|
468
|
+
|
463
469
|
ip = None
|
464
470
|
ips = list(t1) + list(t2)
|
465
471
|
qri = self.args.qri
|
copyparty/u2idx.py
CHANGED
@@ -59,6 +59,17 @@ class U2idx(object):
|
|
59
59
|
def log(self, msg , c = 0) :
|
60
60
|
self.log_func("u2idx", msg, c)
|
61
61
|
|
62
|
+
def shutdown(self) :
|
63
|
+
for cur in self.cur.values():
|
64
|
+
db = cur.connection
|
65
|
+
try:
|
66
|
+
db.interrupt()
|
67
|
+
except:
|
68
|
+
pass
|
69
|
+
|
70
|
+
cur.close()
|
71
|
+
db.close()
|
72
|
+
|
62
73
|
def fsearch(
|
63
74
|
self, uname , vols , body
|
64
75
|
) :
|
@@ -78,14 +89,18 @@ class U2idx(object):
|
|
78
89
|
except:
|
79
90
|
raise Pebkac(500, min_ex())
|
80
91
|
|
81
|
-
def get_cur(self,
|
92
|
+
def get_cur(self, vn ) :
|
82
93
|
if not HAVE_SQLITE3:
|
83
94
|
return None
|
84
95
|
|
85
|
-
cur = self.cur.get(
|
96
|
+
cur = self.cur.get(vn.realpath)
|
86
97
|
if cur:
|
87
98
|
return cur
|
88
99
|
|
100
|
+
if "e2d" not in vn.flags:
|
101
|
+
return None
|
102
|
+
|
103
|
+
ptop = vn.realpath
|
89
104
|
histpath = self.asrv.vfs.histtab.get(ptop)
|
90
105
|
if not histpath:
|
91
106
|
self.log("no histpath for [{}]".format(ptop))
|
@@ -314,7 +329,7 @@ class U2idx(object):
|
|
314
329
|
ptop = vol.realpath
|
315
330
|
flags = vol.flags
|
316
331
|
|
317
|
-
cur = self.get_cur(
|
332
|
+
cur = self.get_cur(vol)
|
318
333
|
if not cur:
|
319
334
|
continue
|
320
335
|
|
copyparty/up2k.py
CHANGED
@@ -183,7 +183,7 @@ class Up2k(object):
|
|
183
183
|
t = "could not initialize sqlite3, will use in-memory registry only"
|
184
184
|
self.log(t, 3)
|
185
185
|
|
186
|
-
self.fstab = Fstab(self.log_func)
|
186
|
+
self.fstab = Fstab(self.log_func, self.args)
|
187
187
|
self.gen_fk = self._gen_fk if self.args.log_fk else gen_filekey
|
188
188
|
|
189
189
|
if self.args.hash_mt < 2:
|
@@ -4376,6 +4376,18 @@ class Up2k(object):
|
|
4376
4376
|
for x in list(self.spools):
|
4377
4377
|
self._unspool(x)
|
4378
4378
|
|
4379
|
+
for cur in self.cur.values():
|
4380
|
+
db = cur.connection
|
4381
|
+
try:
|
4382
|
+
db.interrupt()
|
4383
|
+
except:
|
4384
|
+
pass
|
4385
|
+
|
4386
|
+
cur.close()
|
4387
|
+
db.close()
|
4388
|
+
|
4389
|
+
self.registry = {}
|
4390
|
+
|
4379
4391
|
|
4380
4392
|
def up2k_chunksize(filesize ) :
|
4381
4393
|
chunksize = 1024 * 1024
|
copyparty/util.py
CHANGED
@@ -35,6 +35,9 @@ from .__init__ import ANYWIN, EXE, MACOS, PY2, TYPE_CHECKING, VT100, WINDOWS
|
|
35
35
|
from .__version__ import S_BUILD_DT, S_VERSION
|
36
36
|
from .stolen import surrogateescape
|
37
37
|
|
38
|
+
ub64dec = base64.urlsafe_b64decode
|
39
|
+
ub64enc = base64.urlsafe_b64encode
|
40
|
+
|
38
41
|
try:
|
39
42
|
from datetime import datetime, timezone
|
40
43
|
|
@@ -2010,6 +2013,7 @@ def vsplit(vpath ) :
|
|
2010
2013
|
return vpath.rsplit("/", 1) # type: ignore
|
2011
2014
|
|
2012
2015
|
|
2016
|
+
# vpath-join
|
2013
2017
|
def vjoin(rd , fn ) :
|
2014
2018
|
if rd and fn:
|
2015
2019
|
return rd + "/" + fn
|
@@ -2017,6 +2021,14 @@ def vjoin(rd , fn ) :
|
|
2017
2021
|
return rd or fn
|
2018
2022
|
|
2019
2023
|
|
2024
|
+
# url-join
|
2025
|
+
def ujoin(rd , fn ) :
|
2026
|
+
if rd and fn:
|
2027
|
+
return rd.rstrip("/") + "/" + fn.lstrip("/")
|
2028
|
+
else:
|
2029
|
+
return rd or fn
|
2030
|
+
|
2031
|
+
|
2020
2032
|
def _w8dec2(txt ) :
|
2021
2033
|
"""decodes filesystem-bytes to wtf8"""
|
2022
2034
|
return surrogateescape.decodefilename(txt)
|
copyparty/web/baguettebox.js.gz
CHANGED
Binary file
|
copyparty/web/browser.css.gz
CHANGED
Binary file
|
copyparty/web/browser.html
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
<title>{{ title }}</title>
|
7
7
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
8
8
|
<meta name="viewport" content="width=device-width, initial-scale=0.8, minimum-scale=0.6">
|
9
|
-
<meta name="theme-color" content="#
|
9
|
+
<meta name="theme-color" content="#{{ tcolor }}">
|
10
10
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
|
11
11
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/browser.css?_={{ ts }}">
|
12
12
|
{{ html_head }}
|
copyparty/web/browser.js.gz
CHANGED
Binary file
|
copyparty/web/md.html
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
<title>📝 {{ title }}</title>
|
4
4
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=0.7">
|
6
|
-
<meta name="theme-color" content="#
|
6
|
+
<meta name="theme-color" content="#{{ tcolor }}">
|
7
7
|
<link rel="stylesheet" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
|
8
8
|
<link rel="stylesheet" href="{{ r }}/.cpr/md.css?_={{ ts }}">
|
9
9
|
{%- if edit %}
|
copyparty/web/mde.html
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
<title>📝 {{ title }}</title>
|
4
4
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=0.7">
|
6
|
-
<meta name="theme-color" content="#
|
6
|
+
<meta name="theme-color" content="#{{ tcolor }}">
|
7
7
|
<link rel="stylesheet" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
|
8
8
|
<link rel="stylesheet" href="{{ r }}/.cpr/mde.css?_={{ ts }}">
|
9
9
|
<link rel="stylesheet" href="{{ r }}/.cpr/deps/mini-fa.css?_={{ ts }}">
|
copyparty/web/msg.html
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
<title>{{ s_doctitle }}</title>
|
7
7
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
8
8
|
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
9
|
-
<meta name="theme-color" content="#
|
9
|
+
<meta name="theme-color" content="#{{ tcolor }}">
|
10
10
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/msg.css?_={{ ts }}">
|
11
11
|
{{ html_head }}
|
12
12
|
</head>
|
copyparty/web/splash.html
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
<title>{{ s_doctitle }}</title>
|
7
7
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
8
8
|
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
9
|
-
<meta name="theme-color" content="#
|
9
|
+
<meta name="theme-color" content="#{{ tcolor }}">
|
10
10
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/splash.css?_={{ ts }}">
|
11
11
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
|
12
12
|
{{ html_head }}
|
@@ -95,6 +95,7 @@
|
|
95
95
|
<form method="post" enctype="multipart/form-data" action="{{ r }}/{{ qvpath }}">
|
96
96
|
<input type="hidden" name="act" value="login" />
|
97
97
|
<input type="password" name="cppwd" placeholder=" password" />
|
98
|
+
<input type="hidden" name="uhash" id="uhash" value="x" />
|
98
99
|
<input type="submit" value="Login" />
|
99
100
|
{% if ahttps %}
|
100
101
|
<a id="w" href="{{ ahttps }}">switch to https</a>
|
copyparty/web/splash.js.gz
CHANGED
Binary file
|
copyparty/web/svcs.html
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
<title>{{ s_doctitle }}</title>
|
7
7
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
8
8
|
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
9
|
-
<meta name="theme-color" content="#
|
9
|
+
<meta name="theme-color" content="#{{ tcolor }}">
|
10
10
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/splash.css?_={{ ts }}">
|
11
11
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
|
12
12
|
<style>ul{padding-left:1.3em}li{margin:.4em 0}</style>
|
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.13.
|
3
|
+
Version: 1.13.1
|
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
|
@@ -101,6 +101,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
101
101
|
* [audio equalizer](#audio-equalizer) - and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)
|
102
102
|
* [fix unreliable playback on android](#fix-unreliable-playback-on-android) - due to phone / app settings
|
103
103
|
* [markdown viewer](#markdown-viewer) - and there are *two* editors
|
104
|
+
* [markdown vars](#markdown-vars) - dynamic docs with serverside variable expansion
|
104
105
|
* [other tricks](#other-tricks)
|
105
106
|
* [searching](#searching) - search by size, date, path/name, mp3-tags, ...
|
106
107
|
* [server config](#server-config) - using arguments or config files, or a mix of both
|
@@ -114,6 +115,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
114
115
|
* [tftp server](#tftp-server) - a TFTP server (read/write) can be started using `--tftp 3969`
|
115
116
|
* [smb server](#smb-server) - unsafe, slow, not recommended for wan
|
116
117
|
* [browser ux](#browser-ux) - tweaking the ui
|
118
|
+
* [opengraph](#opengraph) - discord and social-media embeds
|
117
119
|
* [file indexing](#file-indexing) - enables dedup and music search ++
|
118
120
|
* [exclude-patterns](#exclude-patterns) - to save some time
|
119
121
|
* [filesystem guards](#filesystem-guards) - avoid traversing into other filesystems
|
@@ -162,8 +164,9 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
162
164
|
* [dependencies](#dependencies) - mandatory deps
|
163
165
|
* [optional dependencies](#optional-dependencies) - install these to enable bonus features
|
164
166
|
* [optional gpl stuff](#optional-gpl-stuff)
|
165
|
-
* [sfx](#sfx) - the self-contained "binary"
|
167
|
+
* [sfx](#sfx) - the self-contained "binary" (recommended!)
|
166
168
|
* [copyparty.exe](#copypartyexe) - download [copyparty.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty.exe) (win8+) or [copyparty32.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty32.exe) (win7+)
|
169
|
+
* [zipapp](#zipapp) - another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz)
|
167
170
|
* [install on android](#install-on-android)
|
168
171
|
* [reporting bugs](#reporting-bugs) - ideas for context to include, and where to submit them
|
169
172
|
* [devnotes](#devnotes) - for build instructions etc, see [./docs/devnotes.md](./docs/devnotes.md)
|
@@ -177,6 +180,7 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/
|
|
177
180
|
* or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
|
178
181
|
* or install [on arch](#arch-package) ╱ [on NixOS](#nixos-module) ╱ [through nix](#nix-package)
|
179
182
|
* or if you are on android, [install copyparty in termux](#install-on-android)
|
183
|
+
* or if your computer is messed up and nothing else works, [try the pyz](#zipapp)
|
180
184
|
* or if you prefer to [use docker](./scripts/docker/) 🐋 you can do that too
|
181
185
|
* docker has all deps built-in, so skip this step:
|
182
186
|
|
@@ -288,9 +292,11 @@ also see [comparison to similar software](./docs/versus.md)
|
|
288
292
|
* client support
|
289
293
|
* ☑ [folder sync](#folder-sync)
|
290
294
|
* ☑ [curl-friendly](https://user-images.githubusercontent.com/241032/215322619-ea5fd606-3654-40ad-94ee-2bc058647bb2.png)
|
295
|
+
* ☑ [opengraph](#opengraph) (discord embeds)
|
291
296
|
* markdown
|
292
297
|
* ☑ [viewer](#markdown-viewer)
|
293
298
|
* ☑ editor (sure why not)
|
299
|
+
* ☑ [variables](#markdown-vars)
|
294
300
|
|
295
301
|
PS: something missing? post any crazy ideas you've got as a [feature request](https://github.com/9001/copyparty/issues/new?assignees=9001&labels=enhancement&template=feature_request.md) or [discussion](https://github.com/9001/copyparty/discussions/new?category=ideas) 🤙
|
296
302
|
|
@@ -668,9 +674,15 @@ cool trick: download a folder by appending url-params `?tar&opus` or `?tar&mp3`
|
|
668
674
|
|
669
675
|
## uploading
|
670
676
|
|
671
|
-
drag files/folders into the web-browser to upload
|
677
|
+
drag files/folders into the web-browser to upload
|
672
678
|
|
673
|
-
|
679
|
+
dragdrop is the recommended way, but you may also:
|
680
|
+
|
681
|
+
* select some files (not folders) in your file explorer and press CTRL-V inside the browser window
|
682
|
+
* use the [command-line uploader](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy)
|
683
|
+
* upload using [curl or sharex](#client-examples)
|
684
|
+
|
685
|
+
when uploading files through dragdrop or CTRL-V, this initiates an upload using `up2k`; there are two browser-based uploaders available:
|
674
686
|
* `[🎈] bup`, the basic uploader, supports almost every browser since netscape 4.0
|
675
687
|
* `[🚀] up2k`, the good / fancy one
|
676
688
|
|
@@ -897,6 +909,13 @@ other notes,
|
|
897
909
|
* the document preview has a max-width which is the same as an A4 paper when printed
|
898
910
|
|
899
911
|
|
912
|
+
### markdown vars
|
913
|
+
|
914
|
+
dynamic docs with serverside variable expansion to replace stuff like `{{self.ip}}` with the client's IP, or `{{srv.htime}}` with the current time on the server
|
915
|
+
|
916
|
+
see [./srv/expand/](./srv/expand/) for usage and examples
|
917
|
+
|
918
|
+
|
900
919
|
## other tricks
|
901
920
|
|
902
921
|
* you can link a particular timestamp in an audio file by adding it to the URL, such as `&20` / `&20s` / `&1m20` / `&t=1:20` after the `.../#af-c8960dab`
|
@@ -1121,7 +1140,22 @@ tweaking the ui
|
|
1121
1140
|
* 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`
|
1122
1141
|
* 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`
|
1123
1142
|
|
1124
|
-
see [./docs/rice](./docs/rice) for more
|
1143
|
+
see [./docs/rice](./docs/rice) for more, including how to add stuff (css/`<meta>`/...) to the html `<head>` tag
|
1144
|
+
|
1145
|
+
|
1146
|
+
## opengraph
|
1147
|
+
|
1148
|
+
discord and social-media embeds
|
1149
|
+
|
1150
|
+
can be enabled globally with `--og` or per-volume with volflag `og`
|
1151
|
+
|
1152
|
+
note that this disables hotlinking because the opengraph spec demands it; to sneak past this intentional limitation, you can enable opengraph selectively by user-agent, for example `--og-ua '(Discord|Twitter|Slack)bot'` (or volflag `og_ua`)
|
1153
|
+
|
1154
|
+
you can also hotlink files regardless by appending `?raw` to the url
|
1155
|
+
|
1156
|
+
NOTE: because discord (and maybe others) strip query args such as `?raw` in opengraph tags, any links which require a filekey or dirkey will not work
|
1157
|
+
|
1158
|
+
if you want to entirely replace the copyparty response with your own jinja2 template, give the template filepath to `--og-tpl` or volflag `og_tpl` (all members of `HttpCli` are available through the `this` object)
|
1125
1159
|
|
1126
1160
|
|
1127
1161
|
## file indexing
|
@@ -2035,7 +2069,7 @@ these are standalone programs and will never be imported / evaluated by copypart
|
|
2035
2069
|
|
2036
2070
|
# sfx
|
2037
2071
|
|
2038
|
-
the self-contained "binary" [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py) will unpack itself and run copyparty, assuming you have python installed of course
|
2072
|
+
the self-contained "binary" (recommended!) [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py) will unpack itself and run copyparty, assuming you have python installed of course
|
2039
2073
|
|
2040
2074
|
you can reduce the sfx size by repacking it; see [./docs/devnotes.md#sfx-repack](./docs/devnotes.md#sfx-repack)
|
2041
2075
|
|
@@ -2062,6 +2096,16 @@ meanwhile [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/d
|
|
2062
2096
|
then again, if you are already into downloading shady binaries from the internet, you may also want my [minimal builds](./scripts/pyinstaller#ffmpeg) of [ffmpeg](https://ocv.me/stuff/bin/ffmpeg.exe) and [ffprobe](https://ocv.me/stuff/bin/ffprobe.exe) which enables copyparty to extract multimedia-info, do audio-transcoding, and thumbnails/spectrograms/waveforms, however it's much better to instead grab a [recent official build](https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z) every once ina while if you can afford the size
|
2063
2097
|
|
2064
2098
|
|
2099
|
+
## zipapp
|
2100
|
+
|
2101
|
+
another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz) has less features, requires python 3.7 or newer, worse compression, and more importantly is unable to benefit from more recent versions of jinja2 and such (which makes it less secure)... lots of drawbacks with this one really -- but it *may* just work if the regular sfx fails to start because the computer is messed up in certain funky ways, so it's worth a shot if all else fails
|
2102
|
+
|
2103
|
+
run it by doubleclicking it, or try typing `python copyparty.pyz` in your terminal/console/commandline/telex if that fails
|
2104
|
+
|
2105
|
+
it is a python [zipapp](https://docs.python.org/3/library/zipapp.html) meaning it doesn't have to unpack its own python code anywhere to run, so if the filesystem is busted it has a better chance of getting somewhere
|
2106
|
+
* but note that it currently still needs to extract the web-resources somewhere (they'll land in the default TEMP-folder of your OS)
|
2107
|
+
|
2108
|
+
|
2065
2109
|
# install on android
|
2066
2110
|
|
2067
2111
|
install [Termux](https://termux.com/) + its companion app `Termux:API` (see [ocv.me/termux](https://ocv.me/termux/)) and then copy-paste this into Termux (long-tap) all at once:
|
@@ -1,22 +1,22 @@
|
|
1
1
|
copyparty/__init__.py,sha256=fUINM1abqDGzCCH_JcXdOnLdKOV-SrTI2Xo2QgQW2P4,1703
|
2
|
-
copyparty/__main__.py,sha256=
|
3
|
-
copyparty/__version__.py,sha256=
|
4
|
-
copyparty/authsrv.py,sha256
|
2
|
+
copyparty/__main__.py,sha256=7fOoPuMQe4KzHAGS5K7KI35-fyj-3rERZVM0dTInOV4,98648
|
3
|
+
copyparty/__version__.py,sha256=Ev7V864ayuRrHZmn9gLsfpBYVJs05PYyOWl3-oCL_uU,254
|
4
|
+
copyparty/authsrv.py,sha256=UaWwotBSZBEst8LENH7BIaVSL7k2EnUrVzVftTEvtmU,84896
|
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
|
8
8
|
copyparty/broker_util.py,sha256=CnX_LAhQQqouONcDLtVkVlcBX3Z6pWuKDQDmmbHGEg4,1489
|
9
9
|
copyparty/cert.py,sha256=nCeDdzcCpvjPPUcxT4Oh7wvL_8zvddu4oXtbA-zOb8g,7607
|
10
|
-
copyparty/cfg.py,sha256=
|
10
|
+
copyparty/cfg.py,sha256=PSv7T4nm0_ISlijbAP7re61ucvr37xObv8_495ynPws,9584
|
11
11
|
copyparty/dxml.py,sha256=lZpg-kn-kQsXRtNY1n6fRaS-b7uXzMCyv8ovKnhZcZc,1548
|
12
|
-
copyparty/fsutil.py,sha256=
|
12
|
+
copyparty/fsutil.py,sha256=NEdhYYgQxDQ7MmgTbtjMKorikCjDls2AXVX16EH2JfQ,4613
|
13
13
|
copyparty/ftpd.py,sha256=OIExjfqOEw-Y_ygez6cIZUQec4SFOmoxEH_WOVvw-aE,15961
|
14
|
-
copyparty/httpcli.py,sha256=
|
14
|
+
copyparty/httpcli.py,sha256=nuJOPt2T9mjLLRreaCRUmpjoLN5nNkzsXJcCtzn963Q,164338
|
15
15
|
copyparty/httpconn.py,sha256=6MOQgBtOGrlVRr6ZiHBKYzkzcls-YWwaWEtqE6DweM0,6873
|
16
16
|
copyparty/httpsrv.py,sha256=Xf6wI5V25gzAoyEpiKH8VjEFwUqTzW5z8pcRfo2J40c,16421
|
17
17
|
copyparty/ico.py,sha256=AYHdK6NlYBfBgafVYXia3jHQ9XHZdUL1D8WftLMAzIU,3545
|
18
18
|
copyparty/mdns.py,sha256=CcraggbDxTT1ntYzD8Ebgqmw5Q4HkyZcfh5ymtCV_ak,17469
|
19
|
-
copyparty/metrics.py,sha256=
|
19
|
+
copyparty/metrics.py,sha256=O8qiPNDxNjub_PI8C8Qu9rBQ_z0J1mnKonqkcTeAtf4,8845
|
20
20
|
copyparty/mtag.py,sha256=wiXd26ZSYgOu4lkRDn4KLaqo6H2V7cpqUMepTHTCfKE,16851
|
21
21
|
copyparty/multicast.py,sha256=Ha27l2oATEa-Qo2WOzkeRgjAm6G_YDCfbVJWR-ao2UE,12319
|
22
22
|
copyparty/pwhash.py,sha256=D82y8emnwpHDQq7Cr8lNuppHshbNA9ptcR2XsGOOk6E,3937
|
@@ -24,15 +24,15 @@ 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
26
|
copyparty/sutil.py,sha256=_G4TM0YFa1vXzhRypHJ88QBdZWtYgDbom4CZjGvGIwc,3074
|
27
|
-
copyparty/svchub.py,sha256=
|
27
|
+
copyparty/svchub.py,sha256=9DTZLgrHRMrlnq6pc_3UngNpxtdlT2SWnhlE2dPaoBk,32419
|
28
28
|
copyparty/szip.py,sha256=631TsEwGKV22yAnusJtvE-9fGFWr61HPGBinu-jk1QA,8591
|
29
|
-
copyparty/tcpsrv.py,sha256=
|
29
|
+
copyparty/tcpsrv.py,sha256=ym6rda7svl_M4DjNesHMI1_6wO7Csu01UV1zGzXEMxI,17637
|
30
30
|
copyparty/tftpd.py,sha256=7EHAZ9LnjAXupwRNIENJ2eA8Q0lFynnwwbziV3fyzns,13157
|
31
31
|
copyparty/th_cli.py,sha256=eSW7sBiaZAsh_XffXFzb035CTSbS3J3Q0G-BMzQGuSY,4385
|
32
32
|
copyparty/th_srv.py,sha256=C2ZBE6ddINCuYDympRQQmhj0ULdlD6HOM6qNK-UB4so,27191
|
33
|
-
copyparty/u2idx.py,sha256=
|
34
|
-
copyparty/up2k.py,sha256=
|
35
|
-
copyparty/util.py,sha256=
|
33
|
+
copyparty/u2idx.py,sha256=uEUcEbye1jzGlQfEJkLtD060XA6Rv_6lXLgeg6oAU5M,13033
|
34
|
+
copyparty/up2k.py,sha256=D-E5y27lJopCtrxUjEi1-oH-kqrOnHL2hQO1Q_X6tHw,143080
|
35
|
+
copyparty/util.py,sha256=N8rIBtCX_-oEhfLURsyhf3WfrdUzsdsN4Q1qkFtYKHg,83167
|
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
|
@@ -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=
|
59
|
-
copyparty/web/browser.html,sha256
|
60
|
-
copyparty/web/browser.js.gz,sha256=
|
57
|
+
copyparty/web/baguettebox.js.gz,sha256=HdRHC_4Lvepp1DrRwusdcxvAn8IKGMdrKdggGIshKek,7869
|
58
|
+
copyparty/web/browser.css.gz,sha256=GGyPK9BBOX63x9XWqO2jrXewHtfAeZ-Jo0BBxqTOGUM,11491
|
59
|
+
copyparty/web/browser.html,sha256=-tLasq2GKe9mUceqXG4PczQ7odBMrX0qlWuyaA9SjPI,4882
|
60
|
+
copyparty/web/browser.js.gz,sha256=9w6hKIv_v8QERNxRQ5DOdZxBijStf-YE8H7SaWLbi7Y,68707
|
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
64
|
copyparty/web/md.css.gz,sha256=UZpN0J7ubVM05CZkbZYkQRJeGgJt_GNDEzKTGSQd8h4,2032
|
65
|
-
copyparty/web/md.html,sha256=
|
65
|
+
copyparty/web/md.html,sha256=35oLUnDYsAdiW7Zg-iKFEXzEl_bGbnoAxUrNgJL46_o,4119
|
66
66
|
copyparty/web/md.js.gz,sha256=AHRQ3a-PZq_UiGh4CjNwXRllJCvA0IqqYmeHhFWhCig,4179
|
67
67
|
copyparty/web/md2.css.gz,sha256=uIVHKScThdbcfhXNSHgKZnALYpxbnXC-WuEzOJ20Lpc,699
|
68
68
|
copyparty/web/md2.js.gz,sha256=8xLixaTfTXC808538OOSLhp9AqKowYaunjDeBsbiBEw,8350
|
69
69
|
copyparty/web/mde.css.gz,sha256=2SkAEDKIRPqywNJ8t_heQaeBQ_R73Rf-pQI_bDoKF6o,942
|
70
|
-
copyparty/web/mde.html,sha256=
|
70
|
+
copyparty/web/mde.html,sha256=v0MsEinom5LmZzUM-Ht26IEUkrFzMX57XpCyIQXctAg,1687
|
71
71
|
copyparty/web/mde.js.gz,sha256=kN2eUSvr4mFuksfK4-4LimJmWdwsao39Sea2lWtu8L0,2224
|
72
72
|
copyparty/web/msg.css.gz,sha256=u90fXYAVrMD-jqwf5XFVC1ptSpSHZUe8Mez6PX101P8,300
|
73
|
-
copyparty/web/msg.html,sha256=
|
73
|
+
copyparty/web/msg.html,sha256=HcBeXXpcF2JKwcj8KD3dGCvONMnTZ6lXYmm4SYgBMlA,905
|
74
74
|
copyparty/web/splash.css.gz,sha256=zgDs-SY3VrInsXeARRPcGHziVOUs-1hUtSObzybwD1g,1006
|
75
|
-
copyparty/web/splash.html,sha256=
|
76
|
-
copyparty/web/splash.js.gz,sha256=
|
77
|
-
copyparty/web/svcs.html,sha256=
|
75
|
+
copyparty/web/splash.html,sha256=z5OrfZqA5RBxeY86BJiQ5NZNHIIDHDvPlTuht-Q0v64,3917
|
76
|
+
copyparty/web/splash.js.gz,sha256=kPLyo_LaoEdRswRNHU32G4rQY8x5Jes4iAIIvK2ug9U,1442
|
77
|
+
copyparty/web/svcs.html,sha256=Lniv3ndzV1ALGOdvMNKg6za5rafrqltuwoknYbExRxM,11711
|
78
78
|
copyparty/web/svcs.js.gz,sha256=k81ZvZ3I-f4fMHKrNGGOgOlvXnCBz0mVjD-8mieoWCA,520
|
79
79
|
copyparty/web/ui.css.gz,sha256=skuzZHqTU0ag5hButpQmKI9wM7ro-UJ2PnpTodTWYF4,2616
|
80
80
|
copyparty/web/up2k.js.gz,sha256=3IKVXjZq7byJWFKyHVylIIbWozsJ6IL7CrOUCibE8BY,22114
|
81
|
-
copyparty/web/util.js.gz,sha256=
|
81
|
+
copyparty/web/util.js.gz,sha256=eNRKtW7fM9AvipRdJGQyy9mbQIqMda73o8Bo64UWr7s,14416
|
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
|
@@ -102,9 +102,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
|
|
102
102
|
copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
|
103
103
|
copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
|
104
104
|
copyparty/web/deps/sha512.hw.js.gz,sha256=vqoXeracj-99Z5MfY3jK2N4WiSzYQdfjy0RnUlQDhSU,8110
|
105
|
-
copyparty-1.13.
|
106
|
-
copyparty-1.13.
|
107
|
-
copyparty-1.13.
|
108
|
-
copyparty-1.13.
|
109
|
-
copyparty-1.13.
|
110
|
-
copyparty-1.13.
|
105
|
+
copyparty-1.13.1.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
|
106
|
+
copyparty-1.13.1.dist-info/METADATA,sha256=HMm1ZQpKi3_hfKuT0-Rowdv0kdJXGsK2AK4YU7yaW7Q,122167
|
107
|
+
copyparty-1.13.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
108
|
+
copyparty-1.13.1.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
|
109
|
+
copyparty-1.13.1.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
|
110
|
+
copyparty-1.13.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|