copyparty 1.13.0__py3-none-any.whl → 1.13.2__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 +55 -5
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +23 -2
- copyparty/broker_mp.py +2 -5
- copyparty/cfg.py +16 -2
- copyparty/fsutil.py +25 -5
- copyparty/httpcli.py +262 -20
- copyparty/httpsrv.py +1 -4
- copyparty/metrics.py +1 -1
- copyparty/mtag.py +72 -3
- copyparty/smbd.py +1 -1
- copyparty/svchub.py +14 -3
- copyparty/tcpsrv.py +6 -0
- copyparty/th_cli.py +5 -0
- copyparty/th_srv.py +32 -5
- copyparty/u2idx.py +18 -3
- copyparty/up2k.py +27 -9
- copyparty/util.py +41 -6
- copyparty/web/a/u2c.py +14 -5
- 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/deps/marked.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.2.dist-info}/METADATA +55 -8
- {copyparty-1.13.0.dist-info → copyparty-1.13.2.dist-info}/RECORD +37 -37
- {copyparty-1.13.0.dist-info → copyparty-1.13.2.dist-info}/LICENSE +0 -0
- {copyparty-1.13.0.dist-info → copyparty-1.13.2.dist-info}/WHEEL +0 -0
- {copyparty-1.13.0.dist-info → copyparty-1.13.2.dist-info}/entry_points.txt +0 -0
- {copyparty-1.13.0.dist-info → copyparty-1.13.2.dist-info}/top_level.txt +0 -0
copyparty/__main__.py
CHANGED
@@ -43,11 +43,13 @@ from .util import (
|
|
43
43
|
DEF_MTH,
|
44
44
|
IMPLICATIONS,
|
45
45
|
JINJA_VER,
|
46
|
+
MIMES,
|
46
47
|
PARTFTPY_VER,
|
47
48
|
PY_DESC,
|
48
49
|
PYFTPD_VER,
|
49
50
|
SQLITE_VER,
|
50
51
|
UNPLICATIONS,
|
52
|
+
Daemon,
|
51
53
|
align_tab,
|
52
54
|
ansi_re,
|
53
55
|
dedent,
|
@@ -464,6 +466,16 @@ def disable_quickedit() :
|
|
464
466
|
cmode(True, mode | 4)
|
465
467
|
|
466
468
|
|
469
|
+
def sfx_tpoke(top ):
|
470
|
+
files = [os.path.join(dp, p) for dp, dd, df in os.walk(top) for p in dd + df]
|
471
|
+
while True:
|
472
|
+
t = int(time.time())
|
473
|
+
for f in [top] + files:
|
474
|
+
os.utime(f, (t, t))
|
475
|
+
|
476
|
+
time.sleep(78123)
|
477
|
+
|
478
|
+
|
467
479
|
def showlic() :
|
468
480
|
p = os.path.join(E.mod, "res", "COPYING.txt")
|
469
481
|
if not os.path.exists(p):
|
@@ -814,7 +826,7 @@ def build_flags_desc():
|
|
814
826
|
v = v.replace("\n", "\n ")
|
815
827
|
ret += "\n \033[36m{}\033[35m {}".format(k, v)
|
816
828
|
|
817
|
-
return ret
|
829
|
+
return ret
|
818
830
|
|
819
831
|
|
820
832
|
# fmt: off
|
@@ -832,6 +844,8 @@ def add_general(ap, nc, srvname):
|
|
832
844
|
ap2.add_argument("--urlform", metavar="MODE", type=u, default="print,get", help="how to handle url-form POSTs; see \033[33m--help-urlform\033[0m")
|
833
845
|
ap2.add_argument("--wintitle", metavar="TXT", type=u, default="cpp @ $pub", help="server terminal title, for example [\033[32m$ip-10.1.2.\033[0m] or [\033[32m$ip-]")
|
834
846
|
ap2.add_argument("--name", metavar="TXT", type=u, default=srvname, help="server name (displayed topleft in browser and in mDNS)")
|
847
|
+
ap2.add_argument("--mime", metavar="EXT=MIME", type=u, action="append", help="map file \033[33mEXT\033[0mension to \033[33mMIME\033[0mtype, for example [\033[32mjpg=image/jpeg\033[0m]")
|
848
|
+
ap2.add_argument("--mimes", action="store_true", help="list default mimetype mapping and exit")
|
835
849
|
ap2.add_argument("--license", action="store_true", help="show licenses and exit")
|
836
850
|
ap2.add_argument("--version", action="store_true", help="show versions and exit")
|
837
851
|
|
@@ -854,6 +868,7 @@ def add_fs(ap):
|
|
854
868
|
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
869
|
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
870
|
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)")
|
871
|
+
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
872
|
|
858
873
|
|
859
874
|
def add_upload(ap):
|
@@ -1187,7 +1202,8 @@ def add_thumbnail(ap):
|
|
1187
1202
|
ap2.add_argument("--th-r-vips", metavar="T,T", type=u, default="avif,exr,fit,fits,fts,gif,hdr,heic,jp2,jpeg,jpg,jpx,jxl,nii,pfm,pgm,png,ppm,svg,tif,tiff,webp", help="image formats to decode using pyvips")
|
1188
1203
|
ap2.add_argument("--th-r-ffi", metavar="T,T", type=u, default="apng,avif,avifs,bmp,dds,dib,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,icns,ico,jp2,jpeg,jpg,jpx,jxl,pbm,pcx,pfm,pgm,png,pnm,ppm,psd,qoi,sgi,tga,tif,tiff,webp,xbm,xpm", help="image formats to decode using ffmpeg")
|
1189
1204
|
ap2.add_argument("--th-r-ffv", metavar="T,T", type=u, default="3gp,asf,av1,avc,avi,flv,h264,h265,hevc,m4v,mjpeg,mjpg,mkv,mov,mp4,mpeg,mpeg2,mpegts,mpg,mpg2,mts,nut,ogm,ogv,rm,ts,vob,webm,wmv", help="video formats to decode using ffmpeg")
|
1190
|
-
ap2.add_argument("--th-r-ffa", metavar="T,T", type=u, default="aac,ac3,aif,aiff,alac,alaw,amr,apac,ape,au,bonk,dfpwm,dts,flac,gsm,ilbc,it,m4a,mo3,mod,mp2,mp3,mpc,mptm,mt2,mulaw,ogg,okt,opus,ra,s3m,tak,tta,ulaw,wav,wma,wv,xm,xpk", help="audio formats to decode using ffmpeg")
|
1205
|
+
ap2.add_argument("--th-r-ffa", metavar="T,T", type=u, default="aac,ac3,aif,aiff,alac,alaw,amr,apac,ape,au,bonk,dfpwm,dts,flac,gsm,ilbc,it,itgz,itxz,itz,m4a,mdgz,mdxz,mdz,mo3,mod,mp2,mp3,mpc,mptm,mt2,mulaw,ogg,okt,opus,ra,s3m,s3gz,s3xz,s3z,tak,tta,ulaw,wav,wma,wv,xm,xmgz,xmxz,xmz,xpk", help="audio formats to decode using ffmpeg")
|
1206
|
+
ap2.add_argument("--au-unpk", metavar="E=F.C", type=u, default="mdz=mod.zip, mdgz=mod.gz, mdxz=mod.xz, s3z=s3m.zip, s3gz=s3m.gz, s3xz=s3m.xz, xmz=xm.zip, xmgz=xm.gz, xmxz=xm.xz, itz=it.zip, itgz=it.gz, itxz=it.xz", help="audio formats to decompress before passing to ffmpeg")
|
1191
1207
|
|
1192
1208
|
|
1193
1209
|
def add_transcoding(ap):
|
@@ -1250,19 +1266,38 @@ def add_txt(ap):
|
|
1250
1266
|
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
1267
|
|
1252
1268
|
|
1269
|
+
def add_og(ap):
|
1270
|
+
ap2 = ap.add_argument_group('og / open graph / discord-embed options')
|
1271
|
+
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)")
|
1272
|
+
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)")
|
1273
|
+
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)")
|
1274
|
+
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)")
|
1275
|
+
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)")
|
1276
|
+
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)")
|
1277
|
+
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)")
|
1278
|
+
ap2.add_argument("--og-title-v", metavar="T", type=u, default="{{ title }}", help="video title format; takes any metadata key (volflag=og_title_v)")
|
1279
|
+
ap2.add_argument("--og-title-i", metavar="T", type=u, default="{{ title }}", help="image title format; takes any metadata key (volflag=og_title_i)")
|
1280
|
+
ap2.add_argument("--og-s-title", action="store_true", help="force default title; do not read from tags (volflag=og_s_title)")
|
1281
|
+
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)")
|
1282
|
+
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)")
|
1283
|
+
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)")
|
1284
|
+
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")
|
1285
|
+
|
1286
|
+
|
1253
1287
|
def add_ui(ap, retry):
|
1254
1288
|
ap2 = ap.add_argument_group('ui options')
|
1255
1289
|
ap2.add_argument("--grid", action="store_true", help="show grid/thumbnails by default (volflag=grid)")
|
1256
1290
|
ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language; one of the following: \033[32meng nor\033[0m")
|
1257
1291
|
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use (0..7)")
|
1258
1292
|
ap2.add_argument("--themes", metavar="NUM", type=int, default=8, help="number of themes installed")
|
1293
|
+
ap2.add_argument("--au-vol", metavar="0-100", type=int, default=50, choices=range(0, 101), help="default audio/video volume percent")
|
1259
1294
|
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
1295
|
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
1296
|
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
1297
|
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
1298
|
ap2.add_argument("--js-browser", metavar="L", type=u, help="URL to additional JS to include")
|
1264
1299
|
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")
|
1300
|
+
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
1301
|
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
1302
|
ap2.add_argument("--textfiles", metavar="CSV", type=u, default="txt,nfo,diz,cue,readme", help="file extensions to present as plaintext")
|
1268
1303
|
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 +1385,7 @@ def run_argparse(
|
|
1350
1385
|
add_hooks(ap)
|
1351
1386
|
add_stats(ap)
|
1352
1387
|
add_txt(ap)
|
1388
|
+
add_og(ap)
|
1353
1389
|
add_ui(ap, retry)
|
1354
1390
|
add_admin(ap)
|
1355
1391
|
add_logging(ap)
|
@@ -1378,18 +1414,22 @@ def run_argparse(
|
|
1378
1414
|
k2 = "help_" + k.replace("-", "_")
|
1379
1415
|
if vars(ret)[k2]:
|
1380
1416
|
lprint("# %s help page (%s)" % (k, h))
|
1381
|
-
lprint(t + "\033[0m")
|
1417
|
+
lprint(t.rstrip() + "\033[0m")
|
1382
1418
|
sys.exit(0)
|
1383
1419
|
|
1384
1420
|
return ret
|
1385
1421
|
|
1386
1422
|
|
1387
|
-
def main(argv = None) :
|
1423
|
+
def main(argv = None, rsrc = None) :
|
1388
1424
|
time.strptime("19970815", "%Y%m%d") # python#7980
|
1389
1425
|
if WINDOWS:
|
1390
1426
|
os.system("rem") # enables colors
|
1391
1427
|
|
1392
1428
|
init_E(E)
|
1429
|
+
|
1430
|
+
if rsrc: # pyz
|
1431
|
+
E.mod = rsrc
|
1432
|
+
|
1393
1433
|
if argv is None:
|
1394
1434
|
argv = sys.argv
|
1395
1435
|
|
@@ -1413,9 +1453,19 @@ def main(argv = None) :
|
|
1413
1453
|
showlic()
|
1414
1454
|
sys.exit(0)
|
1415
1455
|
|
1456
|
+
if "--mimes" in argv:
|
1457
|
+
print("\n".join("%8s %s" % (k, v) for k, v in sorted(MIMES.items())))
|
1458
|
+
sys.exit(0)
|
1459
|
+
|
1416
1460
|
if EXE:
|
1417
1461
|
print("pybin: {}\n".format(pybin), end="")
|
1418
1462
|
|
1463
|
+
for n, zs in enumerate(argv):
|
1464
|
+
if zs.startswith("--sfx-tpoke="):
|
1465
|
+
Daemon(sfx_tpoke, "sfx-tpoke", (zs.split("=", 1)[1],))
|
1466
|
+
argv.pop(n)
|
1467
|
+
break
|
1468
|
+
|
1419
1469
|
ensure_locale()
|
1420
1470
|
|
1421
1471
|
ensure_webdeps()
|
copyparty/__version__.py
CHANGED
copyparty/authsrv.py
CHANGED
@@ -17,7 +17,9 @@ from .bos import bos
|
|
17
17
|
from .cfg import flagdescs, permdescs, vf_bmap, vf_cmap, vf_vmap
|
18
18
|
from .pwhash import PWHash
|
19
19
|
from .util import (
|
20
|
+
EXTS,
|
20
21
|
IMPLICATIONS,
|
22
|
+
MIMES,
|
21
23
|
SQLITE_VER,
|
22
24
|
UNPLICATIONS,
|
23
25
|
UTC,
|
@@ -1435,6 +1437,7 @@ class AuthSrv(object):
|
|
1435
1437
|
elif "" not in mount:
|
1436
1438
|
# there's volumes but no root; make root inaccessible
|
1437
1439
|
vfs = VFS(self.log_func, "", "", AXS(), {})
|
1440
|
+
vfs.flags["tcolor"] = self.args.tcolor
|
1438
1441
|
vfs.flags["d2d"] = True
|
1439
1442
|
|
1440
1443
|
maxdepth = 0
|
@@ -1720,7 +1723,11 @@ class AuthSrv(object):
|
|
1720
1723
|
if self.args.e2d or "e2ds" in vol.flags:
|
1721
1724
|
vol.flags["e2d"] = True
|
1722
1725
|
|
1723
|
-
for ga, vf in [
|
1726
|
+
for ga, vf in [
|
1727
|
+
["no_hash", "nohash"],
|
1728
|
+
["no_idx", "noidx"],
|
1729
|
+
["og_ua", "og_ua"],
|
1730
|
+
]:
|
1724
1731
|
if vf in vol.flags:
|
1725
1732
|
ptn = re.compile(vol.flags.pop(vf))
|
1726
1733
|
else:
|
@@ -1766,6 +1773,13 @@ class AuthSrv(object):
|
|
1766
1773
|
t = 'volume "/%s" has invalid %stry [%s]'
|
1767
1774
|
raise Exception(t % (vol.vpath, k, vol.flags.get(k + "try")))
|
1768
1775
|
|
1776
|
+
if vol.flags.get("og"):
|
1777
|
+
self.args.uqe = True
|
1778
|
+
|
1779
|
+
zs = str(vol.flags.get("tcolor", "")).lstrip("#")
|
1780
|
+
if len(zs) == 3: # fc5 => ffcc55
|
1781
|
+
vol.flags["tcolor"] = "".join([x * 2 for x in zs])
|
1782
|
+
|
1769
1783
|
for k1, k2 in IMPLICATIONS:
|
1770
1784
|
if k1 in vol.flags:
|
1771
1785
|
vol.flags[k2] = True
|
@@ -2046,6 +2060,13 @@ class AuthSrv(object):
|
|
2046
2060
|
|
2047
2061
|
self.re_pwd = re.compile(zs)
|
2048
2062
|
|
2063
|
+
# to ensure it propagates into tcpsrv with mp on
|
2064
|
+
if self.args.mime:
|
2065
|
+
for zs in self.args.mime:
|
2066
|
+
ext, mime = zs.split("=", 1)
|
2067
|
+
MIMES[ext] = mime
|
2068
|
+
EXTS.update({v: k for k, v in MIMES.items()})
|
2069
|
+
|
2049
2070
|
def setup_pwhash(self, acct ) :
|
2050
2071
|
self.ah = PWHash(self.args)
|
2051
2072
|
if not self.ah.on:
|
@@ -2403,7 +2424,7 @@ def expand_config_file(
|
|
2403
2424
|
if not cnames:
|
2404
2425
|
t = "warning: tried to read config-files from folder '%s' but it does not contain any "
|
2405
2426
|
if names:
|
2406
|
-
t += ".conf files; the following files were ignored: %s"
|
2427
|
+
t += ".conf files; the following files/subfolders were ignored: %s"
|
2407
2428
|
t = t % (fp, ", ".join(names[:8]))
|
2408
2429
|
else:
|
2409
2430
|
t += "files at all"
|
copyparty/broker_mp.py
CHANGED
@@ -53,11 +53,8 @@ class BrokerMp(object):
|
|
53
53
|
def shutdown(self) :
|
54
54
|
self.log("broker", "shutting down")
|
55
55
|
for n, proc in enumerate(self.procs):
|
56
|
-
|
57
|
-
|
58
|
-
name="mp-shutdown-{}-{}".format(n, len(self.procs)),
|
59
|
-
)
|
60
|
-
thr.start()
|
56
|
+
name = "mp-shut-%d-%d" % (n, len(self.procs))
|
57
|
+
Daemon(proc.q_pend.put, name, ((0, "shutdown", []),))
|
61
58
|
|
62
59
|
with self.mutex:
|
63
60
|
procs = self.procs
|
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",
|
@@ -177,6 +190,7 @@ flagcats = {
|
|
177
190
|
"dvthumb": "disables video thumbnails",
|
178
191
|
"dathumb": "disables audio thumbnails (spectrograms)",
|
179
192
|
"dithumb": "disables image thumbnails",
|
193
|
+
"pngquant": "compress audio waveforms 33% better",
|
180
194
|
"thsize": "thumbnail res; WxH",
|
181
195
|
"crop": "center-cropping (y/n/fy/fn)",
|
182
196
|
"th3x": "3x resolution (y/n/fy/fn)",
|
@@ -201,7 +215,7 @@ flagcats = {
|
|
201
215
|
"grid": "show grid/thumbnails by default",
|
202
216
|
"sort": "default sort order",
|
203
217
|
"unlist": "dont list files matching REGEX",
|
204
|
-
"html_head=TXT": "includes TXT in the <head
|
218
|
+
"html_head=TXT": "includes TXT in the <head>, or @PATH for file at PATH",
|
205
219
|
"robots": "allows indexing by search engines (default)",
|
206
220
|
"norobots": "kindly asks search engines to leave",
|
207
221
|
"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
|