From 6b065d507d41c9c1eb9f4a1e4b15be7ca578647b Mon Sep 17 00:00:00 2001 From: ed Date: Fri, 28 May 2021 01:46:27 +0200 Subject: [PATCH] crop thumbs for AESTHETICS --- copyparty/__main__.py | 3 ++- copyparty/httpcli.py | 2 +- copyparty/httpconn.py | 2 +- copyparty/ico.py | 15 ++++++++++----- copyparty/th_srv.py | 33 +++++++++++++++++++++++++-------- 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 64b0e2cf..3b3996de 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -252,7 +252,8 @@ def run_argparse(argv, formatter): ap2 = ap.add_argument_group('thumbnail options') 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("--thumbsz", metavar="WxH", default="352x352", help="thumbnail res") + 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-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") diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index d8647124..4f3ff5d4 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -1225,7 +1225,7 @@ class HttpCli(object): if len(ext) > 11: ext = "⋯" + ext[-9:] - mime, ico = self.ico.get(ext) + mime, ico = self.ico.get(ext, not exact) dt = datetime.utcfromtimestamp(E.t0) lm = dt.strftime("%a, %d %b %Y %H:%M:%S GMT") diff --git a/copyparty/httpconn.py b/copyparty/httpconn.py index cbcaeb2c..b505f2dd 100644 --- a/copyparty/httpconn.py +++ b/copyparty/httpconn.py @@ -39,7 +39,7 @@ class HttpConn(object): enth = HAVE_PIL and not self.args.no_thumb self.thumbcli = ThumbCli(hsrv.broker) if enth else None - self.ico = Ico() + self.ico = Ico(self.args) self.t0 = time.time() self.nbyte = 0 diff --git a/copyparty/ico.py b/copyparty/ico.py index 450d9f23..ea1eb949 100644 --- a/copyparty/ico.py +++ b/copyparty/ico.py @@ -5,10 +5,10 @@ from .__init__ import PY2 class Ico(object): - def __init__(self): - pass + def __init__(self, args): + self.args = args - def get(self, ext): + def get(self, ext, as_thumb): """placeholder to make thumbnails not break""" h = hashlib.md5(ext.encode("utf-8")).digest()[:2] @@ -21,14 +21,19 @@ class Ico(object): c = [int(x * 255) for x in c] c = "".join(["{:02x}".format(x) for x in c]) + h = 30 + if not self.args.th_nocrop and as_thumb: + w, h = self.args.th_size.split("x") + h = int(100 / (float(w) / float(h))) + svg = """\ - + {} """ - svg = svg.format(c[:6], c[6:], ext).encode("utf-8") + svg = svg.format(h, c[:6], c[6:], ext).encode("utf-8") return ["image/svg+xml", svg] diff --git a/copyparty/th_srv.py b/copyparty/th_srv.py index 2d630699..09341b7a 100644 --- a/copyparty/th_srv.py +++ b/copyparty/th_srv.py @@ -18,7 +18,7 @@ if not PY2: try: HAVE_PIL = True - from PIL import Image + from PIL import Image, ImageOps try: HAVE_HEIF = True @@ -83,7 +83,7 @@ class ThumbSrv(object): self.args = hub.args self.log_func = hub.log - res = hub.args.thumbsz.split("x") + res = hub.args.th_size.split("x") self.res = tuple([int(x) for x in res]) self.poke_cd = Cooldown(self.args.th_poke) @@ -207,18 +207,35 @@ class ThumbSrv(object): def conv_pil(self, abspath, tpath): with Image.open(abspath) as im: + crop = not self.args.th_nocrop + res2 = self.res + if crop: + res2 = (res2[0] * 2, res2[1] * 2) + + try: + im.thumbnail(res2, resample=Image.LANCZOS) + if crop: + im = ImageOps.fit(im, self.res, method=Image.LANCZOS) + except: + im.thumbnail(self.res) + if im.mode not in ("RGB", "L"): im = im.convert("RGB") - im.thumbnail(self.res) - im.save(tpath) + im.save(tpath, quality=50) def conv_ffmpeg(self, abspath, tpath): - ret, _ = run_ffprobe(abspath) + ret, _ = ffprobe(abspath) dur = ret[".dur"][1] seek = "{:.0f}".format(dur / 3) - scale = "scale=w={}:h={}:force_original_aspect_ratio=decrease" + + scale = "scale={0}:{1}:force_original_aspect_ratio=" + if self.args.th_nocrop: + scale += "decrease,setsar=1:1" + else: + scale += "increase,crop={0}:{1},setsar=1:1" + scale = scale.format(*list(self.res)).encode("utf-8") cmd = [ b"ffmpeg", @@ -233,11 +250,11 @@ class ThumbSrv(object): b"-vframes", b"1", b"-q:v", - b"5", + b"6", fsenc(tpath), ] p = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE) - r = p.communicate() + p.communicate() def poke(self, tdir): if not self.poke_cd.poke(tdir):