mirror of
https://github.com/9001/copyparty.git
synced 2025-08-18 09:22:31 -06:00
music-thumbs: use embedded art as default (closes #252);
previous behavior can be restored with --th-spec-p 2 thumbnails cache (.hist/th/) must be deleted to take effect
This commit is contained in:
parent
d9046f7e01
commit
98d117b8ad
|
@ -1514,6 +1514,7 @@ def add_thumbnail(ap):
|
||||||
ap2.add_argument("--th-clean", metavar="SEC", type=int, default=43200, help="cleanup interval; 0=disabled")
|
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")
|
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-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")
|
||||||
|
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")
|
||||||
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
|
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
|
||||||
# https://github.com/libvips/libvips
|
# https://github.com/libvips/libvips
|
||||||
# https://stackoverflow.com/a/47612661
|
# https://stackoverflow.com/a/47612661
|
||||||
|
|
|
@ -2227,7 +2227,7 @@ class AuthSrv(object):
|
||||||
if vf not in vol.flags:
|
if vf not in vol.flags:
|
||||||
vol.flags[vf] = getattr(self.args, ga)
|
vol.flags[vf] = getattr(self.args, ga)
|
||||||
|
|
||||||
zs = "forget_ip gid nrand tail_who u2abort u2ow uid ups_who zip_who"
|
zs = "forget_ip gid nrand tail_who th_spec_p u2abort u2ow uid unp_who ups_who zip_who"
|
||||||
for k in zs.split():
|
for k in zs.split():
|
||||||
if k in vol.flags:
|
if k in vol.flags:
|
||||||
vol.flags[k] = int(vol.flags[k])
|
vol.flags[k] = int(vol.flags[k])
|
||||||
|
|
|
@ -112,6 +112,7 @@ def vf_vmap() -> dict[str, str]:
|
||||||
"tail_tmax",
|
"tail_tmax",
|
||||||
"tail_who",
|
"tail_who",
|
||||||
"tcolor",
|
"tcolor",
|
||||||
|
"th_spec_p",
|
||||||
"txt_eol",
|
"txt_eol",
|
||||||
"unlist",
|
"unlist",
|
||||||
"u2abort",
|
"u2abort",
|
||||||
|
@ -264,6 +265,7 @@ flagcats = {
|
||||||
"th3x": "3x resolution (y/n/fy/fn)",
|
"th3x": "3x resolution (y/n/fy/fn)",
|
||||||
"convt": "convert-to-image timeout in seconds",
|
"convt": "convert-to-image timeout in seconds",
|
||||||
"aconvt": "convert-to-audio timeout in seconds",
|
"aconvt": "convert-to-audio timeout in seconds",
|
||||||
|
"th_spec_p=1": "make spectrograms? 0=never 1=fallback 2=always",
|
||||||
"ext_th=s=/b.png": "use /b.png as thumbnail for file-extension s",
|
"ext_th=s=/b.png": "use /b.png as thumbnail for file-extension s",
|
||||||
},
|
},
|
||||||
"handlers\n(better explained in --help-handlers)": {
|
"handlers\n(better explained in --help-handlers)": {
|
||||||
|
|
|
@ -208,7 +208,7 @@ def au_unpk(
|
||||||
|
|
||||||
def ffprobe(
|
def ffprobe(
|
||||||
abspath: str, timeout: int = 60
|
abspath: str, timeout: int = 60
|
||||||
) -> tuple[dict[str, tuple[int, Any]], dict[str, list[Any]]]:
|
) -> tuple[dict[str, tuple[int, Any]], dict[str, list[Any]], list[Any], dict[str, Any]]:
|
||||||
cmd = [
|
cmd = [
|
||||||
b"ffprobe",
|
b"ffprobe",
|
||||||
b"-hide_banner",
|
b"-hide_banner",
|
||||||
|
@ -222,8 +222,17 @@ def ffprobe(
|
||||||
return parse_ffprobe(so)
|
return parse_ffprobe(so)
|
||||||
|
|
||||||
|
|
||||||
def parse_ffprobe(txt: str) -> tuple[dict[str, tuple[int, Any]], dict[str, list[Any]]]:
|
def parse_ffprobe(
|
||||||
"""ffprobe -show_format -show_streams"""
|
txt: str,
|
||||||
|
) -> tuple[dict[str, tuple[int, Any]], dict[str, list[Any]], list[Any], dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
txt: output from ffprobe -show_format -show_streams
|
||||||
|
returns:
|
||||||
|
* normalized tags
|
||||||
|
* original/raw tags
|
||||||
|
* list of streams
|
||||||
|
* format props
|
||||||
|
"""
|
||||||
streams = []
|
streams = []
|
||||||
fmt = {}
|
fmt = {}
|
||||||
g = {}
|
g = {}
|
||||||
|
@ -316,7 +325,7 @@ def parse_ffprobe(txt: str) -> tuple[dict[str, tuple[int, Any]], dict[str, list[
|
||||||
ret[rk] = v1
|
ret[rk] = v1
|
||||||
|
|
||||||
if ret.get("vc") == "ansi": # shellscript
|
if ret.get("vc") == "ansi": # shellscript
|
||||||
return {}, {}
|
return {}, {}, [], {}
|
||||||
|
|
||||||
for strm in streams:
|
for strm in streams:
|
||||||
for sk, sv in strm.items():
|
for sk, sv in strm.items():
|
||||||
|
@ -365,7 +374,7 @@ def parse_ffprobe(txt: str) -> tuple[dict[str, tuple[int, Any]], dict[str, list[
|
||||||
zero = int("0")
|
zero = int("0")
|
||||||
zd = {k: (zero, v) for k, v in ret.items()}
|
zd = {k: (zero, v) for k, v in ret.items()}
|
||||||
|
|
||||||
return zd, md
|
return zd, md, streams, fmt
|
||||||
|
|
||||||
|
|
||||||
def get_cover_from_epub(log: "NamedLogger", abspath: str) -> Optional[IO[bytes]]:
|
def get_cover_from_epub(log: "NamedLogger", abspath: str) -> Optional[IO[bytes]]:
|
||||||
|
@ -706,7 +715,7 @@ class MTag(object):
|
||||||
if not bos.path.isfile(abspath):
|
if not bos.path.isfile(abspath):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
ret, md = ffprobe(abspath, self.args.mtag_to)
|
ret, md, _, _ = ffprobe(abspath, self.args.mtag_to)
|
||||||
|
|
||||||
if self.args.mtag_vv:
|
if self.args.mtag_vv:
|
||||||
for zd in (ret, dict(md)):
|
for zd in (ret, dict(md)):
|
||||||
|
|
|
@ -612,7 +612,7 @@ class ThumbSrv(object):
|
||||||
|
|
||||||
def conv_ffmpeg(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
|
def conv_ffmpeg(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
|
||||||
self.wait4ram(0.2, tpath)
|
self.wait4ram(0.2, tpath)
|
||||||
ret, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
ret, _, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
||||||
if not ret:
|
if not ret:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -623,6 +623,17 @@ class ThumbSrv(object):
|
||||||
dur = ret[".dur"][1] if ".dur" in ret else 4
|
dur = ret[".dur"][1] if ".dur" in ret else 4
|
||||||
seek = [b"-ss", "{:.0f}".format(dur / 3).encode("utf-8")]
|
seek = [b"-ss", "{:.0f}".format(dur / 3).encode("utf-8")]
|
||||||
|
|
||||||
|
self._ffmpeg_im(abspath, tpath, fmt, vn, seek, b"0:v:0")
|
||||||
|
|
||||||
|
def _ffmpeg_im(
|
||||||
|
self,
|
||||||
|
abspath: str,
|
||||||
|
tpath: str,
|
||||||
|
fmt: str,
|
||||||
|
vn: VFS,
|
||||||
|
seek: list[bytes],
|
||||||
|
imap: bytes,
|
||||||
|
) -> None:
|
||||||
scale = "scale={0}:{1}:force_original_aspect_ratio="
|
scale = "scale={0}:{1}:force_original_aspect_ratio="
|
||||||
if "f" in fmt:
|
if "f" in fmt:
|
||||||
scale += "decrease,setsar=1:1"
|
scale += "decrease,setsar=1:1"
|
||||||
|
@ -641,7 +652,7 @@ class ThumbSrv(object):
|
||||||
cmd += seek
|
cmd += seek
|
||||||
cmd += [
|
cmd += [
|
||||||
b"-i", fsenc(abspath),
|
b"-i", fsenc(abspath),
|
||||||
b"-map", b"0:v:0",
|
b"-map", imap,
|
||||||
b"-vf", bscale,
|
b"-vf", bscale,
|
||||||
b"-frames:v", b"1",
|
b"-frames:v", b"1",
|
||||||
b"-metadata:s:v:0", b"rotate=0",
|
b"-metadata:s:v:0", b"rotate=0",
|
||||||
|
@ -710,7 +721,7 @@ class ThumbSrv(object):
|
||||||
raise sp.CalledProcessError(ret, (cmd[0], b"...", cmd[-1]))
|
raise sp.CalledProcessError(ret, (cmd[0], b"...", cmd[-1]))
|
||||||
|
|
||||||
def conv_waves(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
|
def conv_waves(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
|
||||||
ret, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
ret, _, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
||||||
if "ac" not in ret:
|
if "ac" not in ret:
|
||||||
raise Exception("not audio")
|
raise Exception("not audio")
|
||||||
|
|
||||||
|
@ -769,11 +780,31 @@ class ThumbSrv(object):
|
||||||
else:
|
else:
|
||||||
atomic_move(self.log, wtpath, tpath, vn.flags)
|
atomic_move(self.log, wtpath, tpath, vn.flags)
|
||||||
|
|
||||||
|
def conv_emb_cv(
|
||||||
|
self, abspath: str, tpath: str, fmt: str, vn: VFS, strm: dict[str, Any]
|
||||||
|
) -> None:
|
||||||
|
self.wait4ram(0.2, tpath)
|
||||||
|
self._ffmpeg_im(
|
||||||
|
abspath, tpath, fmt, vn, [], b"0:" + strm["index"].encode("ascii")
|
||||||
|
)
|
||||||
|
|
||||||
def conv_spec(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
|
def conv_spec(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
|
||||||
ret, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
ret, raw, strms, ctnr = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
||||||
if "ac" not in ret:
|
if "ac" not in ret:
|
||||||
raise Exception("not audio")
|
raise Exception("not audio")
|
||||||
|
|
||||||
|
want_spec = vn.flags.get("th_spec_p", 1)
|
||||||
|
if want_spec < 2:
|
||||||
|
for strm in strms:
|
||||||
|
if (
|
||||||
|
strm.get("codec_type") == "video"
|
||||||
|
and strm.get("DISPOSITION:attached_pic") == "1"
|
||||||
|
):
|
||||||
|
return self.conv_emb_cv(abspath, tpath, fmt, vn, strm)
|
||||||
|
|
||||||
|
if not want_spec:
|
||||||
|
raise Exception("spectrograms forbidden by volflag")
|
||||||
|
|
||||||
fext = abspath.split(".")[-1].lower()
|
fext = abspath.split(".")[-1].lower()
|
||||||
|
|
||||||
# https://trac.ffmpeg.org/ticket/10797
|
# https://trac.ffmpeg.org/ticket/10797
|
||||||
|
@ -859,7 +890,7 @@ class ThumbSrv(object):
|
||||||
raise Exception("disabled in server config")
|
raise Exception("disabled in server config")
|
||||||
|
|
||||||
self.wait4ram(0.2, tpath)
|
self.wait4ram(0.2, tpath)
|
||||||
tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
tags, rawtags, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
||||||
if "ac" not in tags:
|
if "ac" not in tags:
|
||||||
raise Exception("not audio")
|
raise Exception("not audio")
|
||||||
|
|
||||||
|
@ -897,7 +928,7 @@ class ThumbSrv(object):
|
||||||
raise Exception("flac not permitted in server config")
|
raise Exception("flac not permitted in server config")
|
||||||
|
|
||||||
self.wait4ram(0.2, tpath)
|
self.wait4ram(0.2, tpath)
|
||||||
tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
tags, _, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
||||||
if "ac" not in tags:
|
if "ac" not in tags:
|
||||||
raise Exception("not audio")
|
raise Exception("not audio")
|
||||||
|
|
||||||
|
@ -922,7 +953,7 @@ class ThumbSrv(object):
|
||||||
raise Exception("wav not permitted in server config")
|
raise Exception("wav not permitted in server config")
|
||||||
|
|
||||||
self.wait4ram(0.2, tpath)
|
self.wait4ram(0.2, tpath)
|
||||||
tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
tags, _, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
||||||
if "ac" not in tags:
|
if "ac" not in tags:
|
||||||
raise Exception("not audio")
|
raise Exception("not audio")
|
||||||
|
|
||||||
|
@ -957,7 +988,7 @@ class ThumbSrv(object):
|
||||||
raise Exception("disabled in server config")
|
raise Exception("disabled in server config")
|
||||||
|
|
||||||
self.wait4ram(0.2, tpath)
|
self.wait4ram(0.2, tpath)
|
||||||
tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
tags, rawtags, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
|
||||||
if "ac" not in tags:
|
if "ac" not in tags:
|
||||||
raise Exception("not audio")
|
raise Exception("not audio")
|
||||||
|
|
||||||
|
|
|
@ -155,7 +155,7 @@ class Cfg(Namespace):
|
||||||
ex = "gid uid"
|
ex = "gid uid"
|
||||||
ka.update(**{k: -1 for k in ex.split()})
|
ka.update(**{k: -1 for k in ex.split()})
|
||||||
|
|
||||||
ex = "hash_mt hsortn qdel safe_dedup srch_time tail_fd tail_rate u2abort u2j u2sz unp_who"
|
ex = "hash_mt hsortn qdel safe_dedup srch_time tail_fd tail_rate th_spec_p u2abort u2j u2sz unp_who"
|
||||||
ka.update(**{k: 1 for k in ex.split()})
|
ka.update(**{k: 1 for k in ex.split()})
|
||||||
|
|
||||||
ex = "ac_convt au_vol dl_list mtab_age reg_cap s_thead s_tbody tail_tmax tail_who th_convt ups_who zip_who"
|
ex = "ac_convt au_vol dl_list mtab_age reg_cap s_thead s_tbody tail_tmax tail_who th_convt ups_who zip_who"
|
||||||
|
|
Loading…
Reference in a new issue