Compare commits

..

32 Commits

Author SHA1 Message Date
ed
bca0cdbb62 v1.0.13 2021-10-24 21:06:14 +02:00
ed
1ee11e04e6 v1.0.12 2021-10-24 03:12:54 +02:00
ed
6eef44f212 ie 2021-10-24 02:57:19 +02:00
ed
8bd94f4a1c add readme banner 2021-10-24 01:24:54 +02:00
ed
4bc4701372 "fix" up2k layout 2021-10-24 01:19:48 +02:00
ed
dfd89b503a ajax navigation in table listing too 2021-10-24 00:54:22 +02:00
ed
060dc54832 thumbnail caching 2021-10-24 00:29:04 +02:00
ed
f7a4ea5793 add --js-browser 2021-10-24 00:26:47 +02:00
ed
71b478e6e2 persist webp test result 2021-10-24 00:23:51 +02:00
ed
ed8fff8c52 more ux 2021-10-24 00:22:46 +02:00
ed
95dc78db10 thumbnails alignment 2021-10-23 21:51:16 +02:00
ed
addeac64c7 checkbox selection hilight 2021-10-23 18:28:45 +02:00
ed
d77ec22007 more ux 2021-10-23 16:59:11 +02:00
ed
20030c91b7 looks better 2021-10-23 02:46:18 +02:00
ed
8b366e255c fix thumbnail toggle not giving instant feedback 2021-10-23 02:38:37 +02:00
ed
6da366fcb0 forgot a few 2021-10-23 02:33:51 +02:00
ed
2fa35f851e ux 2021-10-22 11:12:04 +02:00
ed
e4ca4260bb support mounting entire disks on windows 2021-10-20 00:51:00 +02:00
ed
b69aace8d8 v1.0.11 2021-10-19 01:10:16 +02:00
ed
79097bb43c optimize rmtree on windows 2021-10-19 01:04:21 +02:00
ed
806fac1742 nullwrite fixes 2021-10-19 00:58:24 +02:00
ed
4f97d7cf8d normalize collision suffix 2021-10-19 00:49:35 +02:00
ed
42acc457af allow providing target filename in PUT 2021-10-19 00:48:00 +02:00
ed
c02920607f linkable search results 2021-10-18 21:43:16 +02:00
ed
452885c271 replace the mediaplayer modal with malert 2021-10-18 21:18:46 +02:00
ed
5c242a07b6 refresh file listing on upload complete 2021-10-18 21:10:05 +02:00
ed
088899d59f fix unpost in jumpvols 2021-10-18 21:08:31 +02:00
ed
1faff2a37e u2cli: aggressive flushing on windows 2021-10-18 20:35:50 +02:00
ed
23c8d3d045 option to continue running if binds fail 2021-10-18 20:24:11 +02:00
ed
a033388d2b sort volume listing 2021-10-13 00:21:54 +02:00
ed
82fe45ac56 u2cli: add -z / yolo 2021-10-13 00:03:49 +02:00
ed
bcb7fcda6b u2cli: rsync-like source semantics 2021-10-12 22:46:33 +02:00
21 changed files with 555 additions and 375 deletions

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@ buildenv/
build/
dist/
sfx/
py2/
.venv/
# ide

View File

@@ -747,7 +747,7 @@ TLDR: yes
| zip selection | - | yep | yep | yep | yep | yep | yep | yep |
| file rename | - | yep | yep | yep | yep | yep | yep | yep |
| file cut/paste | - | yep | yep | yep | yep | yep | yep | yep |
| navpane | - | `*2` | yep | yep | yep | yep | yep | yep |
| navpane | - | yep | yep | yep | yep | yep | yep | yep |
| image viewer | - | yep | yep | yep | yep | yep | yep | yep |
| video player | - | yep | yep | yep | yep | yep | yep | yep |
| markdown editor | - | - | yep | yep | yep | yep | yep | yep |
@@ -759,7 +759,6 @@ TLDR: yes
* internet explorer 6 to 8 behave the same
* firefox 52 and chrome 49 are the final winxp versions
* `*1` yes, but extremely slow (ie10: `1 MiB/s`, ie11: `270 KiB/s`)
* `*2` causes a full-page refresh on each navigation
* `*3` using a wasm decoder which consumes a bit more power
quick summary of more eccentric web-browsers trying to view a directory index:

View File

@@ -3,7 +3,7 @@ from __future__ import print_function, unicode_literals
"""
up2k.py: upload to copyparty
2021-10-04, v0.7, ed <irc.rizon.net>, MIT-Licensed
2021-10-12, v0.9, ed <irc.rizon.net>, MIT-Licensed
https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py
- dependencies: requests
@@ -33,11 +33,15 @@ import datetime
PY2 = sys.version_info[0] == 2
if PY2:
from Queue import Queue
from urllib import unquote
from urllib import quote
sys.dont_write_bytecode = True
bytes = str
else:
from queue import Queue
from urllib.parse import unquote_to_bytes as unquote
from urllib.parse import quote_from_bytes as quote
unicode = str
@@ -121,17 +125,30 @@ class FileSlice(object):
return ret
_print = print
def eprint(*a, **ka):
ka["file"] = sys.stderr
ka["end"] = ""
if not PY2:
ka["flush"] = True
print(*a, **ka)
if PY2:
_print(*a, **ka)
if PY2 or not VT100:
sys.stderr.flush()
def flushing_print(*a, **ka):
_print(*a, **ka)
if "flush" not in ka:
sys.stdout.flush()
if not VT100:
print = flushing_print
def termsize():
import os
@@ -231,16 +248,29 @@ def walkdir(top):
def walkdirs(tops):
"""recursive statdir for a list of tops, yields [top, relpath, stat]"""
sep = "{0}".format(os.sep).encode("ascii")
for top in tops:
stop = top
if top[-1:] == sep:
stop = os.path.dirname(top.rstrip(sep))
if os.path.isdir(top):
for ap, inf in walkdir(top):
yield top, ap[len(top) + 1 :], inf
yield stop, ap[len(stop) :].lstrip(sep), inf
else:
sep = "{0}".format(os.sep).encode("ascii")
d, n = top.rsplit(sep, 1)
yield d, n, os.stat(top)
# mostly from copyparty/util.py
def quotep(btxt):
quot1 = quote(btxt, safe=b"/")
if not PY2:
quot1 = quot1.encode("ascii")
return quot1.replace(b" ", b"+")
# from copyparty/util.py
def humansize(sz, terse=False):
"""picks a sensible unit for the given extent"""
@@ -334,7 +364,7 @@ def handshake(req_ses, url, file, pw, search):
if file.url:
url = file.url
elif b"/" in file.rel:
url += file.rel.rsplit(b"/", 1)[0].decode("utf-8", "replace")
url += quotep(file.rel.rsplit(b"/", 1)[0]).decode("utf-8", "replace")
while True:
try:
@@ -403,7 +433,9 @@ class Ctl(object):
def __init__(self, ar):
self.ar = ar
ar.files = [
os.path.abspath(os.path.realpath(x.encode("utf-8"))) for x in ar.files
os.path.abspath(os.path.realpath(x.encode("utf-8")))
+ (x[-1:] if x[-1:] == os.sep else "").encode("utf-8")
for x in ar.files
]
ar.url = ar.url.rstrip("/") + "/"
if "://" not in ar.url:
@@ -442,13 +474,14 @@ class Ctl(object):
print("{0} {1}\n hash...".format(self.nfiles - nf, upath))
get_hashlist(file, None)
burl = self.ar.url[:8] + self.ar.url[8:].split("/")[0] + "/"
while True:
print(" hs...")
hs = handshake(req_ses, self.ar.url, file, self.ar.a, search)
if search:
if hs:
for hit in hs:
print(" found: {0}{1}".format(self.ar.url, hit["rp"]))
print(" found: {0}{1}".format(burl, hit["rp"]))
else:
print(" NOT found")
break
@@ -564,7 +597,36 @@ class Ctl(object):
self.st_hash = [file, ofs]
def hasher(self):
prd = None
ls = {}
for top, rel, inf in self.filegen:
if self.ar.z:
rd = os.path.dirname(rel)
if prd != rd:
prd = rd
headers = {}
if self.ar.a:
headers["Cookie"] = "=".join(["cppwd", self.ar.a])
ls = {}
try:
print(" ls ~{0}".format(rd.decode("utf-8", "replace")))
r = req_ses.get(
self.ar.url.encode("utf-8") + quotep(rd) + b"?ls",
headers=headers,
)
for f in r.json()["files"]:
rfn = f["href"].split("?")[0].encode("utf-8", "replace")
ls[unquote(rfn)] = f
except:
print(" mkdir ~{0}".format(rd.decode("utf-8", "replace")))
rf = ls.get(os.path.basename(rel), None)
if rf and rf["sz"] == inf.st_size and abs(rf["ts"] - inf.st_mtime) <= 1:
self.nfiles -= 1
self.nbytes -= inf.st_size
continue
file = File(top, rel, inf.st_size, inf.st_mtime)
while True:
with self.mutex:
@@ -598,6 +660,7 @@ class Ctl(object):
def handshaker(self):
search = self.ar.s
q = self.q_handshake
burl = self.ar.url[:8] + self.ar.url[8:].split("/")[0] + "/"
while True:
file = q.get()
if not file:
@@ -627,7 +690,7 @@ class Ctl(object):
if hs:
for hit in hs:
m = "found: {0}\n {1}{2}\n"
print(m.format(upath, self.ar.url, hit["rp"]), end="")
print(m.format(upath, burl, hit["rp"]), end="")
else:
print("NOT found: {0}\n".format(upath), end="")
@@ -659,7 +722,8 @@ class Ctl(object):
self.handshaker_busy -= 1
if not hs:
print("uploaded {0}".format(upath))
kw = "uploaded" if file.up_b else " found"
print("{0} {1}".format(kw, upath))
for cid in hs:
self.q_upload.put([file, cid])
@@ -696,13 +760,23 @@ class Ctl(object):
self.uploader_busy -= 1
class APF(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter):
pass
def main():
time.strptime("19970815", "%Y%m%d") # python#7980
if not VT100:
os.system("rem") # enables colors
# fmt: off
ap = app = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
ap = app = argparse.ArgumentParser(formatter_class=APF, epilog="""
NOTE:
source file/folder selection uses rsync syntax, meaning that:
"foo" uploads the entire folder to URL/foo/
"foo/" uploads the CONTENTS of the folder into URL/
""")
ap.add_argument("url", type=unicode, help="server url, including destination folder")
ap.add_argument("files", type=unicode, nargs="+", help="files and/or folders to process")
ap.add_argument("-a", metavar="PASSWORD", help="password")
@@ -711,6 +785,7 @@ def main():
ap.add_argument("-j", type=int, metavar="THREADS", default=4, help="parallel connections")
ap.add_argument("-nh", action="store_true", help="disable hashing while uploading")
ap.add_argument("--safe", action="store_true", help="use simple fallback approach")
ap.add_argument("-z", action="store_true", help="ZOOMIN' (skip uploading files if they exist at the destination with the ~same last-modified timestamp, so same as yolo / turbo with date-chk but even faster)")
ap = app.add_argument_group("tls")
ap.add_argument("-te", metavar="PEM_FILE", help="certificate to expect/verify")
ap.add_argument("-td", action="store_true", help="disable certificate check")

View File

@@ -381,6 +381,10 @@ def run_argparse(argv, formatter):
ap2.add_argument("--no-readme", action="store_true", help="disable rendering readme.md into directory listings")
ap2.add_argument("--vague-403", action="store_true", help="send 404 instead of 403 (security through ambiguity, very enterprise)")
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")
ap2.add_argument("--ign-ebind-all", action="store_true", help="continue running even if it's impossible to receive connections at all")
ap2 = ap.add_argument_group('logging options')
ap2.add_argument("-q", action="store_true", help="quiet")
ap2.add_argument("-lo", metavar="PATH", type=u, help="logfile, example: cpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz")
@@ -433,7 +437,8 @@ def run_argparse(argv, formatter):
default=".vq,.aq,vc,ac,res,.fps")
ap2.add_argument("-mtp", metavar="M=[f,]bin", type=u, action="append", help="read tag M using bin")
ap2 = ap.add_argument_group('appearance options')
ap2 = ap.add_argument_group('ui options')
ap2.add_argument("--js-browser", metavar="L", type=u, help="URL to additional JS to include")
ap2.add_argument("--css-browser", metavar="L", type=u, help="URL to additional CSS to include")
ap2 = ap.add_argument_group('debug options')

View File

@@ -1,8 +1,8 @@
# coding: utf-8
VERSION = (1, 0, 10)
VERSION = (1, 0, 13)
CODENAME = "sufficient"
BUILD_DT = (2021, 10, 12)
BUILD_DT = (2021, 10, 24)
S_VERSION = ".".join(map(str, VERSION))
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)

View File

@@ -726,6 +726,7 @@ class AuthSrv(object):
axs = getattr(vol.axs, axs_key)
if usr in axs or "*" in axs:
umap[usr].append(mp)
umap[usr].sort()
setattr(vfs, "a" + perm, umap)
all_users = {}

View File

@@ -59,7 +59,7 @@ class HttpCli(object):
self.bufsz = 1024 * 32
self.hint = None
self.absolute_urls = False
self.trailing_slash = True
self.out_headers = {
"Access-Control-Allow-Origin": "*",
"Cache-Control": "no-store; max-age=0",
@@ -154,6 +154,8 @@ class HttpCli(object):
self.log_src = self.conn.set_rproxy(self.ip)
self.dip = self.ip.replace(":", ".")
if self.args.ihead:
keys = self.args.ihead
if "*" in keys:
@@ -170,15 +172,11 @@ class HttpCli(object):
# split req into vpath + uparam
uparam = {}
if "?" not in self.req:
if not self.req.endswith("/"):
self.absolute_urls = True
self.trailing_slash = self.req.endswith("/")
vpath = undot(self.req)
else:
vpath, arglist = self.req.split("?", 1)
if not vpath.endswith("/"):
self.absolute_urls = True
self.trailing_slash = vpath.endswith("/")
vpath = undot(vpath)
for k in arglist.split("&"):
if "=" in k:
@@ -276,6 +274,15 @@ class HttpCli(object):
except Pebkac:
return False
def permit_caching(self):
cache = self.uparam.get("cache")
if cache is None:
self.out_headers.update(NO_CACHE)
return
n = "604800" if cache == "i" else cache or "69"
self.out_headers["Cache-Control"] = "max-age=" + n
def send_headers(self, length, status=200, mime=None, headers=None):
response = ["{} {} {}".format(self.http_ver, status, HTTPCODE[status])]
@@ -472,13 +479,13 @@ class HttpCli(object):
except:
raise Pebkac(400, "client d/c before 100 continue")
if "raw" in self.uparam:
return self.handle_stash()
ctype = self.headers.get("content-type", "").lower()
if not ctype:
raise Pebkac(400, "you can't post without a content-type header")
if "raw" in self.uparam:
return self.handle_stash()
if "multipart/form-data" in ctype:
return self.handle_post_multipart()
@@ -539,17 +546,16 @@ class HttpCli(object):
fdir = os.path.join(vfs.realpath, rem)
if lim:
fdir, rem = lim.all(self.ip, rem, remains, fdir)
bos.makedirs(fdir)
addr = self.ip.replace(":", ".")
fn = "put-{:.6f}-{}.bin".format(time.time(), addr)
path = os.path.join(fdir, fn)
if self.args.nw:
path = os.devnull
fn = None
if rem and not self.trailing_slash and not bos.path.isdir(fdir):
fdir, fn = os.path.split(fdir)
rem, _ = vsplit(rem)
open_f = open
open_a = [fsenc(path), "wb", 512 * 1024]
open_ka = {}
bos.makedirs(fdir)
open_ka = {"fun": open}
open_a = ["wb", 512 * 1024]
# user-request || config-force
if ("gz" in vfs.flags or "xz" in vfs.flags) and (
@@ -590,16 +596,28 @@ class HttpCli(object):
self.log("compressing with {} level {}".format(alg, lv.get(alg)))
if alg == "gz":
open_f = gzip.GzipFile
open_a = [fsenc(path), "wb", lv[alg], None, 0x5FEE6600] # 2021-01-01
open_ka["fun"] = gzip.GzipFile
open_a = ["wb", lv[alg], None, 0x5FEE6600] # 2021-01-01
elif alg == "xz":
open_f = lzma.open
open_a = [fsenc(path), "wb"]
open_ka = {"preset": lv[alg]}
open_ka = {"fun": lzma.open, "preset": lv[alg]}
open_a = ["wb"]
else:
self.log("fallthrough? thats a bug", 1)
with open_f(*open_a, **open_ka) as f:
suffix = "-{:.6f}-{}".format(time.time(), self.dip)
params = {"suffix": suffix, "fdir": fdir}
if self.args.nw:
params = {}
fn = os.devnull
params.update(open_ka)
if not fn:
fn = "put" + suffix
with ren_open(fn, *open_a, **params) as f:
f, fn = f["orz"]
path = os.path.join(fdir, fn)
post_sz, _, sha_b64 = hashcopy(reader, f)
if lim:
@@ -1036,7 +1054,7 @@ class HttpCli(object):
if not bos.path.isdir(fdir):
raise Pebkac(404, "that folder does not exist")
suffix = ".{:.6f}-{}".format(time.time(), self.ip)
suffix = "-{:.6f}-{}".format(time.time(), self.dip)
open_args = {"fdir": fdir, "suffix": suffix}
else:
open_args = {}
@@ -1440,10 +1458,8 @@ class HttpCli(object):
if is_compressed:
self.out_headers["Cache-Control"] = "max-age=573"
elif "cache" in self.uparam:
self.out_headers["Cache-Control"] = "max-age=69"
else:
self.out_headers.update(NO_CACHE)
self.permit_caching()
self.out_headers["Accept-Ranges"] = "bytes"
self.send_headers(
@@ -1539,6 +1555,7 @@ class HttpCli(object):
return True
def tx_ico(self, ext, exact=False):
self.permit_caching()
if ext.endswith("/"):
ext = "folder"
exact = True
@@ -2072,7 +2089,7 @@ class HttpCli(object):
for fn in vfs_ls:
base = ""
href = fn
if not is_ls and self.absolute_urls and vpath:
if not is_ls and not self.trailing_slash and vpath:
base = "/" + vpath + "/"
href = base + fn
@@ -2207,6 +2224,9 @@ class HttpCli(object):
if "mth" in vn.flags:
j2a["def_hcols"] = vn.flags["mth"].split(",")
if self.args.js_browser:
j2a["js"] = self.args.js_browser
if self.args.css_browser:
j2a["css"] = self.args.css_browser

View File

@@ -38,6 +38,7 @@ class SvcHub(object):
self.stop_req = False
self.stopping = False
self.stop_cond = threading.Condition()
self.retcode = 0
self.httpsrv_up = 0
self.log_mutex = threading.Lock()
@@ -59,9 +60,9 @@ class SvcHub(object):
if not args.no_fpool and args.j != 1:
m = "WARNING: --use-fpool combined with multithreading is untested and can probably cause undefined behavior"
if ANYWIN:
m = "windows cannot do multithreading without --no-fpool, so enabling that -- note that upload performance will suffer if you have microsoft defender \"real-time protection\" enabled, so you probably want to use -j 1 instead"
m = 'windows cannot do multithreading without --no-fpool, so enabling that -- note that upload performance will suffer if you have microsoft defender "real-time protection" enabled, so you probably want to use -j 1 instead'
args.no_fpool = True
self.log("root", m, c=3)
# initiate all services to manage
@@ -98,14 +99,23 @@ class SvcHub(object):
def thr_httpsrv_up(self):
time.sleep(5)
failed = self.broker.num_workers - self.httpsrv_up
expected = self.broker.num_workers * self.tcpsrv.nsrv
failed = expected - self.httpsrv_up
if not failed:
return
m = "{}/{} workers failed to start"
m = m.format(failed, self.broker.num_workers)
m = m.format(failed, expected)
self.log("root", m, 1)
os._exit(1)
if self.args.ign_ebind_all:
return
if self.args.ign_ebind and self.tcpsrv.srv:
return
self.retcode = 1
os.kill(os.getpid(), signal.SIGTERM)
def cb_httpsrv_up(self):
self.httpsrv_up += 1
@@ -242,7 +252,7 @@ class SvcHub(object):
print("waiting for thumbsrv (10sec)...")
print("nailed it", end="")
ret = 0
ret = self.retcode
finally:
print("\033[0m")
if self.logf:

View File

@@ -42,9 +42,21 @@ class TcpSrv(object):
self.log("tcpsrv", m)
self.srv = []
self.nsrv = 0
for ip in self.args.i:
for port in self.args.p:
self.srv.append(self._listen(ip, port))
self.nsrv += 1
try:
self._listen(ip, port)
except Exception as ex:
if self.args.ign_ebind or self.args.ign_ebind_all:
m = "could not listen on {}:{}: {}"
self.log("tcpsrv", m.format(ip, port, ex), c=1)
else:
raise
if not self.srv and not self.args.ign_ebind_all:
raise Exception("could not listen on any of the given interfaces")
def _listen(self, ip, port):
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -52,7 +64,7 @@ class TcpSrv(object):
srv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
try:
srv.bind((ip, port))
return srv
self.srv.append(srv)
except (OSError, socket.error) as ex:
if ex.errno in [98, 48]:
e = "\033[1;31mport {} is busy on interface {}\033[0m".format(port, ip)

View File

@@ -530,7 +530,7 @@ class Up2k(object):
else:
# self.log("file: {}".format(abspath))
seen_files[iname] = 1
rp = abspath[len(top) + 1 :]
rp = abspath[len(top) :].lstrip("/")
if WINDOWS:
rp = rp.replace("\\", "/").strip("/")
@@ -1318,7 +1318,7 @@ class Up2k(object):
# TODO broker which avoid this race and
# provides a new filename if taken (same as bup)
suffix = ".{:.6f}-{}".format(ts, ip)
suffix = "-{:.6f}-{}".format(ts, ip.replace(":", "."))
with ren_open(fname, "wb", fdir=fdir, suffix=suffix) as f:
return f["orz"][1]
@@ -1518,6 +1518,7 @@ class Up2k(object):
try:
permsets = [[True, False, False, True]]
vn, rem = self.asrv.vfs.get(vpath, uname, *permsets[0])
vn, rem = vn.get_dbv(rem)
unpost = False
except:
# unpost with missing permissions? try read+write and verify with db
@@ -1527,6 +1528,7 @@ class Up2k(object):
unpost = True
permsets = [[True, True]]
vn, rem = self.asrv.vfs.get(vpath, uname, *permsets[0])
vn, rem = vn.get_dbv(rem)
_, _, _, _, dip, dat = self._find_from_vpath(vn.realpath, rem)
m = "you cannot delete this: "
@@ -1875,7 +1877,8 @@ class Up2k(object):
del self.registry[job["ptop"]][job["wark"]]
return
suffix = ".{:.6f}-{}".format(job["t0"], job["addr"])
dip = job["addr"].replace(":", ".")
suffix = "-{:.6f}-{}".format(job["t0"], dip)
with ren_open(tnam, "wb", fdir=pdir, suffix=suffix) as f:
f, job["tnam"] = f["orz"]
if (

View File

@@ -478,11 +478,12 @@ def min_ex():
@contextlib.contextmanager
def ren_open(fname, *args, **kwargs):
fun = kwargs.pop("fun", open)
fdir = kwargs.pop("fdir", None)
suffix = kwargs.pop("suffix", None)
if fname == os.devnull:
with open(fname, *args, **kwargs) as f:
with fun(fname, *args, **kwargs) as f:
yield {"orz": [f, fname]}
return
@@ -516,7 +517,7 @@ def ren_open(fname, *args, **kwargs):
fname += suffix
ext += suffix
with open(fsenc(fpath), *args, **kwargs) as f:
with fun(fsenc(fpath), *args, **kwargs) as f:
if b64:
fp2 = "fn-trunc.{}.txt".format(b64)
fp2 = os.path.join(fdir, fp2)
@@ -1190,6 +1191,9 @@ def sendfile_kern(lower, upper, f, s):
def statdir(logger, scandir, lstat, top):
if lstat and ANYWIN:
lstat = False
if lstat and not os.supports_follow_symlinks:
scandir = False

View File

@@ -16,7 +16,6 @@ html,body,tr,th,td,#files,a {
}
html {
color: #ccc;
background: #333;
font-family: sans-serif;
text-shadow: 1px 1px 0px #000;
}
@@ -36,11 +35,9 @@ pre, code, tt {
text-shadow: 1px 1px 0 #000;
font-variant: small-caps;
font-weight: normal;
background: #4c4c4c;
display: inline-block;
padding: .35em .5em .2em .5em;
border-radius: 0 .3em .3em 0;
box-shadow: .1em .1em .4em #222;
margin: 1.3em 0 0 0;
font-size: 1.4em;
}
@@ -71,7 +68,7 @@ a, #files tbody div a:last-child {
}
#files a:hover {
color: #fff;
background: #161616;
background: #111;
text-decoration: underline;
}
#files thead {
@@ -82,38 +79,23 @@ a, #files tbody div a:last-child {
color: #999;
font-weight: normal;
}
#files tr:hover td {
#files tbody tr:hover td {
background: #1c1c1c;
}
#files thead th {
padding: .5em .3em .3em .3em;
border-right: 2px solid #3c3c3c;
border-bottom: 2px solid #444;
background: #333;
padding: 0 .3em .3em .3em;
border-bottom: 1px solid #444;
cursor: pointer;
}
#files thead th+th {
border-left: 2px solid #2a2a2a;
}
#files thead th:last-child {
border-right: none;
}
#files tbody {
background: #222;
}
#files td {
margin: 0;
padding: 0 .5em;
border-bottom: 1px solid #111;
border-left: 1px solid #2c2c2c;
padding: .1em .5em;
border-left: 1px solid #3c3c3c;
}
#files td+td+td {
max-width: 30em;
overflow: hidden;
}
#files tr+tr td {
border-top: 1px solid #383838;
}
#files tbody td:nth-child(3) {
font-family: 'scp', monospace, monospace;
text-align: right;
@@ -121,18 +103,15 @@ a, #files tbody div a:last-child {
white-space: nowrap;
}
#files tbody td:first-child {
padding-left: 1.5em;
color: #888;
}
#files tbody tr:first-child td {
padding-top: .9em;
text-align: center;
}
#files tbody tr:last-child td {
padding-bottom: 1.3em;
border-bottom: .5em solid #444;
border-bottom: 1px solid #444;
}
#files tbody tr td:last-child {
white-space: nowrap;
border-right: 1px solid #3c3c3c;
}
#files thead th[style] {
width: auto !important;
@@ -163,7 +142,7 @@ a, #files tbody div a:last-child {
background: linear-gradient(90deg, rgba(0,0,0,0), rgba(0,0,0,0.2), rgba(0,0,0,0));
}
.logue {
padding: .2em 1.5em;
padding: .2em 0;
}
.logue.hidden,
.logue:empty {
@@ -175,6 +154,21 @@ a, #files tbody div a:last-child {
#epi.logue {
margin: .8em 0;
}
#epi.logue.mdo:before {
content: 'README.md';
text-align: center;
display: block;
margin-top: -1.5em;
}
#epi.logue.mdo {
border-top: 1px solid #555;
margin-top: 2.5em;
}
.mdo>h1:first-child,
.mdo>h2:first-child,
.mdo>h3:first-child {
margin-top: 1.5rem;
}
.mdo {
max-width: 52em;
}
@@ -184,7 +178,6 @@ a, #files tbody div a:last-child {
}
#srv_info {
color: #a73;
background: #333;
position: absolute;
font-size: .8em;
top: .5em;
@@ -286,43 +279,6 @@ html.light #ggrid>a.sel {
#files tr:focus td:first-child {
box-shadow: -.2em .2em 0 #fc0, -.2em -.2em 0 #fc0;
}
#files tr:focus+tr td {
border-top: 1px solid transparent;
}
#blocked {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #333;
font-size: 2.5em;
z-index: 99;
}
#blk_play,
#blk_abrt {
position: fixed;
display: table;
width: 80%;
}
#blk_play {
height: 60%;
left: 10%;
top: 5%;
}
#blk_abrt {
height: 25%;
left: 10%;
bottom: 5%;
}
#blk_play a,
#blk_abrt a {
display: table-cell;
vertical-align: middle;
text-align: center;
background: #444;
border-radius: 2em;
}
#widget {
position: fixed;
font-size: 1.4em;
@@ -344,7 +300,6 @@ html.light #ggrid>a.sel {
z-index: 10;
width: 100%;
height: 100%;
background: #3c3c3c;
}
#wtgrid,
#wtico {
@@ -385,7 +340,6 @@ html.light #ggrid>a.sel {
line-height: 1em;
text-align: center;
text-shadow: none;
background: #3c3c3c;
box-shadow: 0 0 .5em #222;
border-radius: .3em 0 0 0;
padding: 0 0 0 .1em;
@@ -397,7 +351,7 @@ html.light #ggrid>a.sel {
#wzip, #wnp {
margin-right: .2em;
padding-right: .2em;
border: 1px solid #555;
border: 1px solid #444;
border-width: 0 .1em 0 0;
}
#wfm.act+#wzip,
@@ -553,36 +507,28 @@ html.light #wfm a:not(.en) {
box-shadow: 0 -.15em .2em #000 inset;
padding-bottom: .3em;
}
#ops,
.opbox,
#u2etas {
border: 1px solid #3a3a3a;
box-shadow: 0 0 1em #222 inset;
}
#ops {
background: #333;
margin: 1.7em 1.5em 0 1.5em;
padding: .3em .6em;
border-radius: .3em;
border-width: .15em 0;
border-width: 1px 0;
white-space: nowrap;
}
.opbox {
background: #2d2d2d;
margin: 1.5em 0 0 0;
padding: .5em;
border-radius: 0 1em 1em 0;
border-width: .15em .3em .3em 0;
border-radius: 0 .3em .3em 0;
border-width: 1px 1px 1px 0;
max-width: 41em;
max-width: min(41em, calc(100% - 2.6em));
}
.opbox input {
margin: .5em;
}
.opview input[type=text] {
background: #383838;
color: #fff;
border: none;
box-shadow: 0 0 .3em #222;
box-shadow: 0 0 .3em #181818;
border-bottom: 1px solid #fc5;
border-radius: .2em;
padding: .2em .3em;
@@ -599,14 +545,12 @@ html.light .opview input[type="text"].err {
input[type="checkbox"]+label {
color: #f5a;
}
input[type="radio"]:checked+label,
input[type="checkbox"]:checked+label {
color: #fc5;
}
input[type="radio"]:checked+label {
color: #fc0;
}
html.light input[type="radio"]:checked+label {
color: #07c;
.opview input.i {
width: calc(100% - 16.2em);
}
input.eq_gain {
width: 3em;
@@ -636,11 +580,6 @@ input.eq_gain {
#srch_form {
border: 1px solid #3a3a3a;
box-shadow: 0 0 1em #222 inset;
background: #2d2d2d;
border-radius: .4em;
margin: 1.4em;
margin-bottom: 0;
padding: 0 .5em .5em 0;
}
@@ -697,8 +636,8 @@ input.eq_gain {
width: 100%;
}
#wrap {
margin-top: 2em;
min-height: 90vh;
margin: 1.8em 1.5em 0 1.5em;
min-height: 70vh;
padding-bottom: 5em;
}
#tree {
@@ -712,19 +651,23 @@ input.eq_gain {
-ms-scroll-chaining: none;
overscroll-behavior-y: none;
scrollbar-color: #eb0 #333;
border: 1px solid #333;
box-shadow: 0 0 1em #181818;
}
#treeh {
background: #333;
position: sticky;
z-index: 1;
top: 0;
height: 2.2em;
line-height: 2.2em;
border-bottom: 1px solid #555;
border-bottom: 1px solid #111;
overflow: hidden;
}
#thx_ff {
padding: 5em 0;
#tree, #treeh {
border-radius: 0 .3em 0 0;
}
.np_open #thx_ff {
padding: 4.5em 0;
/* widget */
}
#tree::-webkit-scrollbar-track,
@@ -745,8 +688,6 @@ input.eq_gain {
.btn {
padding: .2em .4em;
font-size: 1.2em;
background: #2a2a2a;
box-shadow: 0 .1em .2em #222 inset;
border-radius: .3em;
margin: .2em;
white-space: pre;
@@ -775,13 +716,13 @@ input.eq_gain {
margin: 0;
}
#tree ul {
border-left: .2em solid #555;
border-left: .2em solid #444;
}
#tree li {
margin-left: 1em;
list-style: none;
border-top: 1px solid #4c4c4c;
border-bottom: 1px solid #222;
border-top: 1px solid #444;
border-bottom: 1px solid #111;
}
#tree li:last-child {
border-bottom: none;
@@ -804,7 +745,7 @@ input.eq_gain {
white-space: nowrap;
}
#tree.nowrap #treeul a+a:hover {
background: rgba(34, 34, 34, 0.67);
background: rgba(16, 16, 16, 0.67);
min-width: calc(var(--nav-sz) - 2em);
width: auto;
}
@@ -813,7 +754,7 @@ html.light #tree.nowrap #treeul a+a:hover {
color: #000;
}
#treeul a+a:hover {
background: #222;
background: #181818;
color: #fff;
}
#treeul a:first-child {
@@ -852,22 +793,23 @@ html.light #tree.nowrap #treeul a+a:hover {
#files td:nth-child(2n) {
color: #f5a;
}
#files tr.play td,
#files tr.play div a {
#files tbody tr.play td,
#files tbody tr.play div a {
background: #fc4;
border-color: transparent;
color: #400;
text-shadow: none;
}
#files tr.play a {
#files tbody tr.play a {
color: inherit;
}
#files tr.play a:hover {
#files tbody tr.play a:hover {
color: #300;
background: #fea;
}
.opwide,
#op_unpost {
#op_unpost,
#srch_form {
max-width: none;
margin-right: 1.5em;
}
@@ -911,12 +853,10 @@ html.light #tree.nowrap #treeul a+a:hover {
display: none;
}
#ghead {
background: #3c3c3c;
border: 1px solid #444;
border-radius: .3em;
padding: .2em .5em;
line-height: 2.3em;
margin: 0 1.5em 1em .4em;
margin-bottom: 1em;
position: sticky;
top: -.3em;
z-index: 1;
@@ -935,6 +875,7 @@ html.light #ghead {
}
#ggrid {
padding-top: .5em;
margin: 0 -.5em;
}
#ggrid>a>span {
overflow: hidden;
@@ -950,17 +891,10 @@ html.light #ghead {
width: var(--grid-sz);
vertical-align: top;
overflow-wrap: break-word;
background: #383838;
border: 1px solid #444;
border-top: 1px solid #555;
box-shadow: 0 .1em .2em #222;
border-radius: .3em;
padding: .3em;
margin: .5em;
}
#ggrid>a[tt] {
background: linear-gradient(135deg, #383838 95%, #555 95%);
}
#ggrid>a img {
border-radius: .2em;
max-width: 10em;
@@ -983,25 +917,6 @@ html.light #ghead {
border-radius: .3em;
font-size: 2em;
}
#ggrid>a:hover {
background: #444;
border-color: #555;
color: #fd9;
}
html.light #ggrid>a {
background: #f7f7f7;
border-color: #ddd;
box-shadow: 0 .1em .2em #ddd;
}
html.light #ggrid>a[tt] {
background: linear-gradient(135deg, #f7f7f7 95%, #ccc 95%);
}
html.light #ggrid>a:hover {
background: #fff;
border-color: #ccc;
color: #015;
box-shadow: 0 .1em .5em #aaa;
}
#op_unpost {
padding: 1em;
}
@@ -1022,7 +937,6 @@ html.light #ggrid>a:hover {
max-height: calc(100% - 2em);
border-bottom: .5em solid #999;
box-shadow: 0 0 5em rgba(0,0,0,0.8);
background: #333;
padding: 1em;
z-index: 765;
}
@@ -1106,6 +1020,77 @@ a.btn,
html,
#rui,
#files td,
#files thead th,
#bbox-halp,
#u2notbtn,
#srv_info {
background: #222;
}
#ops,
.opbox,
#path,
#srch_form,
#ghead {
background: #2b2b2b;
border: 1px solid #333;
box-shadow: 0 0 .3em #111;
}
#files tr:nth-child(2n+1) td {
background: #282828;
}
#tree,
#treeh {
background: #2b2b2b;
}
#wtoggle,
#widgeti {
background: #333;
}
.btn,
.opview input[type=text] {
background: #383838;
}
#ggrid>a {
background: #2c2c2c;
border: 1px solid #383838;
border-top: 1px solid #444;
box-shadow: 0 .1em .2em #181818;
}
#ggrid>a[tt] {
background: linear-gradient(135deg, #2c2c2c 95%, #444 95%);
}
#ggrid>a:hover {
background: #383838;
border-color: #555;
color: #fd9;
}
html.light #ggrid>a {
background: #f7f7f7;
border-color: #ddd;
box-shadow: 0 .1em .2em #ddd;
}
html.light #ggrid>a[tt] {
background: linear-gradient(135deg, #f7f7f7 95%, #ccc 95%);
}
html.light #ggrid>a:hover {
background: #fff;
border-color: #ccc;
color: #015;
box-shadow: 0 .1em .5em #aaa;
}
@@ -1113,15 +1098,17 @@ a.btn,
html.light {
color: #333;
background: #eee;
background: #eaeaea;
text-shadow: none;
}
html.light #ops,
html.light .opbox,
html.light #path,
html.light #srch_form,
html.light #ghead,
html.light #u2etas {
background: #f7f7f7;
box-shadow: 0 0 .3em #ddd;
box-shadow: 0 0 .3em #ccc;
border-color: #f7f7f7;
}
html.light #ops a.act {
@@ -1184,25 +1171,19 @@ html.light #ops a,
html.light #files tbody div a:last-child {
color: #06a;
}
html.light #files tbody {
html.light #files thead th {
background: #eaeaea;
border-color: #ccc;
}
html.light #files tbody td {
background: #eee;
border-color: #ccc;
}
html.light #files tr:nth-child(2n+1) td {
background: #f7f7f7;
}
html.light #files {
box-shadow: 0 0 .3em #ccc;
}
html.light #files thead th {
background: #eee;
border: 1px solid #ccc;
border-top: none;
}
html.light #files thead th+th {
border-left: 1px solid #f7f7f7;
}
html.light #files td {
border-color: #fff #fff #ddd #ddd;
}
html.light #files tbody tr:last-child td {
border-bottom: .2em solid #ccc;
border-bottom: 1px solid #ccc;
}
html.light #files tr:focus td {
background: #fff;
@@ -1240,14 +1221,6 @@ html.light tr.play a {
html.light #files th:hover .cfg {
background: #ccc;
}
html.light #blocked {
background: #eee;
}
html.light #blk_play a,
html.light #blk_abrt a {
background: #fff;
box-shadow: 0 .2em .4em #ddd;
}
html.light #widget a {
color: #06a;
}
@@ -1284,6 +1257,10 @@ html.light #files tr.sel a.play.act {
html.light input[type="checkbox"] + label {
color: #333;
}
html.light input[type="radio"]:checked + label,
html.light input[type="checkbox"]:checked + label {
color: #07c;
}
html.light .opwide>div {
border-color: #ccc;
}
@@ -1319,20 +1296,24 @@ html.light #files a:hover,
html.light #files tr.sel a:hover {
color: #000;
background: #fff;
text-decoration: underline;
}
html.light #treeh {
background: #eee;
background: #f7f7f7;
border-color: #ddd;
}
html.light #tree {
scrollbar-color: #a70 #ddd;
border-color: #ddd;
box-shadow: 0 0 1em #ddd;
background: #f7f7f7;
scrollbar-color: #490 #ddd;
}
html.light #tree::-webkit-scrollbar-track,
html.light #tree::-webkit-scrollbar {
background: #ddd;
}
#tree::-webkit-scrollbar-thumb {
background: #da0;
html.light #tree::-webkit-scrollbar-thumb {
background: #490;
}
@@ -1411,7 +1392,7 @@ html.light #tree::-webkit-scrollbar {
box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
}
.full-image video {
background: #333;
background: #222;
}
.full-image figcaption {
display: block;
@@ -1507,7 +1488,6 @@ html.light #bbox-overlay figcaption a {
}
#bbox-halp {
color: #fff;
background: #333;
position: absolute;
top: 0;
left: 0;
@@ -1715,6 +1695,10 @@ html.light #u2err.err {
cursor: pointer;
box-shadow: .4em .4em 0 #111;
}
#u2conf.ww #u2btn {
font-size: 1.3em;
margin-right: .5em;
}
#op_up2k.srch #u2btn {
background: linear-gradient(to bottom, #ca3 0%, #fd8 50%, #fc6 51%, #b92 100%);
text-shadow: 1px 1px 1px #fc6;
@@ -1733,7 +1717,6 @@ html.light #u2err.err {
#u2notbtn {
display: none;
text-align: center;
background: #333;
padding-top: 1em;
}
#u2notbtn * {
@@ -1766,10 +1749,12 @@ html.light #u2err.err {
width: auto;
}
#u2tab tbody tr:hover td {
background: #222;
background: #333;
}
#u2etas {
background: #333;
background: #1c1c1c;
border: 1px solid #282828;
border-width: .1em 0;
padding: .2em .5em;
border-radius: .5em;
border-width: .25em 0;
@@ -1808,16 +1793,22 @@ html.light #u2err.err {
width: 44em;
text-align: left;
}
#u2cards.ww {
display: inline-block;
}
#u2etaw.w {
width: 52em;
text-align: right;
margin: 3em auto -2.7em auto;
}
#u2etaw.ww {
margin: 0 2em 1em 2em;
}
#u2cards a {
padding: .2em 1em;
border: 1px solid #777;
border-width: 0 0 1px 0;
background: linear-gradient(to bottom, #333, #222);
background: linear-gradient(to bottom, #222, #2b2b2b);
}
#u2cards a:first-child {
border-radius: .4em 0 0 0;
@@ -1830,9 +1821,9 @@ html.light #u2err.err {
border-width: 1px 1px .1em 1px;
border-radius: .3em .3em 0 0;
margin-left: -1px;
background: linear-gradient(to bottom, #464, #333 80%);
background: linear-gradient(to bottom, #353, #222 80%);
box-shadow: 0 -.17em .67em #280;
border-color: #7c5 #583 #333 #583;
border-color: #7c5 #583 #222 #583;
position: relative;
color: #fd7;
}
@@ -1843,10 +1834,17 @@ html.light #u2err.err {
margin: 1em auto;
width: 30em;
}
#u2conf.has_btn {
#u2conf.w {
width: 48em;
}
#u2conf * {
#u2conf.ww {
width: 74em;
}
#u2conf.ww #u2c3w {
width: 29em;
}
#u2conf .c,
#u2conf .c * {
text-align: center;
line-height: 1em;
margin: 0;
@@ -1866,7 +1864,7 @@ html.light #u2err.err {
#u2conf .txtbox.err {
background: #922;
}
#u2conf a {
#u2conf a.b {
color: #fff;
background: #c38;
text-decoration: none;
@@ -1880,10 +1878,10 @@ html.light #u2err.err {
position: relative;
bottom: -0.08em;
}
#u2conf input+a {
#u2conf input+a.b {
background: #d80;
}
#u2conf label {
#u2conf .c label {
font-size: 1.6em;
width: 2em;
height: 1em;

View File

@@ -18,9 +18,9 @@
<div id="op_search" class="opview">
{%- if have_tags_idx %}
<div id="srch_form" class="tags"></div>
<div id="srch_form" class="tags opbox"></div>
{%- else %}
<div id="srch_form"></div>
<div id="srch_form" class="opbox"></div>
{%- endif %}
<div id="srch_q"></div>
</div>
@@ -31,7 +31,7 @@
<div id="u2err"></div>
<form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}">
<input type="hidden" name="act" value="bput" />
<input type="file" name="f" multiple><br />
<input type="file" name="f" multiple /><br />
<input type="submit" value="start upload">
</form>
</div>
@@ -39,7 +39,7 @@
<div id="op_mkdir" class="opview opbox act">
<form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}">
<input type="hidden" name="act" value="mkdir" />
📂<input type="text" name="name" size="30">
📂<input type="text" name="name" class="i">
<input type="submit" value="make directory">
</form>
</div>
@@ -47,15 +47,15 @@
<div id="op_new_md" class="opview opbox">
<form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}">
<input type="hidden" name="act" value="new_md" />
📝<input type="text" name="name" size="30">
📝<input type="text" name="name" class="i">
<input type="submit" value="new markdown doc">
</form>
</div>
<div id="op_msg" class="opview opbox act">
<form method="post" enctype="application/x-www-form-urlencoded" accept-charset="utf-8" action="{{ url_suf }}">
📟<input type="text" name="msg" size="30">
<input type="submit" value="send msg to server log">
📟<input type="text" name="msg" class="i">
<input type="submit" value="send msg to srv log">
</form>
</div>
@@ -141,6 +141,9 @@
<script src="/.cpr/util.js?_={{ ts }}"></script>
<script src="/.cpr/browser.js?_={{ ts }}"></script>
<script src="/.cpr/up2k.js?_={{ ts }}"></script>
{%- if js %}
<script src="{{ js }}?_={{ ts }}"></script>
{%- endif %}
</body>
</html>

View File

@@ -61,28 +61,29 @@ ebi('op_up2k').innerHTML = (
'<table id="u2conf">\n' +
' <tr>\n' +
' <td><br />parallel uploads:</td>\n' +
' <td rowspan="2">\n' +
' <td class="c"><br />parallel uploads:</td>\n' +
' <td class="c" rowspan="2">\n' +
' <input type="checkbox" id="multitask" />\n' +
' <label for="multitask" tt="continue hashing other files while uploading">🏃</label>\n' +
' </td>\n' +
' <td rowspan="2">\n' +
' <td class="c" rowspan="2">\n' +
' <input type="checkbox" id="ask_up" />\n' +
' <label for="ask_up" tt="ask for confirmation before upload starts">💭</label>\n' +
' </td>\n' +
(have_up2k_idx ? (
' <td data-perm="read" rowspan="2">\n' +
' <td class="c" data-perm="read" rowspan="2">\n' +
' <input type="checkbox" id="fsearch" />\n' +
' <label for="fsearch" tt="don\'t actually upload, instead check if the files already $N exist on the server (will scan all folders you can read)">🔎</label>\n' +
' </td>\n'
) : '') +
' <td data-perm="read" rowspan="2" id="u2btn_cw"></td>\n' +
' <td data-perm="read" rowspan="2" id="u2c3w"></td>\n' +
' </tr>\n' +
' <tr>\n' +
' <td>\n' +
' <a href="#" id="nthread_sub">&ndash;</a><input\n' +
' <td class="c">\n' +
' <a href="#" class="b" id="nthread_sub">&ndash;</a><input\n' +
' class="txtbox" id="nthread" value="2" tt="pause uploads by setting it to 0"/><a\n' +
' href="#" id="nthread_add">+</a><br />&nbsp;\n' +
' href="#" class="b" id="nthread_add">+</a><br />&nbsp;\n' +
' </td>\n' +
' </tr>\n' +
'</table>\n' +
@@ -98,6 +99,8 @@ ebi('op_up2k').innerHTML = (
' </div>\n' +
'</div>\n' +
'<div id="u2c3t">\n' +
'<div id="u2etaw"><div id="u2etas"><div class="o">\n' +
' hash: <span id="u2etah" tt="average &lt;em&gt;hashing&lt;/em&gt; speed, and estimated time until finish">(no uploads are queued yet)</span><br />\n' +
' send: <span id="u2etau" tt="average &lt;em&gt;upload&lt;/em&gt; speed and estimated time until finish">(no uploads are queued yet)</span><br />\n' +
@@ -112,6 +115,8 @@ ebi('op_up2k').innerHTML = (
' href="#" act="q" tt="idle, pending">que <span>0</span></a>\n' +
'</div>\n' +
'</div>\n' +
'<table id="u2tab">\n' +
' <thead>\n' +
' <tr>\n' +
@@ -263,19 +268,42 @@ function goto(dest) {
}
var have_webp = null;
var have_webp = sread('have_webp');
(function () {
if (have_webp !== null)
return;
var img = new Image();
img.onload = function () {
have_webp = img.width > 0 && img.height > 0;
swrite('have_webp', 'ya');
};
img.onerror = function () {
have_webp = false;
swrite('have_webp', '');
};
img.src = "data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==";
})();
function set_files_html(html) {
var files = ebi('files');
try {
files.innerHTML = html;
return files;
}
catch (e) {
var par = files.parentNode;
par.removeChild(files);
files = mknod('div');
files.innerHTML = '<table id="files">' + html + '</table>';
par.insertBefore(files.childNodes[0], ebi('epi'));
files = ebi('files');
return files;
}
}
var mpl = (function () {
var have_mctl = 'mediaSession' in navigator && window.MediaMetadata;
@@ -593,6 +621,7 @@ var widget = (function () {
if (r.is_open)
return false;
clmod(document.documentElement, 'np_open', 1);
widget.className = 'open';
r.is_open = true;
return true;
@@ -601,6 +630,7 @@ var widget = (function () {
if (!r.is_open)
return false;
clmod(document.documentElement, 'np_open');
widget.className = '';
r.is_open = false;
return true;
@@ -1041,8 +1071,8 @@ var need_ogv = true;
try {
need_ogv = new Audio().canPlayType('audio/ogg; codecs=opus') !== 'probably';
if (/ Edge\//.exec(navigator.userAgent + ''))
need_ogv = true;
if (document.documentMode)
need_ogv = false; // ie8-11
}
catch (ex) { }
@@ -1439,12 +1469,7 @@ function play(tid, is_ev, seek, call_depth) {
if (!seek) {
var o = ebi(oid);
o.setAttribute('id', 'thx_js');
if (window.history && history.replaceState) {
hist_replace(document.location.pathname + '#' + oid);
}
else {
document.location.hash = oid;
}
sethash(oid);
o.setAttribute('id', oid);
}
@@ -1492,44 +1517,14 @@ function evau_error(e) {
}
// show a fullscreen message
function show_modal(html) {
var body = document.body || document.getElementsByTagName('body')[0],
div = mknod('div');
div.setAttribute('id', 'blocked');
div.innerHTML = html;
unblocked();
body.appendChild(div);
}
// hide fullscreen message
function unblocked(e) {
ev(e);
var dom = ebi('blocked');
if (dom)
dom.parentNode.removeChild(dom);
}
// show ui to manually start playback of a linked song
function autoplay_blocked(seek) {
show_modal(
'<div id="blk_play"><a href="#" id="blk_go"></a></div>' +
'<div id="blk_abrt"><a href="#" id="blk_na">Cancel<br />(show file list)</a></div>');
var go = ebi('blk_go'),
na = ebi('blk_na'),
tid = mp.au.tid,
var tid = mp.au.tid,
fn = mp.tracks[tid].split(/\//).pop();
fn = uricom_dec(fn.replace(/\+/g, ' '))[0];
go.textContent = 'Play "' + fn + '"';
go.onclick = function (e) {
unblocked(e);
toast.hide();
modal.confirm('<h6>play this audio file?</h6>\n«' + esc(fn) + '»', function () {
if (mp.au !== mp.au_ogvjs)
// chrome 91 may permanently taint on a failed play()
// depending on win10 settings or something? idk
@@ -1542,14 +1537,16 @@ function autoplay_blocked(seek) {
play(tid, true, seek);
mp.fade_in();
};
na.onclick = unblocked;
}, null);
}
function play_linked() {
function eval_hash() {
var v = location.hash;
if (v && v.indexOf('#af-') === 0) {
if (!v)
return;
if (v.indexOf('#af-') === 0) {
var id = v.slice(2).split('&');
if (id[0].length != 10)
return;
@@ -1563,6 +1560,13 @@ function play_linked() {
return play(id[0], false, parseInt(m[1] || 0) * 60 + parseInt(m[2] || 0));
}
if (v.indexOf('#q=') === 0) {
goto('search');
var i = ebi('q_raw');
i.value = uricom_dec(v.slice(3))[0];
return i.oninput();
}
};
@@ -2313,15 +2317,6 @@ var thegrid = (function () {
for (var a = 0; a < links.length; a++)
links[a].onclick = btnclick;
bcfg_bind(r, 'thumbs', 'thumbs', true, r.setdirty);
bcfg_bind(r, 'sel', 'gridsel', false, r.loadsel);
bcfg_bind(r, 'en', 'griden', false, function (v) {
v ? loadgrid() : ungrid();
pbar.onresize();
vbar.onresize();
});
ebi('wtgrid').onclick = ebi('griden').onclick;
r.setvis = function (vis) {
(r.en ? gfiles : lfiles).style.display = vis ? '' : 'none';
};
@@ -2374,21 +2369,12 @@ var thegrid = (function () {
td = oth.closest('td').nextSibling,
tr = td.parentNode;
if (href.endsWith('/')) {
var ta = QSA('#treeul a.hl+ul>li>a+a'),
txt = oth.textContent.slice(0, -1);
for (var a = 0, aa = ta.length; a < aa; a++) {
if (ta[a].textContent == txt) {
in_tree = ta[a];
break;
}
}
}
if (href.endsWith('/'))
in_tree = treectl.find(oth.textContent.slice(0, -1));
if (r.sel) {
td.click();
this.setAttribute('class', tr.getAttribute('class'));
clmod(this, 'sel', clgot(tr, 'sel'));
}
else if (widget.is_open && aplay)
aplay.click();
@@ -2511,9 +2497,11 @@ var thegrid = (function () {
}
ihref = '/.cpr/ico/' + ihref.slice(0, -1);
}
ihref += (ihref.indexOf('?') > 0 ? '&' : '?') + 'cache=i';
html.push('<a href="' + ohref + '" ref="' + ref +
'"' + ac + ' ttt="' + esc(name) + '"><img src="' +
'"' + ac + ' ttt="' + esc(name) + '"><img style="height:' +
(r.sz / 1.25) + 'em" onload="th_onload(this)" src="' +
ihref + '" /><span' + ac + '>' + ao.innerHTML + '</span></a>');
}
ebi('ggrid').innerHTML = html.join('\n');
@@ -2551,6 +2539,15 @@ var thegrid = (function () {
})[0];
};
bcfg_bind(r, 'thumbs', 'thumbs', true, r.setdirty);
bcfg_bind(r, 'sel', 'gridsel', false, r.loadsel);
bcfg_bind(r, 'en', 'griden', false, function (v) {
v ? loadgrid() : ungrid();
pbar.onresize();
vbar.onresize();
});
ebi('wtgrid').onclick = ebi('griden').onclick;
setTimeout(function () {
import_js('/.cpr/baguettebox.js', r.bagit);
}, 1);
@@ -2563,6 +2560,11 @@ var thegrid = (function () {
})();
function th_onload(el) {
el.style.height = '';
}
function tree_scrollto(e) {
ev(e);
var act = QS('#treeul a.hl'),
@@ -2756,28 +2758,28 @@ document.onkeydown = function (e) {
(function () {
var sconf = [
["size",
["szl", "sz_min", "minimum MiB", ""],
["szu", "sz_max", "maximum MiB", ""]
["szl", "sz_min", "minimum MiB", "16"],
["szu", "sz_max", "maximum MiB", "16"]
],
["date",
["dtl", "dt_min", "min. iso8601", ""],
["dtu", "dt_max", "max. iso8601", ""]
["dtl", "dt_min", "min. iso8601", "16"],
["dtu", "dt_max", "max. iso8601", "16"]
],
["path",
["path", "path", "path contains &nbsp; (space-separated)", "46"]
["path", "path", "path contains &nbsp; (space-separated)", "34"]
],
["name",
["name", "name", "name contains &nbsp; (negate with -nope)", "46"]
["name", "name", "name contains &nbsp; (negate with -nope)", "34"]
]
];
var oldcfg = [];
if (QS('#srch_form.tags')) {
sconf.push(["tags",
["tags", "tags", "tags contains &nbsp; (^=start, end=$)", "46"]
["tags", "tags", "tags contains &nbsp; (^=start, end=$)", "34"]
]);
sconf.push(["adv.",
["adv", "adv", "key>=1A&nbsp; key<=2B&nbsp; .bpm>165", "46"]
["adv", "adv", "key>=1A&nbsp; key<=2B&nbsp; .bpm>165", "34"]
]);
}
@@ -2794,8 +2796,8 @@ document.onkeydown = function (e) {
html.push(
'<td colspan="' + csp + '"><input id="' + hn + 'c" type="checkbox">\n' +
'<label for="' + hn + 'c">' + sconf[a][b][2] + '</label>\n' +
'<br /><input id="' + hn + 'v" type="text" size="' + sconf[a][b][3] +
'" name="' + sconf[a][b][1] + '" /></td>');
'<br /><input id="' + hn + 'v" type="text" style="width:' + sconf[a][b][3] +
'em" name="' + sconf[a][b][1] + '" /></td>');
if (csp == 2)
break;
}
@@ -2962,7 +2964,7 @@ document.onkeydown = function (e) {
var html = mk_files_header(tagord);
html.push('<tbody>');
html.push('<tr><td>-</td><td colspan="42"><a href="#" id="unsearch">! close search results</a></td></tr>');
html.push('<tr><td>-</td><td colspan="42"><a href="#" id="unsearch"><big style="font-weight:bold">[❌] close search results</big></a></td></tr>');
for (var a = 0; a < res.hits.length; a++) {
var r = res.hits[a],
ts = parseInt(r.ts),
@@ -2999,7 +3001,7 @@ document.onkeydown = function (e) {
orig_url = get_evpath();
}
ofiles.innerHTML = html.join('\n');
ofiles = set_files_html(html.join('\n'));
ofiles.setAttribute("ts", this.ts);
ofiles.setAttribute("q_raw", this.q_raw);
set_vq();
@@ -3007,15 +3009,17 @@ document.onkeydown = function (e) {
reload_browser();
filecols.set_style(['File Name']);
sethash('q=' + uricom_enc(this.q_raw));
ebi('unsearch').onclick = unsearch;
}
function unsearch(e) {
ev(e);
treectl.show();
ebi('files').innerHTML = orig_html;
set_files_html(orig_html);
ebi('files').removeAttribute('q_raw');
orig_html = null;
sethash('');
reload_browser();
}
})();
@@ -3075,14 +3079,14 @@ var treectl = (function () {
swrite('entreed', 'na');
treectl.hide();
ebi('path').style.display = 'inline-block';
ebi('path').style.display = '';
}
treectl.hide = function () {
treectl.hidden = true;
ebi('path').style.display = 'none';
ebi('tree').style.display = 'none';
ebi('wrap').style.marginLeft = '0';
ebi('wrap').style.marginLeft = '';
window.removeEventListener('resize', onresize);
window.removeEventListener('scroll', onscroll);
}
@@ -3133,7 +3137,7 @@ var treectl = (function () {
treeh = winh - atop;
tree.style.top = top + 'px';
tree.style.height = treeh < 10 ? '' : treeh + 'px';
tree.style.height = treeh < 10 ? '' : Math.floor(treeh - 2) + 'px';
}
}
timer.add(onscroll2, true);
@@ -3151,20 +3155,30 @@ var treectl = (function () {
if (!QS(q))
break;
}
var w = (treesz + Math.max(0, nq)) + 'em';
var iw = (treesz + Math.max(0, nq)),
w = iw + 'em',
w2 = (iw + 2) + 'em';
try {
document.documentElement.style.setProperty('--nav-sz', w);
}
catch (ex) { }
ebi('tree').style.width = w;
ebi('wrap').style.marginLeft = w;
ebi('wrap').style.marginLeft = w2;
onscroll();
}
treectl.find = function (txt) {
var ta = QSA('#treeul a.hl+ul>li>a+a');
for (var a = 0, aa = ta.length; a < aa; a++)
if (ta[a].textContent == txt)
return ta[a];
};
treectl.goto = function (url, push) {
get_tree("", url, true);
reqls(url, push, true);
}
};
function get_tree(top, dst, rst) {
var xhr = new XMLHttpRequest();
@@ -3376,13 +3390,7 @@ var treectl = (function () {
}
html.push('</tbody>');
html = html.join('\n');
try {
ebi('files').innerHTML = html;
}
catch (ex) { //ie9
window.location.href = this.top;
return;
}
set_files_html(html);
if (this.hpush)
hist_push(this.top);
@@ -3472,10 +3480,7 @@ var treectl = (function () {
treectl.goto(url.pathname);
};
if (window.history && history.pushState) {
hist_replace(get_evpath() + window.location.hash);
}
hist_replace(get_evpath() + window.location.hash);
treectl.onscroll = onscroll;
return treectl;
})();
@@ -4423,6 +4428,20 @@ function goto_unpost(e) {
}
ebi('files').onclick = function (e) {
var tgt = e.target.closest('a[id]');
if (!tgt || tgt.getAttribute('id').indexOf('f-') !== 0 || !tgt.textContent.endsWith('/'))
return;
var el = treectl.find(tgt.textContent.slice(0, -1));
if (!el)
return;
ev(e);
el.click();
}
function reload_mp() {
if (mp && mp.au) {
mp.au.pause();
@@ -4478,4 +4497,4 @@ function reload_browser(not_mp) {
}
reload_browser(true);
mukey.render();
play_linked();
setTimeout(eval_hash, 1);

View File

@@ -11,9 +11,9 @@ html {
max-width: 34em;
max-width: min(34em, 90%);
max-width: min(34em, calc(100% - 7em));
background: #222;
background: #333;
border: 0 solid #777;
box-shadow: 0 .2em .5em #222;
box-shadow: 0 .2em .5em #111;
border-radius: .4em;
z-index: 9001;
}

View File

@@ -246,7 +246,7 @@ function U2pvis(act, btns) {
obj.innerHTML = fo.hp;
obj.style.color = '#fff';
obj.style.background = 'linear-gradient(90deg, #025, #06a ' + o1 + '%, #09d ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)';
obj.style.background = 'linear-gradient(90deg, #025, #06a ' + o1 + '%, #09d ' + o2 + '%, #222 ' + o3 + '%, #222 99%, #555)';
};
r.prog = function (fobj, nchunk, cbd) {
@@ -303,7 +303,7 @@ function U2pvis(act, btns) {
obj.innerHTML = fo.hp;
obj.style.color = '#fff';
obj.style.background = 'linear-gradient(90deg, #050, #270 ' + o1 + '%, #4b0 ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)';
obj.style.background = 'linear-gradient(90deg, #050, #270 ' + o1 + '%, #4b0 ' + o2 + '%, #222 ' + o3 + '%, #222 99%, #555)';
};
r.move = function (nfile, newcat) {
@@ -1162,6 +1162,11 @@ function up2k_init(subtle) {
}
}
is_busy = st.todo.handshake.length;
try {
if (!is_busy && !uc.fsearch && !msel.getsel().length && (!mp.au || mp.au.paused))
treectl.goto(get_evpath());
}
catch (ex) { }
}
if (was_busy != is_busy) {
@@ -1822,16 +1827,28 @@ function up2k_init(subtle) {
wpx = window.innerWidth,
fpx = parseInt(getComputedStyle(bar)['font-size']),
wem = wpx * 1.0 / fpx,
wide = wem > 54,
parent = ebi(wide && has(perms, 'write') ? 'u2btn_cw' : 'u2btn_ct'),
wide = wem > 54 ? 'w' : '',
write = has(perms, 'write'),
parent = ebi(wide && write ? 'u2btn_cw' : 'u2btn_ct'),
btn = ebi('u2btn');
//console.log([wpx, fpx, wem]);
if (btn.parentNode !== parent) {
parent.appendChild(btn);
ebi('u2conf').setAttribute('class', wide ? 'has_btn' : '');
ebi('u2cards').setAttribute('class', wide ? 'w' : '');
ebi('u2etaw').setAttribute('class', wide ? 'w' : '');
ebi('u2conf').setAttribute('class', wide);
ebi('u2cards').setAttribute('class', wide);
ebi('u2etaw').setAttribute('class', wide);
}
wide = wem > 78 ? 'ww' : wide;
parent = ebi(wide == 'ww' && write ? 'u2c3w' : 'u2c3t');
var its = [ebi('u2etaw'), ebi('u2cards')];
if (its[0].parentNode !== parent) {
ebi('u2conf').setAttribute('class', wide);
for (var a = 0; a < 2; a++) {
parent.appendChild(its[a]);
its[a].setAttribute('class', wide);
}
}
}
window.addEventListener('resize', onresize);
@@ -1844,7 +1861,7 @@ function up2k_init(subtle) {
setTimeout(onresize, 500);
}
var o = QSA('#u2conf *[tt]');
var o = QSA('#u2conf .c *[tt]');
for (var a = o.length - 1; a >= 0; a--) {
o[a].parentNode.getElementsByTagName('input')[0].setAttribute('tt', o[a].getAttribute('tt'));
}

View File

@@ -146,7 +146,7 @@ function vis_exh(msg, url, lineNo, columnNo, error) {
var s = mknod('style');
s.innerHTML = (
'#exbox{background:#333;color:#ddd;font-family:sans-serif;font-size:0.8em;padding:0 1em 1em 1em;z-index:80386;position:fixed;top:0;left:0;right:0;bottom:0;width:100%;height:100%;overflow:auto;width:calc(100% - 2em)} ' +
'#exbox{background:#222;color:#ddd;font-family:sans-serif;font-size:0.8em;padding:0 1em 1em 1em;z-index:80386;position:fixed;top:0;left:0;right:0;bottom:0;width:100%;height:100%;overflow:auto;width:calc(100% - 2em)} ' +
'#exbox,#exbox *{line-height:1.5em;overflow-wrap:break-word} ' +
'#exbox code{color:#bf7;background:#222;padding:.1em;margin:.2em;font-size:1.1em;font-family:monospace,monospace} ' +
'#exbox a{text-decoration:underline;color:#fc0} ' +
@@ -715,12 +715,23 @@ function scfg_bind(obj, oname, cname, defval, cb) {
function hist_push(url) {
console.log("h-push " + url);
history.pushState(url, url, url);
if (window.history && history.pushState)
history.pushState(url, url, url);
}
function hist_replace(url) {
console.log("h-repl " + url);
history.replaceState(url, url, url);
if (window.history && history.replaceState)
history.replaceState(url, url, url);
}
function sethash(hv) {
if (window.history && history.replaceState) {
hist_replace(document.location.pathname + '#' + hv);
}
else {
document.location.hash = hv;
}
}
@@ -1244,7 +1255,7 @@ var favico = (function () {
clearTimeout(r.to);
scfg_bind(r, 'txt', 'icot', '', r.upd);
scfg_bind(r, 'fg', 'icof', 'fc5', r.upd);
scfg_bind(r, 'bg', 'icob', '333', r.upd);
scfg_bind(r, 'bg', 'icob', '222', r.upd);
r.upd();
};

View File

@@ -1,11 +1,11 @@
html {
background: #333 url('/wp/wallhaven-mdjrqy.jpg') center / cover no-repeat fixed;
background: #222 url('/wp/wallhaven-mdjrqy.jpg') center / cover no-repeat fixed;
}
#files th {
background: rgba(32, 32, 32, 0.9) !important;
}
#ops,
#treeul,
#tree,
#files td {
background: rgba(32, 32, 32, 0.3) !important;
}
@@ -19,7 +19,7 @@ html.light #files th {
}
html.light .logue,
html.light #ops,
html.light #treeul,
html.light #tree,
html.light #files td {
background: rgba(248, 248, 248, 0.8) !important;
}

View File

@@ -27,7 +27,7 @@
#u2conf #u2btn, #u2btn {padding:1.5em 0}
/* adjust the button area a bit */
#u2conf.has_btn {width: 35em !important; margin: 5em auto}
#u2conf.w, #u2conf.ww {width: 35em !important; margin: 5em auto}
/* a */
#op_up2k {min-height: 0}

View File

@@ -50,6 +50,7 @@ class Cfg(Namespace):
hist=None,
no_idx=None,
no_hash=None,
js_browser=None,
css_browser=None,
**{k: False for k in "e2d e2ds e2dsa e2t e2ts e2tsr".split()}
)

View File

@@ -25,6 +25,7 @@ class Cfg(Namespace):
"hist": None,
"no_idx": None,
"no_hash": None,
"js_browser": None,
"css_browser": None,
"no_voldump": True,
"no_logues": False,