mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
create webp thumbnails by default
This commit is contained in:
parent
6b065d507d
commit
5d63949e98
|
@ -253,7 +253,9 @@ def run_argparse(argv, formatter):
|
|||
ap2.add_argument("--no-thumb", action="store_true", help="disable all thumbnails")
|
||||
ap2.add_argument("--no-vthumb", action="store_true", help="disable video thumbnails")
|
||||
ap2.add_argument("--th-size", metavar="WxH", default="320x256", help="thumbnail res")
|
||||
ap2.add_argument("--th-nocrop", action="store_true", help="dynamic height (no crop)")
|
||||
ap2.add_argument("--th-no-crop", action="store_true", help="dynamic height; show full image")
|
||||
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-poke", metavar="SEC", type=int, default=300, help="activity labeling cooldown")
|
||||
ap2.add_argument("--th-clean", metavar="SEC", type=int, default=1800, help="cleanup interval")
|
||||
ap2.add_argument("--th-maxage", metavar="SEC", type=int, default=604800, help="max folder age")
|
||||
|
|
|
@ -1385,10 +1385,11 @@ class HttpCli(object):
|
|||
if rem.startswith(".hist/up2k."):
|
||||
raise Pebkac(403)
|
||||
|
||||
if "th" in self.uparam:
|
||||
th_fmt = self.uparam.get("th")
|
||||
if th_fmt is not None:
|
||||
thp = None
|
||||
if self.thumbcli:
|
||||
thp = self.thumbcli.get(vn.realpath, rem, int(st.st_mtime))
|
||||
thp = self.thumbcli.get(vn.realpath, rem, int(st.st_mtime), th_fmt)
|
||||
|
||||
if thp:
|
||||
return self.tx_file(thp)
|
||||
|
|
|
@ -22,7 +22,7 @@ class Ico(object):
|
|||
c = "".join(["{:02x}".format(x) for x in c])
|
||||
|
||||
h = 30
|
||||
if not self.args.th_nocrop and as_thumb:
|
||||
if not self.args.th_no_crop and as_thumb:
|
||||
w, h = self.args.th_size.split("x")
|
||||
h = int(100 / (float(w) / float(h)))
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ class ThumbCli(object):
|
|||
# cache on both sides for less broker spam
|
||||
self.cooldown = Cooldown(self.args.th_poke)
|
||||
|
||||
def get(self, ptop, rem, mtime):
|
||||
def get(self, ptop, rem, mtime, fmt):
|
||||
ext = rem.rsplit(".")[-1].lower()
|
||||
if ext not in THUMBABLE:
|
||||
return None
|
||||
|
@ -21,7 +21,13 @@ class ThumbCli(object):
|
|||
if self.args.no_vthumb and ext in FMT_FF:
|
||||
return None
|
||||
|
||||
tpath = thumb_path(ptop, rem, mtime)
|
||||
if fmt == "w" and self.args.th_no_webp:
|
||||
fmt = "j"
|
||||
|
||||
if fmt == "j" and self.args.th_no_jpg:
|
||||
fmt = "w"
|
||||
|
||||
tpath = thumb_path(ptop, rem, mtime, fmt)
|
||||
ret = None
|
||||
try:
|
||||
st = os.stat(tpath)
|
||||
|
@ -39,5 +45,5 @@ class ThumbCli(object):
|
|||
|
||||
return ret
|
||||
|
||||
x = self.broker.put(True, "thumbsrv.get", ptop, rem, mtime)
|
||||
x = self.broker.put(True, "thumbsrv.get", ptop, rem, mtime, fmt)
|
||||
return x.get()
|
||||
|
|
|
@ -51,7 +51,7 @@ if HAVE_FFMPEG and HAVE_FFPROBE:
|
|||
THUMBABLE.update(FMT_FF)
|
||||
|
||||
|
||||
def thumb_path(ptop, rem, mtime):
|
||||
def thumb_path(ptop, rem, mtime, fmt):
|
||||
# base16 = 16 = 256
|
||||
# b64-lc = 38 = 1444
|
||||
# base64 = 64 = 4096
|
||||
|
@ -72,7 +72,9 @@ def thumb_path(ptop, rem, mtime):
|
|||
h = hashlib.sha512(fsenc(fn)).digest()[:24]
|
||||
fn = base64.urlsafe_b64encode(h).decode("ascii")[:24]
|
||||
|
||||
return "{}/.hist/th/{}/{}.{:x}.jpg".format(ptop, rd, fn, int(mtime))
|
||||
return "{}/.hist/th/{}/{}.{:x}.{}".format(
|
||||
ptop, rd, fn, int(mtime), "webp" if fmt == "w" else "jpg"
|
||||
)
|
||||
|
||||
|
||||
class ThumbSrv(object):
|
||||
|
@ -129,8 +131,8 @@ class ThumbSrv(object):
|
|||
with self.mutex:
|
||||
return not self.nthr
|
||||
|
||||
def get(self, ptop, rem, mtime):
|
||||
tpath = thumb_path(ptop, rem, mtime)
|
||||
def get(self, ptop, rem, mtime, fmt):
|
||||
tpath = thumb_path(ptop, rem, mtime, fmt)
|
||||
abspath = os.path.join(ptop, rem)
|
||||
cond = threading.Condition()
|
||||
with self.mutex:
|
||||
|
@ -207,7 +209,7 @@ class ThumbSrv(object):
|
|||
|
||||
def conv_pil(self, abspath, tpath):
|
||||
with Image.open(abspath) as im:
|
||||
crop = not self.args.th_nocrop
|
||||
crop = not self.args.th_no_crop
|
||||
res2 = self.res
|
||||
if crop:
|
||||
res2 = (res2[0] * 2, res2[1] * 2)
|
||||
|
@ -222,7 +224,15 @@ class ThumbSrv(object):
|
|||
if im.mode not in ("RGB", "L"):
|
||||
im = im.convert("RGB")
|
||||
|
||||
im.save(tpath, quality=50)
|
||||
if tpath.endswith(".webp"):
|
||||
# quality 80 = pillow-default
|
||||
# quality 75 = ffmpeg-default
|
||||
# method 0 = pillow-default, fast
|
||||
# method 4 = ffmpeg-default
|
||||
# method 6 = max, slow
|
||||
im.save(tpath, quality=40, method=6)
|
||||
else:
|
||||
im.save(tpath, quality=40) # default=75
|
||||
|
||||
def conv_ffmpeg(self, abspath, tpath):
|
||||
ret, _ = ffprobe(abspath)
|
||||
|
@ -231,7 +241,7 @@ class ThumbSrv(object):
|
|||
seek = "{:.0f}".format(dur / 3)
|
||||
|
||||
scale = "scale={0}:{1}:force_original_aspect_ratio="
|
||||
if self.args.th_nocrop:
|
||||
if self.args.th_no_crop:
|
||||
scale += "decrease,setsar=1:1"
|
||||
else:
|
||||
scale += "increase,crop={0}:{1},setsar=1:1"
|
||||
|
@ -249,10 +259,23 @@ class ThumbSrv(object):
|
|||
scale,
|
||||
b"-vframes",
|
||||
b"1",
|
||||
b"-q:v",
|
||||
b"6",
|
||||
fsenc(tpath),
|
||||
]
|
||||
|
||||
if tpath.endswith(".jpg"):
|
||||
cmd += [
|
||||
b"-q:v",
|
||||
b"6", # default=??
|
||||
]
|
||||
else:
|
||||
cmd += [
|
||||
b"-q:v",
|
||||
b"50", # default=75
|
||||
b"-compression_level:v",
|
||||
b"6", # default=4, 0=fast, 6=max
|
||||
]
|
||||
|
||||
cmd += [fsenc(tpath)]
|
||||
|
||||
p = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE)
|
||||
p.communicate()
|
||||
|
||||
|
|
|
@ -944,6 +944,9 @@ def guess_mime(url, fallback="application/octet-stream"):
|
|||
if url.endswith(".md"):
|
||||
return ["text/plain; charset=UTF-8"]
|
||||
|
||||
if url.endswith(".webp"):
|
||||
return ["image/webp"]
|
||||
|
||||
return mimetypes.guess_type(url) or fallback
|
||||
|
||||
|
||||
|
|
|
@ -29,6 +29,19 @@ ebi('widget').innerHTML = (
|
|||
);
|
||||
|
||||
|
||||
var have_webp = null;
|
||||
(function () {
|
||||
var img = new Image();
|
||||
img.onload = function () {
|
||||
have_webp = img.width > 0 && img.height > 0;
|
||||
};
|
||||
img.onerror = function () {
|
||||
have_webp = false;
|
||||
};
|
||||
img.src = "data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA";
|
||||
})();
|
||||
|
||||
|
||||
// extract songs + add play column
|
||||
function MPlayer() {
|
||||
this.id = Date.now();
|
||||
|
@ -816,6 +829,9 @@ var thegrid = (function () {
|
|||
}
|
||||
|
||||
function loadgrid() {
|
||||
if (have_webp === null)
|
||||
return setTimeout(loadgrid, 50);
|
||||
|
||||
if (!r.dirty)
|
||||
return r.loadsel();
|
||||
|
||||
|
@ -832,7 +848,7 @@ var thegrid = (function () {
|
|||
ihref = '/.cpr/ico/folder'
|
||||
}
|
||||
else if (r.thumbs) {
|
||||
ihref += ihref.indexOf('?') === -1 ? '?th' : '&th';
|
||||
ihref += (ihref.indexOf('?') === -1 ? '?' : '&') + 'th=' + (have_webp ? 'w' : 'j');
|
||||
}
|
||||
else {
|
||||
var ar = href.split('?')[0].split('.');
|
||||
|
|
Loading…
Reference in a new issue