th-covers volflag; no disables folderthumbs

This commit is contained in:
ed 2026-07-17 22:18:17 +00:00
parent aa86235360
commit 856fada3c7
8 changed files with 26 additions and 46 deletions

View file

@ -782,6 +782,7 @@ audio files are converted into spectrograms using FFmpeg unless you `--no-athumb
images with the following names (see `--th-covers`) become the thumbnail of the folder they're in: `folder.png`, `folder.jpg`, `cover.png`, `cover.jpg`
* the order is significant, so if both `cover.png` and `folder.jpg` exist in a folder, it will pick the first matching `--th-covers` entry (`folder.jpg`)
* and, if you enable [file indexing](#file-indexing), it will also try those names as dotfiles (`.folder.jpg` and so), and then fallback on the first picture in the folder (if it has any pictures at all)
* disable folderthumbs with `--th-covers no`
enabling `multiselect` lets you click files to select them, and then shift-click another file for range-select
* `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

View file

@ -1807,7 +1807,7 @@ def add_thumbnail(ap):
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")
ap2.add_argument("--th-pregen", metavar="F,F", type=u, default="", help="pregenerate thumbnails on startup; \033[33mF,F\033[0m is comma-separated list of formats; example: [\033[32mj,jf,w,w3,wf,wf3,x,xf\033[0m] NOTE: remember to set \033[33m--th-maxage 123456789\033[0m (volflag=th_pregen)")
ap2.add_argument("--th-pre-rl", metavar="SEC", type=int, default=30, help="while pregen is running, ratelimit the thumbnailer logger to one message every \033[33mSEC\033[0m seconds (only works with \033[33m-j1\033[0m); set 0 to disable ratelimit")
ap2.add_argument("--th-covers", metavar="N,N", type=u, default="folder.png,folder.jpg,cover.png,cover.jpg", help="folder thumbnails to stat/look for; enabling \033[33m-e2d\033[0m will make these case-insensitive, and try them as dotfiles (.folder.jpg), and also automatically select thumbnails for all folders that contain pics, even if none match this pattern")
ap2.add_argument("--th-covers", metavar="N,N", type=u, default="folder.png,folder.jpg,cover.png,cover.jpg", help="folder thumbnails to stat/look for; enabling \033[33m-e2d\033[0m will make these case-insensitive, and try them as dotfiles (.folder.jpg), and also automatically select thumbnails for all folders that contain pics, even if none match this pattern (volflag=th_covers)")
ap2.add_argument("--th-spec-p", metavar="N", type=u, default=1, help="for music, do spectrograms or embedded coverart? [\033[32m0\033[0m]=only-art, [\033[32m1\033[0m]=prefer-art, [\033[32m2\033[0m]=only-spec")
ap2.add_argument("--th-spec-fl", action="store_true", help="generate spectrograms with logarithmic frequency scale instead of linear")
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html

View file

@ -2612,6 +2612,13 @@ class AuthSrv(object):
t = "WARNING: volume [/%s]: invalid value specified for ext-th: %s"
self.log(t % (vol.vpath, etv), 3)
zsl = [x.strip() for x in vol.flags["th_covers"].split(",")]
zsl = [x for x in zsl if x not in ("", "no")]
vol.flags["th_coversl"] = zsl
vol.flags["th_coversd"] = zsl + ["." + x for x in zsl]
vol.flags["th_covers_set"] = set(vol.flags["th_coversl"])
vol.flags["th_coversd_set"] = set(vol.flags["th_coversd"])
zsl = [x.strip() for x in vol.flags["rw_edit"].split(",")]
zsl = [x for x in zsl if x]
vol.flags["rw_edit"] = ",".join(zsl)
@ -3789,7 +3796,7 @@ class AuthSrv(object):
"",
]
csv = set("i p th_covers zm_on zm_off zs_on zs_off".split())
csv = set("i p zm_on zm_off zs_on zs_off".split())
zs = "c ihead ohead mtm mtp on403 on404 xac xad xar xau xiu xban xbc xbd xbr xbu xm"
lst = set(zs.split())
askip = set("a v c vc cgen exp_lg exp_md theme".split())

View file

@ -157,6 +157,7 @@ def vf_vmap() -> dict[str, str]:
"tail_tmax",
"tail_who",
"tcolor",
"th_covers",
"th_pregen",
"th_qv",
"th_qvx",
@ -321,6 +322,7 @@ flagcats = {
"th3x": "3x resolution (y/n/fy/fn)",
"th_qv=40": "webp/jpg thumbnail quality (10~90)",
"th_qvx=40": "jxl thumbnail quality (10~90)",
"th_covers=a.jpg,a.png": "filenames to prefer as folder-thumbs",
"convt": "convert-to-image timeout in seconds",
"aconvt": "convert-to-audio timeout in seconds",
"th_spec_p=1": "make spectrograms? 0=never 1=fallback 2=always",

View file

@ -6856,7 +6856,8 @@ class HttpCli(object):
nothumb = "dthumb" in dbv.flags
if is_dir:
vrem = vrem.rstrip("/")
if nothumb:
cvs = dbv.flags["th_coversl"]
if nothumb or not cvs:
pass
elif icur and vrem:
q = "select fn from cv where rd=? and dn=?"
@ -6873,7 +6874,7 @@ class HttpCli(object):
except:
pass
else:
for fn in self.args.th_covers:
for fn in cvs:
fp = os.path.join(abspath, fn)
try:
st = bos.stat(fp)
@ -7636,7 +7637,7 @@ class HttpCli(object):
self.conn.hsrv.j2[tpl] = j2env.get_template(tname)
thumb = ""
is_pic = is_vid = is_au = False
for fn in self.args.th_coversd:
for fn in vn.flags["th_coversd"]:
if fn in lnames:
thumb = lnames[fn]
break

View file

@ -1183,14 +1183,6 @@ class SvcHub(object):
if al.rsp_jtr:
al.rsp_slp = 0.000001
zsl = al.th_covers.split(",")
zsl = [x.strip() for x in zsl]
zsl = [x for x in zsl if x]
al.th_covers = zsl
al.th_coversd = zsl + ["." + x for x in zsl]
al.th_covers_set = set(al.th_covers)
al.th_coversd_set = set(al.th_coversd)
zs = al.use_bwrap.strip().lower()
if zs == "a":
if not HAVE_BWRAP:

View file

@ -1159,7 +1159,7 @@ class Up2k(object):
ft = "\033[0;32m{}{:.0}"
ff = "\033[0;35m{}{:.0}"
fv = "\033[0;36m{}:\033[90m{}"
zs = "bcasechk du_iwho emb_all emb_lgs emb_mds ext_th_d html_head html_head_d html_head_s ls_q_m put_name2 mv_re_r mv_re_t rm_re_r rm_re_t oh_f oh_g rw_edit_set srch_re_dots srch_re_nodot zipmax zipmaxn_v zipmaxs_v"
zs = "bcasechk du_iwho emb_all emb_lgs emb_mds ext_th_d html_head html_head_d html_head_s ls_q_m put_name2 mv_re_r mv_re_t rm_re_r rm_re_t oh_f oh_g rw_edit_set srch_re_dots srch_re_nodot th_coversd th_coversl th_covers_set th_coversd_set zipmax zipmaxn_v zipmaxs_v"
fx = set(zs.split())
fd = vf_bmap()
fd.update(vf_cmap())
@ -1431,6 +1431,8 @@ class Up2k(object):
rtop,
rei,
reh,
vol.flags["th_coversd"],
vol.flags["th_coversd_set"],
n4g,
ffat,
[],
@ -1508,6 +1510,8 @@ class Up2k(object):
rcdir: str,
rei: Optional[Pattern[str]],
reh: Optional[Pattern[str]],
th_cvd: list[str],
th_cvds: set[str],
n4g: bool,
ffat: bool,
seen: list[str],
@ -1533,8 +1537,6 @@ class Up2k(object):
cv = vcv = acv = ""
e_d = {}
th_cvd = self.args.th_coversd
th_cvds = self.args.th_coversd_set
scan_pr_s = self.args.scan_pr_s
assert self.pp and self.mem_cur # !rm
@ -1601,6 +1603,8 @@ class Up2k(object):
rap,
rei,
reh,
th_cvd,
th_cvds,
n4g,
fat32,
seen,
@ -1631,7 +1635,7 @@ class Up2k(object):
rsz += sz
files.append((sz, lmod, iname))
if sz:
if sz and th_cvd:
liname = iname.lower()
ext = liname.rsplit(".", 1)[-1]
if (
@ -4117,31 +4121,7 @@ class Up2k(object):
with self.rescan_cond:
self.rescan_cond.notify_all()
if rd and sz and fn.lower() in self.args.th_coversd_set:
# wasteful; db_add will re-index actual covers
# but that won't catch existing files
crd, cdn = rd.rsplit("/", 1) if "/" in rd else ("", rd)
try:
q = "select fn from cv where rd=? and dn=?"
db_cv = db.execute(q, (crd, cdn)).fetchone()[0]
db_lcv = db_cv.lower()
if db_lcv in self.args.th_coversd_set:
idx_db = self.args.th_coversd.index(db_lcv)
idx_fn = self.args.th_coversd.index(fn.lower())
add_cv = idx_fn < idx_db
else:
add_cv = True
except:
add_cv = True
if add_cv:
try:
db.execute("delete from cv where rd=? and dn=?", (crd, cdn))
db.execute("insert into cv values (?,?,?)", (crd, cdn, fn))
except:
pass
if "nodirsz" not in vflags:
if vflags and "nodirsz" not in vflags:
try:
q = "update ds set nf=nf+1, sz=sz+? where rd=?"
q2 = "insert into ds values(?,?,1)"

View file

@ -223,10 +223,7 @@ class Cfg(Namespace):
sort="href",
srch_hits=99999,
SRS="/",
th_covers=["folder.png"],
th_coversd=["folder.png"],
th_covers_set=set(["folder.png"]),
th_coversd_set=set(["folder.png"]),
th_covers="folder.png",
th_crop="y",
th_size="320x256",
th_x3="n",