diff --git a/copyparty/__main__.py b/copyparty/__main__.py index d95fabe6..e0a46e79 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1723,6 +1723,7 @@ def add_thumbnail(ap): ap2.add_argument("--th-no-jxl", action="store_true", help="disable jpeg-xl output") ap2.add_argument("--th-ff-jpg", action="store_true", help="force jpg output for video thumbs (avoids issues on some FFmpeg builds)") ap2.add_argument("--th-ff-swr", action="store_true", help="use swresample instead of soxr for audio thumbs (faster, lower accuracy, avoids issues on some FFmpeg builds)") + ap2.add_argument("--th-vips-jxl", metavar="N", type=int, default=1, help="when to allow generating jxl thumbnails with libvips; 0=never, 1=musl-mallocng, 2=always") ap2.add_argument("--th-poke", metavar="SEC", type=int, default=300, help="activity labeling cooldown -- avoids doing keepalive pokes (updating the mtime) on thumbnail folders more often than \033[33mSEC\033[0m seconds") ap2.add_argument("--th-clean", metavar="SEC", type=int, default=43200, help="cleanup interval; 0=disabled") ap2.add_argument("--th-maxage", metavar="SEC", type=int, default=604800, help="max folder age -- folders which haven't been poked for longer than \033[33m--th-poke\033[0m seconds will get deleted every \033[33m--th-clean\033[0m seconds") diff --git a/copyparty/th_srv.py b/copyparty/th_srv.py index 8c6275a4..93ce8c22 100644 --- a/copyparty/th_srv.py +++ b/copyparty/th_srv.py @@ -186,6 +186,10 @@ try: if os.environ.get("PRTY_NO_VIPS"): raise ImportError() + if "VIPS_CONCURRENCY" not in os.environ: + # reduces glibc RAM usage from 4.7 to 3.5 GiB ...yep, still bonkers + os.environ["VIPS_CONCURRENCY"] = "1" + HAVE_VIPS = True import pyvips @@ -272,6 +276,18 @@ class ThumbSrv(object): self.exts_spec_unsafe = set(self.args.th_spec_cnv.split(",")) + # libvips can easily gobble up 4 GiB of RAM when generating JXL thumbnails on glibc so let's not + self.vips_jxl = False + if HAVE_VIPS and self.args.th_vips_jxl == 2: + self.vips_jxl = True + elif HAVE_VIPS and self.args.th_vips_jxl == 1: + try: + with open("/proc/self/maps", "rb") as f: + zb = f.read() + self.vips_jxl = b"/ld-musl-" in zb and b"mimalloc" not in zb + except: + pass + self.q: Queue[Optional[tuple[str, str, str, VFS]]] = Queue(self.nthr * 4) for n in range(self.nthr): Daemon(self.worker, "thumb-{}-{}".format(n, self.nthr)) @@ -535,7 +551,11 @@ class ThumbSrv(object): if lib == "pil" and ext in self.fmt_pil and tex in self.fmt_pil: funs.append(self.conv_pil) - elif lib == "vips" and ext in self.fmt_vips: + elif ( + lib == "vips" + and ext in self.fmt_vips + and (tex != "jxl" or self.vips_jxl) + ): funs.append(self.conv_vips) elif lib == "raw" and ext in self.fmt_raw: funs.append(self.conv_raw) @@ -825,7 +845,7 @@ class ThumbSrv(object): b"-q:v", unicode(vn.flags["th_qvx"]).encode("ascii"), # default=?? b"-effort:v", - b"8", # default=7, 1=fast, 9=max, 9~=8 but slower + b"7", # default=7, 1=fast, 9=max, 9~=8 but slower ] else: cmd += [ @@ -854,6 +874,7 @@ class ThumbSrv(object): elif cmd[-1].lower().endswith(b".jxl") and ( "Error selecting an encoder" in serr + or "find a suitable output format" in serr or "Automatic encoder selection failed" in serr or "Default encoder for format webp" in serr or "Unrecognized option 'effort:v" in serr