Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c110ccb9ae | ||
|
|
0143380306 | ||
|
|
af9000d3c8 | ||
|
|
097d798e5e | ||
|
|
1d9f9f221a | ||
|
|
214a367f48 | ||
|
|
2fb46551a2 | ||
|
|
6bcf330ae0 | ||
|
|
2075a8b18c | ||
|
|
1275ac6c42 | ||
|
|
708f20b7af | ||
|
|
a2c0c708e8 | ||
|
|
2f2c65d91e | ||
|
|
cd5fcc7ca7 | ||
|
|
aa29e7be48 | ||
|
|
93febe34b0 | ||
|
|
f086e6d3c1 | ||
|
|
22e51e1c96 | ||
|
|
63a5336f31 | ||
|
|
bfc6c53cc5 | ||
|
|
236017f310 | ||
|
|
0a1d9b4dfd | ||
|
|
b50d090946 | ||
|
|
00b5db52cf | ||
|
|
24cb30e2c5 | ||
|
|
4549145ab5 | ||
|
|
67b0217754 | ||
|
|
ccae9efdf0 | ||
|
|
59d596b222 | ||
|
|
4878eb2c45 | ||
|
|
7755392f57 |
@@ -881,7 +881,7 @@ quick summary of more eccentric web-browsers trying to view a directory index:
|
||||
| **w3m** (0.5.3/macports) | can browse, login, upload at 100kB/s, mkdir/msg |
|
||||
| **netsurf** (3.10/arch) | is basically ie6 with much better css (javascript has almost no effect) |
|
||||
| **opera** (11.60/winxp) | OK: thumbnails, image-viewer, zip-selection, rename/cut/paste. NG: up2k, navpane, markdown, audio |
|
||||
| **ie4** and **netscape** 4.0 | can browse, upload with `?b=u` |
|
||||
| **ie4** and **netscape** 4.0 | can browse, upload with `?b=u`, auth with `&pw=wark` |
|
||||
| **SerenityOS** (7e98457) | hits a page fault, works with `?b=u`, file upload not-impl |
|
||||
|
||||
|
||||
@@ -1175,12 +1175,16 @@ for the `re`pack to work, first run one of the sfx'es once to unpack it
|
||||
|
||||
install [Termux](https://termux.com/) (see [ocv.me/termux](https://ocv.me/termux/)) and then copy-paste this into Termux (long-tap) all at once:
|
||||
```sh
|
||||
apt update && apt -y full-upgrade && termux-setup-storage && apt -y install python && python -m ensurepip && python -m pip install -U copyparty
|
||||
apt update && apt -y full-upgrade && apt update && termux-setup-storage && apt -y install python && python -m ensurepip && python -m pip install --user -U copyparty
|
||||
echo $?
|
||||
```
|
||||
|
||||
after the initial setup, you can launch copyparty at any time by running `copyparty` anywhere in Termux
|
||||
|
||||
if you want thumbnails, `apt -y install ffmpeg`
|
||||
|
||||
* or if you want to use vips instead, `apt -y install libvips && python -m pip install --user -U wheel && python -m pip install --user -U pyvips && (cd /data/data/com.termux/files/usr/lib/; ln -s libgobject-2.0.so{,.0}; ln -s libvips.so{,.42})`
|
||||
|
||||
|
||||
# reporting bugs
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ however if your copyparty is behind a reverse-proxy, you may want to use [`share
|
||||
### [`cfssl.sh`](cfssl.sh)
|
||||
* creates CA and server certificates using cfssl
|
||||
* give a 3rd argument to install it to your copyparty config
|
||||
* systemd service at [`systemd/cfssl.service`](systemd/cfssl.service)
|
||||
|
||||
# OS integration
|
||||
init-scripts to start copyparty as a service
|
||||
|
||||
@@ -7,7 +7,7 @@ srv_fqdn="$2"
|
||||
|
||||
[ -z "$srv_fqdn" ] && {
|
||||
echo "need arg 1: ca name"
|
||||
echo "need arg 2: server fqdn"
|
||||
echo "need arg 2: server fqdn and/or IPs, comma-separated"
|
||||
echo "optional arg 3: if set, write cert into copyparty cfg"
|
||||
exit 1
|
||||
}
|
||||
|
||||
23
contrib/systemd/cfssl.service
Normal file
23
contrib/systemd/cfssl.service
Normal file
@@ -0,0 +1,23 @@
|
||||
# systemd service which generates a new TLS certificate on each boot,
|
||||
# that way the one-year expiry time won't cause any issues --
|
||||
# just have everyone trust the ca.pem once every 10 years
|
||||
#
|
||||
# assumptions/placeholder values:
|
||||
# * this script and copyparty runs as user "cpp"
|
||||
# * copyparty repo is at ~cpp/dev/copyparty
|
||||
# * CA is named partylan
|
||||
# * server IPs = 10.1.2.3 and 192.168.123.1
|
||||
# * server hostname = party.lan
|
||||
|
||||
[Unit]
|
||||
Description=copyparty certificate generator
|
||||
Before=copyparty.service
|
||||
|
||||
[Service]
|
||||
User=cpp
|
||||
Type=oneshot
|
||||
SyslogIdentifier=cpp-cert
|
||||
ExecStart=/bin/bash -c 'cd ~/dev/copyparty/contrib && ./cfssl.sh partylan 10.1.2.3,192.168.123.1,party.lan y'
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -2,15 +2,19 @@
|
||||
# and share '/mnt' with anonymous read+write
|
||||
#
|
||||
# installation:
|
||||
# cp -pv copyparty.service /etc/systemd/system && systemctl enable --now copyparty
|
||||
# cp -pv copyparty.service /etc/systemd/system
|
||||
# restorecon -vr /etc/systemd/system/copyparty.service
|
||||
# firewall-cmd --permanent --add-port={80,443,3923}/tcp
|
||||
# firewall-cmd --reload
|
||||
# systemctl daemon-reload && systemctl enable --now copyparty
|
||||
#
|
||||
# you may want to:
|
||||
# change "User=cpp" and "/home/cpp/" to another user
|
||||
# remove the nft lines to only listen on port 3923
|
||||
# and in the ExecStart= line:
|
||||
# change '/usr/bin/python3' to another interpreter
|
||||
# change '/mnt::rw' to another location or permission-set
|
||||
# remove '-p 80,443,3923' to only listen on port 3923
|
||||
# add '-q' to disable logging on busy servers
|
||||
# add '-i 127.0.0.1' to only allow local connections
|
||||
# add '-e2dsa' to enable filesystem scanning + indexing
|
||||
# add '-e2ts' to enable metadata indexing
|
||||
@@ -21,8 +25,8 @@
|
||||
# python disabling line-buffering, so messages are out-of-order:
|
||||
# https://user-images.githubusercontent.com/241032/126040249-cb535cc7-c599-4931-a796-a5d9af691bad.png
|
||||
#
|
||||
# if you remove -q to enable logging, you may also want to remove the
|
||||
# following line to enable buffering (slightly better performance):
|
||||
# unless you add -q to disable logging, you may want to remove the
|
||||
# following line to allow buffering (slightly better performance):
|
||||
# Environment=PYTHONUNBUFFERED=x
|
||||
#
|
||||
# keep ExecStartPre before ExecStart, at least on rhel8
|
||||
@@ -35,8 +39,23 @@ Type=notify
|
||||
SyslogIdentifier=copyparty
|
||||
Environment=PYTHONUNBUFFERED=x
|
||||
ExecReload=/bin/kill -s USR1 $MAINPID
|
||||
ExecStartPre=/bin/bash -c 'mkdir -p /run/tmpfiles.d/ && echo "x /tmp/pe-copyparty*" > /run/tmpfiles.d/copyparty.conf'
|
||||
ExecStart=/usr/bin/python3 /usr/local/bin/copyparty-sfx.py -q -p 80,443,3923 -e2d -v /mnt::rw
|
||||
|
||||
# user to run as + where the TLS certificate is (if any)
|
||||
User=cpp
|
||||
Environment=XDG_CONFIG_HOME=/home/cpp/.config
|
||||
|
||||
# setup forwarding from ports 80 and 443 to port 3923
|
||||
ExecStartPre=+/bin/bash -c 'nft -n -a list table nat | awk "/ to :3923 /{print\$NF}" | xargs -rL1 nft delete rule nat prerouting handle; true'
|
||||
ExecStartPre=+nft add table ip nat
|
||||
ExecStartPre=+nft -- add chain ip nat prerouting { type nat hook prerouting priority -100 \; }
|
||||
ExecStartPre=+nft add rule ip nat prerouting tcp dport 80 redirect to :3923
|
||||
ExecStartPre=+nft add rule ip nat prerouting tcp dport 443 redirect to :3923
|
||||
|
||||
# stop systemd-tmpfiles-clean.timer from deleting copyparty while it's running
|
||||
ExecStartPre=+/bin/bash -c 'mkdir -p /run/tmpfiles.d/ && echo "x /tmp/pe-copyparty*" > /run/tmpfiles.d/copyparty.conf'
|
||||
|
||||
# copyparty settings
|
||||
ExecStart=/usr/bin/python3 /usr/local/bin/copyparty-sfx.py -e2d -v /mnt::rw
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -487,6 +487,7 @@ def run_argparse(argv, formatter):
|
||||
ap2.add_argument("--vague-403", action="store_true", help="send 404 instead of 403 (security through ambiguity, very enterprise)")
|
||||
ap2.add_argument("--force-js", action="store_true", help="don't send folder listings as HTML, force clients to use the embedded json instead -- slight protection against misbehaving search engines which ignore --no-robots")
|
||||
ap2.add_argument("--no-robots", action="store_true", help="adds http and html headers asking search engines to not index anything")
|
||||
ap2.add_argument("--logout", metavar="H", type=float, default="8086", help="logout clients after H hours of inactivity (0.0028=10sec, 0.1=6min, 24=day, 168=week, 720=month, 8760=year)")
|
||||
|
||||
ap2 = ap.add_argument_group('yolo options')
|
||||
ap2.add_argument("--ign-ebind", action="store_true", help="continue running even if it's impossible to listen on some of the requested endpoints")
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# coding: utf-8
|
||||
|
||||
VERSION = (1, 2, 8)
|
||||
VERSION = (1, 2, 10)
|
||||
CODENAME = "ftp btw"
|
||||
BUILD_DT = (2022, 4, 30)
|
||||
BUILD_DT = (2022, 5, 13)
|
||||
|
||||
S_VERSION = ".".join(map(str, VERSION))
|
||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||
|
||||
@@ -411,7 +411,7 @@ class VFS(object):
|
||||
|
||||
return [abspath, real, virt_vis]
|
||||
|
||||
def walk(self, rel, rem, seen, uname, permsets, dots, scandir, lstat):
|
||||
def walk(self, rel, rem, seen, uname, permsets, dots, scandir, lstat, subvols=True):
|
||||
"""
|
||||
recursively yields from ./rem;
|
||||
rel is a unix-style user-defined vpath (not vfs-related)
|
||||
@@ -444,9 +444,14 @@ class VFS(object):
|
||||
|
||||
wrel = (rel + "/" + rdir).lstrip("/")
|
||||
wrem = (rem + "/" + rdir).lstrip("/")
|
||||
for x in self.walk(wrel, wrem, seen, uname, permsets, dots, scandir, lstat):
|
||||
for x in self.walk(
|
||||
wrel, wrem, seen, uname, permsets, dots, scandir, lstat, subvols
|
||||
):
|
||||
yield x
|
||||
|
||||
if not subvols:
|
||||
return
|
||||
|
||||
for n, vfs in sorted(vfs_virt.items()):
|
||||
if not dots and n.startswith("."):
|
||||
continue
|
||||
@@ -1107,7 +1112,7 @@ class AuthSrv(object):
|
||||
flag_p = "p" in flags
|
||||
flag_r = "r" in flags
|
||||
|
||||
n_bads = 0
|
||||
bads = []
|
||||
for v in vols:
|
||||
v = v[1:]
|
||||
vtop = "/{}/".format(v) if v else "/"
|
||||
@@ -1119,10 +1124,19 @@ class AuthSrv(object):
|
||||
continue
|
||||
|
||||
atop = vn.realpath
|
||||
safeabs = atop + os.sep
|
||||
g = vn.walk(
|
||||
vn.vpath, "", [], u, [[True]], True, not self.args.no_scandir, False
|
||||
vn.vpath,
|
||||
"",
|
||||
[],
|
||||
u,
|
||||
[[True]],
|
||||
True,
|
||||
not self.args.no_scandir,
|
||||
False,
|
||||
False,
|
||||
)
|
||||
for _, _, vpath, apath, files, _, _ in g:
|
||||
for _, _, vpath, apath, files, dirs, _ in g:
|
||||
fnames = [n[0] for n in files]
|
||||
vpaths = [vpath + "/" + n for n in fnames] if vpath else fnames
|
||||
vpaths = [vtop + x for x in vpaths]
|
||||
@@ -1130,21 +1144,28 @@ class AuthSrv(object):
|
||||
files = [[vpath + "/", apath + os.sep]] + list(zip(vpaths, apaths))
|
||||
|
||||
if flag_ln:
|
||||
files = [x for x in files if not x[1].startswith(atop + os.sep)]
|
||||
n_bads += len(files)
|
||||
files = [x for x in files if not x[1].startswith(safeabs)]
|
||||
if files:
|
||||
dirs[:] = [] # stop recursion
|
||||
bads.append(files[0][0])
|
||||
|
||||
if flag_v:
|
||||
msg = [
|
||||
if not files:
|
||||
continue
|
||||
elif flag_v:
|
||||
msg = [""] + [
|
||||
'# user "{}", vpath "{}"\n{}'.format(u, vp, ap)
|
||||
for vp, ap in files
|
||||
]
|
||||
else:
|
||||
msg = [x[1] for x in files]
|
||||
msg = ["user {}, vol {}: {} =>".format(u, vtop, files[0][0])]
|
||||
msg += [x[1] for x in files]
|
||||
|
||||
if msg:
|
||||
self.log("\n" + "\n".join(msg))
|
||||
self.log("\n".join(msg))
|
||||
|
||||
if n_bads and flag_p:
|
||||
if bads:
|
||||
self.log("\n ".join(["found symlinks leaving volume:"] + bads))
|
||||
|
||||
if bads and flag_p:
|
||||
raise Exception("found symlink leaving volume, and strict is set")
|
||||
|
||||
if not flag_r:
|
||||
|
||||
@@ -143,6 +143,10 @@ class HttpCli(object):
|
||||
if self.args.rsp_slp:
|
||||
time.sleep(self.args.rsp_slp)
|
||||
|
||||
self.ua = self.headers.get("user-agent", "")
|
||||
self.is_rclone = self.ua.startswith("rclone/")
|
||||
self.is_ancient = self.ua.startswith("Mozilla/4.")
|
||||
|
||||
v = self.headers.get("connection", "").lower()
|
||||
self.keepalive = not v.startswith("close") and self.http_ver != "HTTP/1.0"
|
||||
self.is_https = (self.headers.get("x-forwarded-proto", "").lower() == "https" or self.tls)
|
||||
@@ -241,11 +245,9 @@ class HttpCli(object):
|
||||
self.dvol = self.asrv.vfs.adel[self.uname]
|
||||
self.gvol = self.asrv.vfs.aget[self.uname]
|
||||
|
||||
if pwd and "pw" in self.ouparam and pwd != cookies.get("cppwd"):
|
||||
if pwd:
|
||||
self.out_headerlist.append(("Set-Cookie", self.get_pwd_cookie(pwd)[0]))
|
||||
|
||||
self.ua = self.headers.get("user-agent", "")
|
||||
self.is_rclone = self.ua.startswith("rclone/")
|
||||
if self.is_rclone:
|
||||
uparam["raw"] = False
|
||||
uparam["dots"] = False
|
||||
@@ -282,10 +284,11 @@ class HttpCli(object):
|
||||
msg = str(ex) if pex == ex else min_ex()
|
||||
self.log("{}\033[0m, {}".format(msg, self.vpath), 3)
|
||||
|
||||
msg = "<pre>{}\r\nURL: {}\r\n".format(str(ex), self.vpath)
|
||||
msg = "{}\r\nURL: {}\r\n".format(str(ex), self.vpath)
|
||||
if self.hint:
|
||||
msg += "hint: {}\r\n".format(self.hint)
|
||||
|
||||
msg = "<pre>" + html_escape(msg)
|
||||
self.reply(msg.encode("utf-8", "replace"), status=pex.code, volsan=True)
|
||||
return self.keepalive
|
||||
except Pebkac:
|
||||
@@ -1007,9 +1010,15 @@ class HttpCli(object):
|
||||
pwd = self.parser.require("cppwd", 64)
|
||||
self.parser.drop()
|
||||
|
||||
dst = "/?h"
|
||||
self.out_headerlist = [
|
||||
x
|
||||
for x in self.out_headerlist
|
||||
if x[0] != "Set-Cookie" or "cppwd=" not in x[1]
|
||||
]
|
||||
|
||||
dst = "/"
|
||||
if self.vpath:
|
||||
dst = "/" + quotep(self.vpath)
|
||||
dst += quotep(self.vpath)
|
||||
|
||||
ck, msg = self.get_pwd_cookie(pwd)
|
||||
html = self.j2("msg", h1=msg, h2='<a href="' + dst + '">ack</a>', redir=dst)
|
||||
@@ -1019,13 +1028,17 @@ class HttpCli(object):
|
||||
def get_pwd_cookie(self, pwd):
|
||||
if pwd in self.asrv.iacct:
|
||||
msg = "login ok"
|
||||
dur = 60 * 60 * 24 * 365
|
||||
dur = int(60 * 60 * self.args.logout)
|
||||
else:
|
||||
msg = "naw dude"
|
||||
pwd = "x" # nosec
|
||||
dur = None
|
||||
|
||||
return [gencookie("cppwd", pwd, dur), msg]
|
||||
r = gencookie("cppwd", pwd, dur)
|
||||
if self.is_ancient:
|
||||
r = r.rsplit(" ", 1)[0]
|
||||
|
||||
return [r, msg]
|
||||
|
||||
def handle_mkdir(self):
|
||||
new_dir = self.parser.require("name", 512)
|
||||
@@ -1814,15 +1827,17 @@ class HttpCli(object):
|
||||
self.redirect("", "?h#cc")
|
||||
|
||||
def tx_404(self, is_403=False):
|
||||
rc = 404
|
||||
if self.args.vague_403:
|
||||
m = '<h1>404 not found ┐( ´ -`)┌</h1><p>or maybe you don\'t have access -- try logging in or <a href="/?h">go home</a></p>'
|
||||
elif is_403:
|
||||
m = '<h1>403 forbiddena ~┻━┻</h1><p>you\'ll have to log in or <a href="/?h">go home</a></p>'
|
||||
rc = 403
|
||||
else:
|
||||
m = '<h1>404 not found ┐( ´ -`)┌</h1><p><a href="/?h">go home</a></p>'
|
||||
|
||||
html = self.j2("splash", this=self, qvpath=quotep(self.vpath), msg=m)
|
||||
self.reply(html.encode("utf-8"), status=404)
|
||||
self.reply(html.encode("utf-8"), status=rc)
|
||||
return True
|
||||
|
||||
def scanvol(self):
|
||||
@@ -1865,7 +1880,7 @@ class HttpCli(object):
|
||||
if self.args.no_stack:
|
||||
raise Pebkac(403, "the stackdump feature is disabled in server config")
|
||||
|
||||
ret = "<pre>{}\n{}".format(time.time(), alltrace())
|
||||
ret = "<pre>{}\n{}".format(time.time(), html_escape(alltrace()))
|
||||
self.reply(ret.encode("utf-8"))
|
||||
|
||||
def tx_tree(self):
|
||||
@@ -2178,7 +2193,7 @@ class HttpCli(object):
|
||||
if self.can_get:
|
||||
perms.append("get")
|
||||
|
||||
url_suf = self.urlq({}, [])
|
||||
url_suf = self.urlq({}, ["k"])
|
||||
is_ls = "ls" in self.uparam
|
||||
is_js = self.args.force_js or self.cookies.get("js") == "y"
|
||||
|
||||
@@ -2398,7 +2413,7 @@ class HttpCli(object):
|
||||
continue
|
||||
|
||||
w = r[0][:16]
|
||||
q = "select k, v from mt where w = ? and k != 'x'"
|
||||
q = "select k, v from mt where w = ? and +k != 'x'"
|
||||
try:
|
||||
for k, v in icur.execute(q, (w,)):
|
||||
taglist[k] = True
|
||||
|
||||
@@ -18,6 +18,7 @@ from .httpcli import HttpCli
|
||||
from .u2idx import U2idx
|
||||
from .th_cli import ThumbCli
|
||||
from .th_srv import HAVE_PIL, HAVE_VIPS
|
||||
from .mtag import HAVE_FFMPEG
|
||||
from .ico import Ico
|
||||
|
||||
|
||||
@@ -38,7 +39,7 @@ class HttpConn(object):
|
||||
self.cert_path = hsrv.cert_path
|
||||
self.u2fh = hsrv.u2fh
|
||||
|
||||
enth = (HAVE_PIL or HAVE_VIPS) and not self.args.no_thumb
|
||||
enth = (HAVE_PIL or HAVE_VIPS or HAVE_FFMPEG) and not self.args.no_thumb
|
||||
self.thumbcli = ThumbCli(hsrv) if enth else None
|
||||
self.ico = Ico(self.args)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import shutil
|
||||
import subprocess as sp
|
||||
|
||||
from .__init__ import PY2, WINDOWS, unicode
|
||||
from .util import fsenc, fsdec, uncyg, runcmd, REKOBO_LKEY
|
||||
from .util import fsenc, fsdec, uncyg, runcmd, retchk, REKOBO_LKEY
|
||||
from .bos import bos
|
||||
|
||||
|
||||
@@ -82,8 +82,9 @@ def ffprobe(abspath, timeout=10):
|
||||
b"--",
|
||||
fsenc(abspath),
|
||||
]
|
||||
rc = runcmd(cmd, timeout=timeout)
|
||||
return parse_ffprobe(rc[1])
|
||||
rc, so, se = runcmd(cmd, timeout=timeout)
|
||||
retchk(rc, cmd, se)
|
||||
return parse_ffprobe(so)
|
||||
|
||||
|
||||
def parse_ffprobe(txt):
|
||||
@@ -491,12 +492,14 @@ class MTag(object):
|
||||
cmd = ["nice"] + cmd
|
||||
|
||||
cmd = [fsenc(x) for x in cmd]
|
||||
v = sp.check_output(cmd, **args).strip()
|
||||
rc, v, err = runcmd(cmd, **args)
|
||||
retchk(rc, cmd, err, self.log, 5)
|
||||
v = v.strip()
|
||||
if not v:
|
||||
continue
|
||||
|
||||
if "," not in tagname:
|
||||
ret[tagname] = v.decode("utf-8")
|
||||
ret[tagname] = v
|
||||
else:
|
||||
v = json.loads(v)
|
||||
for tag in tagname.split(","):
|
||||
|
||||
@@ -136,13 +136,16 @@ class SvcHub(object):
|
||||
self.broker = Broker(self)
|
||||
|
||||
def thr_httpsrv_up(self):
|
||||
time.sleep(5)
|
||||
time.sleep(1 if self.args.ign_ebind_all else 5)
|
||||
expected = self.broker.num_workers * self.tcpsrv.nsrv
|
||||
failed = expected - self.httpsrv_up
|
||||
if not failed:
|
||||
return
|
||||
|
||||
if self.args.ign_ebind_all:
|
||||
if not self.tcpsrv.srv:
|
||||
for _ in range(self.broker.num_workers):
|
||||
self.broker.put(False, "cb_httpsrv_up")
|
||||
return
|
||||
|
||||
if self.args.ign_ebind and self.tcpsrv.srv:
|
||||
|
||||
@@ -253,7 +253,9 @@ class ThumbSrv(object):
|
||||
fun(abspath, tpath)
|
||||
except:
|
||||
msg = "{} could not create thumbnail of {}\n{}"
|
||||
self.log(msg.format(fun.__name__, abspath, min_ex()), "1;30")
|
||||
msg = msg.format(fun.__name__, abspath, min_ex())
|
||||
c = 1 if "<Signals.SIG" in msg else "1;30"
|
||||
self.log(msg, c)
|
||||
with open(tpath, "wb") as _:
|
||||
pass
|
||||
|
||||
@@ -343,6 +345,8 @@ class ThumbSrv(object):
|
||||
|
||||
def conv_ffmpeg(self, abspath, tpath):
|
||||
ret, _ = ffprobe(abspath)
|
||||
if not ret:
|
||||
return
|
||||
|
||||
ext = abspath.rsplit(".")[-1].lower()
|
||||
if ext in ["h264", "h265"] or ext in self.fmt_ffi:
|
||||
@@ -417,8 +421,15 @@ class ThumbSrv(object):
|
||||
m = "FFmpeg failed because it was compiled without libsox; you must set --th-ff-swr to force swr resampling:\n"
|
||||
c = 1
|
||||
|
||||
m += "\n".join(["ff: {}".format(x) for x in serr.split("\n")])
|
||||
self.log(m, c=c)
|
||||
lines = serr.strip("\n").split("\n")
|
||||
if len(lines) > 50:
|
||||
lines = lines[:25] + ["[...]"] + lines[-25:]
|
||||
|
||||
txt = "\n".join(["ff: " + str(x) for x in lines])
|
||||
if len(txt) > 5000:
|
||||
txt = txt[:2500] + "...\nff: [...]\nff: ..." + txt[-2500:]
|
||||
|
||||
self.log(m + txt, c=c)
|
||||
raise sp.CalledProcessError(ret, (cmd[0], b"...", cmd[-1]))
|
||||
|
||||
def conv_spec(self, abspath, tpath):
|
||||
|
||||
@@ -328,7 +328,7 @@ class U2idx(object):
|
||||
w = hit["w"]
|
||||
del hit["w"]
|
||||
tags = {}
|
||||
q2 = "select k, v from mt where w = ? and k != 'x'"
|
||||
q2 = "select k, v from mt where w = ? and +k != 'x'"
|
||||
for k, v2 in cur.execute(q2, (w,)):
|
||||
taglist[k] = True
|
||||
tags[k] = v2
|
||||
|
||||
@@ -554,12 +554,16 @@ class Up2k(object):
|
||||
for d in all_vols
|
||||
if d != vol and (d.vpath.startswith(vol.vpath + "/") or not vol.vpath)
|
||||
]
|
||||
excl += [absreal(x) for x in excl]
|
||||
excl += list(self.asrv.vfs.histtab.values())
|
||||
if WINDOWS:
|
||||
excl = [x.replace("/", "\\") for x in excl]
|
||||
|
||||
excl = set(excl)
|
||||
rtop = absreal(top)
|
||||
n_add = n_rm = 0
|
||||
try:
|
||||
n_add = self._build_dir(dbw, top, set(excl), top, rei, reh, [])
|
||||
n_add = self._build_dir(dbw, top, excl, top, rtop, rei, reh, [])
|
||||
n_rm = self._drop_lost(dbw[0], top)
|
||||
except:
|
||||
m = "failed to index volume [{}]:\n{}"
|
||||
@@ -572,8 +576,7 @@ class Up2k(object):
|
||||
|
||||
return True, n_add or n_rm or do_vac
|
||||
|
||||
def _build_dir(self, dbw, top, excl, cdir, rei, reh, seen):
|
||||
rcdir = absreal(cdir) # a bit expensive but worth
|
||||
def _build_dir(self, dbw, top, excl, cdir, rcdir, rei, reh, seen):
|
||||
if rcdir in seen:
|
||||
m = "bailing from symlink loop,\n prev: {}\n curr: {}\n from: {}"
|
||||
self.log(m.format(seen[-1], rcdir, cdir), 3)
|
||||
@@ -581,7 +584,6 @@ class Up2k(object):
|
||||
|
||||
seen = seen + [rcdir]
|
||||
self.pp.msg = "a{} {}".format(self.pp.n, cdir)
|
||||
histpath = self.asrv.vfs.histtab[top]
|
||||
ret = 0
|
||||
seen_files = {} # != inames; files-only for dropcheck
|
||||
g = statdir(self.log_func, not self.args.no_scandir, False, cdir)
|
||||
@@ -596,17 +598,20 @@ class Up2k(object):
|
||||
lmod = int(inf.st_mtime)
|
||||
sz = inf.st_size
|
||||
if stat.S_ISDIR(inf.st_mode):
|
||||
if abspath in excl or abspath == histpath:
|
||||
rap = absreal(abspath)
|
||||
if abspath in excl or rap in excl:
|
||||
continue
|
||||
if iname == ".th" and bos.path.isdir(os.path.join(abspath, "top")):
|
||||
# abandoned or foreign, skip
|
||||
continue
|
||||
# self.log(" dir: {}".format(abspath))
|
||||
try:
|
||||
ret += self._build_dir(dbw, top, excl, abspath, rei, reh, seen)
|
||||
ret += self._build_dir(dbw, top, excl, abspath, rap, rei, reh, seen)
|
||||
except:
|
||||
m = "failed to index subdir [{}]:\n{}"
|
||||
self.log(m.format(abspath, min_ex()), c=1)
|
||||
elif not stat.S_ISREG(inf.st_mode):
|
||||
self.log("skip type-{:x} file [{}]".format(inf.st_mode, abspath))
|
||||
else:
|
||||
# self.log("file: {}".format(abspath))
|
||||
seen_files[iname] = 1
|
||||
@@ -944,7 +949,7 @@ class Up2k(object):
|
||||
n_done += 1
|
||||
|
||||
for w in to_delete.keys():
|
||||
q = "delete from mt where w = ? and k = 't:mtp'"
|
||||
q = "delete from mt where w = ? and +k = 't:mtp'"
|
||||
cur.execute(q, (w,))
|
||||
|
||||
to_delete = {}
|
||||
@@ -982,7 +987,7 @@ class Up2k(object):
|
||||
with self.mutex:
|
||||
done = self._flush_mpool(wcur)
|
||||
for w in done:
|
||||
q = "delete from mt where w = ? and k = 't:mtp'"
|
||||
q = "delete from mt where w = ? and +k = 't:mtp'"
|
||||
cur.execute(q, (w,))
|
||||
|
||||
cur.connection.commit()
|
||||
@@ -1080,18 +1085,20 @@ class Up2k(object):
|
||||
if parser == "mtag":
|
||||
parser = self.mtag.backend
|
||||
|
||||
msg = "{} failed to read tags from {}:\n{}"
|
||||
self.log(msg.format(parser, abspath, ex), c=3)
|
||||
self._log_tag_err(parser, abspath, ex)
|
||||
|
||||
q.task_done()
|
||||
|
||||
def _log_tag_err(self, parser, abspath, ex):
|
||||
msg = "{} failed to read tags from {}:\n{}".format(parser, abspath, ex)
|
||||
self.log(msg.lstrip(), c=1 if "<Signals.SIG" in msg else 3)
|
||||
|
||||
def _tag_file(self, write_cur, entags, wark, abspath, tags=None):
|
||||
if tags is None:
|
||||
try:
|
||||
tags = self.mtag.get(abspath)
|
||||
except Exception as ex:
|
||||
msg = "failed to read tags from {}:\n{}"
|
||||
self.log(msg.format(abspath, ex), c=3)
|
||||
self._log_tag_err("", abspath, ex)
|
||||
return 0
|
||||
|
||||
if not bos.path.isfile(abspath):
|
||||
@@ -1108,7 +1115,7 @@ class Up2k(object):
|
||||
|
||||
for k in tags.keys():
|
||||
q = "delete from mt where w = ? and ({})".format(
|
||||
" or ".join(["k = ?"] * len(tags))
|
||||
" or ".join(["+k = ?"] * len(tags))
|
||||
)
|
||||
args = [wark[:16]] + list(tags.keys())
|
||||
write_cur.execute(q, tuple(args))
|
||||
@@ -2188,8 +2195,7 @@ class Up2k(object):
|
||||
if parsers:
|
||||
tags.update(self.mtag.get_bin(parsers, abspath))
|
||||
except Exception as ex:
|
||||
msg = "failed to read tags from {}:\n{}"
|
||||
self.log(msg.format(abspath, ex), c=3)
|
||||
self._log_tag_err("", abspath, ex)
|
||||
continue
|
||||
|
||||
with self.mutex:
|
||||
|
||||
@@ -9,6 +9,7 @@ import time
|
||||
import base64
|
||||
import select
|
||||
import struct
|
||||
import signal
|
||||
import hashlib
|
||||
import platform
|
||||
import traceback
|
||||
@@ -1350,8 +1351,8 @@ def guess_mime(url, fallback="application/octet-stream"):
|
||||
return ret
|
||||
|
||||
|
||||
def runcmd(argv, timeout=None):
|
||||
p = sp.Popen(argv, stdout=sp.PIPE, stderr=sp.PIPE)
|
||||
def runcmd(argv, timeout=None, **ka):
|
||||
p = sp.Popen(argv, stdout=sp.PIPE, stderr=sp.PIPE, **ka)
|
||||
if not timeout or PY2:
|
||||
stdout, stderr = p.communicate()
|
||||
else:
|
||||
@@ -1366,9 +1367,10 @@ def runcmd(argv, timeout=None):
|
||||
return [p.returncode, stdout, stderr]
|
||||
|
||||
|
||||
def chkcmd(argv):
|
||||
ok, sout, serr = runcmd(argv)
|
||||
def chkcmd(argv, **ka):
|
||||
ok, sout, serr = runcmd(argv, **ka)
|
||||
if ok != 0:
|
||||
retchk(ok, argv, serr)
|
||||
raise Exception(serr)
|
||||
|
||||
return sout, serr
|
||||
@@ -1385,6 +1387,46 @@ def mchkcmd(argv, timeout=10):
|
||||
raise sp.CalledProcessError(rv, (argv[0], b"...", argv[-1]))
|
||||
|
||||
|
||||
def retchk(rc, cmd, serr, logger=None, color=None):
|
||||
if rc < 0:
|
||||
rc = 128 - rc
|
||||
|
||||
if rc < 126:
|
||||
return
|
||||
|
||||
s = None
|
||||
if rc > 128:
|
||||
try:
|
||||
s = str(signal.Signals(rc - 128))
|
||||
except:
|
||||
pass
|
||||
elif rc == 126:
|
||||
s = "invalid program"
|
||||
elif rc == 127:
|
||||
s = "program not found"
|
||||
else:
|
||||
s = "invalid retcode"
|
||||
|
||||
if s:
|
||||
m = "{} <{}>".format(rc, s)
|
||||
else:
|
||||
m = str(rc)
|
||||
|
||||
try:
|
||||
c = " ".join([fsdec(x) for x in cmd])
|
||||
except:
|
||||
c = str(cmd)
|
||||
|
||||
m = "error {} from [{}]".format(m, c)
|
||||
if serr:
|
||||
m += "\n" + serr
|
||||
|
||||
if logger:
|
||||
logger(m, color)
|
||||
else:
|
||||
raise Exception(m)
|
||||
|
||||
|
||||
def gzip_orig_sz(fn):
|
||||
with open(fsenc(fn), "rb") as f:
|
||||
f.seek(-4, 2)
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
--btn-h-bg: #805;
|
||||
--btn-1-fg: #400;
|
||||
--btn-1-bg: var(--a);
|
||||
--btn-1h-fg: var(--btn-1-fg);
|
||||
--btn-1h-bg: #fe8;
|
||||
--chk-fg: var(--tab-alt);
|
||||
--txt-sh: var(--bg-d2);
|
||||
@@ -66,6 +67,7 @@
|
||||
--u2-ok-b1: #8e4;
|
||||
--u2-err-bg: #900;
|
||||
--u2-err-b1: #d06;
|
||||
--ud-b1: #888;
|
||||
|
||||
--sort-1: #fb0;
|
||||
--sort-2: #d09;
|
||||
@@ -100,8 +102,10 @@
|
||||
--g-f-fg: var(--a-hil);
|
||||
--g-sh: rgba(0,0,0,0.3);
|
||||
|
||||
--f-sh1: 1px 0 0 0 rgba(128,128,128,0.4) inset, 0 1px 0 rgba(255,255,255,0.02) inset, 0 -1px 0 rgba(255,255,255,0.02) inset;
|
||||
--f-sh2: 1px 0 0 0 rgba(128,128,128,0.4) inset, 0 1px 0 rgba(0,0,0,0.2) inset, 0 -1px 0 rgba(0,0,0,0.2) inset;
|
||||
--f-sh1: 0.33;
|
||||
--f-sh2: 0.02;
|
||||
--f-sh3: 0.2;
|
||||
--f-h-b1: rgba(128,128,128,0.7);
|
||||
|
||||
--f-play-bg: #fc5;
|
||||
--f-play-fg: #000;
|
||||
@@ -162,6 +166,7 @@ html.y {
|
||||
--u2-tab-1-b2: #05a;
|
||||
--u2-tab-1-fg: var(--fg-max);
|
||||
--u2-tab-1-bg: inherit;
|
||||
--ud-b1: #bbb;
|
||||
|
||||
--sort-1: #059;
|
||||
--sort-2: #f5d;
|
||||
@@ -183,8 +188,9 @@ html.y {
|
||||
--g-f-b1: var(--bg-u5);
|
||||
--g-sh: rgba(0,0,0,0.07);
|
||||
|
||||
--f-sh1: 1px 0 0 0 rgba(128,128,128,0.3) inset, 0 1px 0 rgba(255,255,255,0.5) inset, 0 -1px 0 rgba(255,255,255,0.5) inset;
|
||||
--f-sh2: 1px 0 0 0 rgba(128,128,128,0.3) inset, 0 1px 0 rgba(0,0,0,0.02) inset, 0 -1px 0 rgba(0,0,0,0.02) inset;
|
||||
--f-sh1: 0.3;
|
||||
--f-sh2: 0.5;
|
||||
--f-sh3: 0.02;
|
||||
|
||||
--f-sel-sh: #e80;
|
||||
|
||||
@@ -232,11 +238,39 @@ html.b {
|
||||
--u2-o-h-bg: var(--btn-h-bg);
|
||||
--u2-o-1-bg: var(--a);
|
||||
--u2-o-1h-bg: var(--a-hil);
|
||||
|
||||
--f-sh1: 0.1;
|
||||
}
|
||||
html.bz {
|
||||
--u2-tab-bg: linear-gradient(to bottom, var(--bg), var(--bg-u2));
|
||||
--fg: #bbd;
|
||||
--bg-u5: #3b3f58;
|
||||
--bg-u4: #1e2130;
|
||||
--bg-u3: #1e2130;
|
||||
--bg-u1: #1e2130;
|
||||
--bg: #11121d;
|
||||
--bgg: #11121d;
|
||||
--bg-d1: #232536;
|
||||
--bg-d2: #34384e;
|
||||
--bg-d3: #34384e;
|
||||
|
||||
--row-alt: rgba(139, 150, 205, 0.06);
|
||||
|
||||
--btn-bg: #202231;
|
||||
--btn-h-bg: #2d2f45;
|
||||
--btn-1-bg: #ba2959;
|
||||
--btn-1-fg: #fff;
|
||||
--btn-1h-fg: #000;
|
||||
|
||||
--u2-tab-1-fg: var(--fg-max);
|
||||
--u2-tab-1-bg: var(--bg);
|
||||
|
||||
--srv-1: #79b;
|
||||
|
||||
--g-sel-bg: #ba2959;
|
||||
--g-fsel-bg: #e6336e;
|
||||
|
||||
--f-h-b1: #34384e;
|
||||
--mp-sh: #11121d;
|
||||
}
|
||||
html.by {
|
||||
--scroll: var(--a);
|
||||
@@ -425,11 +459,11 @@ a:hover {
|
||||
#files thead th {
|
||||
padding: .3em;
|
||||
background: var(--bg);
|
||||
border-bottom: 1px solid var(--bg-u5);
|
||||
border-bottom: 1px solid var(--f-h-b1);
|
||||
cursor: pointer;
|
||||
}
|
||||
html.y #files thead th {
|
||||
box-shadow: 0 -1px 0 rgba(0,0,0,0.33) inset;
|
||||
box-shadow: 0 1px 0 rgba(0,0,0,0.12);
|
||||
}
|
||||
#files td {
|
||||
margin: 0;
|
||||
@@ -443,10 +477,10 @@ html.y #files thead th {
|
||||
overflow: hidden;
|
||||
}
|
||||
#files td+td {
|
||||
box-shadow: var(--f-sh1);
|
||||
box-shadow: 1px 0 0 0 rgba(128,128,128,var(--f-sh1)) inset, 0 1px 0 rgba(255,255,255,var(--f-sh2)) inset, 0 -1px 0 rgba(255,255,255,var(--f-sh2)) inset;
|
||||
}
|
||||
#files tr:nth-child(2n+1) td+td {
|
||||
box-shadow: var(--f-sh2);
|
||||
box-shadow: 1px 0 0 0 rgba(128,128,128,var(--f-sh1)) inset, 0 1px 0 rgba(0,0,0,var(--f-sh3)) inset, 0 -1px 0 rgba(0,0,0,var(--f-sh3)) inset;
|
||||
}
|
||||
#files td:first-child {
|
||||
border-radius: .25em 0 0 .25em;
|
||||
@@ -591,6 +625,21 @@ html.y #path a:hover {
|
||||
padding: .3em;
|
||||
margin: -.3em;
|
||||
}
|
||||
#files tbody tr.play td,
|
||||
#files tbody tr.play td+td,
|
||||
#files tbody tr.play div a {
|
||||
background: #fc0;
|
||||
background: var(--f-play-bg);
|
||||
color: var(--f-play-fg);
|
||||
text-shadow: none;
|
||||
}
|
||||
#files tbody tr.play a {
|
||||
color: inherit;
|
||||
}
|
||||
#files tbody tr.play a:hover {
|
||||
color: var(--btn-1h-fg);
|
||||
background: var(--btn-1h-bg);
|
||||
}
|
||||
#ggrid {
|
||||
margin: -.2em -.5em;
|
||||
}
|
||||
@@ -668,6 +717,7 @@ html.np_open #ggrid>a.au:before {
|
||||
#ggrid>a.play,
|
||||
#ggrid>a[tt].play {
|
||||
color: var(--g-sel-fg);
|
||||
background: #fc0;
|
||||
background: var(--g-play-bg);
|
||||
border-color: var(--g-play-b1);
|
||||
border-top: 1px solid var(--g-play-b2);
|
||||
@@ -677,6 +727,7 @@ html.np_open #ggrid>a.au:before {
|
||||
#ggrid>a.sel,
|
||||
#ggrid>a[tt].sel {
|
||||
color: var(--g-sel-fg);
|
||||
background: #f3c;
|
||||
background: var(--g-sel-bg);
|
||||
border-color: var(--g-sel-b1);
|
||||
}
|
||||
@@ -729,7 +780,7 @@ html.np_open #ggrid>a.au:before {
|
||||
background: var(--bg-d3);
|
||||
box-shadow: 0 .2em 0 var(--f-sel-sh), 0 -.2em 0 var(--f-sel-sh);
|
||||
}
|
||||
#files tr:focus td:first-child {
|
||||
#files tr:focus:not(.play):not(.sel) td:first-child {
|
||||
background: var(--bg-d3);
|
||||
box-shadow: -.2em .2em 0 var(--f-sel-sh), -.2em -.2em 0 var(--f-sel-sh);
|
||||
}
|
||||
@@ -806,6 +857,7 @@ html.y #widget.open {
|
||||
}
|
||||
#wtoggle,
|
||||
#widgeti {
|
||||
background: #fff;
|
||||
background: var(--bg-u3);
|
||||
}
|
||||
#wfm, #wzip, #wnp {
|
||||
@@ -931,7 +983,9 @@ html.y #widget.open {
|
||||
outline: none;
|
||||
}
|
||||
#ops a.act {
|
||||
color: #fff;
|
||||
color: var(--op-aa-fg);
|
||||
background: #000;
|
||||
background: var(--op-aa-bg);
|
||||
border-radius: 0 0 .2em .2em;
|
||||
border-bottom: .3em solid var(--a-b);
|
||||
@@ -964,7 +1018,8 @@ html.y #ops svg circle {
|
||||
background: var(--btn-bg);
|
||||
border: none;
|
||||
box-shadow: 0 0 2px var(--txt-sh);
|
||||
border-bottom: 1px solid var(--a);
|
||||
border-bottom: 1px solid #999;
|
||||
border-color: var(--a);
|
||||
border-radius: .2em;
|
||||
padding: .2em .3em;
|
||||
}
|
||||
@@ -981,6 +1036,7 @@ input[type="checkbox"]+label {
|
||||
}
|
||||
input[type="radio"]:checked+label,
|
||||
input[type="checkbox"]:checked+label {
|
||||
color: #0e0;
|
||||
color: var(--a);
|
||||
}
|
||||
.opwide div>span>input+label {
|
||||
@@ -1141,6 +1197,7 @@ html {
|
||||
}
|
||||
.btn {
|
||||
color: var(--btn-fg);
|
||||
background: #eee;
|
||||
background: var(--btn-bg);
|
||||
border-radius: .3em;
|
||||
padding: .2em .4em;
|
||||
@@ -1162,7 +1219,9 @@ html.ca .btn {
|
||||
background: var(--btn-h-bg);
|
||||
}
|
||||
.tgl.btn.on {
|
||||
background: #000;
|
||||
background: var(--btn-1-bg);
|
||||
color: #fff;
|
||||
color: var(--btn-1-fg);
|
||||
text-shadow: none;
|
||||
}
|
||||
@@ -1172,7 +1231,7 @@ html.ca .tgl.btn.on {
|
||||
}
|
||||
.tgl.btn.on:hover {
|
||||
background: var(--btn-1h-bg);
|
||||
color: var(--btn-1-fg);
|
||||
color: var(--btn-1h-fg);
|
||||
}
|
||||
#detree {
|
||||
padding: .3em .5em;
|
||||
@@ -1193,8 +1252,10 @@ html.ca .tgl.btn.on {
|
||||
border-top: 1px solid var(--bg-u5);
|
||||
}
|
||||
#tree ul a.sel {
|
||||
background: #000;
|
||||
background: var(--bg-d3);
|
||||
box-shadow: -.8em 0 0 var(--g-sel-b1) inset;
|
||||
color: #fff;
|
||||
color: var(--fg-max);
|
||||
}
|
||||
#tree ul a.hl {
|
||||
@@ -1267,20 +1328,6 @@ html.y #tree.nowrap .ntree a+a:hover {
|
||||
#files td:nth-child(2n) {
|
||||
color: var(--tab-alt);
|
||||
}
|
||||
#files tbody tr.play td,
|
||||
#files tbody tr.play td+td,
|
||||
#files tbody tr.play div a {
|
||||
background: var(--f-play-bg);
|
||||
color: var(--f-play-fg);
|
||||
text-shadow: none;
|
||||
}
|
||||
#files tbody tr.play a {
|
||||
color: inherit;
|
||||
}
|
||||
#files tbody tr.play a:hover {
|
||||
color: var(--btn-1-fg);
|
||||
background: var(--btn-1h-bg);
|
||||
}
|
||||
.opwide,
|
||||
#op_unpost,
|
||||
#srch_form {
|
||||
@@ -1742,7 +1789,7 @@ html.y #bbox-overlay figcaption a {
|
||||
background: rgba(48, 48, 48, 0.7);
|
||||
}
|
||||
#drops.vis,
|
||||
#drops .dropzone {
|
||||
.dropzone {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@@ -1750,61 +1797,76 @@ html.y #bbox-overlay figcaption a {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
#drops .dropdesc {
|
||||
.dropdesc {
|
||||
position: fixed;
|
||||
display: table;
|
||||
left: 10%;
|
||||
width: 78%;
|
||||
height: 30%;
|
||||
height: 26%;
|
||||
margin: 0;
|
||||
font-size: 3em;
|
||||
font-weight: bold;
|
||||
text-shadow: .05em .05em .1em var(--bg-max);
|
||||
text-shadow: .05em .05em .1em #333;
|
||||
background: rgba(224, 224, 224, 0.2);
|
||||
box-shadow: 0 0 0 #999;
|
||||
border: .5em solid #999;
|
||||
border: .5em solid var(--ud-b1);
|
||||
border-radius: .5em;
|
||||
border-width: 1vw;
|
||||
color: #fff;
|
||||
transition: all 0.12s;
|
||||
}
|
||||
#drops .dropdesc.hl.ok {
|
||||
.dropdesc.hl.ok {
|
||||
border-color: #fff;
|
||||
box-shadow: 0 0 1em #cf5;
|
||||
box-shadow: 0 0 1em .4em #cf5, 0 0 1em #000 inset;
|
||||
background: rgba(24, 24, 24, 0.7);
|
||||
left: 8%;
|
||||
width: 82%;
|
||||
height: 32%;
|
||||
margin: -1vh 0;
|
||||
margin: -3vh 0;
|
||||
}
|
||||
#drops .dropdesc.hl.err {
|
||||
.dropdesc.hl.err {
|
||||
background: rgba(224, 32, 65, 0.2);
|
||||
box-shadow: 0 0 1em #f26;
|
||||
box-shadow: 0 0 1em .4em #f26;
|
||||
border-color: #fab;
|
||||
}
|
||||
#drops .dropdesc>div {
|
||||
.dropdesc>div {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
#drops .dropdesc>div>div {
|
||||
.dropdesc>div>div {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
top: calc(50% - .5em);
|
||||
left: -.8em;
|
||||
}
|
||||
#drops .dropdesc>div>div+div {
|
||||
.dropdesc>div>div+div {
|
||||
left: auto;
|
||||
right: -.8em;
|
||||
}
|
||||
#drops .dropzone {
|
||||
.dropdesc b {
|
||||
position: relative;
|
||||
font-size: .5em;
|
||||
font-size: 2vw;
|
||||
top: -.25em;
|
||||
top: -.2vw;
|
||||
margin: 0 .8em;
|
||||
margin: 0 1.25vw;
|
||||
transition: font-size 0.12s;
|
||||
}
|
||||
.dropdesc.hl b {
|
||||
border-bottom: .1em solid #fff;
|
||||
font-size: .6em;
|
||||
font-size: 2.5vw;
|
||||
}
|
||||
.dropzone {
|
||||
z-index: 80386;
|
||||
height: 50%;
|
||||
}
|
||||
#drops #up_dz {
|
||||
#up_dz {
|
||||
bottom: 50%;
|
||||
}
|
||||
#drops #srch_dz {
|
||||
#srch_dz {
|
||||
top: 50%;
|
||||
}
|
||||
#up_zd {
|
||||
@@ -1817,7 +1879,7 @@ html.y #bbox-overlay figcaption a {
|
||||
color: #fff;
|
||||
background: #490;
|
||||
border-bottom: .3em solid #6d2;
|
||||
text-shadow: 1px 1px 1px var(--bg-max);
|
||||
text-shadow: 1px 1px 1px #000;
|
||||
border-radius: .3em;
|
||||
padding: .4em .8em;
|
||||
font-size: .4em;
|
||||
@@ -2145,7 +2207,7 @@ html.b #u2conf a.b:hover {
|
||||
#u2tab span.inf,
|
||||
#u2tab span.ok,
|
||||
#u2tab span.err {
|
||||
color: var(--fg-max);
|
||||
color: #fff;
|
||||
padding: .22em;
|
||||
border-radius: .2em;
|
||||
border: 2px solid #f0f;
|
||||
@@ -2393,6 +2455,9 @@ html.b #op_up2k {
|
||||
html.b #tree {
|
||||
box-shadow: 0 -1px 0 rgba(128,128,128,0.4);
|
||||
}
|
||||
html.bz #tree {
|
||||
box-shadow: 0 -1px 0 var(--bg-d3);
|
||||
}
|
||||
html.b #treeh,
|
||||
html.b #tree li {
|
||||
border: none;
|
||||
@@ -2401,7 +2466,7 @@ html.b .ntree a {
|
||||
padding: .6em .2em;
|
||||
}
|
||||
html.b #treepar {
|
||||
border-bottom: .2em solid #999;
|
||||
border-bottom: .2em solid var(--f-h-b1);
|
||||
}
|
||||
html.b #wrap {
|
||||
margin-top: 2em;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
function dbg(msg) {
|
||||
ebi('path').innerHTML = msg;
|
||||
}
|
||||
var XHR = XMLHttpRequest;
|
||||
|
||||
|
||||
// toolbar
|
||||
@@ -128,8 +129,8 @@ ebi('op_up2k').innerHTML = (
|
||||
var o = mknod('div');
|
||||
o.innerHTML = (
|
||||
'<div id="drops">\n' +
|
||||
' <div class="dropdesc" id="up_zd"><div>🚀 Upload<br /><span></span><div>🚀</div><div>🚀</div></div></div>\n' +
|
||||
' <div class="dropdesc" id="srch_zd"><div>🔎 Search<br /><span></span><div>🔎</div><div>🔎</div></div></div>\n' +
|
||||
' <div class="dropdesc" id="up_zd"><div>🚀 Upload<br /><span></span><div>🚀<b>Upload</b></div><div><b>Upload</b>🚀</div></div></div>\n' +
|
||||
' <div class="dropdesc" id="srch_zd"><div>🔎 Search<br /><span></span><div>🔎<b>Search</b></div><div><b>Search</b>🔎</div></div></div>\n' +
|
||||
' <div class="dropzone" id="up_dz" v="up_zd"></div>\n' +
|
||||
' <div class="dropzone" id="srch_dz" v="srch_zd"></div>\n' +
|
||||
'</div>'
|
||||
@@ -148,7 +149,6 @@ ebi('op_cfg').innerHTML = (
|
||||
' <a id="thumbs" class="tgl btn" href="#" tt="in icon view, toggle icons or thumbnails$NHotkey: T">🖼️ thumbs</a>\n' +
|
||||
' <a id="dotfiles" class="tgl btn" href="#" tt="show hidden files (if server permits)">dotfiles</a>\n' +
|
||||
' <a id="ireadme" class="tgl btn" href="#" tt="show README.md in folder listings">📜 readme</a>\n' +
|
||||
' <a id="spafiles" class="tgl btn" href="#" tt="speedboost when not using the navpane;$Nturn it off if things arent loading somehow">spa</a>\n' +
|
||||
' </div>\n' +
|
||||
'</div>\n' +
|
||||
'<div>\n' +
|
||||
@@ -164,7 +164,7 @@ ebi('op_cfg').innerHTML = (
|
||||
' <div>\n' +
|
||||
' <a id="u2turbo" class="tgl btn ttb" href="#" tt="the yolo button, you probably DO NOT want to enable this:$N$Nuse this if you were uploading a huge amount of files and had to restart for some reason, and want to continue the upload ASAP$N$Nthis replaces the hash-check with a simple <em>"does this have the same filesize on the server?"</em> so if the file contents are different it will NOT be uploaded$N$Nyou should turn this off when the upload is done, and then "upload" the same files again to let the client verify them">turbo</a>\n' +
|
||||
' <a id="u2tdate" class="tgl btn ttb" href="#" tt="has no effect unless the turbo button is enabled$N$Nreduces the yolo factor by a tiny amount; checks whether the file timestamps on the server matches yours$N$Nshould <em>theoretically</em> catch most unfinished/corrupted uploads, but is not a substitute for doing a verification pass with turbo disabled afterwards">date-chk</a>\n' +
|
||||
' <a id="flag_en" class="tgl btn" href="#" tt="ensure only one tab is uploading at a time $N (other tabs must have this enabled too)">💤</a>\n' +
|
||||
' <a id="flag_en" class="tgl btn" href="#" tt="ensure only one tab is uploading at a time $N -- other tabs must have this enabled too $N -- only affects tabs on the same domain">💤</a>\n' +
|
||||
' </td>\n' +
|
||||
' </div>\n' +
|
||||
'</div>\n' +
|
||||
@@ -319,8 +319,8 @@ var mpl = (function () {
|
||||
ebi('op_player').innerHTML = (
|
||||
'<div><h3>switches</h3><div>' +
|
||||
'<a href="#" class="tgl btn" id="au_preload" tt="start loading the next song near the end for gapless playback">preload</a>' +
|
||||
'<a href="#" class="tgl btn" id="au_fullpre" tt="try to preload the entire song;$N✔️ enable on <b>unreliable</b> connections,$N❌ <b>disable</b> on slow connections probably">full</a>' +
|
||||
'<a href="#" class="tgl btn" id="au_npclip" tt="show buttons for clipboarding the currently playing song">/np clip</a>' +
|
||||
'<a href="#" class="tgl btn" id="au_fullpre" tt="try to preload the entire song;$N✅ enable on <b>unreliable</b> connections,$N❌ <b>disable</b> on slow connections probably">full</a>' +
|
||||
'<a href="#" class="tgl btn" id="au_npclip" tt="show buttons for clipboarding the currently playing song">/np</a>' +
|
||||
'<a href="#" class="tgl btn" id="au_os_ctl" tt="os integration (media hotkeys / osd)">os-ctl</a>' +
|
||||
'<a href="#" class="tgl btn" id="au_os_seek" tt="allow seeking through os integration">seek</a>' +
|
||||
'<a href="#" class="tgl btn" id="au_osd_cv" tt="show album cover in osd">art</a>' +
|
||||
@@ -612,6 +612,10 @@ function MPlayer() {
|
||||
r.fvol = 0;
|
||||
r.au.pause();
|
||||
mpl.pp();
|
||||
|
||||
var t = mp.au.currentTime - 0.8;
|
||||
if (isFinite(t))
|
||||
mp.au.currentTime = Math.max(t, 0);
|
||||
}
|
||||
else if (r.fvol > r.vol)
|
||||
r.fvol = r.vol;
|
||||
@@ -1099,6 +1103,7 @@ var mpui = (function () {
|
||||
fpreloaded = null;
|
||||
|
||||
r.progress_updater = function () {
|
||||
//console.trace();
|
||||
timer.add(updater_impl, true);
|
||||
};
|
||||
|
||||
@@ -1462,6 +1467,7 @@ function play(tid, is_ev, seek) {
|
||||
mp.au2 = new Audio();
|
||||
mp.au.onerror = evau_error;
|
||||
mp.au.onprogress = pbar.drawpos;
|
||||
mp.au.onplaying = mpui.progress_updater;
|
||||
mp.au.onended = next_song;
|
||||
widget.open();
|
||||
}
|
||||
@@ -1478,6 +1484,7 @@ function play(tid, is_ev, seek) {
|
||||
t.onerror = t.onprogress = t.onended = null;
|
||||
mp.au.onerror = evau_error;
|
||||
mp.au.onprogress = pbar.drawpos;
|
||||
mp.au.onplaying = mpui.progress_updater;
|
||||
mp.au.onended = next_song;
|
||||
}
|
||||
else
|
||||
@@ -1562,12 +1569,37 @@ function evau_error(e) {
|
||||
err = 'Unknown Errol';
|
||||
break;
|
||||
}
|
||||
if (eplaya.error.message)
|
||||
err += '\n\n' + eplaya.error.message;
|
||||
var em = '' + eplaya.error.message,
|
||||
mfile = '\n\nFile: «' + uricom_dec(eplaya.src.split('/').pop())[0] + '»',
|
||||
e404 = 'Could not play audio; error 404: File not found.',
|
||||
e403 = 'Could not play audio; error 403: Access denied.\n\nTry pressing F5 to reload, maybe you got logged out';
|
||||
|
||||
err += '\n\nFile: «' + uricom_dec(eplaya.src.split('/').pop())[0] + '»';
|
||||
if (em)
|
||||
err += '\n\n' + em;
|
||||
|
||||
toast.warn(15, esc(basenames(err)));
|
||||
if (em.startsWith('403: '))
|
||||
err = e403;
|
||||
|
||||
if (em.startsWith('404: '))
|
||||
err = e404;
|
||||
|
||||
toast.warn(15, esc(basenames(err + mfile)));
|
||||
|
||||
if (em.startsWith('MEDIA_ELEMENT_ERROR:')) {
|
||||
// chromish for 40x
|
||||
var xhr = new XHR();
|
||||
xhr.open('HEAD', eplaya.src, true);
|
||||
xhr.onreadystatechange = function () {
|
||||
if (this.readyState != XHR.DONE || this.status < 400)
|
||||
return;
|
||||
|
||||
err = this.status == 403 ? e403 : this.status == 404 ? e404 :
|
||||
'Could not play audio; server error ' + this.status;
|
||||
|
||||
toast.warn(15, esc(basenames(err + mfile)));
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2147,7 +2179,7 @@ var fileman = (function () {
|
||||
var dst = base + uricom_enc(f[0].inew.value, false);
|
||||
|
||||
function rename_cb() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
if (this.status !== 200) {
|
||||
@@ -2160,7 +2192,7 @@ var fileman = (function () {
|
||||
return rn_apply();
|
||||
}
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr = new XHR();
|
||||
xhr.open('GET', f[0].src + '?move=' + dst, true);
|
||||
xhr.onreadystatechange = rename_cb;
|
||||
xhr.send();
|
||||
@@ -2182,7 +2214,7 @@ var fileman = (function () {
|
||||
return toast.err(3, 'select at least 1 item to delete');
|
||||
|
||||
function deleter() {
|
||||
var xhr = new XMLHttpRequest(),
|
||||
var xhr = new XHR(),
|
||||
vp = vps.shift();
|
||||
|
||||
if (!vp) {
|
||||
@@ -2197,7 +2229,7 @@ var fileman = (function () {
|
||||
xhr.send();
|
||||
}
|
||||
function delete_cb() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
if (this.status !== 200) {
|
||||
@@ -2290,7 +2322,7 @@ var fileman = (function () {
|
||||
return;
|
||||
|
||||
function paster() {
|
||||
var xhr = new XMLHttpRequest(),
|
||||
var xhr = new XHR(),
|
||||
vp = req.shift();
|
||||
|
||||
if (!vp) {
|
||||
@@ -2308,7 +2340,7 @@ var fileman = (function () {
|
||||
xhr.send();
|
||||
}
|
||||
function paste_cb() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
if (this.status !== 200) {
|
||||
@@ -2461,7 +2493,7 @@ var showfile = (function () {
|
||||
};
|
||||
|
||||
r.show = function (url, no_push) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr = new XHR();
|
||||
xhr.url = url;
|
||||
xhr.no_push = no_push;
|
||||
xhr.ts = Date.now();
|
||||
@@ -2471,13 +2503,11 @@ var showfile = (function () {
|
||||
};
|
||||
|
||||
function load_cb() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
if (this.status !== 200) {
|
||||
toast.err(0, "recvtree, http " + this.status + ": " + this.responseText);
|
||||
if (!xhrchk(this, "could not load textfile:\n\nerror ", "404, file not found"))
|
||||
return;
|
||||
}
|
||||
|
||||
render([this.url, '', this.responseText], this.no_push);
|
||||
}
|
||||
@@ -2824,7 +2854,7 @@ var thegrid = (function () {
|
||||
else if (in_tree && !have_sel)
|
||||
in_tree.click();
|
||||
|
||||
else if (is_dir && !have_sel && treectl.spa)
|
||||
else if (is_dir && !have_sel)
|
||||
treectl.reqls(href, true, true);
|
||||
|
||||
else if (!is_img && have_sel)
|
||||
@@ -3464,7 +3494,7 @@ document.onkeydown = function (e) {
|
||||
srch_msg(false, "searching...");
|
||||
clearTimeout(search_timeout);
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr = new XHR();
|
||||
xhr.open('POST', '/?srch', true);
|
||||
xhr.setRequestHeader('Content-Type', 'text/plain');
|
||||
xhr.onreadystatechange = xhr_search_results;
|
||||
@@ -3474,7 +3504,7 @@ document.onkeydown = function (e) {
|
||||
}
|
||||
|
||||
function xhr_search_results() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
if (this.status !== 200) {
|
||||
@@ -3594,7 +3624,6 @@ var treectl = (function () {
|
||||
mentered = null,
|
||||
treesz = clamp(icfg_get('treesz', 16), 10, 50);
|
||||
|
||||
bcfg_bind(r, 'spa', 'spafiles', true);
|
||||
bcfg_bind(r, 'ireadme', 'ireadme', true);
|
||||
bcfg_bind(r, 'dyn', 'dyntree', true, onresize);
|
||||
bcfg_bind(r, 'dots', 'dotfiles', false, function (v) {
|
||||
@@ -3802,7 +3831,7 @@ var treectl = (function () {
|
||||
};
|
||||
|
||||
function get_tree(top, dst, rst) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr = new XHR();
|
||||
xhr.top = top;
|
||||
xhr.dst = dst;
|
||||
xhr.rst = rst;
|
||||
@@ -3814,13 +3843,11 @@ var treectl = (function () {
|
||||
}
|
||||
|
||||
function recvtree() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
if (this.status !== 200) {
|
||||
toast.err(0, "recvtree, http " + this.status + ": " + this.responseText);
|
||||
if (!xhrchk(this, "could not list subfolders:\n\nerror ", "404, folder not found"))
|
||||
return;
|
||||
}
|
||||
|
||||
var cur = ebi('treeul').getAttribute('ts');
|
||||
if (cur && parseInt(cur) > this.ts) {
|
||||
@@ -3973,7 +4000,7 @@ var treectl = (function () {
|
||||
}
|
||||
|
||||
r.reqls = function (url, hpush, no_tree) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr = new XHR();
|
||||
xhr.top = url;
|
||||
xhr.hpush = hpush;
|
||||
xhr.ts = Date.now();
|
||||
@@ -4002,13 +4029,11 @@ var treectl = (function () {
|
||||
}
|
||||
|
||||
function recvls() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
if (this.status !== 200) {
|
||||
toast.err(0, "recvls, http " + this.status + ": " + this.responseText);
|
||||
if (!xhrchk(this, "could not list files in folder:\n\nerror ", "404, folder not found"))
|
||||
return;
|
||||
}
|
||||
|
||||
var cur = ebi('files').getAttribute('ts');
|
||||
if (cur && parseInt(cur) > this.ts) {
|
||||
@@ -4137,7 +4162,7 @@ var treectl = (function () {
|
||||
r.hydrate = function () {
|
||||
qsr('#bbsw');
|
||||
if (ls0 === null) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr = new XHR();
|
||||
xhr.open('GET', '/?am_js', true);
|
||||
xhr.send();
|
||||
|
||||
@@ -4898,7 +4923,7 @@ var msel = (function () {
|
||||
fd.append("act", "mkdir");
|
||||
fd.append("name", tb.value);
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr = new XHR();
|
||||
xhr.vp = get_evpath();
|
||||
xhr.dn = tb.value;
|
||||
xhr.open('POST', xhr.vp, true);
|
||||
@@ -4910,7 +4935,7 @@ var msel = (function () {
|
||||
};
|
||||
|
||||
function cb() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
if (this.vp !== get_evpath()) {
|
||||
@@ -4918,6 +4943,8 @@ var msel = (function () {
|
||||
return;
|
||||
}
|
||||
|
||||
xhrchk(this, "could not create subfolder:\n\nerror ", "404, parent folder not found");
|
||||
|
||||
if (this.status !== 200) {
|
||||
sf.textContent = 'error: ' + this.responseText;
|
||||
return;
|
||||
@@ -4947,7 +4974,7 @@ var msel = (function () {
|
||||
clmod(sf, 'vis', 1);
|
||||
sf.textContent = 'sending...';
|
||||
|
||||
var xhr = new XMLHttpRequest(),
|
||||
var xhr = new XHR(),
|
||||
ct = 'application/x-www-form-urlencoded;charset=UTF-8';
|
||||
|
||||
xhr.msg = tb.value;
|
||||
@@ -4963,9 +4990,11 @@ var msel = (function () {
|
||||
};
|
||||
|
||||
function cb() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
xhrchk(this, "could not send message:\n\nerror ", "404, parent folder not found");
|
||||
|
||||
if (this.status !== 200) {
|
||||
sf.textContent = 'error: ' + this.responseText;
|
||||
return;
|
||||
@@ -5080,15 +5109,11 @@ var unpost = (function () {
|
||||
html = [];
|
||||
|
||||
function unpost_load_cb() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
if (this.status !== 200) {
|
||||
var msg = this.responseText;
|
||||
toast.err(9, 'unpost-load failed:\n' + msg);
|
||||
ebi('op_unpost').innerHTML = html.join('\n');
|
||||
return;
|
||||
}
|
||||
if (!xhrchk(this, "unpost-load failed:\n\nerror ", "404, file not found??"))
|
||||
return ebi('op_unpost').innerHTML = 'failed to load unpost list from server';
|
||||
|
||||
var res = JSON.parse(this.responseText);
|
||||
if (res.length) {
|
||||
@@ -5128,7 +5153,7 @@ var unpost = (function () {
|
||||
if (filt.value)
|
||||
q += '&filter=' + uricom_enc(filt.value, true);
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr = new XHR();
|
||||
xhr.open('GET', q, true);
|
||||
xhr.onreadystatechange = unpost_load_cb;
|
||||
xhr.send();
|
||||
@@ -5137,7 +5162,7 @@ var unpost = (function () {
|
||||
};
|
||||
|
||||
function unpost_delete_cb() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
if (this.status !== 200) {
|
||||
@@ -5188,7 +5213,7 @@ var unpost = (function () {
|
||||
|
||||
toast.inf(0, "deleting " + req.length + " files...");
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr = new XHR();
|
||||
xhr.n = n;
|
||||
xhr.n2 = n2;
|
||||
xhr.open('POST', '/?delete', true);
|
||||
@@ -5229,7 +5254,7 @@ function wintitle(txt) {
|
||||
|
||||
ebi('path').onclick = function (e) {
|
||||
var a = e.target.closest('a[href]');
|
||||
if (!treectl.spa || !a || !(a = a.getAttribute('href') + '') || !a.endsWith('/'))
|
||||
if (!a || !(a = a.getAttribute('href') + '') || !a.endsWith('/'))
|
||||
return;
|
||||
|
||||
thegrid.setvis(true);
|
||||
@@ -5249,11 +5274,8 @@ ebi('files').onclick = ebi('docul').onclick = function (e) {
|
||||
el.click();
|
||||
return ev(e);
|
||||
}
|
||||
if (treectl.spa) {
|
||||
treectl.reqls(tgt.getAttribute('href'), true, true);
|
||||
return ev(e);
|
||||
}
|
||||
return;
|
||||
treectl.reqls(tgt.getAttribute('href'), true, true);
|
||||
return ev(e);
|
||||
}
|
||||
|
||||
tgt = e.target.closest('a[hl]');
|
||||
|
||||
@@ -45,7 +45,9 @@
|
||||
<tr><td></td><td><a href="../{{ url_suf }}">parent folder</a></td><td>-</td><td>-</td></tr>
|
||||
|
||||
{%- for f in files %}
|
||||
<tr><td>{{ f.lead }}</td><td><a href="{{ f.href }}{{ url_suf }}">{{ f.name|e }}</a></td><td>{{ f.sz }}</td><td>{{ f.dt }}</td></tr>
|
||||
<tr><td>{{ f.lead }}</td><td><a href="{{ f.href }}{{
|
||||
'&' + url_suf[1:] if url_suf[:1] == '?' and '?' in f.href else url_suf
|
||||
}}">{{ f.name|e }}</a></td><td>{{ f.sz }}</td><td>{{ f.dt }}</td></tr>
|
||||
{%- endfor %}
|
||||
|
||||
</tbody>
|
||||
|
||||
@@ -255,7 +255,7 @@ function Modpoll() {
|
||||
|
||||
console.log('modpoll...');
|
||||
var url = (document.location + '').split('?')[0] + '?raw&_=' + Date.now();
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr = new XHR();
|
||||
xhr.open('GET', url, true);
|
||||
xhr.responseType = 'text';
|
||||
xhr.onreadystatechange = r.cb;
|
||||
@@ -268,7 +268,7 @@ function Modpoll() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
if (this.status !== 200) {
|
||||
@@ -336,7 +336,7 @@ function save(e) {
|
||||
fd.append("body", txt);
|
||||
|
||||
var url = (document.location + '').split('?')[0];
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr = new XHR();
|
||||
xhr.open('POST', url, true);
|
||||
xhr.responseType = 'text';
|
||||
xhr.onreadystatechange = save_cb;
|
||||
@@ -356,7 +356,7 @@ function save(e) {
|
||||
}
|
||||
|
||||
function save_cb() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
if (this.status !== 200)
|
||||
@@ -397,7 +397,7 @@ function save_cb() {
|
||||
function run_savechk(lastmod, txt, btn, ntry) {
|
||||
// download the saved doc from the server and compare
|
||||
var url = (document.location + '').split('?')[0] + '?raw&_=' + Date.now();
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr = new XHR();
|
||||
xhr.open('GET', url, true);
|
||||
xhr.responseType = 'text';
|
||||
xhr.onreadystatechange = savechk_cb;
|
||||
@@ -409,7 +409,7 @@ function run_savechk(lastmod, txt, btn, ntry) {
|
||||
}
|
||||
|
||||
function savechk_cb() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
if (this.status !== 200)
|
||||
|
||||
@@ -114,7 +114,7 @@ function save(mde) {
|
||||
fd.append("body", txt);
|
||||
|
||||
var url = (document.location + '').split('?')[0];
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr = new XHR();
|
||||
xhr.open('POST', url, true);
|
||||
xhr.responseType = 'text';
|
||||
xhr.onreadystatechange = save_cb;
|
||||
@@ -133,7 +133,7 @@ function save(mde) {
|
||||
}
|
||||
|
||||
function save_cb() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
if (this.status !== 200)
|
||||
@@ -170,7 +170,7 @@ function save_cb() {
|
||||
|
||||
// download the saved doc from the server and compare
|
||||
var url = (document.location + '').split('?')[0] + '?raw';
|
||||
var xhr = new XMLHttpRequest();
|
||||
var xhr = new XHR();
|
||||
xhr.open('GET', url, true);
|
||||
xhr.responseType = 'text';
|
||||
xhr.onreadystatechange = save_chk;
|
||||
@@ -182,7 +182,7 @@ function save_cb() {
|
||||
}
|
||||
|
||||
function save_chk() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
if (this.readyState != XHR.DONE)
|
||||
return;
|
||||
|
||||
if (this.status !== 200)
|
||||
|
||||
@@ -101,6 +101,11 @@ document.documentElement.className = localStorage.light == 1 ? "y" : "z";
|
||||
|
||||
</script>
|
||||
<script src="/.cpr/util.js?_={{ ts }}"></script>
|
||||
<script>tt.init();</script>
|
||||
<script>
|
||||
tt.init();
|
||||
{%- if this.uname == '*' %}
|
||||
QS('input[name="cppwd"]').focus();
|
||||
{%- endif %}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -11,6 +11,7 @@ html {
|
||||
max-width: 34em;
|
||||
max-width: min(34em, 90%);
|
||||
max-width: min(34em, calc(100% - 7em));
|
||||
color: #ddd;
|
||||
background: #333;
|
||||
border: 0 solid #777;
|
||||
box-shadow: 0 .2em .5em #111;
|
||||
|
||||
@@ -1811,11 +1811,8 @@ function up2k_init(subtle) {
|
||||
tasker();
|
||||
return;
|
||||
}
|
||||
toast.err(0, "server broke; hs-err {0} on file [{1}]:\n".format(
|
||||
xhr.status, t.name) + (
|
||||
(xhr.response && xhr.response.err) ||
|
||||
(xhr.responseText && xhr.responseText) ||
|
||||
"no further information"));
|
||||
err = t.t_uploading ? "finalize upload" : t.srch ? "perform search" : "initiate upload";
|
||||
xhrchk(xhr, "server rejected the request to " + err + ";\n\nfile: " + t.name + "\n\nerror ", "404, target folder not found");
|
||||
}
|
||||
}
|
||||
xhr.onload = function (e) {
|
||||
@@ -1874,8 +1871,7 @@ function up2k_init(subtle) {
|
||||
console.log("ignoring dupe-segment error", t);
|
||||
}
|
||||
else {
|
||||
toast.err(0, "server broke; cu-err {0} on file [{1}]:\n".format(
|
||||
xhr.status, t.name) + (txt || "no further information"));
|
||||
xhrchk(xhr, "server rejected upload (chunk {0} of {1});\n\nfile: {2}\n\nerror ".format(npart, Math.ceil(t.size / chunksize), t.name), "404, target folder not found (???)");
|
||||
|
||||
chill(t);
|
||||
}
|
||||
@@ -1904,7 +1900,7 @@ function up2k_init(subtle) {
|
||||
return;
|
||||
|
||||
if (!toast.visible)
|
||||
toast.warn(9.98, "failed to upload a chunk;\nprobably harmless, continuing\n\n" + t.name);
|
||||
toast.warn(9.98, "failed to upload chunk {0} of {1};\nprobably harmless, continuing\n\nfile: {2}".format(npart, Math.ceil(t.size / chunksize), t.name));
|
||||
|
||||
console.log('chunkpit onerror,', ++tries, t);
|
||||
orz2(xhr);
|
||||
|
||||
@@ -14,7 +14,8 @@ var is_touch = 'ontouchstart' in window,
|
||||
var ebi = document.getElementById.bind(document),
|
||||
QS = document.querySelector.bind(document),
|
||||
QSA = document.querySelectorAll.bind(document),
|
||||
mknod = document.createElement.bind(document);
|
||||
mknod = document.createElement.bind(document),
|
||||
XHR = XMLHttpRequest;
|
||||
|
||||
|
||||
function qsr(sel) {
|
||||
@@ -1185,6 +1186,9 @@ var modal = (function () {
|
||||
return ok();
|
||||
}
|
||||
|
||||
if ((k == 'ArrowLeft' || k == 'ArrowRight') && eng && (ae == eok || ae == eng))
|
||||
return (ae == eok ? eng : eok).focus() || ev(e);
|
||||
|
||||
if (k == 'Escape')
|
||||
return ng();
|
||||
}
|
||||
@@ -1386,3 +1390,18 @@ var favico = (function () {
|
||||
r.to = setTimeout(r.init, 100);
|
||||
return r;
|
||||
})();
|
||||
|
||||
|
||||
function xhrchk(xhr, prefix, e404) {
|
||||
if (xhr.status < 400 && xhr.status >= 200)
|
||||
return true;
|
||||
|
||||
if (xhr.status == 403)
|
||||
return toast.err(0, prefix + "403, access denied\n\ntry pressing F5, maybe you got logged out");
|
||||
|
||||
if (xhr.status == 404)
|
||||
return toast.err(0, prefix + e404);
|
||||
|
||||
return toast.err(0, prefix + xhr.status + ": " + (
|
||||
(xhr.response && xhr.response.err) || xhr.responseText));
|
||||
}
|
||||
|
||||
4
docs/notes.bat
Normal file
4
docs/notes.bat
Normal file
@@ -0,0 +1,4 @@
|
||||
rem appending a static ip to a dhcp nic on windows 10-1703 or later
|
||||
netsh interface ipv4 show interface
|
||||
netsh interface ipv4 set interface interface="Ethernet 2" dhcpstaticipcoexistence=enabled
|
||||
netsh interface ipv4 add address "Ethernet 2" 10.1.2.4 255.255.255.0
|
||||
@@ -59,6 +59,7 @@ class Cfg(Namespace):
|
||||
theme=0,
|
||||
themes=0,
|
||||
turbo=0,
|
||||
logout=573,
|
||||
hist=None,
|
||||
no_idx=None,
|
||||
no_hash=None,
|
||||
|
||||
@@ -39,6 +39,7 @@ class Cfg(Namespace):
|
||||
"theme": 0,
|
||||
"themes": 0,
|
||||
"turbo": 0,
|
||||
"logout": 573,
|
||||
}
|
||||
ex.update(ex2)
|
||||
super(Cfg, self).__init__(a=a or [], v=v or [], c=c, **ex)
|
||||
|
||||
Reference in New Issue
Block a user