thumbs: fix th=x on non-jxl-capable servers;

if FFmpeg was compiled without jxl support, then every
request for a jxl thumbnail would result in a placeholder svg

detect this and switch to webp as fallback; the initial request
will still fail but successive ones are fine

closes #1333, closes #1372
This commit is contained in:
ed
2026-03-21 19:28:06 +00:00
parent bbe58105e7
commit 1afe48b85d
8 changed files with 47 additions and 35 deletions

View File

@@ -146,21 +146,17 @@ class BrokerMp(object):
returns a Queue object which eventually contains the response if want_retval
(not-impl here since nothing uses it yet)
"""
if dest == "httpsrv.listen":
for p in self.procs:
p.q_pend.put((0, dest, [args[0], len(self.procs)]))
elif dest == "httpsrv.set_netdevs":
for p in self.procs:
p.q_pend.put((0, dest, list(args)))
if dest.startswith("httpsrv."):
if dest == "httpsrv.listen":
for p in self.procs:
p.q_pend.put((0, dest, [args[0], len(self.procs)]))
else:
for p in self.procs:
p.q_pend.put((0, dest, list(args)))
elif dest == "cb_httpsrv_up":
self.hub.cb_httpsrv_up()
elif dest == "httpsrv.set_bad_ver":
for p in self.procs:
p.q_pend.put((0, dest, list(args)))
else:
raise Exception("what is " + str(dest))

View File

@@ -58,13 +58,8 @@ class BrokerThr(BrokerCli):
self.httpsrv.listen(args[0], 1)
return
if dest == "httpsrv.set_netdevs":
self.httpsrv.set_netdevs(args[0])
return
if dest == "httpsrv.set_bad_ver":
self.httpsrv.set_bad_ver()
return
getattr(self.httpsrv, dest[8:])(*args)
return
# new ipc invoking managed service in hub
obj = self.hub

View File

@@ -229,7 +229,7 @@ class HttpCli(object):
self.E: EnvParams = self.args.E
self.asrv = conn.asrv # mypy404
self.ico = conn.ico # mypy404
self.thumbcli = conn.thumbcli # mypy404
self.thumbcli = conn.hsrv.thumbcli
self.u2fh = conn.u2fh # mypy404
self.pipes = conn.pipes # mypy404
self.log_func = conn.log_func # mypy404

View File

@@ -69,8 +69,6 @@ class HttpConn(object):
self.bans: dict[str, int] = hsrv.bans
self.aclose: dict[str, int] = hsrv.aclose
enth = (HAVE_PIL or HAVE_VIPS or HAVE_FFMPEG) and not self.args.no_thumb
self.thumbcli: Optional[ThumbCli] = ThumbCli(hsrv) if enth else None # mypy404
self.ico: Ico = Ico(self.args) # mypy404
self.t0: float = time.time() # mypy404

View File

@@ -57,6 +57,9 @@ except SyntaxError:
from .httpconn import HttpConn
from .metrics import Metrics
from .mtag import HAVE_FFMPEG
from .th_cli import ThumbCli
from .th_srv import HAVE_PIL, HAVE_VIPS
from .u2idx import U2idx
from .util import (
E_SCK,
@@ -133,6 +136,8 @@ class HttpSrv(object):
self.bans: dict[str, int] = {}
self.aclose: dict[str, int] = {}
self.thumbcli: Optional[ThumbCli] = None
dli: dict[str, tuple[float, int, "VFS", str, str]] = {} # info
dls: dict[str, tuple[float, int]] = {} # state
self.dli = self.tdli = dli
@@ -230,16 +235,20 @@ class HttpSrv(object):
if self.args.log_thrs:
start_log_thrs(self.log, self.args.log_thrs, nid)
self.th_cfg: dict[str, set[str]] = {}
Daemon(self.post_init, "hsrv-init2")
if (HAVE_PIL or HAVE_VIPS or HAVE_FFMPEG) and not self.args.no_thumb:
Daemon(self.post_init, "hsrv-init2")
def post_init(self) -> None:
try:
x = self.broker.ask("thumbsrv.getcfg")
self.th_cfg = x.get()
self.thumbcli = ThumbCli(self, x.get())
except:
pass
def set_th_cfg(self, c: dict[str, set[str]], opts: tuple[bool]) -> None:
self.args.th_no_jxl = opts[0]
self.thumbcli = ThumbCli(self, c)
def set_bad_ver(self) -> None:
self.bad_ver = True

View File

@@ -24,7 +24,7 @@ IMG_EXTS = set(["webp", "jpg", "png", "jxl"])
class ThumbCli(object):
def __init__(self, hsrv: "HttpSrv") -> None:
def __init__(self, hsrv: "HttpSrv", c: dict[str, set[str]]) -> None:
self.broker = hsrv.broker
self.log_func = hsrv.log
self.args = hsrv.args
@@ -33,16 +33,6 @@ class ThumbCli(object):
# cache on both sides for less broker spam
self.cooldown = Cooldown(self.args.th_poke)
try:
c = hsrv.th_cfg
if not c:
raise Exception()
except:
c = {
k: set()
for k in ["thumbable", "pil", "vips", "raw", "ffi", "ffv", "ffa"]
}
self.thumbable = c["thumbable"]
self.fmt_pil = c["pil"]
self.fmt_vips = c["vips"]

View File

@@ -327,6 +327,10 @@ class ThumbSrv(object):
self.fmt_pil.discard(f)
self.thumbable: set[str] = set()
self._build_thumbable()
def _build_thumbable(self) -> None:
self.thumbable.clear()
if "pil" in self.args.th_dec:
self.thumbable |= self.fmt_pil
@@ -417,6 +421,10 @@ class ThumbSrv(object):
return None
def _rebuild_thumbable(self) -> None:
self._build_thumbable()
self.hub.broker.say("httpsrv.set_th_cfg", self.getcfg(), (self.args.th_no_jxl,))
def getcfg(self) -> dict[str, set[str]]:
return {
"thumbable": self.thumbable,
@@ -834,6 +842,21 @@ class ThumbSrv(object):
ret = 321
c = 3
elif cmd[-1].lower().endswith(b".jxl") and (
"Error selecting an encoder" in serr
or "Automatic encoder selection failed" in serr
or "Default encoder for format webp" in serr
or "Unrecognized option 'effort:v" in serr
or "Please choose an encoder manually" in serr
):
self.args.th_no_jxl = True
self.fmt_ffi.discard("jxl")
self.fmt_ffv.discard("jxl")
self._rebuild_thumbable()
t = "FFmpeg failed because it was compiled without jpegxl; enabling --th-no-jxl to force webp output:\n"
ret = 321
c = 1
elif (
(not self.args.th_ff_jpg or time.time() - int(self.args.th_ff_jpg) < 60)
and cmd[-1].lower().endswith(b".webp")

View File

@@ -316,6 +316,7 @@ class VHttpSrv(object):
self.g403 = Garda("")
self.gurl = Garda("")
self.thumbcli = None
self.u2idx = None
def cachebuster(self):