diff --git a/copyparty/__main__.py b/copyparty/__main__.py index f4faa5ce..60bb6919 100755 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1170,7 +1170,8 @@ def add_thumbnail(ap): ap2.add_argument("--th-mt", metavar="CORES", type=int, default=CORES, help="num cpu cores to use for generating thumbnails") ap2.add_argument("--th-convt", metavar="SEC", type=float, default=60, help="conversion timeout in seconds (volflag=convt)") ap2.add_argument("--th-ram-max", metavar="GB", type=float, default=6, help="max memory usage (GiB) permitted by thumbnailer; not very accurate") - ap2.add_argument("--th-no-crop", action="store_true", help="dynamic height; show full image by default (client can override in UI) (volflag=nocrop)") + ap2.add_argument("--th-crop", metavar="TXT", type=u, default="y", help="crop thumbnails to 4:3 or keep dynamic height; client can override in UI unless force. [\033[32mfy\033[0m]=crop, [\033[32mfn\033[0m]=nocrop, [\033[32mfy\033[0m]=force-y, [\033[32mfn\033[0m]=force-n (volflag=crop)") + ap2.add_argument("--th-x3", metavar="TXT", type=u, default="n", help="show thumbs at 3x resolution; client can override in UI unless force. [\033[32mfy\033[0m]=yes, [\033[32mfn\033[0m]=no, [\033[32mfy\033[0m]=force-yes, [\033[32mfn\033[0m]=force-no (volflag=th3x)") ap2.add_argument("--th-dec", metavar="LIBS", default="vips,pil,ff", help="image decoders, in order of preference") ap2.add_argument("--th-no-jpg", action="store_true", help="disable jpg output") ap2.add_argument("--th-no-webp", action="store_true", help="disable webp output") @@ -1430,6 +1431,7 @@ def main(argv: Optional[list[str]] = None) -> None: deprecated: list[tuple[str, str]] = [ ("--salt", "--warksalt"), ("--hdr-au-usr", "--idp-h-usr"), + ("--th-no-crop", "--th-crop=n"), ] for dk, nk in deprecated: idx = -1 diff --git a/copyparty/cfg.py b/copyparty/cfg.py index cc4f77a1..10e921e8 100644 --- a/copyparty/cfg.py +++ b/copyparty/cfg.py @@ -20,7 +20,6 @@ def vf_bmap() -> dict[str, str]: "no_thumb": "dthumb", "no_vthumb": "dvthumb", "no_athumb": "dathumb", - "th_no_crop": "nocrop", } for k in ( "dotsrch", @@ -56,6 +55,8 @@ def vf_vmap() -> dict[str, str]: "re_maxage": "scan", "th_convt": "convt", "th_size": "thsize", + "th_crop": "crop", + "th_x3": "th3x", } for k in ( "dbd", @@ -172,7 +173,8 @@ flagcats = { "dathumb": "disables audio thumbnails (spectrograms)", "dithumb": "disables image thumbnails", "thsize": "thumbnail res; WxH", - "nocrop": "disable center-cropping by default", + "crop": "center-cropping (y/n/fy/fn)", + "th3x": "3x resolution (y/n/fy/fn)", "convt": "conversion timeout in seconds", }, "handlers\n(better explained in --help-handlers)": { diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 153a6763..fc12be4f 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -3973,7 +3973,8 @@ class HttpCli(object): "idx": e2d, "itag": e2t, "dsort": vf["sort"], - "dfull": "nocrop" in vf, + "dcrop": vf["crop"], + "dth3x": vf["th3x"], "u2ts": vf["u2ts"], "lifetime": vn.flags.get("lifetime") or 0, "frand": bool(vn.flags.get("rand")), @@ -4000,8 +4001,9 @@ class HttpCli(object): "sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"), "readme": readme, "dgrid": "grid" in vf, - "dfull": "nocrop" in vf, "dsort": vf["sort"], + "dcrop": vf["crop"], + "dth3x": vf["th3x"], "themes": self.args.themes, "turbolvl": self.args.turbo, "u2j": self.args.u2j, diff --git a/copyparty/ico.py b/copyparty/ico.py index 00da00dd..28537d25 100644 --- a/copyparty/ico.py +++ b/copyparty/ico.py @@ -31,7 +31,7 @@ class Ico(object): w = 100 h = 30 - if not self.args.th_no_crop and as_thumb: + if "n" in self.args.th_crop and as_thumb: sw, sh = self.args.th_size.split("x") h = int(100.0 / (float(sw) / float(sh))) w = 100 diff --git a/copyparty/th_srv.py b/copyparty/th_srv.py index 10ab1223..3a45aaf3 100644 --- a/copyparty/th_srv.py +++ b/copyparty/th_srv.py @@ -97,8 +97,8 @@ def thumb_path(histpath: str, rem: str, mtime: float, fmt: str, ffa: set[str]) - # spectrograms are never cropped; strip fullsize flag ext = rem.split(".")[-1].lower() - if ext in ffa and fmt in ("wf", "jf"): - fmt = fmt[:1] + if ext in ffa and fmt[:2] in ("wf", "jf"): + fmt = fmt.replace("f", "") rd += "\n" + fmt h = hashlib.sha512(afsenc(rd)).digest() @@ -200,9 +200,10 @@ class ThumbSrv(object): with self.mutex: return not self.nthr - def getres(self, vn: VFS) -> tuple[int, int]: + def getres(self, vn: VFS, fmt: str) -> tuple[int, int]: + mul = 3 if "3" in fmt else 1 w, h = vn.flags["thsize"].split("x") - return int(w), int(h) + return int(w) * mul, int(h) * mul def get(self, ptop: str, rem: str, mtime: float, fmt: str) -> Optional[str]: histpath = self.asrv.vfs.histtab.get(ptop) @@ -364,7 +365,7 @@ class ThumbSrv(object): def fancy_pillow(self, im: "Image.Image", fmt: str, vn: VFS) -> "Image.Image": # exif_transpose is expensive (loads full image + unconditional copy) - res = self.getres(vn) + res = self.getres(vn, fmt) r = max(*res) * 2 im.thumbnail((r, r), resample=Image.LANCZOS) try: @@ -379,7 +380,7 @@ class ThumbSrv(object): if rot in rots: im = im.transpose(rots[rot]) - if fmt.endswith("f"): + if "f" in fmt: im.thumbnail(res, resample=Image.LANCZOS) else: iw, ih = im.size @@ -396,7 +397,7 @@ class ThumbSrv(object): im = self.fancy_pillow(im, fmt, vn) except Exception as ex: self.log("fancy_pillow {}".format(ex), "90") - im.thumbnail(self.getres(vn)) + im.thumbnail(self.getres(vn, fmt)) fmts = ["RGB", "L"] args = {"quality": 40} @@ -422,10 +423,10 @@ class ThumbSrv(object): def conv_vips(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None: self.wait4ram(0.2, tpath) crops = ["centre", "none"] - if fmt.endswith("f"): + if "f" in fmt: crops = ["none"] - w, h = self.getres(vn) + w, h = self.getres(vn, fmt) kw = {"height": h, "size": "down", "intent": "relative"} for c in crops: @@ -454,12 +455,12 @@ class ThumbSrv(object): seek = [b"-ss", "{:.0f}".format(dur / 3).encode("utf-8")] scale = "scale={0}:{1}:force_original_aspect_ratio=" - if fmt.endswith("f"): + if "f" in fmt: scale += "decrease,setsar=1:1" else: scale += "increase,crop={0}:{1},setsar=1:1" - res = self.getres(vn) + res = self.getres(vn, fmt) bscale = scale.format(*list(res)).encode("utf-8") # fmt: off cmd = [ @@ -594,7 +595,11 @@ class ThumbSrv(object): need = 0.2 + dur / coeff self.wait4ram(need, tpath) - fc = "[0:a:0]aresample=48000{},showspectrumpic=s=640x512,crop=780:544:70:50[o]" + fc = "[0:a:0]aresample=48000{},showspectrumpic=s=" + if "3" in fmt: + fc += "1280x1024,crop=1420:1056:70:48[o]" + else: + fc += "640x512,crop=780:544:70:48[o]" if self.args.th_ff_swr: fco = ":filter_size=128:cutoff=0.877" diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index 2d49f450..7d71d34a 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -349,7 +349,8 @@ var Ls = { "tvt_edit": "open file in text editor$NHotkey: E\">✏️ edit", "gt_msel": "enable file selection; ctrl-click a file to override$N$N<em>when active: doubleclick a file / folder to open it</em>$N$NHotkey: S\">multiselect", - "gt_full": "show uncropped thumbnails\">full", + "gt_crop": "center-crop thumbnails\">crop", + "gt_3x": "hi-res thumbnails\">3x", "gt_zoom": "zoom", "gt_chop": "chop", "gt_sort": "sort by", @@ -844,7 +845,8 @@ var Ls = { "tvt_edit": "redigér filen$NSnarvei: E\">✏️ endre", "gt_msel": "markér filer istedenfor å åpne dem; ctrl-klikk filer for å overstyre$N$N<em>når aktiv: dobbelklikk en fil / mappe for å åpne</em>$N$NSnarvei: S\">markering", - "gt_full": "ikke beskjær bildene\">full", + "gt_crop": "beskjær ikonene så de passer bedre\">✂", + "gt_3x": "høyere oppløsning på ikoner\">3x", "gt_zoom": "zoom", "gt_chop": "trim", "gt_sort": "sorter", @@ -4515,7 +4517,9 @@ var thegrid = (function () { gfiles.innerHTML = ( '