diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 0f25d2a7..5ff828db 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -1846,7 +1846,9 @@ class HttpCli(object): if len(ext) > 11: ext = "⋯" + ext[-9:] - mime, ico = self.ico.get(ext, not exact) + # chrome cannot handle more than ~2000 unique SVGs + chrome = " rv:" not in self.ua + mime, ico = self.ico.get(ext, not exact, chrome) dt = datetime.utcfromtimestamp(self.E.t0) lm = dt.strftime("%a, %d %b %Y %H:%M:%S GMT") diff --git a/copyparty/ico.py b/copyparty/ico.py index 721f17a1..3ef812de 100644 --- a/copyparty/ico.py +++ b/copyparty/ico.py @@ -6,13 +6,15 @@ import colorsys import hashlib from .__init__ import PY2 +from .th_srv import HAVE_PIL +from .util import BytesIO class Ico(object): def __init__(self, args: argparse.Namespace) -> None: self.args = args - def get(self, ext: str, as_thumb: bool) -> tuple[str, bytes]: + def get(self, ext: str, as_thumb: bool, chrome: bool) -> tuple[str, bytes]: """placeholder to make thumbnails not break""" zb = hashlib.sha1(ext.encode("utf-8")).digest()[2:4] @@ -24,10 +26,44 @@ class Ico(object): ci = [int(x * 255) for x in list(c1) + list(c2)] c = "".join(["{:02x}".format(x) for x in ci]) + w = 100 h = 30 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))) + sw, sh = self.args.th_size.split("x") + h = int(100 / (float(sw) / float(sh))) + w = 100 + + if chrome and as_thumb: + # cannot handle more than ~2000 unique SVGs + if HAVE_PIL: + # svg: 3s, cache: 6s, this: 8s + from PIL import Image, ImageDraw + + h = int(64 * h / w) + w = 64 + img = Image.new("RGB", (w, h), "#" + c[:6]) + pb = ImageDraw.Draw(img) + tw, th = pb.textsize(ext) + pb.text(((w - tw) // 2, (h - th) // 2), ext, fill="#" + c[6:]) + img = img.resize((w * 3, h * 3), Image.Resampling.NEAREST) + + buf = BytesIO() + img.save(buf, format="PNG", compress_level=1) + return "image/png", buf.getvalue() + + elif False: + # 48s, too slow + import pyvips + + h = int(192 * h / w) + w = 192 + img = pyvips.Image.text( + ext, width=w, height=h, dpi=192, align=pyvips.Align.CENTRE + ) + img = img.ifthenelse(ci[3:], ci[:3], blend=True) + # i = i.resize(3, kernel=pyvips.Kernel.NEAREST) + buf = img.write_to_buffer(".png[compression=1]") + return "image/png", buf svg = """\ diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index 1ab22ead..f9af6b83 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -3787,8 +3787,11 @@ var thegrid = (function () { if (!r.dirty) return r.loadsel(); - var html = []; - var files = QSA('#files>tbody>tr>td:nth-child(2) a[id]'); + var html = [], + svgs = new Set(), + max_svgs = CHROME ? 500 : 5000, + files = QSA('#files>tbody>tr>td:nth-child(2) a[id]'); + for (var a = 0, aa = files.length; a < aa; a++) { var ao = files[a], ohref = esc(ao.getAttribute('href')), @@ -3823,7 +3826,14 @@ var thegrid = (function () { if (!ihref) { ihref = 'unk.'; } - ihref = '/.cpr/ico/' + ihref.slice(0, -1); + var ext = ihref.slice(0, -1); + if (!svgs.has(ext)) { + if (svgs.size < max_svgs) + svgs.add(ext); + else + ext = "unk"; + } + ihref = '/.cpr/ico/' + ext; } ihref += (ihref.indexOf('?') > 0 ? '&' : '?') + 'cache=i';