diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 1d8a4056..b6ea33fe 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1377,6 +1377,7 @@ def add_thumbnail(ap): ap2.add_argument("--th-r-ffi", metavar="T,T", type=u, default="apng,avif,avifs,bmp,cbz,dds,dib,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,icns,ico,jp2,jpeg,jpg,jpx,jxl,pbm,pcx,pfm,pgm,png,pnm,ppm,psd,qoi,sgi,tga,tif,tiff,webp,xbm,xpm", help="image formats to decode using ffmpeg") ap2.add_argument("--th-r-ffv", metavar="T,T", type=u, default="3gp,asf,av1,avc,avi,flv,h264,h265,hevc,m4v,mjpeg,mjpg,mkv,mov,mp4,mpeg,mpeg2,mpegts,mpg,mpg2,mts,nut,ogm,ogv,rm,ts,vob,webm,wmv", help="video formats to decode using ffmpeg") ap2.add_argument("--th-r-ffa", metavar="T,T", type=u, default="aac,ac3,aif,aiff,alac,alaw,amr,apac,ape,au,bonk,dfpwm,dts,flac,gsm,ilbc,it,itgz,itxz,itz,m4a,mdgz,mdxz,mdz,mo3,mod,mp2,mp3,mpc,mptm,mt2,mulaw,ogg,okt,opus,ra,s3m,s3gz,s3xz,s3z,tak,tta,ulaw,wav,wma,wv,xm,xmgz,xmxz,xmz,xpk", help="audio formats to decode using ffmpeg") + ap2.add_argument("--th-spec-cnv", metavar="T,T", type=u, default="it,itgz,itxz,itz,mdgz,mdxz,mdz,mo3,mod,s3m,s3gz,s3xz,s3z,xm,xmgz,xmxz,xmz,xpk", help="audio formats which provoke https://trac.ffmpeg.org/ticket/10797 (huge ram usage for s3xmodit spectrograms)") ap2.add_argument("--au-unpk", metavar="E=F.C", type=u, default="mdz=mod.zip, mdgz=mod.gz, mdxz=mod.xz, s3z=s3m.zip, s3gz=s3m.gz, s3xz=s3m.xz, xmz=xm.zip, xmgz=xm.gz, xmxz=xm.xz, itz=it.zip, itgz=it.gz, itxz=it.xz, cbz=jpg.cbz", help="audio/image formats to decompress before passing to ffmpeg") diff --git a/copyparty/th_srv.py b/copyparty/th_srv.py index dc07556f..14ea4796 100644 --- a/copyparty/th_srv.py +++ b/copyparty/th_srv.py @@ -7,6 +7,7 @@ import os import re import shutil import subprocess as sp +import tempfile import threading import time @@ -19,6 +20,7 @@ from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, au_unpk, ffprobe from .util import BytesIO # type: ignore from .util import ( FFMPEG_URL, + VF_CAREFUL, Cooldown, Daemon, afsenc, @@ -49,6 +51,7 @@ HAVE_WEBP = False EXTS_TH = set(["jpg", "webp", "png"]) EXTS_AC = set(["opus", "owa", "caf", "mp3"]) +EXTS_SPEC_SAFE = set("aif aiff flac mp3 opus wav".split()) PTN_TS = re.compile("^-?[0-9a-f]{8,10}$") @@ -167,12 +170,15 @@ class ThumbSrv(object): self.mutex = threading.Lock() self.busy: dict[str, list[threading.Condition]] = {} + self.untemp: dict[str, list[str]] = {} self.ram: dict[str, float] = {} self.memcond = threading.Condition(self.mutex) self.stopping = False self.rm_nullthumbs = True # forget failed conversions on startup self.nthr = max(1, self.args.th_mt) + self.exts_spec_unsafe = set(self.args.th_spec_cnv.split(",")) + 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)) @@ -413,10 +419,18 @@ class ThumbSrv(object): self.log(t % (ttpath, tpath, ex), 3) pass + untemp = [] with self.mutex: subs = self.busy[tpath] del self.busy[tpath] self.ram.pop(ttpath, None) + untemp = self.untemp.pop(ttpath, None) or [] + + for ap in untemp: + try: + wunlink(self.log, ap, VF_CAREFUL) + except: + pass for x in subs: with x: @@ -670,15 +684,43 @@ class ThumbSrv(object): if "ac" not in ret: raise Exception("not audio") + fext = abspath.split(".")[-1].lower() + # https://trac.ffmpeg.org/ticket/10797 # expect 1 GiB every 600 seconds when duration is tricky; # simple filetypes are generally safer so let's special-case those - safe = ("flac", "wav", "aif", "aiff", "opus") - coeff = 1800 if abspath.split(".")[-1].lower() in safe else 600 - dur = ret[".dur"][1] if ".dur" in ret else 300 + coeff = 1800 if fext in EXTS_SPEC_SAFE else 600 + dur = ret[".dur"][1] if ".dur" in ret else 900 need = 0.2 + dur / coeff self.wait4ram(need, tpath) + infile = abspath + if dur >= 900 or fext in self.exts_spec_unsafe: + with tempfile.NamedTemporaryFile(suffix=".spec.flac", delete=False) as f: + f.write(b"h") + infile = f.name + try: + self.untemp[tpath].append(infile) + except: + self.untemp[tpath] = [infile] + + # fmt: off + cmd = [ + b"ffmpeg", + b"-nostdin", + b"-v", b"error", + b"-hide_banner", + b"-i", fsenc(abspath), + b"-map", b"0:a:0", + b"-ac", b"1", + b"-ar", b"48000", + b"-sample_fmt", b"s16", + b"-t", b"900", + b"-y", fsenc(infile), + ] + # fmt: on + self._run_ff(cmd, vn) + fc = "[0:a:0]aresample=48000{},showspectrumpic=s=" if "3" in fmt: fc += "1280x1024,crop=1420:1056:70:48[o]" @@ -698,7 +740,7 @@ class ThumbSrv(object): b"-nostdin", b"-v", b"error", b"-hide_banner", - b"-i", fsenc(abspath), + b"-i", fsenc(infile), b"-filter_complex", fc.encode("utf-8"), b"-map", b"[o]", b"-frames:v", b"1",