mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
add og / opengraph / discord embeds
This commit is contained in:
parent
b6cf2d3089
commit
ea270ab9f2
17
README.md
17
README.md
|
@ -60,6 +60,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
||||||
* [tftp server](#tftp-server) - a TFTP server (read/write) can be started using `--tftp 3969`
|
* [tftp server](#tftp-server) - a TFTP server (read/write) can be started using `--tftp 3969`
|
||||||
* [smb server](#smb-server) - unsafe, slow, not recommended for wan
|
* [smb server](#smb-server) - unsafe, slow, not recommended for wan
|
||||||
* [browser ux](#browser-ux) - tweaking the ui
|
* [browser ux](#browser-ux) - tweaking the ui
|
||||||
|
* [opengraph](#opengraph) - discord and social-media embeds
|
||||||
* [file indexing](#file-indexing) - enables dedup and music search ++
|
* [file indexing](#file-indexing) - enables dedup and music search ++
|
||||||
* [exclude-patterns](#exclude-patterns) - to save some time
|
* [exclude-patterns](#exclude-patterns) - to save some time
|
||||||
* [filesystem guards](#filesystem-guards) - avoid traversing into other filesystems
|
* [filesystem guards](#filesystem-guards) - avoid traversing into other filesystems
|
||||||
|
@ -234,6 +235,7 @@ also see [comparison to similar software](./docs/versus.md)
|
||||||
* client support
|
* client support
|
||||||
* ☑ [folder sync](#folder-sync)
|
* ☑ [folder sync](#folder-sync)
|
||||||
* ☑ [curl-friendly](https://user-images.githubusercontent.com/241032/215322619-ea5fd606-3654-40ad-94ee-2bc058647bb2.png)
|
* ☑ [curl-friendly](https://user-images.githubusercontent.com/241032/215322619-ea5fd606-3654-40ad-94ee-2bc058647bb2.png)
|
||||||
|
* ☑ [opengraph](#opengraph) (discord embeds)
|
||||||
* markdown
|
* markdown
|
||||||
* ☑ [viewer](#markdown-viewer)
|
* ☑ [viewer](#markdown-viewer)
|
||||||
* ☑ editor (sure why not)
|
* ☑ editor (sure why not)
|
||||||
|
@ -1070,6 +1072,21 @@ tweaking the ui
|
||||||
see [./docs/rice](./docs/rice) for more, including how to add stuff (css/`<meta>`/...) to the html `<head>` tag
|
see [./docs/rice](./docs/rice) for more, including how to add stuff (css/`<meta>`/...) to the html `<head>` tag
|
||||||
|
|
||||||
|
|
||||||
|
## opengraph
|
||||||
|
|
||||||
|
discord and social-media embeds
|
||||||
|
|
||||||
|
can be enabled globally with `--og` or per-volume with volflag `og`
|
||||||
|
|
||||||
|
note that this disables hotlinking because the opengraph spec demands it; to sneak past this intentional limitation, you can enable opengraph selectively by user-agent, for example `--og-ua '(Discord|Twitter|Slack)bot'` (or volflag `og_ua`)
|
||||||
|
|
||||||
|
you can also hotlink files regardless by appending `?raw` to the url
|
||||||
|
|
||||||
|
if you want to entirely replace the copyparty response with your own jinja2 template, give the template filepath to `--og-tpl` or volflag `og_tpl` (all members of `HttpCli` are available through the `this` object)
|
||||||
|
|
||||||
|
because discord (and maybe others) strip query args such as `?raw`, opengraph is incompatible with filekeys and dirkeys
|
||||||
|
|
||||||
|
|
||||||
## file indexing
|
## file indexing
|
||||||
|
|
||||||
enables dedup and music search ++
|
enables dedup and music search ++
|
||||||
|
|
|
@ -1257,6 +1257,18 @@ def add_txt(ap):
|
||||||
ap2.add_argument("--exp-lg", metavar="V,V,V", type=u, default=DEF_EXP, help="comma/space-separated list of placeholders to expand in prologue/epilogue files (volflag=exp_lg)")
|
ap2.add_argument("--exp-lg", metavar="V,V,V", type=u, default=DEF_EXP, help="comma/space-separated list of placeholders to expand in prologue/epilogue files (volflag=exp_lg)")
|
||||||
|
|
||||||
|
|
||||||
|
def add_og(ap):
|
||||||
|
ap2 = ap.add_argument_group('og / open graph / discord-embed options')
|
||||||
|
ap2.add_argument("--og", action="store_true", help="disable hotlinking and return an html document instead; this is required by open-graph, but can also be useful on its own (volflag=og)")
|
||||||
|
ap2.add_argument("--og-ua", metavar="RE", type=u, default="", help="only disable hotlinking / engage OG behavior if the useragent matches regex \033[33mRE\033[0m (volflag=og_ua)")
|
||||||
|
ap2.add_argument("--og-tpl", metavar="PATH", type=u, default="", help="do not return the regular copyparty html, but instead load the jinja2 template at \033[33mPATH\033[0m (if path contains 'EXT' then EXT will be replaced with the requested file's extension) (volflag=og_tpl)")
|
||||||
|
ap2.add_argument("--og-no-head", action="store_true", help="do not automatically add OG entries into <head> (useful if you're doing this yourself in a template or such) (volflag=og_no_head)")
|
||||||
|
ap2.add_argument("--og-th", metavar="FMT", type=u, default="jf3", help="thumbnail format; j=jpeg, jf=jpeg-uncropped, jf3=jpeg-uncropped-large, w=webm, ... (volflag=og_th)")
|
||||||
|
ap2.add_argument("--og-title", metavar="TXT", type=u, default="", help="fallback title if there is nothing in the \033[33m-e2t\033[0m database (volflag=og_site)")
|
||||||
|
ap2.add_argument("--og-desc", metavar="TXT", type=u, default="", help="description text; same for all files, disable with [\033[32m-\033[0m] (volflag=og_desc)")
|
||||||
|
ap2.add_argument("--og-site", metavar="TXT", type=u, default="", help="sitename; defaults to \033[33m--name\033[0m, disable with [\033[32m-\033[0m] (volflag=og_site)")
|
||||||
|
|
||||||
|
|
||||||
def add_ui(ap, retry):
|
def add_ui(ap, retry):
|
||||||
ap2 = ap.add_argument_group('ui options')
|
ap2 = ap.add_argument_group('ui options')
|
||||||
ap2.add_argument("--grid", action="store_true", help="show grid/thumbnails by default (volflag=grid)")
|
ap2.add_argument("--grid", action="store_true", help="show grid/thumbnails by default (volflag=grid)")
|
||||||
|
@ -1357,6 +1369,7 @@ def run_argparse(
|
||||||
add_hooks(ap)
|
add_hooks(ap)
|
||||||
add_stats(ap)
|
add_stats(ap)
|
||||||
add_txt(ap)
|
add_txt(ap)
|
||||||
|
add_og(ap)
|
||||||
add_ui(ap, retry)
|
add_ui(ap, retry)
|
||||||
add_admin(ap)
|
add_admin(ap)
|
||||||
add_logging(ap)
|
add_logging(ap)
|
||||||
|
|
|
@ -1727,7 +1727,11 @@ class AuthSrv(object):
|
||||||
if self.args.e2d or "e2ds" in vol.flags:
|
if self.args.e2d or "e2ds" in vol.flags:
|
||||||
vol.flags["e2d"] = True
|
vol.flags["e2d"] = True
|
||||||
|
|
||||||
for ga, vf in [["no_hash", "nohash"], ["no_idx", "noidx"]]:
|
for ga, vf in [
|
||||||
|
["no_hash", "nohash"],
|
||||||
|
["no_idx", "noidx"],
|
||||||
|
["og_ua", "og_ua"],
|
||||||
|
]:
|
||||||
if vf in vol.flags:
|
if vf in vol.flags:
|
||||||
ptn = re.compile(vol.flags.pop(vf))
|
ptn = re.compile(vol.flags.pop(vf))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -39,6 +39,8 @@ def vf_bmap() -> dict[str, str]:
|
||||||
"magic",
|
"magic",
|
||||||
"no_sb_md",
|
"no_sb_md",
|
||||||
"no_sb_lg",
|
"no_sb_lg",
|
||||||
|
"og",
|
||||||
|
"og_no_head",
|
||||||
"rand",
|
"rand",
|
||||||
"xdev",
|
"xdev",
|
||||||
"xlink",
|
"xlink",
|
||||||
|
@ -65,6 +67,12 @@ def vf_vmap() -> dict[str, str]:
|
||||||
"lg_sbf",
|
"lg_sbf",
|
||||||
"md_sbf",
|
"md_sbf",
|
||||||
"nrand",
|
"nrand",
|
||||||
|
"og_desc",
|
||||||
|
"og_site",
|
||||||
|
"og_th",
|
||||||
|
"og_title",
|
||||||
|
"og_tpl",
|
||||||
|
"og_ua",
|
||||||
"mv_retry",
|
"mv_retry",
|
||||||
"rm_retry",
|
"rm_retry",
|
||||||
"sort",
|
"sort",
|
||||||
|
|
|
@ -84,6 +84,7 @@ from .util import (
|
||||||
sanitize_vpath,
|
sanitize_vpath,
|
||||||
sendfile_kern,
|
sendfile_kern,
|
||||||
sendfile_py,
|
sendfile_py,
|
||||||
|
ujoin,
|
||||||
undot,
|
undot,
|
||||||
unescape_cookie,
|
unescape_cookie,
|
||||||
unquotep,
|
unquotep,
|
||||||
|
@ -4074,6 +4075,27 @@ class HttpCli(object):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def tx_browser(self) -> bool:
|
def tx_browser(self) -> bool:
|
||||||
|
vn = self.vn
|
||||||
|
rem = self.rem
|
||||||
|
|
||||||
|
add_og = "og" in vn.flags
|
||||||
|
if add_og:
|
||||||
|
if ".og-raw/" in rem:
|
||||||
|
# sad workaround: discord strips ?raw=1 so give it a unique url instead
|
||||||
|
self.uparam["raw"] = True
|
||||||
|
self.vpath = self.vpath.replace(".og-raw/", "")
|
||||||
|
vn, rem = self.asrv.vfs.get(self.vpath, self.uname, False, False)
|
||||||
|
self.vn = vn
|
||||||
|
self.rem = rem
|
||||||
|
if "th" in self.uparam or "raw" in self.uparam:
|
||||||
|
og_ua = add_og = False
|
||||||
|
elif self.args.og_ua:
|
||||||
|
og_ua = add_og = self.args.og_ua.search(self.ua)
|
||||||
|
else:
|
||||||
|
og_ua = False
|
||||||
|
add_og = True
|
||||||
|
og_fn = ""
|
||||||
|
|
||||||
vpath = ""
|
vpath = ""
|
||||||
vpnodes = [["", "/"]]
|
vpnodes = [["", "/"]]
|
||||||
if self.vpath:
|
if self.vpath:
|
||||||
|
@ -4085,8 +4107,6 @@ class HttpCli(object):
|
||||||
|
|
||||||
vpnodes.append([quotep(vpath) + "/", html_escape(node, crlf=True)])
|
vpnodes.append([quotep(vpath) + "/", html_escape(node, crlf=True)])
|
||||||
|
|
||||||
vn = self.vn
|
|
||||||
rem = self.rem
|
|
||||||
abspath = vn.dcanonical(rem)
|
abspath = vn.dcanonical(rem)
|
||||||
dbv, vrem = vn.get_dbv(rem)
|
dbv, vrem = vn.get_dbv(rem)
|
||||||
|
|
||||||
|
@ -4117,7 +4137,6 @@ class HttpCli(object):
|
||||||
e2d = "e2d" in vn.flags
|
e2d = "e2d" in vn.flags
|
||||||
e2t = "e2t" in vn.flags
|
e2t = "e2t" in vn.flags
|
||||||
|
|
||||||
self.html_head += vn.flags.get("html_head", "")
|
|
||||||
if "b" in self.uparam:
|
if "b" in self.uparam:
|
||||||
self.out_headers["X-Robots-Tag"] = "noindex, nofollow"
|
self.out_headers["X-Robots-Tag"] = "noindex, nofollow"
|
||||||
|
|
||||||
|
@ -4125,13 +4144,15 @@ class HttpCli(object):
|
||||||
is_dk = False
|
is_dk = False
|
||||||
fk_pass = False
|
fk_pass = False
|
||||||
icur = None
|
icur = None
|
||||||
if is_dir and (e2t or e2d):
|
if (e2t or e2d) and (is_dir or add_og):
|
||||||
idx = self.conn.get_u2idx()
|
idx = self.conn.get_u2idx()
|
||||||
if idx and hasattr(idx, "p_end"):
|
if idx and hasattr(idx, "p_end"):
|
||||||
icur = idx.get_cur(dbv)
|
icur = idx.get_cur(dbv)
|
||||||
|
|
||||||
th_fmt = self.uparam.get("th")
|
th_fmt = self.uparam.get("th")
|
||||||
if self.can_read or (self.can_get and vn.flags.get("dk")):
|
if self.can_read or (
|
||||||
|
self.can_get and (vn.flags.get("dk") or "fk" not in vn.flags)
|
||||||
|
):
|
||||||
if th_fmt is not None:
|
if th_fmt is not None:
|
||||||
nothumb = "dthumb" in dbv.flags
|
nothumb = "dthumb" in dbv.flags
|
||||||
if is_dir:
|
if is_dir:
|
||||||
|
@ -4178,7 +4199,7 @@ class HttpCli(object):
|
||||||
elif self.can_write and th_fmt is not None:
|
elif self.can_write and th_fmt is not None:
|
||||||
return self.tx_svg("upload\nonly")
|
return self.tx_svg("upload\nonly")
|
||||||
|
|
||||||
elif self.can_get and self.avn:
|
if not self.can_read and self.can_get and self.avn:
|
||||||
axs = self.avn.axs
|
axs = self.avn.axs
|
||||||
if self.uname not in axs.uhtml:
|
if self.uname not in axs.uhtml:
|
||||||
pass
|
pass
|
||||||
|
@ -4224,6 +4245,17 @@ class HttpCli(object):
|
||||||
self.log(t % (correct, got, self.req, abspath), 6)
|
self.log(t % (correct, got, self.req, abspath), 6)
|
||||||
return self.tx_404()
|
return self.tx_404()
|
||||||
|
|
||||||
|
if add_og:
|
||||||
|
if og_ua or self.host not in self.headers.get("referer", ""):
|
||||||
|
self.vpath, og_fn = vsplit(self.vpath)
|
||||||
|
vpath = self.vpath
|
||||||
|
vn, rem = self.asrv.vfs.get(self.vpath, self.uname, False, False)
|
||||||
|
abspath = vn.dcanonical(rem)
|
||||||
|
dbv, vrem = vn.get_dbv(rem)
|
||||||
|
is_dir = stat.S_ISDIR(st.st_mode)
|
||||||
|
is_dk = True
|
||||||
|
vpnodes.pop()
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(abspath.endswith(".md") or self.can_delete)
|
(abspath.endswith(".md") or self.can_delete)
|
||||||
and "nohtml" not in vn.flags
|
and "nohtml" not in vn.flags
|
||||||
|
@ -4235,9 +4267,10 @@ class HttpCli(object):
|
||||||
):
|
):
|
||||||
return self.tx_md(vn, abspath)
|
return self.tx_md(vn, abspath)
|
||||||
|
|
||||||
return self.tx_file(
|
if not add_og or not og_fn:
|
||||||
abspath, None if st.st_size or "nopipe" in vn.flags else vn.realpath
|
return self.tx_file(
|
||||||
)
|
abspath, None if st.st_size or "nopipe" in vn.flags else vn.realpath
|
||||||
|
)
|
||||||
|
|
||||||
elif is_dir and not self.can_read:
|
elif is_dir and not self.can_read:
|
||||||
if self._use_dirkey(abspath):
|
if self._use_dirkey(abspath):
|
||||||
|
@ -4284,7 +4317,11 @@ class HttpCli(object):
|
||||||
is_ls = "ls" in self.uparam
|
is_ls = "ls" in self.uparam
|
||||||
is_js = self.args.force_js or self.cookies.get("js") == "y"
|
is_js = self.args.force_js or self.cookies.get("js") == "y"
|
||||||
|
|
||||||
if not is_ls and (self.ua.startswith("curl/") or self.ua.startswith("fetch")):
|
if (
|
||||||
|
not is_ls
|
||||||
|
and not add_og
|
||||||
|
and (self.ua.startswith("curl/") or self.ua.startswith("fetch"))
|
||||||
|
):
|
||||||
self.uparam["ls"] = "v"
|
self.uparam["ls"] = "v"
|
||||||
is_ls = True
|
is_ls = True
|
||||||
|
|
||||||
|
@ -4409,7 +4446,7 @@ class HttpCli(object):
|
||||||
|
|
||||||
for k in ["zip", "tar"]:
|
for k in ["zip", "tar"]:
|
||||||
v = self.uparam.get(k)
|
v = self.uparam.get(k)
|
||||||
if v is not None:
|
if v is not None and (not add_og or not og_fn):
|
||||||
return self.tx_zip(k, v, self.vpath, vn, rem, [])
|
return self.tx_zip(k, v, self.vpath, vn, rem, [])
|
||||||
|
|
||||||
fsroot, vfs_ls, vfs_virt = vn.ls(
|
fsroot, vfs_ls, vfs_virt = vn.ls(
|
||||||
|
@ -4423,6 +4460,10 @@ class HttpCli(object):
|
||||||
ls_names = [x[0] for x in vfs_ls]
|
ls_names = [x[0] for x in vfs_ls]
|
||||||
ls_names.extend(list(vfs_virt.keys()))
|
ls_names.extend(list(vfs_virt.keys()))
|
||||||
|
|
||||||
|
if add_og and og_fn and not self.can_read:
|
||||||
|
ls_names = [og_fn]
|
||||||
|
is_js = True
|
||||||
|
|
||||||
# check for old versions of files,
|
# check for old versions of files,
|
||||||
# [num-backups, most-recent, hist-path]
|
# [num-backups, most-recent, hist-path]
|
||||||
hist: dict[str, tuple[int, float, str]] = {}
|
hist: dict[str, tuple[int, float, str]] = {}
|
||||||
|
@ -4684,6 +4725,121 @@ class HttpCli(object):
|
||||||
if "mth" in vn.flags:
|
if "mth" in vn.flags:
|
||||||
j2a["def_hcols"] = list(vn.flags["mth"])
|
j2a["def_hcols"] = list(vn.flags["mth"])
|
||||||
|
|
||||||
|
if add_og and "raw" not in self.uparam:
|
||||||
|
j2a["this"] = self
|
||||||
|
cgv["og_fn"] = og_fn
|
||||||
|
if og_fn and vn.flags.get("og_tpl"):
|
||||||
|
tpl = vn.flags["og_tpl"]
|
||||||
|
if "EXT" in tpl:
|
||||||
|
zs = og_fn.split(".")[-1].lower()
|
||||||
|
tpl2 = tpl.replace("EXT", zs)
|
||||||
|
if os.path.exists(tpl2):
|
||||||
|
tpl = tpl2
|
||||||
|
with self.conn.hsrv.mutex:
|
||||||
|
if tpl not in self.conn.hsrv.j2:
|
||||||
|
tdir, tname = os.path.split(tpl)
|
||||||
|
j2env = jinja2.Environment()
|
||||||
|
j2env.loader = jinja2.FileSystemLoader(tdir)
|
||||||
|
self.conn.hsrv.j2[tpl] = j2env.get_template(tname)
|
||||||
|
thumb = ""
|
||||||
|
is_pic = is_vid = is_au = False
|
||||||
|
covernames = self.args.th_coversd
|
||||||
|
for fn in ls_names:
|
||||||
|
if fn.lower() in covernames:
|
||||||
|
thumb = fn
|
||||||
|
break
|
||||||
|
if og_fn:
|
||||||
|
ext = og_fn.split(".")[-1].lower()
|
||||||
|
if ext in self.thumbcli.thumbable:
|
||||||
|
is_pic = (
|
||||||
|
ext in self.thumbcli.fmt_pil
|
||||||
|
or ext in self.thumbcli.fmt_vips
|
||||||
|
or ext in self.thumbcli.fmt_ffi
|
||||||
|
)
|
||||||
|
is_vid = ext in self.thumbcli.fmt_ffv
|
||||||
|
is_au = ext in self.thumbcli.fmt_ffa
|
||||||
|
if not thumb or not is_au:
|
||||||
|
thumb = og_fn
|
||||||
|
file = next((x for x in files if x["name"] == og_fn), None)
|
||||||
|
else:
|
||||||
|
file = None
|
||||||
|
|
||||||
|
url_base = "%s://%s/%s" % (
|
||||||
|
"https" if self.is_https else "http",
|
||||||
|
self.host,
|
||||||
|
self.args.RS + quotep(vpath),
|
||||||
|
)
|
||||||
|
j2a["og_is_pic"] = is_pic
|
||||||
|
j2a["og_is_vid"] = is_vid
|
||||||
|
j2a["og_is_au"] = is_au
|
||||||
|
if thumb:
|
||||||
|
fmt = vn.flags.get("og_th", "j")
|
||||||
|
zs = ujoin(url_base, quotep(thumb))
|
||||||
|
j2a["og_thumb"] = "%s?th=%s&cache" % (zs, fmt)
|
||||||
|
|
||||||
|
j2a["og_fn"] = og_fn
|
||||||
|
j2a["og_file"] = file
|
||||||
|
if og_fn:
|
||||||
|
og_fn_q = quotep(og_fn)
|
||||||
|
j2a["og_url"] = ujoin(url_base, og_fn_q)
|
||||||
|
j2a["og_raw"] = ujoin(url_base, vjoin(".og-raw", og_fn_q))
|
||||||
|
# discord strips ?raw so it always downloads the html... orz
|
||||||
|
else:
|
||||||
|
j2a["og_url"] = j2a["og_raw"] = url_base
|
||||||
|
|
||||||
|
if not vn.flags.get("og_no_head"):
|
||||||
|
ogh = {"twitter:card": "summary"}
|
||||||
|
|
||||||
|
if thumb:
|
||||||
|
ogh["og:image"] = j2a["og_thumb"]
|
||||||
|
|
||||||
|
zso = vn.flags.get("og_title")
|
||||||
|
if zso:
|
||||||
|
ogh["og:title"] = str(zso)
|
||||||
|
|
||||||
|
zso = vn.flags.get("og_desc") or ""
|
||||||
|
if zso != "-":
|
||||||
|
ogh["og:description"] = str(zso)
|
||||||
|
|
||||||
|
zs = vn.flags.get("og_site") or self.args.name
|
||||||
|
if zs not in ("", "-"):
|
||||||
|
ogh["og:site_name"] = zs
|
||||||
|
|
||||||
|
tagmap = {}
|
||||||
|
if is_au:
|
||||||
|
ogh["og:type"] = "music.song"
|
||||||
|
ogh["og:audio"] = j2a["og_raw"]
|
||||||
|
tagmap = {
|
||||||
|
"title": "og:title",
|
||||||
|
"artist": "og:music:musician",
|
||||||
|
"album": "og:music:album",
|
||||||
|
".dur": "og:music:duration",
|
||||||
|
}
|
||||||
|
elif is_vid:
|
||||||
|
ogh["og:type"] = "video.other"
|
||||||
|
ogh["og:video"] = j2a["og_raw"]
|
||||||
|
tagmap = {
|
||||||
|
"title": "og:title",
|
||||||
|
".dur": "og:video:duration",
|
||||||
|
}
|
||||||
|
elif is_pic:
|
||||||
|
ogh["og:type"] = "video.other"
|
||||||
|
ogh["og:image"] = j2a["og_raw"]
|
||||||
|
|
||||||
|
for tag, hname in tagmap.items():
|
||||||
|
try:
|
||||||
|
v = file["tags"][tag]
|
||||||
|
if not v:
|
||||||
|
continue
|
||||||
|
ogh[hname] = int(v) if tag == ".dur" else v
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
zs = '\t<meta property="%s" content="%s">'
|
||||||
|
oghs = [zs % (k, v) for k, v in ogh.items()]
|
||||||
|
zs = self.html_head + "\n%s\n" % ("\n".join(oghs),)
|
||||||
|
self.html_head = zs.replace("\n\n", "\n")
|
||||||
|
|
||||||
html = self.j2s(tpl, **j2a)
|
html = self.j2s(tpl, **j2a)
|
||||||
self.reply(html.encode("utf-8", "replace"))
|
self.reply(html.encode("utf-8", "replace"))
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -526,7 +526,7 @@ class SvcHub(object):
|
||||||
al.exp_md = odfusion(exp, al.exp_md.replace(" ", ","))
|
al.exp_md = odfusion(exp, al.exp_md.replace(" ", ","))
|
||||||
al.exp_lg = odfusion(exp, al.exp_lg.replace(" ", ","))
|
al.exp_lg = odfusion(exp, al.exp_lg.replace(" ", ","))
|
||||||
|
|
||||||
for k in ["no_hash", "no_idx"]:
|
for k in ["no_hash", "no_idx", "og_ua"]:
|
||||||
ptn = getattr(self.args, k)
|
ptn = getattr(self.args, k)
|
||||||
if ptn:
|
if ptn:
|
||||||
setattr(self.args, k, re.compile(ptn))
|
setattr(self.args, k, re.compile(ptn))
|
||||||
|
|
|
@ -2031,6 +2031,7 @@ def vsplit(vpath: str) -> tuple[str, str]:
|
||||||
return vpath.rsplit("/", 1) # type: ignore
|
return vpath.rsplit("/", 1) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
# vpath-join
|
||||||
def vjoin(rd: str, fn: str) -> str:
|
def vjoin(rd: str, fn: str) -> str:
|
||||||
if rd and fn:
|
if rd and fn:
|
||||||
return rd + "/" + fn
|
return rd + "/" + fn
|
||||||
|
@ -2038,6 +2039,14 @@ def vjoin(rd: str, fn: str) -> str:
|
||||||
return rd or fn
|
return rd or fn
|
||||||
|
|
||||||
|
|
||||||
|
# url-join
|
||||||
|
def ujoin(rd: str, fn: str) -> str:
|
||||||
|
if rd and fn:
|
||||||
|
return rd.rstrip("/") + "/" + fn.lstrip("/")
|
||||||
|
else:
|
||||||
|
return rd or fn
|
||||||
|
|
||||||
|
|
||||||
def _w8dec2(txt: bytes) -> str:
|
def _w8dec2(txt: bytes) -> str:
|
||||||
"""decodes filesystem-bytes to wtf8"""
|
"""decodes filesystem-bytes to wtf8"""
|
||||||
return surrogateescape.decodefilename(txt)
|
return surrogateescape.decodefilename(txt)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var XHR = XMLHttpRequest;
|
var XHR = XMLHttpRequest,
|
||||||
|
img_re = /\.(a?png|avif|bmp|gif|heif|jpe?g|jfif|svg|webp|webm|mkv|mp4)(\?|$)/i;
|
||||||
|
|
||||||
var Ls = {
|
var Ls = {
|
||||||
"eng": {
|
"eng": {
|
||||||
"tt": "English",
|
"tt": "English",
|
||||||
|
@ -1419,6 +1421,12 @@ var ACtx = !IPHONE && (window.AudioContext || window.webkitAudioContext),
|
||||||
dk, mp;
|
dk, mp;
|
||||||
|
|
||||||
|
|
||||||
|
if (window.og_fn) {
|
||||||
|
hash0 = 1;
|
||||||
|
hist_replace(vsplit(get_evpath())[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var mpl = (function () {
|
var mpl = (function () {
|
||||||
var have_mctl = 'mediaSession' in navigator && window.MediaMetadata;
|
var have_mctl = 'mediaSession' in navigator && window.MediaMetadata;
|
||||||
|
|
||||||
|
@ -3289,6 +3297,21 @@ function scan_hash(v) {
|
||||||
function eval_hash() {
|
function eval_hash() {
|
||||||
window.onpopstate = treectl.onpopfun;
|
window.onpopstate = treectl.onpopfun;
|
||||||
|
|
||||||
|
if (hash0 && window.og_fn) {
|
||||||
|
var all = msel.getall(), mi;
|
||||||
|
for (var a = 0; a < all.length; a++)
|
||||||
|
if (og_fn == uricom_dec(vsplit(all[a].vp)[1].split('?')[0])) {
|
||||||
|
mi = all[a];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mi && img_re.exec(og_fn))
|
||||||
|
hash0 = '#g' + mi.id;
|
||||||
|
|
||||||
|
if (ebi('a' + mi.id))
|
||||||
|
hash0 = '#a' + mi.id;
|
||||||
|
}
|
||||||
|
|
||||||
var v = hash0;
|
var v = hash0;
|
||||||
hash0 = null;
|
hash0 = null;
|
||||||
if (!v)
|
if (!v)
|
||||||
|
@ -4756,7 +4779,7 @@ var thegrid = (function () {
|
||||||
aplay = ebi('a' + fid),
|
aplay = ebi('a' + fid),
|
||||||
atext = ebi('t' + fid),
|
atext = ebi('t' + fid),
|
||||||
is_txt = atext && showfile.getlang(href),
|
is_txt = atext && showfile.getlang(href),
|
||||||
is_img = /\.(a?png|avif|bmp|gif|heif|jpe?g|jfif|svg|webp|webm|mkv|mp4)(\?|$)/i.test(href),
|
is_img = img_re.test(href),
|
||||||
is_dir = href.endsWith('/'),
|
is_dir = href.endsWith('/'),
|
||||||
is_srch = !!ebi('unsearch'),
|
is_srch = !!ebi('unsearch'),
|
||||||
in_tree = is_dir && treectl.find(oth.textContent.slice(0, -1)),
|
in_tree = is_dir && treectl.find(oth.textContent.slice(0, -1)),
|
||||||
|
|
|
@ -110,13 +110,13 @@ class Cfg(Namespace):
|
||||||
def __init__(self, a=None, v=None, c=None, **ka0):
|
def __init__(self, a=None, v=None, c=None, **ka0):
|
||||||
ka = {}
|
ka = {}
|
||||||
|
|
||||||
ex = "daw dav_auth dav_inf dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid hardlink ih ihead magic never_symlink nid nih no_acode no_athumb no_dav no_dedup no_del no_dupe no_lifetime no_logues no_mv no_pipe no_readme no_robots no_sb_md no_sb_lg no_scandir no_tarcmp no_thumb no_vthumb no_zip nrand nw q rand smb srch_dbg stats vague_403 vc ver xdev xlink xvol"
|
ex = "daw dav_auth dav_inf dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid hardlink ih ihead magic never_symlink nid nih no_acode no_athumb no_dav no_dedup no_del no_dupe no_lifetime no_logues no_mv no_pipe no_readme no_robots no_sb_md no_sb_lg no_scandir no_tarcmp no_thumb no_vthumb no_zip nrand nw og og_no_head q rand smb srch_dbg stats vague_403 vc ver xdev xlink xvol"
|
||||||
ka.update(**{k: False for k in ex.split()})
|
ka.update(**{k: False for k in ex.split()})
|
||||||
|
|
||||||
ex = "dotpart dotsrch no_dhash no_fastboot no_rescan no_sendfile no_snap no_voldump re_dhash plain_ip"
|
ex = "dotpart dotsrch no_dhash no_fastboot no_rescan no_sendfile no_snap no_voldump re_dhash plain_ip"
|
||||||
ka.update(**{k: True for k in ex.split()})
|
ka.update(**{k: True for k in ex.split()})
|
||||||
|
|
||||||
ex = "ah_cli ah_gen css_browser hist js_browser no_forget no_hash no_idx nonsus_urls"
|
ex = "ah_cli ah_gen css_browser hist js_browser no_forget no_hash no_idx nonsus_urls og_tpl og_ua"
|
||||||
ka.update(**{k: None for k in ex.split()})
|
ka.update(**{k: None for k in ex.split()})
|
||||||
|
|
||||||
ex = "hash_mt srch_time u2abort u2j"
|
ex = "hash_mt srch_time u2abort u2j"
|
||||||
|
@ -128,7 +128,7 @@ class Cfg(Namespace):
|
||||||
ex = "db_act df k304 loris re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo"
|
ex = "db_act df k304 loris re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo"
|
||||||
ka.update(**{k: 0 for k in ex.split()})
|
ka.update(**{k: 0 for k in ex.split()})
|
||||||
|
|
||||||
ex = "ah_alg bname doctitle exit favico idp_h_usr html_head lg_sbf log_fk md_sbf name textfiles unlist vname R RS SR"
|
ex = "ah_alg bname doctitle exit favico idp_h_usr html_head lg_sbf log_fk md_sbf name og_desc og_site og_th og_title textfiles unlist vname R RS SR"
|
||||||
ka.update(**{k: "" for k in ex.split()})
|
ka.update(**{k: "" for k in ex.split()})
|
||||||
|
|
||||||
ex = "grp on403 on404 xad xar xau xban xbd xbr xbu xiu xm"
|
ex = "grp on403 on404 xad xar xau xban xbd xbr xbu xiu xm"
|
||||||
|
|
Loading…
Reference in a new issue