add hi-res thumbs (togglebtn/servercfg)

This commit is contained in:
ed 2024-02-18 13:04:22 +00:00
parent 655f6d00f8
commit 33f41f3e61
7 changed files with 85 additions and 36 deletions

View file

@ -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-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-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-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-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-jpg", action="store_true", help="disable jpg output")
ap2.add_argument("--th-no-webp", action="store_true", help="disable webp 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]] = [ deprecated: list[tuple[str, str]] = [
("--salt", "--warksalt"), ("--salt", "--warksalt"),
("--hdr-au-usr", "--idp-h-usr"), ("--hdr-au-usr", "--idp-h-usr"),
("--th-no-crop", "--th-crop=n"),
] ]
for dk, nk in deprecated: for dk, nk in deprecated:
idx = -1 idx = -1

View file

@ -20,7 +20,6 @@ def vf_bmap() -> dict[str, str]:
"no_thumb": "dthumb", "no_thumb": "dthumb",
"no_vthumb": "dvthumb", "no_vthumb": "dvthumb",
"no_athumb": "dathumb", "no_athumb": "dathumb",
"th_no_crop": "nocrop",
} }
for k in ( for k in (
"dotsrch", "dotsrch",
@ -56,6 +55,8 @@ def vf_vmap() -> dict[str, str]:
"re_maxage": "scan", "re_maxage": "scan",
"th_convt": "convt", "th_convt": "convt",
"th_size": "thsize", "th_size": "thsize",
"th_crop": "crop",
"th_x3": "th3x",
} }
for k in ( for k in (
"dbd", "dbd",
@ -172,7 +173,8 @@ flagcats = {
"dathumb": "disables audio thumbnails (spectrograms)", "dathumb": "disables audio thumbnails (spectrograms)",
"dithumb": "disables image thumbnails", "dithumb": "disables image thumbnails",
"thsize": "thumbnail res; WxH", "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", "convt": "conversion timeout in seconds",
}, },
"handlers\n(better explained in --help-handlers)": { "handlers\n(better explained in --help-handlers)": {

View file

@ -3973,7 +3973,8 @@ class HttpCli(object):
"idx": e2d, "idx": e2d,
"itag": e2t, "itag": e2t,
"dsort": vf["sort"], "dsort": vf["sort"],
"dfull": "nocrop" in vf, "dcrop": vf["crop"],
"dth3x": vf["th3x"],
"u2ts": vf["u2ts"], "u2ts": vf["u2ts"],
"lifetime": vn.flags.get("lifetime") or 0, "lifetime": vn.flags.get("lifetime") or 0,
"frand": bool(vn.flags.get("rand")), "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"), "sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"),
"readme": readme, "readme": readme,
"dgrid": "grid" in vf, "dgrid": "grid" in vf,
"dfull": "nocrop" in vf,
"dsort": vf["sort"], "dsort": vf["sort"],
"dcrop": vf["crop"],
"dth3x": vf["th3x"],
"themes": self.args.themes, "themes": self.args.themes,
"turbolvl": self.args.turbo, "turbolvl": self.args.turbo,
"u2j": self.args.u2j, "u2j": self.args.u2j,

View file

@ -31,7 +31,7 @@ class Ico(object):
w = 100 w = 100
h = 30 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") sw, sh = self.args.th_size.split("x")
h = int(100.0 / (float(sw) / float(sh))) h = int(100.0 / (float(sw) / float(sh)))
w = 100 w = 100

View file

@ -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 # spectrograms are never cropped; strip fullsize flag
ext = rem.split(".")[-1].lower() ext = rem.split(".")[-1].lower()
if ext in ffa and fmt in ("wf", "jf"): if ext in ffa and fmt[:2] in ("wf", "jf"):
fmt = fmt[:1] fmt = fmt.replace("f", "")
rd += "\n" + fmt rd += "\n" + fmt
h = hashlib.sha512(afsenc(rd)).digest() h = hashlib.sha512(afsenc(rd)).digest()
@ -200,9 +200,10 @@ class ThumbSrv(object):
with self.mutex: with self.mutex:
return not self.nthr 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") 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]: def get(self, ptop: str, rem: str, mtime: float, fmt: str) -> Optional[str]:
histpath = self.asrv.vfs.histtab.get(ptop) 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": def fancy_pillow(self, im: "Image.Image", fmt: str, vn: VFS) -> "Image.Image":
# exif_transpose is expensive (loads full image + unconditional copy) # exif_transpose is expensive (loads full image + unconditional copy)
res = self.getres(vn) res = self.getres(vn, fmt)
r = max(*res) * 2 r = max(*res) * 2
im.thumbnail((r, r), resample=Image.LANCZOS) im.thumbnail((r, r), resample=Image.LANCZOS)
try: try:
@ -379,7 +380,7 @@ class ThumbSrv(object):
if rot in rots: if rot in rots:
im = im.transpose(rots[rot]) im = im.transpose(rots[rot])
if fmt.endswith("f"): if "f" in fmt:
im.thumbnail(res, resample=Image.LANCZOS) im.thumbnail(res, resample=Image.LANCZOS)
else: else:
iw, ih = im.size iw, ih = im.size
@ -396,7 +397,7 @@ class ThumbSrv(object):
im = self.fancy_pillow(im, fmt, vn) im = self.fancy_pillow(im, fmt, vn)
except Exception as ex: except Exception as ex:
self.log("fancy_pillow {}".format(ex), "90") self.log("fancy_pillow {}".format(ex), "90")
im.thumbnail(self.getres(vn)) im.thumbnail(self.getres(vn, fmt))
fmts = ["RGB", "L"] fmts = ["RGB", "L"]
args = {"quality": 40} args = {"quality": 40}
@ -422,10 +423,10 @@ class ThumbSrv(object):
def conv_vips(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None: def conv_vips(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
self.wait4ram(0.2, tpath) self.wait4ram(0.2, tpath)
crops = ["centre", "none"] crops = ["centre", "none"]
if fmt.endswith("f"): if "f" in fmt:
crops = ["none"] crops = ["none"]
w, h = self.getres(vn) w, h = self.getres(vn, fmt)
kw = {"height": h, "size": "down", "intent": "relative"} kw = {"height": h, "size": "down", "intent": "relative"}
for c in crops: for c in crops:
@ -454,12 +455,12 @@ class ThumbSrv(object):
seek = [b"-ss", "{:.0f}".format(dur / 3).encode("utf-8")] seek = [b"-ss", "{:.0f}".format(dur / 3).encode("utf-8")]
scale = "scale={0}:{1}:force_original_aspect_ratio=" scale = "scale={0}:{1}:force_original_aspect_ratio="
if fmt.endswith("f"): if "f" in fmt:
scale += "decrease,setsar=1:1" scale += "decrease,setsar=1:1"
else: else:
scale += "increase,crop={0}:{1},setsar=1:1" 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") bscale = scale.format(*list(res)).encode("utf-8")
# fmt: off # fmt: off
cmd = [ cmd = [
@ -594,7 +595,11 @@ class ThumbSrv(object):
need = 0.2 + dur / coeff need = 0.2 + dur / coeff
self.wait4ram(need, tpath) 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: if self.args.th_ff_swr:
fco = ":filter_size=128:cutoff=0.877" fco = ":filter_size=128:cutoff=0.877"

View file

@ -349,7 +349,8 @@ var Ls = {
"tvt_edit": "open file in text editor$NHotkey: E\">✏️ edit", "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_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_zoom": "zoom",
"gt_chop": "chop", "gt_chop": "chop",
"gt_sort": "sort by", "gt_sort": "sort by",
@ -844,7 +845,8 @@ var Ls = {
"tvt_edit": "redigér filen$NSnarvei: E\">✏️ endre", "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_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_zoom": "zoom",
"gt_chop": "trim", "gt_chop": "trim",
"gt_sort": "sorter", "gt_sort": "sorter",
@ -4515,7 +4517,9 @@ var thegrid = (function () {
gfiles.innerHTML = ( gfiles.innerHTML = (
'<div id="ghead" class="ghead">' + '<div id="ghead" class="ghead">' +
'<a href="#" class="tgl btn" id="gridsel" tt="' + L.gt_msel + '</a> ' + '<a href="#" class="tgl btn" id="gridsel" tt="' + L.gt_msel + '</a> ' +
'<a href="#" class="tgl btn" id="gridfull" tt="' + L.gt_full + '</a> <span>' + L.gt_zoom + ': ' + '<a href="#" class="tgl btn" id="gridcrop" tt="' + L.gt_crop + '</a> ' +
'<a href="#" class="tgl btn" id="grid3x" tt="' + L.gt_3x + '</a> ' +
'<span>' + L.gt_zoom + ': ' +
'<a href="#" class="btn" z="-1.2" tt="Hotkey: shift-A">&ndash;</a> ' + '<a href="#" class="btn" z="-1.2" tt="Hotkey: shift-A">&ndash;</a> ' +
'<a href="#" class="btn" z="1.2" tt="Hotkey: shift-D">+</a></span> <span>' + L.gt_chop + ': ' + '<a href="#" class="btn" z="1.2" tt="Hotkey: shift-D">+</a></span> <span>' + L.gt_chop + ': ' +
'<a href="#" class="btn" l="-1" tt="' + L.gt_c1 + '">&ndash;</a> ' + '<a href="#" class="btn" l="-1" tt="' + L.gt_c1 + '">&ndash;</a> ' +
@ -4530,7 +4534,7 @@ var thegrid = (function () {
lfiles.parentNode.insertBefore(gfiles, lfiles); lfiles.parentNode.insertBefore(gfiles, lfiles);
var r = { var r = {
'sz': clamp(fcfg_get('gridsz', 10), 4, 40), 'sz': clamp(fcfg_get('gridsz', 10), 4, 80),
'ln': clamp(icfg_get('gridln', 3), 1, 7), 'ln': clamp(icfg_get('gridln', 3), 1, 7),
'isdirty': true, 'isdirty': true,
'bbox': null 'bbox': null
@ -4593,9 +4597,9 @@ var thegrid = (function () {
r.setdirty = function () { r.setdirty = function () {
r.dirty = true; r.dirty = true;
if (r.en) { if (r.en)
loadgrid(); loadgrid();
} else
r.setvis(); r.setvis();
}; };
@ -4616,7 +4620,7 @@ var thegrid = (function () {
function setsz(v) { function setsz(v) {
if (v !== undefined) { if (v !== undefined) {
r.sz = clamp(v, 4, 40); r.sz = clamp(v, 4, 80);
swrite('gridsz', r.sz); swrite('gridsz', r.sz);
setTimeout(r.tippen, 20); setTimeout(r.tippen, 20);
} }
@ -4624,6 +4628,7 @@ var thegrid = (function () {
document.documentElement.style.setProperty('--grid-sz', r.sz + 'em'); document.documentElement.style.setProperty('--grid-sz', r.sz + 'em');
} }
catch (ex) { } catch (ex) { }
aligngriditems();
} }
setsz(); setsz();
@ -4776,8 +4781,11 @@ var thegrid = (function () {
if (!r.dirty) if (!r.dirty)
return r.loadsel(); return r.loadsel();
if (dfull != r.full && !sread('gridfull')) if (dcrop.startsWith('f') || !sread('gridcrop'))
bcfg_upd_ui('gridfull', r.full = dfull); bcfg_upd_ui('gridcrop', r.crop = ('y' == dcrop.slice(-1)));
if (dth3x.startsWith('f') || !sread('grid3x'))
bcfg_upd_ui('grid3x', r.x3 = ('y' == dth3x.slice(-1)));
var html = [], var html = [],
svgs = new Set(), svgs = new Set(),
@ -4796,8 +4804,10 @@ var thegrid = (function () {
if (r.thumbs) { if (r.thumbs) {
ihref += '?th=' + (have_webp ? 'w' : 'j'); ihref += '?th=' + (have_webp ? 'w' : 'j');
if (r.full) if (!r.crop)
ihref += 'f' ihref += 'f';
if (r.x3)
ihref += '3';
if (href == "#") if (href == "#")
ihref = SR + '/.cpr/ico/' + (ref == 'moar' ? '++' : 'exit'); ihref = SR + '/.cpr/ico/' + (ref == 'moar' ? '++' : 'exit');
} }
@ -4833,7 +4843,7 @@ var thegrid = (function () {
html.push('<a href="' + ohref + '" ref="' + ref + html.push('<a href="' + ohref + '" ref="' + ref +
'"' + ac + ' ttt="' + esc(name) + '"><img style="height:' + '"' + ac + ' ttt="' + esc(name) + '"><img style="height:' +
(r.sz / 1.25) + 'em" onload="th_onload(this)" src="' + (r.sz / 1.25) + 'em" loading="lazy" onload="th_onload(this)" src="' +
ihref + '" /><span' + ac + '>' + ao.innerHTML + '</span></a>'); ihref + '" /><span' + ac + '>' + ao.innerHTML + '</span></a>');
} }
ebi('ggrid').innerHTML = html.join('\n'); ebi('ggrid').innerHTML = html.join('\n');
@ -4884,8 +4894,29 @@ var thegrid = (function () {
})[0]; })[0];
}; };
r.set_crop = function (en) {
if (!dcrop.startsWith('f'))
return r.setdirty();
r.crop = dcrop.startsWith('y');
bcfg_upd_ui('gridcrop', r.crop);
if (r.crop != en)
toast.warn(10, L.ul_btnlk);
};
r.set_x3 = function (en) {
if (!dth3x.startsWith('f'))
return r.setdirty();
r.x3 = dth3x.startsWith('y');
bcfg_upd_ui('grid3x', r.x3);
if (r.x3 != en)
toast.warn(10, L.ul_btnlk);
};
bcfg_bind(r, 'thumbs', 'thumbs', true, r.setdirty); bcfg_bind(r, 'thumbs', 'thumbs', true, r.setdirty);
bcfg_bind(r, 'full', 'gridfull', false, r.setdirty); bcfg_bind(r, 'crop', 'gridcrop', !dcrop.endsWith('n'), r.set_crop);
bcfg_bind(r, 'x3', 'grid3x', dth3x.endsWith('y'), r.set_x3);
bcfg_bind(r, 'sel', 'gridsel', false, r.loadsel); bcfg_bind(r, 'sel', 'gridsel', false, r.loadsel);
bcfg_bind(r, 'en', 'griden', dgrid, function (v) { bcfg_bind(r, 'en', 'griden', dgrid, function (v) {
v ? loadgrid() : r.setvis(true); v ? loadgrid() : r.setvis(true);
@ -5575,11 +5606,15 @@ function aligngriditems() {
if (/b/.test(themen + '')) if (/b/.test(themen + ''))
totalgapwidth *= 2.8; totalgapwidth *= 2.8;
var val, st = ebi('ggrid').style;
if (((griditemcount * em2px) * gridsz) + totalgapwidth < gridwidth) { if (((griditemcount * em2px) * gridsz) + totalgapwidth < gridwidth) {
ebi('ggrid').style.justifyContent = 'left'; val = 'left';
} else { } else {
ebi('ggrid').style.justifyContent = treectl.hidden ? 'center' : 'space-between'; val = treectl.hidden ? 'center' : 'space-between';
} }
if (st.justifyContent != val)
st.justifyContent = val;
} }
onresize100.add(aligngriditems); onresize100.add(aligngriditems);
@ -6110,7 +6145,8 @@ var treectl = (function () {
res.files[a].tags = {}; res.files[a].tags = {};
read_dsort(res.dsort); read_dsort(res.dsort);
dfull = res.dfull; dcrop = res.dcrop;
dth3x = res.dth3x;
srvinf = res.srvinf; srvinf = res.srvinf;
try { try {

View file

@ -110,7 +110,7 @@ class Cfg(Namespace):
def __init__(self, a=None, v=None, c=None, **ka0): def __init__(self, a=None, v=None, c=None, **ka0):
ka = {} ka = {}
ex = "daw dav_auth dav_inf dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp ed emp exp force_js getmod grid hardlink ih ihead magic never_symlink nid nih no_acode no_athumb no_dav no_dedup no_del no_dupe no_lifetime no_logues no_mv no_readme no_robots no_sb_md no_sb_lg no_scandir no_tarcmp no_thumb no_vthumb no_zip nrand nw q rand smb srch_dbg stats th_no_crop vague_403 vc ver xdev xlink xvol" ex = "daw dav_auth dav_inf dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp ed emp exp force_js getmod grid hardlink ih ihead magic never_symlink nid nih no_acode no_athumb no_dav no_dedup no_del no_dupe no_lifetime no_logues no_mv no_readme no_robots no_sb_md no_sb_lg no_scandir no_tarcmp no_thumb no_vthumb no_zip nrand nw q rand smb srch_dbg stats th_x3 vague_403 vc ver xdev xlink xvol"
ka.update(**{k: False for k in ex.split()}) ka.update(**{k: False for k in ex.split()})
ex = "dotpart dotsrch no_dhash no_fastboot no_rescan no_sendfile no_voldump re_dhash plain_ip" ex = "dotpart dotsrch no_dhash no_fastboot no_rescan no_sendfile no_voldump re_dhash plain_ip"
@ -156,7 +156,9 @@ class Cfg(Namespace):
s_wr_sz=512 * 1024, s_wr_sz=512 * 1024,
sort="href", sort="href",
srch_hits=99999, srch_hits=99999,
th_crop="y",
th_size="320x256", th_size="320x256",
th_x3="n",
u2sort="s", u2sort="s",
u2ts="c", u2ts="c",
unpost=600, unpost=600,