From 470b504843e94cd7f26ff27287eb8a3c066e8a69 Mon Sep 17 00:00:00 2001 From: ed Date: Mon, 29 Sep 2025 21:50:13 +0000 Subject: [PATCH] raster favicons; closes #383, #473 --- copyparty/__main__.py | 1 + copyparty/authsrv.py | 11 +++++++++++ copyparty/cfg.py | 2 ++ copyparty/util.py | 7 +++++++ tests/util.py | 2 +- 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 9b75b1bb..1fc56b2e 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1769,6 +1769,7 @@ def add_ui(ap, retry: int): ap2.add_argument("--qdel", metavar="LVL", type=int, default=2, help="number of confirmations to show when deleting files (2/1/0)") ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files/folders matching \033[33mREGEX\033[0m in file list. WARNING: Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)") ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable") + ap2.add_argument("--ufavico", metavar="TXT", type=u, default="", help="URL to .ico/png/gif/svg file; \033[33m--favico\033[0m takes precedence unless disabled (volflag=ufavico)") ap2.add_argument("--ext-th", metavar="E=VP", type=u, action="append", help="\033[34mREPEATABLE:\033[0m use thumbnail-image \033[33mVP\033[0m for file-extension \033[33mE\033[0m, example: [\033[32mexe=/.res/exe.png\033[0m] (volflag=ext_th)") ap2.add_argument("--mpmc", type=u, default="", help=argparse.SUPPRESS) ap2.add_argument("--spinner", metavar="TXT", type=u, default="🌲", help="\033[33memoji\033[0m or \033[33memoji,css\033[0m Example: [\033[32m🥖,padding:0\033[0m]") diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index daa0c21a..0dc35e13 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -21,6 +21,7 @@ from .util import ( DEF_MTE, DEF_MTH, EXTS, + FAVICON_MIMES, HAVE_SQLITE3, IMPLICATIONS, MIMES, @@ -2510,6 +2511,16 @@ class AuthSrv(object): if "norobots" in vol.flags: head_s += META_NOBOTS + ico_url = vol.flags.get("ufavico") + if ico_url: + ico_ext = ico_url.split("?")[0].split(".")[-1].lower() + if ico_ext in FAVICON_MIMES: + zs = '\n' + head_s += zs % (FAVICON_MIMES[ico_ext], ico_url) + elif ico_ext == "ico": + zs = '\n' + head_s += zs % (ico_url,) + if head_s: vol.flags["html_head_s"] = head_s else: diff --git a/copyparty/cfg.py b/copyparty/cfg.py index 40b325cf..7acf75c7 100644 --- a/copyparty/cfg.py +++ b/copyparty/cfg.py @@ -88,6 +88,7 @@ def vf_vmap() -> dict[str, str]: "chmod_f", "dbd", "du_who", + "ufavico", "forget_ip", "hsortn", "html_head", @@ -302,6 +303,7 @@ flagcats = { "sort": "default sort order", "nsort": "natural-sort of leading digits in filenames", "hsortn": "number of sort-rules to add to media URLs", + "ufavico=URL": "per-volume favicon (.ico/png/gif/svg)", "unlist": "dont list files matching REGEX", "html_head=TXT": "includes TXT in the , or @PATH for file at PATH", "html_head_s=TXT": "additional static text in the html ", diff --git a/copyparty/util.py b/copyparty/util.py index e2ca6338..67391967 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -378,6 +378,13 @@ DAV_ALLPROP_L = [ DAV_ALLPROPS = set(DAV_ALLPROP_L) +FAVICON_MIMES = { + "gif": "image/gif", + "png": "image/png", + "svg": "image/svg+xml", +} + + MIMES = { "opus": "audio/ogg; codecs=opus", "owa": "audio/webm; codecs=opus", diff --git a/tests/util.py b/tests/util.py index 6bcbb9d7..e3663c8c 100644 --- a/tests/util.py +++ b/tests/util.py @@ -164,7 +164,7 @@ class Cfg(Namespace): ex = "ctl_re db_act forget_ip idp_cookie idp_store k304 loris no304 nosubtle qr_pin qr_wait re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo u2ow zipmaxn zipmaxs" ka.update(**{k: 0 for k in ex.split()}) - ex = "ah_alg bname chdir chmod_f chpw_db doctitle df exit favico ipa html_head html_head_d html_head_s idp_login idp_logout lg_sba lg_sbf log_fk md_sba md_sbf name og_desc og_site og_th og_title og_title_a og_title_v og_title_i opds_exts shr tcolor textfiles txt_eol unlist vname xff_src zipmaxt R RS SR" + ex = "ah_alg bname chdir chmod_f chpw_db doctitle df exit favico ipa html_head html_head_d html_head_s idp_login idp_logout lg_sba lg_sbf log_fk md_sba md_sbf name og_desc og_site og_th og_title og_title_a og_title_v og_title_i opds_exts shr tcolor textfiles txt_eol ufavico unlist vname xff_src zipmaxt R RS SR" ka.update(**{k: "" for k in ex.split()}) ex = "ban_403 ban_404 ban_422 ban_pw ban_pwc ban_url spinner"