From 7e4702cf09cda6d96c12b0a459b9f04e2efb4169 Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 13 Feb 2025 18:32:01 +0000 Subject: [PATCH] file-extension icons global-option / volflag `ext_th` specifies custom thumbnail for a given file extension --- README.md | 3 +++ copyparty/__main__.py | 1 + copyparty/authsrv.py | 15 +++++++++++++-- copyparty/cfg.py | 2 ++ copyparty/web/browser.js | 39 ++++++++++++++++++++++----------------- 5 files changed, 41 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index b49fe7f5..a6f4195b 100644 --- a/README.md +++ b/README.md @@ -664,6 +664,8 @@ enabling `multiselect` lets you click files to select them, and then shift-click * `multiselect` is mostly intended for phones/tablets, but the `sel` option in the `[⚙️] settings` tab is better suited for desktop use, allowing selection by CTRL-clicking and range-selection with SHIFT-click, all without affecting regular clicking * the `sel` option can be made default globally with `--gsel` or per-volume with volflag `gsel` +to show `/icons/exe.png` as the thumbnail for all .exe files, `--ext-th=exe=/icons/exe.png` (optionally as a volflag) + config file example: ```yaml @@ -678,6 +680,7 @@ config file example: flags: dthumb # disable ALL thumbnails and audio transcoding dvthumb # only disable video thumbnails + ext-th: exe=/ico/exe.png # /ico/exe.png is the thumbnail of *.exe th-covers: folder.png,folder.jpg,cover.png,cover.jpg # the default ``` diff --git a/copyparty/__main__.py b/copyparty/__main__.py index b0cd766b..e4cb3ef3 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1487,6 +1487,7 @@ def add_ui(ap, retry): ap2.add_argument("--hsortn", metavar="N", type=int, default=2, help="number of sorting rules to include in media URLs by default (volflag=hsortn)") 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)") 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") + ap2.add_argument("--ext-th", metavar="E=VP", type=u, action="append", help="use thumbnail-image \033[33mVP\033[0m for file-extension \033[33mE\033[0m, example: [\033[32mexe=/.res/exe.png\033[0m] (volflag=ext_th)") 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])") ap2.add_argument("--spinner", metavar="TXT", type=u, default="🌲", help="\033[33memoji\033[0m or \033[33memoji,css\033[0m Example: [\033[32m🥖,padding:0\033[0m]") ap2.add_argument("--css-browser", metavar="L", type=u, default="", help="URL to additional CSS to include in the filebrowser html") diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index ccb91c61..be131bd6 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -1406,7 +1406,7 @@ class AuthSrv(object): flags[name] = True return - zs = "mtp on403 on404 xbu xau xiu xbc xac xbr xar xbd xad xm xban" + zs = "ext_th mtp on403 on404 xbu xau xiu xbc xac xbr xar xbd xad xm xban" if name not in zs.split(): if value is True: t = "└─add volflag [{}] = {} ({})" @@ -1999,7 +1999,7 @@ class AuthSrv(object): # append additive args from argv to volflags hooks = "xbu xau xiu xbc xac xbr xar xbd xad xm xban".split() - for name in "mtp on404 on403".split() + hooks: + for name in "ext_th mtp on404 on403".split() + hooks: self._read_volflag( vol.vpath, vol.flags, name, getattr(self.args, name), True ) @@ -2030,6 +2030,16 @@ class AuthSrv(object): ncmds.append(ocmd) vol.flags[hn] = ncmds + ext_th = vol.flags["ext_th_d"] = {} + etv = "(?)" + try: + for etv in vol.flags.get("ext_th") or []: + k, v = etv.split("=") + ext_th[k] = v + except: + t = "WARNING: volume [/%s]: invalid value specified for ext-th: %s" + self.log(t % (vol.vpath, etv), 3) + # d2d drops all database features for a volume for grp, rm in [["d2d", "e2d"], ["d2t", "e2t"], ["d2d", "e2v"]]: if not vol.flags.get(grp, False): @@ -2391,6 +2401,7 @@ class AuthSrv(object): "have_del": not self.args.no_del, "have_unpost": int(self.args.unpost), "have_emp": self.args.emp, + "ext_th": vf.get("ext_th_d") or {}, "sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"), "sba_md": vf.get("md_sba") or "", "sba_lg": vf.get("lg_sba") or "", diff --git a/copyparty/cfg.py b/copyparty/cfg.py index 5521b7cc..224f1955 100644 --- a/copyparty/cfg.py +++ b/copyparty/cfg.py @@ -109,6 +109,7 @@ def vf_cmap() -> dict[str, str]: for k in ( "exp_lg", "exp_md", + "ext_th", "mte", "mth", "mtp", @@ -221,6 +222,7 @@ flagcats = { "crop": "center-cropping (y/n/fy/fn)", "th3x": "3x resolution (y/n/fy/fn)", "convt": "conversion timeout in seconds", + "ext_th=s=/b.png": "use /b.png as thumbnail for file-extension s", }, "handlers\n(better explained in --help-handlers)": { "on404=PY": "handle 404s by executing PY file", diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index b4283733..9af663ac 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -6294,19 +6294,40 @@ var thegrid = (function () { var html = [], svgs = new Set(), max_svgs = CHROME ? 500 : 5000, + need_ext = !r.thumbs || !!ext_th, + use_ext_th = r.thumbs && ext_th, files = QSA('#files>tbody>tr>td:nth-child(2) a[id]'); for (var a = 0, aa = files.length; a < aa; a++) { var ao = files[a], ohref = esc(ao.getAttribute('href')), href = ohref.split('?')[0], + ext = '', name = uricom_dec(vsplit(href)[1]), ref = ao.getAttribute('id'), isdir = href.endsWith('/'), ac = isdir ? ' class="dir"' : '', ihref = ohref; - if (r.thumbs) { + if (need_ext && href != "#") { + var ar = href.split('.'); + if (ar.length > 1) + ar.shift(); + + ar.reverse(); + for (var b = 0; b < Math.min(2, ar.length); b++) { + if (ar[b].length > 7) + break; + + ext = ar[b] + '.' + ext; + } + ext = (ext || 'unk.').slice(0, -1); + } + + if (use_ext_th && ext_th[ext]) { + ihref = ext_th[ext]; + } + else if (r.thumbs) { ihref = addq(ihref, 'th=' + (have_webp ? 'w' : 'j')); if (!r.crop) ihref += 'f'; @@ -6319,22 +6340,6 @@ var thegrid = (function () { ihref = SR + '/.cpr/ico/folder'; } else { - var ar = href.split('.'); - if (ar.length > 1) - ar = ar.slice(1); - - ihref = ''; - ar.reverse(); - for (var b = 0; b < ar.length; b++) { - if (ar[b].length > 7) - break; - - ihref = ar[b] + '.' + ihref; - } - if (!ihref) { - ihref = 'unk.'; - } - var ext = ihref.slice(0, -1); if (!svgs.has(ext)) { if (svgs.size < max_svgs) svgs.add(ext);