Merge branch 'hovudstraum' into hovudstraum

Signed-off-by: stackxp <170874486+stackxp@users.noreply.github.com>
This commit is contained in:
stackxp 2025-12-13 17:46:00 +01:00 committed by GitHub
commit 80e1d11cd4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 240 additions and 58 deletions

View file

@ -1275,6 +1275,8 @@ def add_network(ap):
ap2.add_argument("--ll", action="store_true", help="include link-local IPv4/IPv6 in mDNS replies, even if the NIC has routable IPs (breaks some mDNS clients)")
ap2.add_argument("--rproxy", metavar="DEPTH", type=int, default=9999999, help="which ip to associate clients with; [\033[32m0\033[0m]=tcp, [\033[32m1\033[0m]=origin (first x-fwd, unsafe), [\033[32m-1\033[0m]=closest-proxy, [\033[32m-2\033[0m]=second-hop, [\033[32m-3\033[0m]=third-hop")
ap2.add_argument("--xff-hdr", metavar="NAME", type=u, default="x-forwarded-for", help="if reverse-proxied, which http header to read the client's real ip from")
ap2.add_argument("--xf-host", metavar="NAME", type=u, default="x-forwarded-host", help="if reverse-proxied, which http header to read the correct Host value from; this header must contain the server's external domain name")
ap2.add_argument("--xf-proto", metavar="NAME", type=u, default="x-forwarded-proto", help="if reverse-proxied, which http header to read the correct protocol value from; this header must contain either 'http' or 'https'")
ap2.add_argument("--xff-src", metavar="CIDR", type=u, default="127.0.0.0/8, ::1/128", help="list of trusted reverse-proxy CIDRs (comma-separated); only accept the real-ip header (\033[33m--xff-hdr\033[0m) and IdP headers if the incoming connection is from an IP within either of these subnets. Specify [\033[32mlan\033[0m] to allow all LAN / private / non-internet IPs. Can be disabled with [\033[32many\033[0m] if you are behind cloudflare (or similar) and are using \033[32m--xff-hdr=cf-connecting-ip\033[0m (or similar)")
ap2.add_argument("--ipa", metavar="CIDR", type=u, default="", help="only accept connections from IP-addresses inside \033[33mCIDR\033[0m (comma-separated); examples: [\033[32mlan\033[0m] or [\033[32m10.89.0.0/16, 192.168.33.0/24\033[0m]")
ap2.add_argument("--rp-loc", metavar="PATH", type=u, default="", help="if reverse-proxying on a location instead of a dedicated domain/subdomain, provide the base location here; example: [\033[32m/foo/bar\033[0m]")
@ -1556,6 +1558,8 @@ def add_safety(ap):
ap2.add_argument("--logout", metavar="H", type=float, default=8086.0, help="logout clients after \033[33mH\033[0m hours of inactivity; [\033[32m0.0028\033[0m]=10sec, [\033[32m0.1\033[0m]=6min, [\033[32m24\033[0m]=day, [\033[32m168\033[0m]=week, [\033[32m720\033[0m]=month, [\033[32m8760\033[0m]=year)")
ap2.add_argument("--dont-ban", metavar="TXT", type=u, default="no", help="anyone at this accesslevel or above will not get banned: [\033[32mav\033[0m]=admin-in-volume, [\033[32maa\033[0m]=has-admin-anywhere, [\033[32mrw\033[0m]=read-write, [\033[32mauth\033[0m]=authenticated, [\033[32many\033[0m]=disable-all-bans, [\033[32mno\033[0m]=anyone-can-get-banned")
ap2.add_argument("--ban-pw", metavar="N,W,B", type=u, default="9,60,1440", help="more than \033[33mN\033[0m wrong or blank passwords in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; disable with [\033[32mno\033[0m]")
ap2.add_argument("--banmsg", metavar="TXT", type=u, default="thank you for playing \u00a0 (see serverlog and readme)", help="the response to send to banned users; can be @ban.html to send the contents of ban.html")
ap2.add_argument("--ban-pw", metavar="N,W,B", type=u, default="9,60,1440", help="more than \033[33mN\033[0m wrong passwords in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; disable with [\033[32mno\033[0m]")
ap2.add_argument("--ban-pwc", metavar="N,W,B", type=u, default="5,60,1440", help="more than \033[33mN\033[0m password-changes in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; disable with [\033[32mno\033[0m]")
ap2.add_argument("--ban-404", metavar="N,W,B", type=u, default="50,60,1440", help="hitting more than \033[33mN\033[0m 404's in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; only affects users who cannot see directory listings because their access is either g/G/h")
ap2.add_argument("--ban-403", metavar="N,W,B", type=u, default="9,2,1440", help="hitting more than \033[33mN\033[0m 403's in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; [\033[32m1440\033[0m]=day, [\033[32m10080\033[0m]=week, [\033[32m43200\033[0m]=month")
@ -1643,6 +1647,7 @@ def add_thumbnail(ap):
ap2.add_argument("--th-ram-max", metavar="GB", type=float, default=th_ram, help="max memory usage (GiB) permitted by thumbnailer; not very accurate")
ap2.add_argument("--th-crop", metavar="TXT", type=u, default="y", help="crop thumbnails to 4:3 or keep dynamic height; client can override in UI unless force. [\033[32my\033[0m]=crop, [\033[32mn\033[0m]=nocrop, [\033[32mfy\033[0m]=force-y, [\033[32mfn\033[0m]=force-n (volflag=crop)")
ap2.add_argument("--th-x3", metavar="TXT", type=u, default="n", help="show thumbs at 3x resolution; client can override in UI unless force. [\033[32my\033[0m]=yes, [\033[32mn\033[0m]=no, [\033[32mfy\033[0m]=force-yes, [\033[32mfn\033[0m]=force-no (volflag=th3x)")
ap2.add_argument("--th-qv", metavar="N", type=int, default=40, help="thumbnail quality (10~90); higher is larger filesize and better quality (volflag=th_qv)")
ap2.add_argument("--th-dec", metavar="LIBS", default="vips,pil,raw,ff", help="image decoders, in order of preference")
ap2.add_argument("--th-no-jpg", action="store_true", help="disable jpg output")
ap2.add_argument("--th-no-webp", action="store_true", help="disable webp output")

View file

@ -2384,7 +2384,7 @@ class AuthSrv(object):
if vf not in vol.flags:
vol.flags[vf] = getattr(self.args, ga)
zs = "forget_ip gid nrand tail_who th_spec_p u2abort u2ow uid unp_who ups_who zip_who"
zs = "forget_ip gid nrand tail_who th_qv th_spec_p u2abort u2ow uid unp_who ups_who zip_who"
for k in zs.split():
if k in vol.flags:
vol.flags[k] = int(vol.flags[k])

View file

@ -133,6 +133,7 @@ def vf_vmap() -> dict[str, str]:
"tail_tmax",
"tail_who",
"tcolor",
"th_qv",
"th_spec_p",
"txt_eol",
"unlist",
@ -289,6 +290,7 @@ flagcats = {
"thsize": "thumbnail res; WxH",
"crop": "center-cropping (y/n/fy/fn)",
"th3x": "3x resolution (y/n/fy/fn)",
"th_qv=40": "thumbnail quality (10~90)",
"convt": "convert-to-image timeout in seconds",
"aconvt": "convert-to-audio timeout in seconds",
"th_spec_p=1": "make spectrograms? 0=never 1=fallback 2=always",

View file

@ -127,6 +127,24 @@ class Fstab(object):
self.log("mtab has changed; reevaluating support for sparse files")
try:
fuses = [mp for mp, fs in dtab.items() if fs == "fuseblk"]
if not fuses or MACOS:
raise Exception()
try:
so, _ = chkcmd(["lsblk", "-nrfo", "FSTYPE,MOUNTPOINT"]) # centos6
except:
so, _ = chkcmd(["lsblk", "-nrfo", "FSTYPE,MOUNTPOINTS"]) # future
for ln in so.split("\n"):
zsl = ln.split(" ", 1)
if len(zsl) != 2:
continue
fs, mp = zsl
if mp in fuses:
dtab[mp] = fs
except:
pass
tab1 = list(dtab.items())
tab1.sort(key=lambda x: (len(x[0]), x[0]))
path1, fs1 = tab1[0]

View file

@ -151,6 +151,7 @@ NO_CACHE = {"Cache-Control": "no-cache"}
ALL_COOKIES = "k304 no304 js idxh dots cppwd cppws".split()
BADXFF = " due to dangerous misconfiguration (the http-header specified by --xff-hdr was received from an untrusted reverse-proxy)"
BADXFF2 = ". Some copyparty features are now disabled as a safety measure."
H_CONN_KEEPALIVE = "Connection: Keep-Alive"
H_CONN_CLOSE = "Connection: Close"
@ -221,12 +222,11 @@ class HttpCli(object):
self.log_func = conn.log_func # mypy404
self.log_src = conn.log_src # mypy404
self.gen_fk = self._gen_fk if self.args.log_fk else gen_filekey
self.tls: bool = hasattr(self.s, "cipher")
self.tls = self.is_https = hasattr(self.s, "cipher")
self.is_vproxied = bool(self.args.R)
# placeholders; assigned by run()
self.keepalive = False
self.is_https = False
self.in_hdr_recv = True
self.headers: dict[str, str] = {}
self.mode = " " # http verb
@ -390,9 +390,6 @@ class HttpCli(object):
self.keepalive = "close" not in zs and (
self.http_ver != "HTTP/1.0" or zs == "keep-alive"
)
self.is_https = (
self.headers.get("x-forwarded-proto", "").lower() == "https" or self.tls
)
self.host = self.headers.get("host") or ""
if not self.host:
if self.s.family == socket.AF_UNIX:
@ -417,7 +414,7 @@ class HttpCli(object):
self.bad_xff = True
if self.args.rproxy != 9999999:
t = "global-option --rproxy %d could not be used (out-of-bounds) for the received header [%s]"
self.log(t % (self.args.rproxy, zso), c=3)
self.log(t % (self.args.rproxy, zso) + BADXFF2, c=3)
else:
zsl = [
" rproxy: %d if this client's IP-address is [%s]"
@ -436,6 +433,7 @@ class HttpCli(object):
t += ' Note: if you are behind cloudflare, then this default header is not a good choice; please first make sure your local reverse-proxy (if any) does not allow non-cloudflare IPs from providing cf-* headers, and then add this additional global setting: "--xff-hdr=cf-connecting-ip"'
else:
t += ' Note: depending on your reverse-proxy, and/or WAF, and/or other intermediates, you may want to read the true client IP from another header by also specifying "--xff-hdr=SomeOtherHeader"'
t += BADXFF2
if "." in pip:
zs = ".".join(pip.split(".")[:2]) + ".0.0/16"
@ -448,7 +446,19 @@ class HttpCli(object):
else:
self.ip = cli_ip
self.log_src = self.conn.set_rproxy(self.ip)
self.host = self.headers.get("x-forwarded-host") or self.host
self.host = self.headers.get(self.args.xf_host, self.host)
try:
self.is_https = len(self.headers[self.args.xf_proto]) == 5
except:
self.bad_xff = True
self.host = "example.com"
t = 'got proxied request without header "%s" (global-option "xf-proto"). This header must contain either "http" or "https". Either fix your reverse-proxy config to include this header, or change the copyparty global-option "xf-proto" to another header-name to read this value from'
self.log(t % (self.args.xf_proto,) + BADXFF2, 3)
# the semantics of trusted_xff and bad_xff are different;
# trusted_xff is whether the connection came from a trusted reverseproxy,
# regardless of whether the client ip detection is correctly configured
# (the primary safeguard for idp is --idp-h-key)
trusted_xff = True
m = RE_HOST.search(self.host)
@ -623,7 +633,7 @@ class HttpCli(object):
if relchk(self.vpath) and (self.vpath != "*" or self.mode != "OPTIONS"):
self.log("illegal relpath; req(%r) => %r" % (self.req, "/" + self.vpath))
self.cbonk(self.conn.hsrv.gmal, self.req, "bad_vp", "invalid relpaths")
return self.tx_404() and self.keepalive
return self.tx_404() and False
zso = self.headers.get("authorization")
bauth = ""
@ -955,7 +965,7 @@ class HttpCli(object):
return False
self.log("banned for {:.0f} sec".format(rt), 6)
self.terse_reply(b"thank you for playing (see serverlog and readme)", 403)
self.terse_reply(self.args.banmsg_b, 403)
return True
def permit_caching(self) -> None:
@ -1138,7 +1148,10 @@ class HttpCli(object):
]
if body:
lines.append("Content-Length: " + unicode(len(body)))
lines.append(
"Content-Type: text/html; charset=utf-8\r\nContent-Length: "
+ unicode(len(body))
)
lines.append("\r\n")
self.s.sendall("\r\n".join(lines).encode("utf-8") + body)
@ -5718,17 +5731,18 @@ class HttpCli(object):
and (self.uname in vol.axs.uread or self.uname in vol.axs.upget)
}
bad_xff = hasattr(self, "bad_xff")
if bad_xff:
if hasattr(self, "bad_xff"):
allvols = []
t = "will not return list of recent uploads" + BADXFF
self.log(t, 1)
if self.avol:
raise Pebkac(500, t)
x = self.conn.hsrv.broker.ask(
"up2k.get_unfinished_by_user", self.uname, "" if bad_xff else self.ip
)
x = self.conn.hsrv.broker.ask("up2k.get_unfinished_by_user", self.uname, "")
else:
x = self.conn.hsrv.broker.ask(
"up2k.get_unfinished_by_user", self.uname, self.ip
)
zdsa: dict[str, Any] = x.get()
uret: list[dict[str, Any]] = []
if "timeout" in zdsa:

View file

@ -1102,6 +1102,12 @@ class SvcHub(object):
else:
setattr(al, k, re.compile("^" + vs + "$"))
if al.banmsg.startswith("@"):
with open(al.banmsg[1:], "rb") as f:
al.banmsg_b = f.read()
else:
al.banmsg_b = al.banmsg.encode("utf-8") + b"\n"
if not al.sus_urls:
al.ban_url = "no"
elif al.ban_url == "no":

View file

@ -14,7 +14,7 @@ import time
from queue import Queue
from .__init__ import ANYWIN, PY2, TYPE_CHECKING
from .__init__ import ANYWIN, PY2, TYPE_CHECKING, unicode
from .authsrv import VFS
from .bos import bos
from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, au_unpk, ffprobe
@ -56,6 +56,56 @@ EXTS_SPEC_SAFE = set("aif aiff flac mp3 opus wav".split())
PTN_TS = re.compile("^-?[0-9a-f]{8,10}$")
# for n in {1..100}; do rm -rf /home/ed/Pictures/wp/.hist/th/ ; python3 -m copyparty -qv /home/ed/Pictures/wp/::r --th-no-webp --th-qv $n --th-dec pil >/dev/null 2>&1 & p=$!; printf '\033[A\033[J%3d ' $n; while true; do sleep 0.1; curl -s 127.1:3923 >/dev/null && break; done; curl -s '127.1:3923/?tar=j' >/dev/null ; cat /home/ed/Pictures/wp/.hist/th/1n/bs/1nBsjDetfie1iDq3y2D4YzF5/*.* | wc -c; kill $p; wait >/dev/null 2>&1; done
# filesize-equivalent, not quality (ff looks much shittier)
FF_JPG_Q = {
0: b"30", # 0
1: b"30", # 5
2: b"30", # 10
3: b"30", # 15
4: b"28", # 20
5: b"21", # 25
6: b"17", # 30
7: b"15", # 35
8: b"13", # 40
9: b"12", # 45
10: b"11", # 50
11: b"10", # 55
12: b"9", # 60
13: b"8", # 65
14: b"7", # 70
15: b"6", # 75
16: b"5", # 80
17: b"4", # 85
18: b"3", # 90
19: b"2", # 95
20: b"2", # 100
}
# FF_JPG_Q = {xn: ("%d" % (xn,)).encode("ascii") for xn in range(2, 33)}
VIPS_JPG_Q = {
0: 4, # 0
1: 7, # 5
2: 12, # 10
3: 17, # 15
4: 22, # 20
5: 27, # 25
6: 32, # 30
7: 37, # 35
8: 42, # 40
9: 47, # 45
10: 52, # 50
11: 56, # 55
12: 61, # 60
13: 66, # 65
14: 71, # 70
15: 75, # 75
16: 80, # 80
17: 85, # 85
18: 89, # 90 (vips explodes past this point)
19: 91, # 95
20: 97, # 100
}
try:
if os.environ.get("PRTY_NO_PIL"):
@ -308,6 +358,7 @@ class ThumbSrv(object):
if not bos.path.exists(inf_path):
with open(inf_path, "wb") as f:
f.write(afsenc(os.path.dirname(abspath)))
self.writevolcfg(histpath)
self.busy[tpath] = [cond]
do_conv = True
@ -351,6 +402,47 @@ class ThumbSrv(object):
"ffa": self.fmt_ffa,
}
def volcfgi(self, vn: VFS) -> str:
ret = []
zs = "th_dec th_no_webp th_no_jpg"
for zs in zs.split(" "):
ret.append("%s(%s)\n" % (zs, getattr(self.args, zs)))
zs = "th_qv thsize th_spec_p convt"
for zs in zs.split(" "):
ret.append("%s(%s)\n" % (zs, vn.flags.get(zs)))
return "".join(ret)
def volcfga(self, vn: VFS) -> str:
ret = []
zs = "q_opus q_mp3"
for zs in zs.split(" "):
ret.append("%s(%s)\n" % (zs, getattr(self.args, zs)))
zs = "aconvt"
for zs in zs.split(" "):
ret.append("%s(%s)\n" % (zs, vn.flags.get(zs)))
return "".join(ret)
def writevolcfg(self, histpath: str) -> None:
try:
bos.stat(os.path.join(histpath, "th", "cfg.txt"))
bos.stat(os.path.join(histpath, "ac", "cfg.txt"))
return
except:
pass
cfgi = cfga = ""
for vn in self.asrv.vfs.all_vols.values():
if vn.histpath == histpath:
cfgi = self.volcfgi(vn)
cfga = self.volcfga(vn)
break
t = "writing thumbnailer-config %d,%d to %s"
self.log(t % (len(cfgi), len(cfga), histpath))
chmod = bos.MKD_700 if self.args.free_umask else bos.MKD_755
for cfg, cat in ((cfgi, "th"), (cfga, "ac")):
bos.makedirs(os.path.join(histpath, cat), vf=chmod)
with open(os.path.join(histpath, cat, "cfg.txt"), "wb") as f:
f.write(cfg.encode("utf-8"))
def wait4ram(self, need: float, ttpath: str) -> None:
ram = self.args.th_ram_max
if need > ram * 0.99:
@ -529,7 +621,7 @@ class ThumbSrv(object):
im.thumbnail(self.getres(vn, fmt))
fmts = ["RGB", "L"]
args = {"quality": 40}
args = {"quality": vn.flags["th_qv"]}
if tpath.endswith(".webp"):
# quality 80 = pillow-default
@ -573,7 +665,12 @@ class ThumbSrv(object):
raise
assert img # type: ignore # !rm
img.write_to_file(tpath, Q=40)
args = {}
qv = vn.flags["th_qv"]
if tpath.endswith("jpg"):
qv = VIPS_JPG_Q[qv // 5]
args["optimize_coding"] = True
img.write_to_file(tpath, Q=qv, strip=True, **args)
def conv_raw(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
self.wait4ram(0.2, tpath)
@ -607,7 +704,12 @@ class ThumbSrv(object):
raise
assert img # type: ignore # !rm
img.write_to_file(tpath, Q=40)
args = {}
qv = vn.flags["th_qv"]
if tpath.endswith("jpg"):
qv = VIPS_JPG_Q[qv // 5]
args["optimize_coding"] = True
img.write_to_file(tpath, Q=qv, strip=True, **args)
elif HAVE_PIL:
if thumb.format == rawpy.ThumbFormat.BITMAP:
im = Image.fromarray(thumb.data, "RGB")
@ -671,12 +773,12 @@ class ThumbSrv(object):
if tpath.endswith(".jpg"):
cmd += [
b"-q:v",
b"6", # default=??
FF_JPG_Q[vn.flags["th_qv"] // 5], # default=??
]
else:
cmd += [
b"-q:v",
b"50", # default=75
unicode(vn.flags["th_qv"]).encode("ascii"), # default=75
b"-compression_level:v",
b"6", # default=4, 0=fast, 6=max
]
@ -722,7 +824,7 @@ class ThumbSrv(object):
if len(lines) > 50:
lines = lines[:25] + ["[...]"] + lines[-25:]
txt = "\n".join(["ff: " + str(x) for x in lines])
txt = "\n".join(["ff: " + unicode(x) for x in lines])
if len(txt) > 5000:
txt = txt[:2500] + "...\nff: [...]\nff: ..." + txt[-2500:]
@ -880,12 +982,12 @@ class ThumbSrv(object):
if tpath.endswith(".jpg"):
cmd += [
b"-q:v",
b"6", # default=??
FF_JPG_Q[vn.flags["th_qv"] // 5], # default=??
]
else:
cmd += [
b"-q:v",
b"50", # default=75
unicode(vn.flags["th_qv"]).encode("ascii"), # default=75
b"-compression_level:v",
b"6", # default=4, 0=fast, 6=max
]
@ -1143,7 +1245,7 @@ class ThumbSrv(object):
ret = []
for k, vs in raw_tags.items():
for v in vs:
if len(str(v)) >= 1024:
if len(unicode(v)) >= 1024:
bv = k.encode("utf-8", "replace")
ret += [b"-metadata", bv + b"="]
break
@ -1181,6 +1283,28 @@ class ThumbSrv(object):
time.sleep(interval)
def clean(self, histpath: str) -> int:
cfgi = cfga = ""
for vn in self.asrv.vfs.all_vols.values():
if vn.histpath == histpath:
cfgi = self.volcfgi(vn)
cfga = self.volcfga(vn)
break
for cfg, cat in ((cfgi, "th"), (cfga, "ac")):
if not cfg:
continue
try:
with open(os.path.join(histpath, cat, "cfg.txt"), "rb") as f:
oldcfg = f.read().decode("utf-8")
except:
oldcfg = ""
if cfg == oldcfg:
continue
zs = os.path.join(histpath, cat)
if not os.path.exists(zs):
continue
self.log("thumbnailer-config changed; deleting %s" % (zs,), 3)
shutil.rmtree(zs)
ret = 0
for cat in ["th", "ac"]:
top = os.path.join(histpath, cat)
@ -1239,7 +1363,7 @@ class ThumbSrv(object):
if len(b64) != 24 or len(ts) != 8 or ext not in exts:
raise Exception()
except:
if f != "dir.txt":
if f != "dir.txt" and f != "cfg.txt":
self.log("foreign file in thumbs dir: [{}]".format(fp), 1)
continue

View file

@ -1139,6 +1139,20 @@ var ACtx = !IPHONE && (window.AudioContext || window.webkitAudioContext),
dk, mp;
var x = '';
if (!fullui) {
if (window.ui_nombar || /[?&]nombar\b/.exec(sloc0)) x += '#ops,';
if (window.ui_noacci || /[?&]noacci\b/.exec(sloc0)) x += '#acc_info,';
if (window.ui_nosrvi || /[?&]nosrvi\b/.exec(sloc0)) x += '#srv_info,#srv_info2,';
if (window.ui_nocpla || /[?&]nocpla\b/.exec(sloc0)) x += '#goh,';
if (window.ui_nolbar || /[?&]nolbar\b/.exec(sloc0)) x += '#wfp,';
if (window.ui_noctxb || /[?&]noctxb\b/.exec(sloc0)) x += '#wtoggle,';
if (window.ui_norepl || /[?&]norepl\b/.exec(sloc0)) x += '#repl,';
}
if (x)
document.head.appendChild(mknod('style', '', x.slice(0, -1) + '{display:none!important}'));
if (location.pathname.indexOf('//') === 0)
hist_replace(location.pathname.replace(/^\/+/, '/'));
@ -1242,6 +1256,7 @@ var mpl = (function () {
"os_ctl": bcfg_get('au_os_ctl', have_mctl) && have_mctl,
'traversals': 0,
'm3ut': '#EXTM3U\n',
'np': [{'file': 'nothing'}, ['file']],
};
bcfg_bind(r, 'one', 'au_one', false, function (v) {
if (mp.au)
@ -1438,7 +1453,7 @@ var mpl = (function () {
if (!r.os_ctl || !mp.au)
return;
var np = get_np()[0],
var np = mpl.np[0],
fns = np.file.split(' - '),
artist = (np.circle && np.circle != np.artist ? np.circle + ' // ' : '') + (np.artist || (fns.length > 1 ? fns[0] : '')),
title = np.title || fns.pop(),
@ -1784,12 +1799,6 @@ function ft2dict(tr, skip) {
}
function get_np() {
var tr = QS('#files tr.play');
return ft2dict(tr, { 'up_ip': 1 });
};
// toggle player widget
var widget = (function () {
var r = {},
@ -1847,9 +1856,8 @@ var widget = (function () {
ck = irc ? '06' : '',
cv = irc ? '07' : '',
m = ck + 'np: ',
npr = get_np(),
npk = npr[1],
np = npr[0];
npk = mpl.np[1],
np = mpl.np[0];
for (var a = 0; a < npk.length; a++)
m += (npk[a] == 'file' ? '' : npk[a]).replace(/^\./, '') + '(' + cv + np[npk[a]] + ck + ') // ';
@ -2548,6 +2556,9 @@ var mpui = (function () {
if (mpl.prescan_evp == evp)
throw "evp match";
if (treectl.trunc)
return treectl.showmore(99999, repreload);
if (mpl.traversals++ > 4) {
mpl.prescan_evp = null;
toast.inf(10, L.mm_nof);
@ -3024,6 +3035,9 @@ function play(tid, is_ev, seek) {
}
if (tn >= mp.order.length) {
if (treectl.trunc)
return treectl.showmore(99999, next_song);
if (mpl.pb_mode == 'loop' || ebi('unsearch')) {
tn = 0;
}
@ -3097,9 +3111,12 @@ function play(tid, is_ev, seek) {
for (var a = 0, aa = trs.length; a < aa; a++)
clmod(trs[a], 'play');
var oid = 'a' + tid;
clmod(ebi(oid), 'act', 1);
clmod(ebi(oid).closest('tr'), 'play', 1);
var oid = 'a' + tid,
t_a = ebi(oid),
t_tr = t_a.closest('tr');
clmod(t_a, 'act', 1);
clmod(t_tr, 'play', 1);
clmod(ebi('wtoggle'), 'np', mpl.clip);
clmod(ebi('wtoggle'), 'm3u', mpl.m3uen);
if (thegrid)
@ -3121,12 +3138,12 @@ function play(tid, is_ev, seek) {
}
if (!seek && !ebi('unsearch')) {
var o = ebi(oid);
o.setAttribute('id', 'thx_js');
t_a.setAttribute('id', 'thx_js');
if (mpl.aplay)
sethash(oid + getsort());
o.setAttribute('id', oid);
t_a.setAttribute('id', oid);
}
mpl.np = ft2dict(t_tr, { 'up_ip': 1 });
pbar.unwave();
if (mpl.waves)
@ -3141,7 +3158,7 @@ function play(tid, is_ev, seek) {
catch (ex) {
toast.err(0, esc(L.mm_playerr + basenames(ex)));
}
clmod(ebi(oid), 'act');
clmod(t_a, 'act');
mpl.t_eplay = setTimeout(next_song, 5000);
}
@ -7520,7 +7537,7 @@ var treectl = (function () {
catch (ex) { }
};
r.showmore = function (n) {
r.showmore = function (n, cb) {
window.removeEventListener('scroll', r.tscroll);
console.log('nvis {0} -> {1}'.format(r.nvis, n));
r.nvis = n;
@ -7530,6 +7547,8 @@ var treectl = (function () {
setTimeout(function () {
r.gentab(get_evpath(), r.lsc);
ebi('wrap').style.opacity = CLOSEST ? 'unset' : 1;
if (cb)
cb();
}, 1);
};
@ -8226,7 +8245,12 @@ var settheme = (function () {
freshen();
};
freshen();
var m = /[?&]theme=([0-9]+)/.exec(sloc0);
if (m)
r.go(parseInt(m[1]));
else
freshen();
return r;
})();
@ -9437,14 +9461,3 @@ function reload_browser() {
msel.render();
}
treectl.hydrate();
if (!fullui && (window.ui_nombar || /[?&]nombar\b/.exec(sloc0))) ebi('ops').style.display = 'none';
if (!fullui && (window.ui_noacci || /[?&]noacci\b/.exec(sloc0))) ebi('acc_info').style.display = 'none';
if (!fullui && (window.ui_nosrvi || /[?&]nosrvi\b/.exec(sloc0))) ebi('srv_info').style.display = 'none';
if (!fullui && (window.ui_nocpla || /[?&]nocpla\b/.exec(sloc0))) ebi('goh').style.display = 'none';
if (!fullui && (window.ui_nolbar || /[?&]nolbar\b/.exec(sloc0))) ebi('wfp').style.display = 'none';
if (!fullui && (window.ui_noctxb || /[?&]noctxb\b/.exec(sloc0))) ebi('wtoggle').style.display = 'none';
if (!fullui && (window.ui_norepl || /[?&]norepl\b/.exec(sloc0))) ebi('repl').style.display = 'none';
var m = /[?&]theme=([0-9]+)/.exec(sloc0);
if (m) settheme.go(parseInt(m[1]));

View file

@ -158,7 +158,7 @@ class Cfg(Namespace):
ex = "hash_mt hsortn qdel safe_dedup scan_pr_r scan_pr_s scan_st_r srch_time tail_fd tail_rate th_spec_p u2abort u2j u2sz unp_who"
ka.update(**{k: 1 for k in ex.split()})
ex = "ac_convt au_vol dl_list du_iwho mtab_age reg_cap s_thead s_tbody tail_tmax tail_who th_convt ups_who ver_iwho zip_who"
ex = "ac_convt au_vol dl_list du_iwho mtab_age reg_cap s_thead s_tbody tail_tmax tail_who th_convt th_qv ups_who ver_iwho zip_who"
ka.update(**{k: 9 for k in ex.split()})
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"