Compare commits

...

2 commits

Author SHA1 Message Date
ed
470b504843 raster favicons; closes #383, #473 2025-09-29 21:50:13 +00:00
ed
435db14798 pregen html_head when static 2025-09-29 21:47:53 +00:00
17 changed files with 71 additions and 20 deletions

View file

@ -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]")
@ -1776,6 +1777,7 @@ def add_ui(ap, retry: int):
ap2.add_argument("--js-browser", metavar="L", type=u, default="", help="URL to additional JS to include in the filebrowser html")
ap2.add_argument("--js-other", metavar="L", type=u, default="", help="URL to additional JS to include in all other pages")
ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages (except for basic-browser); can be @PATH to send the contents of a file at PATH, and/or begin with %% to render as jinja2 template (volflag=html_head)")
ap2.add_argument("--html-head-s", metavar="T", type=u, default="", help="text to append to the <head> of all HTML pages (except for basic-browser); similar to (and can be combined with) --html-head but only accepts static text (volflag=html_head_s)")
ap2.add_argument("--ih", action="store_true", help="if a folder contains index.html, show that instead of the directory listing by default (can be changed in the client settings UI, or add ?v to URL for override)")
ap2.add_argument("--textfiles", metavar="CSV", type=u, default="txt,nfo,diz,cue,readme", help="file extensions to present as plaintext")
ap2.add_argument("--txt-max", metavar="KiB", type=int, default=64, help="max size of embedded textfiles on ?doc= (anything bigger will be lazy-loaded by JS)")

View file

@ -21,6 +21,7 @@ from .util import (
DEF_MTE,
DEF_MTH,
EXTS,
FAVICON_MIMES,
HAVE_SQLITE3,
IMPLICATIONS,
MIMES,
@ -2493,6 +2494,41 @@ class AuthSrv(object):
t = "WARNING: volume [/%s]: invalid value specified for ext-th: %s"
self.log(t % (vol.vpath, etv), 3)
zs = str(vol.flags.get("html_head") or "")
if zs and zs[:1] in "%@":
vol.flags["html_head_d"] = zs
head_s = str(vol.flags.get("html_head_s") or "")
else:
zs2 = str(vol.flags.get("html_head_s") or "")
if zs2 and zs:
head_s = "%s\n%s\n" % (zs2.strip(), zs.strip())
else:
head_s = zs2 or zs
if head_s and not head_s.endswith("\n"):
head_s += "\n"
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 = '<link rel="icon" type="%s" href="%s">\n'
head_s += zs % (FAVICON_MIMES[ico_ext], ico_url)
elif ico_ext == "ico":
zs = '<link rel="shortcut icon" href="%s">\n'
head_s += zs % (ico_url,)
if head_s:
vol.flags["html_head_s"] = head_s
else:
vol.flags.pop("html_head_s", None)
if not vol.flags.get("html_head_d"):
vol.flags.pop("html_head_d", None)
vol.check_landmarks()
# d2d drops all database features for a volume

View file

@ -88,9 +88,11 @@ def vf_vmap() -> dict[str, str]:
"chmod_f",
"dbd",
"du_who",
"ufavico",
"forget_ip",
"hsortn",
"html_head",
"html_head_s",
"lg_sbf",
"md_sbf",
"lg_sba",
@ -301,8 +303,10 @@ 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 <head>, or @PATH for file at PATH",
"html_head_s=TXT": "additional static text in the html <head>",
"tcolor=#fc0": "theme color (a hint for webbrowsers, discord, etc.)",
"nodirsz": "don't show total folder size",
"du_who=all": "show disk-usage info to everyone",

View file

@ -286,10 +286,9 @@ class HttpCli(object):
zs += "&" if "?" in zs else "?"
ka["js"] = zs
zso = self.vn.flags.get("html_head")
if zso:
if "html_head_d" in self.vn.flags:
ka["this"] = self
self._build_html_head(zso, ka)
self._build_html_head(ka)
ka["html_head"] = self.html_head
return tpl.render(**ka) # type: ignore
@ -758,9 +757,11 @@ class HttpCli(object):
self.s.settimeout(self.args.s_tbody or None)
if "norobots" in vn.flags:
self.html_head += META_NOBOTS
self.out_headers["X-Robots-Tag"] = "noindex, nofollow"
if "html_head_s" in vn.flags:
self.html_head += vn.flags["html_head_s"]
try:
cors_k = self._cors()
if self.mode in ("GET", "HEAD"):
@ -931,8 +932,8 @@ class HttpCli(object):
no304 = self.cookies.get("no304")
return no304 == "y" or (self.args.no304 == 2 and no304 != "n")
def _build_html_head(self, maybe_html: Any, kv: dict[str, Any]) -> None:
html = str(maybe_html)
def _build_html_head(self, kv: dict[str, Any]) -> None:
html = str(self.vn.flags["html_head_d"])
is_jinja = html[:2] in "%@%"
if is_jinja:
html = html.replace("%", "", 1)
@ -5032,10 +5033,9 @@ class HttpCli(object):
zs += "&" if "?" in zs else "?"
targs["js"] = zs
zfv = self.vn.flags.get("html_head")
if zfv:
if "html_head_d" in self.vn.flags:
targs["this"] = self
self._build_html_head(zfv, targs)
self._build_html_head(targs)
targs["html_head"] = self.html_head
zs = template.render(**targs).encode("utf-8", "replace")

View file

@ -1142,7 +1142,7 @@ class Up2k(object):
ft = "\033[0;32m{}{:.0}"
ff = "\033[0;35m{}{:.0}"
fv = "\033[0;36m{}:\033[90m{}"
zs = "bcasechk du_iwho ext_th_d html_head put_name2 mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot zipmax zipmaxn_v zipmaxs_v"
zs = "bcasechk du_iwho ext_th_d html_head html_head_d html_head_s put_name2 mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot zipmax zipmaxn_v zipmaxs_v"
fx = set(zs.split())
fd = vf_bmap()
fd.update(vf_cmap())

View file

@ -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",

View file

@ -9,7 +9,7 @@
<meta name="theme-color" content="#{{ tcolor }}">
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/browser.css?_={{ ts }}">
{{- html_head }}
{{ html_head }}
{%- if css %}
<link rel="stylesheet" media="screen" href="{{ css }}_={{ ts }}">
{%- endif %}

View file

@ -10,7 +10,7 @@
<meta name="theme-color" content="#{{ tcolor }}">
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/shares.css?_={{ ts }}">
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
{{- html_head }}
{{ html_head }}
</head>
<body>

View file

@ -9,7 +9,7 @@
{%- if edit %}
<link rel="stylesheet" href="{{ r }}/.cpr/md2.css?_={{ ts }}">
{%- endif %}
{{- html_head }}
{{ html_head }}
</head>
<body>
<div id="mn"></div>

View file

@ -8,7 +8,7 @@
<link rel="stylesheet" href="{{ r }}/.cpr/mde.css?_={{ ts }}">
<link rel="stylesheet" href="{{ r }}/.cpr/deps/mini-fa.css?_={{ ts }}">
<link rel="stylesheet" href="{{ r }}/.cpr/deps/easymde.css?_={{ ts }}">
{{- html_head }}
{{ html_head }}
</head>
<body>
<div id="mw">

View file

@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=0.8">
<meta name="theme-color" content="#{{ tcolor }}">
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/msg.css?_={{ ts }}">
{{- html_head }}
{{ html_head }}
</head>
<body>

View file

@ -10,7 +10,7 @@
<meta name="theme-color" content="#{{ tcolor }}">
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/rups.css?_={{ ts }}">
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
{{- html_head }}
{{ html_head }}
</head>
<body>

View file

@ -10,7 +10,7 @@
<meta name="theme-color" content="#{{ tcolor }}">
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/shares.css?_={{ ts }}">
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
{{- html_head }}
{{ html_head }}
</head>
<body>

View file

@ -9,7 +9,7 @@
<meta name="theme-color" content="#{{ tcolor }}">
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/splash.css?_={{ ts }}">
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
{{- html_head }}
{{ html_head }}
</head>
<body>

View file

@ -10,7 +10,7 @@
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/splash.css?_={{ ts }}">
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
<style>ul{padding-left:1.3em}li{margin:.4em 0}.txa{float:right;margin:0 0 0 1em}</style>
{{- html_head }}
{{ html_head }}
</head>
<body>

View file

@ -64,6 +64,8 @@ if you give it the value `@ASDF` it will try to open a file named ASDF and send
if the value starts with `%` it will assume a jinja2 template and expand it; the template has access to the `HttpCli` object through a property named `this` as well as everything in `j2a` and the stuff added by `self.j2s`; see [browser.html](https://github.com/9001/copyparty/blob/hovudstraum/copyparty/web/browser.html) for inspiration or look under the hood in [httpcli.py](https://github.com/9001/copyparty/blob/hovudstraum/copyparty/httpcli.py)
there is also `--html-head-s` and volflag `html_head_s` to add a plain static bit of text, possibly in addition to `--html-head`
# translations

View file

@ -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 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"