copyparty 1.12.2__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 +31 -4
- copyparty/__version__.py +3 -3
- copyparty/authsrv.py +14 -2
- copyparty/cfg.py +16 -2
- copyparty/fsutil.py +25 -5
- copyparty/httpcli.py +433 -18
- copyparty/httpconn.py +1 -0
- copyparty/httpsrv.py +2 -0
- copyparty/metrics.py +1 -1
- copyparty/svchub.py +5 -1
- copyparty/tcpsrv.py +6 -0
- copyparty/u2idx.py +18 -3
- copyparty/up2k.py +128 -53
- copyparty/util.py +49 -3
- copyparty/web/a/u2c.py +8 -4
- 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/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.12.2.dist-info → copyparty-1.13.1.dist-info}/METADATA +66 -9
- {copyparty-1.12.2.dist-info → copyparty-1.13.1.dist-info}/RECORD +33 -33
- {copyparty-1.12.2.dist-info → copyparty-1.13.1.dist-info}/LICENSE +0 -0
- {copyparty-1.12.2.dist-info → copyparty-1.13.1.dist-info}/WHEEL +0 -0
- {copyparty-1.12.2.dist-info → copyparty-1.13.1.dist-info}/entry_points.txt +0 -0
- {copyparty-1.12.2.dist-info → copyparty-1.13.1.dist-info}/top_level.txt +0 -0
copyparty/__main__.py
CHANGED
@@ -850,10 +850,11 @@ def add_qr(ap, tty):
|
|
850
850
|
|
851
851
|
def add_fs(ap):
|
852
852
|
ap2 = ap.add_argument_group("filesystem options")
|
853
|
-
rm_re_def = "
|
853
|
+
rm_re_def = "15/0.1" if ANYWIN else "0/0"
|
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):
|
@@ -1085,6 +1086,8 @@ def add_optouts(ap):
|
|
1085
1086
|
ap2.add_argument("--no-zip", action="store_true", help="disable download as zip/tar")
|
1086
1087
|
ap2.add_argument("--no-tarcmp", action="store_true", help="disable download as compressed tar (?tar=gz, ?tar=bz2, ?tar=xz, ?tar=gz:9, ...)")
|
1087
1088
|
ap2.add_argument("--no-lifetime", action="store_true", help="do not allow clients (or server config) to schedule an upload to be deleted after a given time")
|
1089
|
+
ap2.add_argument("--no-pipe", action="store_true", help="disable race-the-beam (lockstep download of files which are currently being uploaded) (volflag=nopipe)")
|
1090
|
+
ap2.add_argument("--no-db-ip", action="store_true", help="do not write uploader IPs into the database")
|
1088
1091
|
|
1089
1092
|
|
1090
1093
|
def add_safety(ap):
|
@@ -1210,7 +1213,7 @@ def add_db_general(ap, hcores):
|
|
1210
1213
|
ap2.add_argument("--no-hash", metavar="PTN", type=u, help="regex: disable hashing of matching absolute-filesystem-paths during e2ds folder scans (volflag=nohash)")
|
1211
1214
|
ap2.add_argument("--no-idx", metavar="PTN", type=u, default=noidx, help="regex: disable indexing of matching absolute-filesystem-paths during e2ds folder scans (volflag=noidx)")
|
1212
1215
|
ap2.add_argument("--no-dhash", action="store_true", help="disable rescan acceleration; do full database integrity check -- makes the db ~5%% smaller and bootup/rescans 3~10x slower")
|
1213
|
-
ap2.add_argument("--re-dhash", action="store_true", help="
|
1216
|
+
ap2.add_argument("--re-dhash", action="store_true", help="force a cache rebuild on startup; enable this once if it gets out of sync (should never be necessary)")
|
1214
1217
|
ap2.add_argument("--no-forget", action="store_true", help="never forget indexed files, even when deleted from disk -- makes it impossible to ever upload the same file twice -- only useful for offloading uploads to a cloud service or something (volflag=noforget)")
|
1215
1218
|
ap2.add_argument("--dbd", metavar="PROFILE", default="wal", help="database durability profile; sets the tradeoff between robustness and speed, see \033[33m--help-dbd\033[0m (volflag=dbd)")
|
1216
1219
|
ap2.add_argument("--xlink", action="store_true", help="on upload: check all volumes for dupes, not just the target volume (volflag=xlink)")
|
@@ -1248,19 +1251,38 @@ def add_txt(ap):
|
|
1248
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)")
|
1249
1252
|
|
1250
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
|
+
|
1251
1272
|
def add_ui(ap, retry):
|
1252
1273
|
ap2 = ap.add_argument_group('ui options')
|
1253
1274
|
ap2.add_argument("--grid", action="store_true", help="show grid/thumbnails by default (volflag=grid)")
|
1254
1275
|
ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language; one of the following: \033[32meng nor\033[0m")
|
1255
1276
|
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use (0..7)")
|
1256
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")
|
1257
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)")
|
1258
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)")
|
1259
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")
|
1260
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])")
|
1261
1283
|
ap2.add_argument("--js-browser", metavar="L", type=u, help="URL to additional JS to include")
|
1262
1284
|
ap2.add_argument("--css-browser", metavar="L", type=u, help="URL to additional CSS to include")
|
1263
|
-
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)")
|
1264
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)")
|
1265
1287
|
ap2.add_argument("--textfiles", metavar="CSV", type=u, default="txt,nfo,diz,cue,readme", help="file extensions to present as plaintext")
|
1266
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)")
|
@@ -1348,6 +1370,7 @@ def run_argparse(
|
|
1348
1370
|
add_hooks(ap)
|
1349
1371
|
add_stats(ap)
|
1350
1372
|
add_txt(ap)
|
1373
|
+
add_og(ap)
|
1351
1374
|
add_ui(ap, retry)
|
1352
1375
|
add_admin(ap)
|
1353
1376
|
add_logging(ap)
|
@@ -1382,12 +1405,16 @@ def run_argparse(
|
|
1382
1405
|
return ret
|
1383
1406
|
|
1384
1407
|
|
1385
|
-
def main(argv = None) :
|
1408
|
+
def main(argv = None, rsrc = None) :
|
1386
1409
|
time.strptime("19970815", "%Y%m%d") # python#7980
|
1387
1410
|
if WINDOWS:
|
1388
1411
|
os.system("rem") # enables colors
|
1389
1412
|
|
1390
1413
|
init_E(E)
|
1414
|
+
|
1415
|
+
if rsrc: # pyz
|
1416
|
+
E.mod = rsrc
|
1417
|
+
|
1391
1418
|
if argv is None:
|
1392
1419
|
argv = sys.argv
|
1393
1420
|
|
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, 13, 1)
|
4
|
+
CODENAME = "race the beam"
|
5
|
+
BUILD_DT = (2024, 5, 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
@@ -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
@@ -16,6 +16,7 @@ def vf_bmap() :
|
|
16
16
|
"no_dedup": "copydupes",
|
17
17
|
"no_dupe": "nodupe",
|
18
18
|
"no_forget": "noforget",
|
19
|
+
"no_pipe": "nopipe",
|
19
20
|
"no_robots": "norobots",
|
20
21
|
"no_thumb": "dthumb",
|
21
22
|
"no_vthumb": "dvthumb",
|
@@ -38,6 +39,9 @@ def vf_bmap() :
|
|
38
39
|
"magic",
|
39
40
|
"no_sb_md",
|
40
41
|
"no_sb_lg",
|
42
|
+
"og",
|
43
|
+
"og_no_head",
|
44
|
+
"og_s_title",
|
41
45
|
"rand",
|
42
46
|
"xdev",
|
43
47
|
"xlink",
|
@@ -60,12 +64,23 @@ def vf_vmap() :
|
|
60
64
|
}
|
61
65
|
for k in (
|
62
66
|
"dbd",
|
67
|
+
"html_head",
|
63
68
|
"lg_sbf",
|
64
69
|
"md_sbf",
|
65
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",
|
66
80
|
"mv_retry",
|
67
81
|
"rm_retry",
|
68
82
|
"sort",
|
83
|
+
"tcolor",
|
69
84
|
"unlist",
|
70
85
|
"u2abort",
|
71
86
|
"u2ts",
|
@@ -80,7 +95,6 @@ def vf_cmap() :
|
|
80
95
|
for k in (
|
81
96
|
"exp_lg",
|
82
97
|
"exp_md",
|
83
|
-
"html_head",
|
84
98
|
"mte",
|
85
99
|
"mth",
|
86
100
|
"mtp",
|
@@ -200,7 +214,7 @@ flagcats = {
|
|
200
214
|
"grid": "show grid/thumbnails by default",
|
201
215
|
"sort": "default sort order",
|
202
216
|
"unlist": "dont list files matching REGEX",
|
203
|
-
"html_head=TXT": "includes TXT in the <head
|
217
|
+
"html_head=TXT": "includes TXT in the <head>, or @PATH for file at PATH",
|
204
218
|
"robots": "allows indexing by search engines (default)",
|
205
219
|
"norobots": "kindly asks search engines to leave",
|
206
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
|