Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e8daf650e | ||
|
|
0cf737b4ce | ||
|
|
74635e0113 | ||
|
|
e5c4f49901 | ||
|
|
e4654ee7f1 | ||
|
|
e5d05c05ed | ||
|
|
73c4f99687 | ||
|
|
28c12ef3bf | ||
|
|
eed82dbb54 | ||
|
|
2c4b4ab928 | ||
|
|
505a8fc6f6 | ||
|
|
e4801d9b06 | ||
|
|
04f1b2cf3a | ||
|
|
c06d928bb5 | ||
|
|
ab09927e7b | ||
|
|
779437db67 | ||
|
|
28cbdb652e | ||
|
|
2b2415a7d8 | ||
|
|
746a8208aa | ||
|
|
a2a041a98a | ||
|
|
10b436e449 | ||
|
|
4d62b34786 | ||
|
|
0546210687 | ||
|
|
f8c11faada | ||
|
|
16d6e9be1f | ||
|
|
aff8185f2e | ||
|
|
217d15fe81 | ||
|
|
171e93c201 | ||
|
|
acc1d2e9e3 | ||
|
|
49c2f37154 | ||
|
|
69e54497aa | ||
|
|
9aa1885669 | ||
|
|
4418508513 |
10
.vscode/launch.py
vendored
10
.vscode/launch.py
vendored
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import shlex
|
||||||
|
|
||||||
sys.path.insert(0, os.getcwd())
|
sys.path.insert(0, os.getcwd())
|
||||||
|
|
||||||
@@ -16,9 +17,16 @@ with open(".vscode/launch.json", "r") as f:
|
|||||||
|
|
||||||
oj = jstyleson.loads(tj)
|
oj = jstyleson.loads(tj)
|
||||||
argv = oj["configurations"][0]["args"]
|
argv = oj["configurations"][0]["args"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
sargv = " ".join([shlex.quote(x) for x in argv])
|
||||||
|
print(sys.executable + " -m copyparty " + sargv + "\n")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
argv = [os.path.expanduser(x) if x.startswith("~") else x for x in argv]
|
argv = [os.path.expanduser(x) if x.startswith("~") else x for x in argv]
|
||||||
try:
|
try:
|
||||||
copyparty(argv)
|
copyparty(["a"] + argv)
|
||||||
except SystemExit as ex:
|
except SystemExit as ex:
|
||||||
if ex.code:
|
if ex.code:
|
||||||
raise
|
raise
|
||||||
|
|||||||
@@ -101,6 +101,11 @@ summary: it works! you can use it! (but technically not even close to beta)
|
|||||||
* hiding the contents at url `/d1/d2/d3` using `-v :d1/d2/d3:cd2d` has the side-effect of creating databases (for files/tags) inside folders d1 and d2, and those databases take precedence over the main db at the top of the vfs - this means all files in d2 and below will be reindexed unless you already had a vfs entry at or below d2
|
* hiding the contents at url `/d1/d2/d3` using `-v :d1/d2/d3:cd2d` has the side-effect of creating databases (for files/tags) inside folders d1 and d2, and those databases take precedence over the main db at the top of the vfs - this means all files in d2 and below will be reindexed unless you already had a vfs entry at or below d2
|
||||||
* probably more, pls let me know
|
* probably more, pls let me know
|
||||||
|
|
||||||
|
## not my bugs
|
||||||
|
|
||||||
|
* Windows: msys2-python 3.8.6 occasionally throws "RuntimeError: release unlocked lock" when leaving a scoped mutex in up2k
|
||||||
|
* this is an msys2 bug, the regular windows edition of python is fine
|
||||||
|
|
||||||
|
|
||||||
# usage
|
# usage
|
||||||
|
|
||||||
@@ -111,6 +116,8 @@ the browser has the following hotkeys
|
|||||||
* `I/K` prev/next folder
|
* `I/K` prev/next folder
|
||||||
* `P` parent folder
|
* `P` parent folder
|
||||||
|
|
||||||
|
you can link a particular timestamp in an audio file by adding it to the URL, such as `&20` / `&20s` / `&1m20` / `&1:20` after the `.../#af-c8960dab`
|
||||||
|
|
||||||
|
|
||||||
## zip downloads
|
## zip downloads
|
||||||
|
|
||||||
@@ -339,7 +346,6 @@ in the `scripts` folder:
|
|||||||
|
|
||||||
roughly sorted by priority
|
roughly sorted by priority
|
||||||
|
|
||||||
* audio link with timestamp
|
|
||||||
* separate sqlite table per tag
|
* separate sqlite table per tag
|
||||||
* audio fingerprinting
|
* audio fingerprinting
|
||||||
* readme.md as epilogue
|
* readme.md as epilogue
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ if platform.system() == "Windows":
|
|||||||
VT100 = not WINDOWS or WINDOWS >= [10, 0, 14393]
|
VT100 = not WINDOWS or WINDOWS >= [10, 0, 14393]
|
||||||
# introduced in anniversary update
|
# introduced in anniversary update
|
||||||
|
|
||||||
|
ANYWIN = WINDOWS or sys.platform in ["msys"]
|
||||||
|
|
||||||
MACOS = platform.system() == "Darwin"
|
MACOS = platform.system() == "Darwin"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -247,6 +247,7 @@ def run_argparse(argv, formatter):
|
|||||||
ap.add_argument("--no-zip", action="store_true", help="disable download as zip/tar")
|
ap.add_argument("--no-zip", action="store_true", help="disable download as zip/tar")
|
||||||
ap.add_argument("--no-sendfile", action="store_true", help="disable sendfile (for debugging)")
|
ap.add_argument("--no-sendfile", action="store_true", help="disable sendfile (for debugging)")
|
||||||
ap.add_argument("--no-scandir", action="store_true", help="disable scandir (for debugging)")
|
ap.add_argument("--no-scandir", action="store_true", help="disable scandir (for debugging)")
|
||||||
|
ap.add_argument("--sparse", metavar="MiB", type=int, default=4, help="up2k min.size threshold (mswin-only)")
|
||||||
ap.add_argument("--urlform", metavar="MODE", type=str, default="print,get", help="how to handle url-forms")
|
ap.add_argument("--urlform", metavar="MODE", type=str, default="print,get", help="how to handle url-forms")
|
||||||
ap.add_argument("--salt", type=str, default="hunter2", help="up2k file-hash salt")
|
ap.add_argument("--salt", type=str, default="hunter2", help="up2k file-hash salt")
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
VERSION = (0, 10, 8)
|
VERSION = (0, 10, 14)
|
||||||
CODENAME = "zip it"
|
CODENAME = "zip it"
|
||||||
BUILD_DT = (2021, 4, 11)
|
BUILD_DT = (2021, 4, 21)
|
||||||
|
|
||||||
S_VERSION = ".".join(map(str, VERSION))
|
S_VERSION = ".".join(map(str, VERSION))
|
||||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||||
|
|||||||
@@ -111,7 +111,27 @@ class VFS(object):
|
|||||||
if rem:
|
if rem:
|
||||||
rp += "/" + rem
|
rp += "/" + rem
|
||||||
|
|
||||||
|
try:
|
||||||
return fsdec(os.path.realpath(fsenc(rp)))
|
return fsdec(os.path.realpath(fsenc(rp)))
|
||||||
|
except:
|
||||||
|
if not WINDOWS:
|
||||||
|
raise
|
||||||
|
|
||||||
|
# cpython bug introduced in 3.8, still exists in 3.9.1;
|
||||||
|
# some win7sp1 and win10:20H2 boxes cannot realpath a
|
||||||
|
# networked drive letter such as b"n:" or b"n:\\"
|
||||||
|
#
|
||||||
|
# requirements to trigger:
|
||||||
|
# * bytestring (not unicode str)
|
||||||
|
# * just the drive letter (subfolders are ok)
|
||||||
|
# * networked drive (regular disks and vmhgfs are ok)
|
||||||
|
# * on an enterprise network (idk, cannot repro with samba)
|
||||||
|
#
|
||||||
|
# hits the following exceptions in succession:
|
||||||
|
# * access denied at L601: "path = _getfinalpathname(path)"
|
||||||
|
# * "cant concat str to bytes" at L621: "return path + tail"
|
||||||
|
#
|
||||||
|
return os.path.realpath(rp)
|
||||||
|
|
||||||
def ls(self, rem, uname, scandir, lstat=False):
|
def ls(self, rem, uname, scandir, lstat=False):
|
||||||
"""return user-readable [fsdir,real,virt] items at vpath"""
|
"""return user-readable [fsdir,real,virt] items at vpath"""
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import ctypes
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import calendar
|
import calendar
|
||||||
|
|
||||||
from .__init__ import E, PY2, WINDOWS
|
from .__init__ import E, PY2, WINDOWS, ANYWIN
|
||||||
from .util import * # noqa # pylint: disable=unused-wildcard-import
|
from .util import * # noqa # pylint: disable=unused-wildcard-import
|
||||||
from .szip import StreamZip
|
from .szip import StreamZip
|
||||||
from .star import StreamTar
|
from .star import StreamTar
|
||||||
@@ -261,12 +261,14 @@ class HttpCli(object):
|
|||||||
|
|
||||||
self.absolute_urls = True
|
self.absolute_urls = True
|
||||||
|
|
||||||
# go home if verboten
|
|
||||||
self.readable, self.writable = self.conn.auth.vfs.can_access(
|
self.readable, self.writable = self.conn.auth.vfs.can_access(
|
||||||
self.vpath, self.uname
|
self.vpath, self.uname
|
||||||
)
|
)
|
||||||
if not self.readable and not self.writable:
|
if not self.readable and not self.writable:
|
||||||
|
if self.vpath:
|
||||||
self.log("inaccessible: [{}]".format(self.vpath))
|
self.log("inaccessible: [{}]".format(self.vpath))
|
||||||
|
raise Pebkac(404)
|
||||||
|
|
||||||
self.uparam = {"h": False}
|
self.uparam = {"h": False}
|
||||||
|
|
||||||
if "h" in self.uparam:
|
if "h" in self.uparam:
|
||||||
@@ -626,7 +628,7 @@ class HttpCli(object):
|
|||||||
self.loud_reply(x, status=500)
|
self.loud_reply(x, status=500)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not WINDOWS and num_left == 0:
|
if not ANYWIN and num_left == 0:
|
||||||
times = (int(time.time()), int(lastmod))
|
times = (int(time.time()), int(lastmod))
|
||||||
self.log("no more chunks, setting times {}".format(times))
|
self.log("no more chunks, setting times {}".format(times))
|
||||||
try:
|
try:
|
||||||
@@ -680,7 +682,7 @@ class HttpCli(object):
|
|||||||
raise Pebkac(500, "mkdir failed, check the logs")
|
raise Pebkac(500, "mkdir failed, check the logs")
|
||||||
|
|
||||||
vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/")
|
vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/")
|
||||||
esc_paths = [quotep(vpath), html_escape(vpath)]
|
esc_paths = [quotep(vpath), html_escape(vpath, crlf=True)]
|
||||||
html = self.j2(
|
html = self.j2(
|
||||||
"msg",
|
"msg",
|
||||||
h2='<a href="/{}">go to /{}</a>'.format(*esc_paths),
|
h2='<a href="/{}">go to /{}</a>'.format(*esc_paths),
|
||||||
@@ -1181,17 +1183,16 @@ class HttpCli(object):
|
|||||||
template = self.j2(tpl)
|
template = self.j2(tpl)
|
||||||
|
|
||||||
st = os.stat(fsenc(fs_path))
|
st = os.stat(fsenc(fs_path))
|
||||||
# sz_md = st.st_size
|
|
||||||
ts_md = st.st_mtime
|
ts_md = st.st_mtime
|
||||||
|
|
||||||
st = os.stat(fsenc(html_path))
|
st = os.stat(fsenc(html_path))
|
||||||
ts_html = st.st_mtime
|
ts_html = st.st_mtime
|
||||||
|
|
||||||
# TODO dont load into memory ;_;
|
sz_md = 0
|
||||||
# (trivial fix, count the &'s)
|
for buf in yieldfile(fs_path):
|
||||||
with open(fsenc(fs_path), "rb") as f:
|
sz_md += len(buf)
|
||||||
md = f.read().replace(b"&", b"&")
|
for c, v in [[b"&", 4], [b"<", 3], [b">", 3]]:
|
||||||
sz_md = len(md)
|
sz_md += (len(buf) - len(buf.replace(c, b""))) * v
|
||||||
|
|
||||||
file_ts = max(ts_md, ts_html)
|
file_ts = max(ts_md, ts_html)
|
||||||
file_lastmod, do_send = self._chk_lastmod(file_ts)
|
file_lastmod, do_send = self._chk_lastmod(file_ts)
|
||||||
@@ -1199,27 +1200,34 @@ class HttpCli(object):
|
|||||||
self.out_headers["Cache-Control"] = "no-cache"
|
self.out_headers["Cache-Control"] = "no-cache"
|
||||||
status = 200 if do_send else 304
|
status = 200 if do_send else 304
|
||||||
|
|
||||||
|
boundary = "\roll\tide"
|
||||||
targs = {
|
targs = {
|
||||||
"edit": "edit" in self.uparam,
|
"edit": "edit" in self.uparam,
|
||||||
"title": html_escape(self.vpath),
|
"title": html_escape(self.vpath, crlf=True),
|
||||||
"lastmod": int(ts_md * 1000),
|
"lastmod": int(ts_md * 1000),
|
||||||
"md_plug": "true" if self.args.emp else "false",
|
"md_plug": "true" if self.args.emp else "false",
|
||||||
"md_chk_rate": self.args.mcr,
|
"md_chk_rate": self.args.mcr,
|
||||||
"md": "",
|
"md": boundary,
|
||||||
}
|
}
|
||||||
sz_html = len(template.render(**targs).encode("utf-8"))
|
html = template.render(**targs).encode("utf-8")
|
||||||
self.send_headers(sz_html + sz_md, status)
|
html = html.split(boundary.encode("utf-8"))
|
||||||
|
if len(html) != 2:
|
||||||
|
raise Exception("boundary appears in " + html_path)
|
||||||
|
|
||||||
|
self.send_headers(sz_md + len(html[0]) + len(html[1]), status)
|
||||||
|
|
||||||
logmsg += unicode(status)
|
logmsg += unicode(status)
|
||||||
if self.mode == "HEAD" or not do_send:
|
if self.mode == "HEAD" or not do_send:
|
||||||
self.log(logmsg)
|
self.log(logmsg)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# TODO jinja2 can stream this right?
|
|
||||||
targs["md"] = md.decode("utf-8", "replace")
|
|
||||||
html = template.render(**targs).encode("utf-8")
|
|
||||||
try:
|
try:
|
||||||
self.s.sendall(html)
|
self.s.sendall(html[0])
|
||||||
|
for buf in yieldfile(fs_path):
|
||||||
|
self.s.sendall(html_bescape(buf))
|
||||||
|
|
||||||
|
self.s.sendall(html[1])
|
||||||
|
|
||||||
except:
|
except:
|
||||||
self.log(logmsg + " \033[31md/c\033[0m")
|
self.log(logmsg + " \033[31md/c\033[0m")
|
||||||
return False
|
return False
|
||||||
@@ -1300,7 +1308,7 @@ class HttpCli(object):
|
|||||||
else:
|
else:
|
||||||
vpath += "/" + node
|
vpath += "/" + node
|
||||||
|
|
||||||
vpnodes.append([quotep(vpath) + "/", html_escape(node)])
|
vpnodes.append([quotep(vpath) + "/", html_escape(node, crlf=True)])
|
||||||
|
|
||||||
vn, rem = self.auth.vfs.get(
|
vn, rem = self.auth.vfs.get(
|
||||||
self.vpath, self.uname, self.readable, self.writable
|
self.vpath, self.uname, self.readable, self.writable
|
||||||
@@ -1394,7 +1402,7 @@ class HttpCli(object):
|
|||||||
margin = '<a href="{}?zip">zip</a>'.format(quotep(href))
|
margin = '<a href="{}?zip">zip</a>'.format(quotep(href))
|
||||||
elif fn in hist:
|
elif fn in hist:
|
||||||
margin = '<a href="{}.hist/{}">#{}</a>'.format(
|
margin = '<a href="{}.hist/{}">#{}</a>'.format(
|
||||||
base, html_escape(hist[fn][2], quote=True), hist[fn][0]
|
base, html_escape(hist[fn][2], quote=True, crlf=True), hist[fn][0]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
margin = "-"
|
margin = "-"
|
||||||
@@ -1536,7 +1544,7 @@ class HttpCli(object):
|
|||||||
have_b_u=(self.writable and self.uparam.get("b") == "u"),
|
have_b_u=(self.writable and self.uparam.get("b") == "u"),
|
||||||
url_suf=url_suf,
|
url_suf=url_suf,
|
||||||
logues=logues,
|
logues=logues,
|
||||||
title=html_escape(self.vpath),
|
title=html_escape(self.vpath, crlf=True),
|
||||||
srv_info=srv_info,
|
srv_info=srv_info,
|
||||||
)
|
)
|
||||||
self.reply(html.encode("utf-8", "replace"))
|
self.reply(html.encode("utf-8", "replace"))
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import traceback
|
|||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from .__init__ import WINDOWS
|
from .__init__ import WINDOWS, ANYWIN
|
||||||
from .util import (
|
from .util import (
|
||||||
Pebkac,
|
Pebkac,
|
||||||
Queue,
|
Queue,
|
||||||
@@ -79,7 +79,7 @@ class Up2k(object):
|
|||||||
if self.sqlite_ver < (3, 9):
|
if self.sqlite_ver < (3, 9):
|
||||||
self.no_expr_idx = True
|
self.no_expr_idx = True
|
||||||
|
|
||||||
if WINDOWS:
|
if ANYWIN:
|
||||||
# usually fails to set lastmod too quickly
|
# usually fails to set lastmod too quickly
|
||||||
self.lastmod_q = Queue()
|
self.lastmod_q = Queue()
|
||||||
thr = threading.Thread(target=self._lastmodder)
|
thr = threading.Thread(target=self._lastmodder)
|
||||||
@@ -101,11 +101,12 @@ class Up2k(object):
|
|||||||
thr.daemon = True
|
thr.daemon = True
|
||||||
thr.start()
|
thr.start()
|
||||||
|
|
||||||
thr = threading.Thread(target=self._tagger)
|
thr = threading.Thread(target=self._hasher)
|
||||||
thr.daemon = True
|
thr.daemon = True
|
||||||
thr.start()
|
thr.start()
|
||||||
|
|
||||||
thr = threading.Thread(target=self._hasher)
|
if self.mtag:
|
||||||
|
thr = threading.Thread(target=self._tagger)
|
||||||
thr.daemon = True
|
thr.daemon = True
|
||||||
thr.start()
|
thr.start()
|
||||||
|
|
||||||
@@ -667,12 +668,6 @@ class Up2k(object):
|
|||||||
cur.close()
|
cur.close()
|
||||||
|
|
||||||
def _start_mpool(self):
|
def _start_mpool(self):
|
||||||
if WINDOWS and False:
|
|
||||||
nah = open(os.devnull, "wb")
|
|
||||||
wmic = "processid={}".format(os.getpid())
|
|
||||||
wmic = ["wmic", "process", "where", wmic, "call", "setpriority"]
|
|
||||||
sp.call(wmic + ["below normal"], stdout=nah, stderr=nah)
|
|
||||||
|
|
||||||
# mp.pool.ThreadPool and concurrent.futures.ThreadPoolExecutor
|
# mp.pool.ThreadPool and concurrent.futures.ThreadPoolExecutor
|
||||||
# both do crazy runahead so lets reinvent another wheel
|
# both do crazy runahead so lets reinvent another wheel
|
||||||
nw = os.cpu_count() if hasattr(os, "cpu_count") else 4
|
nw = os.cpu_count() if hasattr(os, "cpu_count") else 4
|
||||||
@@ -697,12 +692,6 @@ class Up2k(object):
|
|||||||
|
|
||||||
mpool.join()
|
mpool.join()
|
||||||
done = self._flush_mpool(wcur)
|
done = self._flush_mpool(wcur)
|
||||||
if WINDOWS and False:
|
|
||||||
nah = open(os.devnull, "wb")
|
|
||||||
wmic = "processid={}".format(os.getpid())
|
|
||||||
wmic = ["wmic", "process", "where", wmic, "call", "setpriority"]
|
|
||||||
sp.call(wmic + ["below normal"], stdout=nah, stderr=nah)
|
|
||||||
|
|
||||||
return done
|
return done
|
||||||
|
|
||||||
def _tag_thr(self, q):
|
def _tag_thr(self, q):
|
||||||
@@ -1068,6 +1057,8 @@ class Up2k(object):
|
|||||||
with self.mutex:
|
with self.mutex:
|
||||||
job = self.registry[ptop].get(wark, None)
|
job = self.registry[ptop].get(wark, None)
|
||||||
if not job:
|
if not job:
|
||||||
|
known = " ".join([x for x in self.registry[ptop].keys()])
|
||||||
|
self.log("unknown wark [{}], known: {}".format(wark, known))
|
||||||
raise Pebkac(400, "unknown wark")
|
raise Pebkac(400, "unknown wark")
|
||||||
|
|
||||||
if chash not in job["need"]:
|
if chash not in job["need"]:
|
||||||
@@ -1107,8 +1098,9 @@ class Up2k(object):
|
|||||||
|
|
||||||
atomic_move(src, dst)
|
atomic_move(src, dst)
|
||||||
|
|
||||||
if WINDOWS:
|
if ANYWIN:
|
||||||
self.lastmod_q.put([dst, (int(time.time()), int(job["lmod"]))])
|
a = [dst, job["size"], (int(time.time()), int(job["lmod"]))]
|
||||||
|
self.lastmod_q.put(a)
|
||||||
|
|
||||||
# legit api sware 2 me mum
|
# legit api sware 2 me mum
|
||||||
if self.idx_wark(
|
if self.idx_wark(
|
||||||
@@ -1209,6 +1201,17 @@ class Up2k(object):
|
|||||||
suffix = ".{:.6f}-{}".format(job["t0"], job["addr"])
|
suffix = ".{:.6f}-{}".format(job["t0"], job["addr"])
|
||||||
with ren_open(tnam, "wb", fdir=pdir, suffix=suffix) as f:
|
with ren_open(tnam, "wb", fdir=pdir, suffix=suffix) as f:
|
||||||
f, job["tnam"] = f["orz"]
|
f, job["tnam"] = f["orz"]
|
||||||
|
if (
|
||||||
|
ANYWIN
|
||||||
|
and self.args.sparse
|
||||||
|
and self.args.sparse * 1024 * 1024 <= job["size"]
|
||||||
|
):
|
||||||
|
fp = os.path.join(pdir, job["tnam"])
|
||||||
|
try:
|
||||||
|
sp.check_call(["fsutil", "sparse", "setflag", fp])
|
||||||
|
except:
|
||||||
|
self.log("could not sparse [{}]".format(fp), 3)
|
||||||
|
|
||||||
f.seek(job["size"] - 1)
|
f.seek(job["size"] - 1)
|
||||||
f.write(b"e")
|
f.write(b"e")
|
||||||
|
|
||||||
@@ -1220,13 +1223,19 @@ class Up2k(object):
|
|||||||
|
|
||||||
# self.log("lmod: got {}".format(len(ready)))
|
# self.log("lmod: got {}".format(len(ready)))
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
for path, times in ready:
|
for path, sz, times in ready:
|
||||||
self.log("lmod: setting times {} on {}".format(times, path))
|
self.log("lmod: setting times {} on {}".format(times, path))
|
||||||
try:
|
try:
|
||||||
os.utime(fsenc(path), times)
|
os.utime(fsenc(path), times)
|
||||||
except:
|
except:
|
||||||
self.log("lmod: failed to utime ({}, {})".format(path, times))
|
self.log("lmod: failed to utime ({}, {})".format(path, times))
|
||||||
|
|
||||||
|
if self.args.sparse and self.args.sparse * 1024 * 1024 <= sz:
|
||||||
|
try:
|
||||||
|
sp.check_call(["fsutil", "sparse", "setflag", path, "0"])
|
||||||
|
except:
|
||||||
|
self.log("could not unsparse [{}]".format(path), 3)
|
||||||
|
|
||||||
def _snapshot(self):
|
def _snapshot(self):
|
||||||
persist_interval = 30 # persist unfinished uploads index every 30 sec
|
persist_interval = 30 # persist unfinished uploads index every 30 sec
|
||||||
discard_interval = 21600 # drop unfinished uploads after 6 hours inactivity
|
discard_interval = 21600 # drop unfinished uploads after 6 hours inactivity
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import mimetypes
|
|||||||
import contextlib
|
import contextlib
|
||||||
import subprocess as sp # nosec
|
import subprocess as sp # nosec
|
||||||
|
|
||||||
from .__init__ import PY2, WINDOWS
|
from .__init__ import PY2, WINDOWS, ANYWIN
|
||||||
from .stolen import surrogateescape
|
from .stolen import surrogateescape
|
||||||
|
|
||||||
FAKE_MP = False
|
FAKE_MP = False
|
||||||
@@ -580,8 +580,8 @@ def sanitize_fn(fn, ok=""):
|
|||||||
if "/" not in ok:
|
if "/" not in ok:
|
||||||
fn = fn.replace("\\", "/").split("/")[-1]
|
fn = fn.replace("\\", "/").split("/")[-1]
|
||||||
|
|
||||||
if WINDOWS:
|
if ANYWIN:
|
||||||
for bad, good in [x for x in [
|
remap = [
|
||||||
["<", "<"],
|
["<", "<"],
|
||||||
[">", ">"],
|
[">", ">"],
|
||||||
[":", ":"],
|
[":", ":"],
|
||||||
@@ -591,7 +591,8 @@ def sanitize_fn(fn, ok=""):
|
|||||||
["|", "|"],
|
["|", "|"],
|
||||||
["?", "?"],
|
["?", "?"],
|
||||||
["*", "*"],
|
["*", "*"],
|
||||||
] if x[0] not in ok]:
|
]
|
||||||
|
for bad, good in [x for x in remap if x[0] not in ok]:
|
||||||
fn = fn.replace(bad, good)
|
fn = fn.replace(bad, good)
|
||||||
|
|
||||||
bad = ["con", "prn", "aux", "nul"]
|
bad = ["con", "prn", "aux", "nul"]
|
||||||
@@ -615,17 +616,24 @@ def exclude_dotfiles(filepaths):
|
|||||||
return [x for x in filepaths if not x.split("/")[-1].startswith(".")]
|
return [x for x in filepaths if not x.split("/")[-1].startswith(".")]
|
||||||
|
|
||||||
|
|
||||||
def html_escape(s, quote=False):
|
def html_escape(s, quote=False, crlf=False):
|
||||||
"""html.escape but also newlines"""
|
"""html.escape but also newlines"""
|
||||||
s = (
|
s = s.replace("&", "&").replace("<", "<").replace(">", ">")
|
||||||
s.replace("&", "&")
|
|
||||||
.replace("<", "<")
|
|
||||||
.replace(">", ">")
|
|
||||||
.replace("\r", " ")
|
|
||||||
.replace("\n", " ")
|
|
||||||
)
|
|
||||||
if quote:
|
if quote:
|
||||||
s = s.replace('"', """).replace("'", "'")
|
s = s.replace('"', """).replace("'", "'")
|
||||||
|
if crlf:
|
||||||
|
s = s.replace("\r", " ").replace("\n", " ")
|
||||||
|
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def html_bescape(s, quote=False, crlf=False):
|
||||||
|
"""html.escape but bytestrings"""
|
||||||
|
s = s.replace(b"&", b"&").replace(b"<", b"<").replace(b">", b">")
|
||||||
|
if quote:
|
||||||
|
s = s.replace(b'"', b""").replace(b"'", b"'")
|
||||||
|
if crlf:
|
||||||
|
s = s.replace(b"\r", b" ").replace(b"\n", b" ")
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ a, #files tbody div a:last-child {
|
|||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
}
|
}
|
||||||
#wtoggle.sel {
|
#wtoggle.sel {
|
||||||
width: 6em;
|
width: 6.4em;
|
||||||
}
|
}
|
||||||
#wtoggle.sel #wzip {
|
#wtoggle.sel #wzip {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -685,3 +685,173 @@ input[type="checkbox"]:checked+label {
|
|||||||
font-family: monospace, monospace;
|
font-family: monospace, monospace;
|
||||||
line-height: 2em;
|
line-height: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
html.light {
|
||||||
|
color: #333;
|
||||||
|
background: #eee;
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
html.light #ops,
|
||||||
|
html.light .opbox,
|
||||||
|
html.light #srch_form {
|
||||||
|
background: #f7f7f7;
|
||||||
|
box-shadow: 0 0 .3em #ddd;
|
||||||
|
border-color: #f7f7f7;
|
||||||
|
}
|
||||||
|
html.light #ops a.act {
|
||||||
|
box-shadow: 0 .2em .2em #ccc;
|
||||||
|
background: #f7f7f7;
|
||||||
|
border-color: #07a;
|
||||||
|
padding-top: .4em;
|
||||||
|
}
|
||||||
|
html.light #op_cfg h3 {
|
||||||
|
border-color: #ccc;
|
||||||
|
}
|
||||||
|
html.light .tglbtn,
|
||||||
|
html.light #tree > a + a {
|
||||||
|
color: #666;
|
||||||
|
background: #ddd;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
html.light .tglbtn:hover,
|
||||||
|
html.light #tree > a + a:hover {
|
||||||
|
background: #caf;
|
||||||
|
}
|
||||||
|
html.light .tglbtn.on,
|
||||||
|
html.light #tree > a + a.on {
|
||||||
|
background: #4a0;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
html.light #srv_info {
|
||||||
|
color: #c83;
|
||||||
|
text-shadow: 1px 1px 0 #fff;
|
||||||
|
}
|
||||||
|
html.light #srv_info span {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
html.light #treeul a+a {
|
||||||
|
background: inherit;
|
||||||
|
color: #06a;
|
||||||
|
}
|
||||||
|
html.light #treeul a.hl {
|
||||||
|
background: #07a;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
html.light #tree li {
|
||||||
|
border-color: #ddd #fff #f7f7f7 #fff;
|
||||||
|
}
|
||||||
|
html.light #tree ul {
|
||||||
|
border-color: #ccc;
|
||||||
|
}
|
||||||
|
html.light a,
|
||||||
|
html.light #ops a,
|
||||||
|
html.light #files tbody div a:last-child {
|
||||||
|
color: #06a;
|
||||||
|
}
|
||||||
|
html.light #files tbody {
|
||||||
|
background: #f7f7f7;
|
||||||
|
}
|
||||||
|
html.light #files {
|
||||||
|
box-shadow: 0 0 .3em #ccc;
|
||||||
|
}
|
||||||
|
html.light #files thead th {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
html.light #files tr+tr td {
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
html.light #files td {
|
||||||
|
border-bottom: 1px solid #f7f7f7;
|
||||||
|
}
|
||||||
|
html.light #files tbody tr:last-child td {
|
||||||
|
border-bottom: .2em solid #ccc;
|
||||||
|
}
|
||||||
|
html.light #files td:nth-child(2n) {
|
||||||
|
color: #d38;
|
||||||
|
}
|
||||||
|
html.light #files tr:hover td {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
html.light #files tbody a.play {
|
||||||
|
color: #c0f;
|
||||||
|
}
|
||||||
|
html.light tr.play td {
|
||||||
|
background: #fc5;
|
||||||
|
}
|
||||||
|
html.light tr.play a {
|
||||||
|
color: #406;
|
||||||
|
}
|
||||||
|
html.light #files > thead > tr > th.min span {
|
||||||
|
background: linear-gradient(90deg, rgba(68,68,68,0), rgba(68,68,68,0.2) 70%, rgba(68,68,68,0.5));
|
||||||
|
}
|
||||||
|
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: #fc5;
|
||||||
|
}
|
||||||
|
html.light #files tr.sel:hover td {
|
||||||
|
background: #c37;
|
||||||
|
}
|
||||||
|
html.light #files tr.sel td {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
html.light #files tr.sel a {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
html.light input[type="checkbox"] + label {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
html.light .opview input[type="text"] {
|
||||||
|
background: #fff;
|
||||||
|
color: #333;
|
||||||
|
box-shadow: 0 0 2px #888;
|
||||||
|
border-color: #38d;
|
||||||
|
}
|
||||||
|
html.light #ops:hover #opdesc {
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 .3em 1em #ccc;
|
||||||
|
}
|
||||||
|
html.light #opdesc code {
|
||||||
|
background: #060;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
html.light #u2tab a>span,
|
||||||
|
html.light #files td div span {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
html.light #path {
|
||||||
|
background: #f7f7f7;
|
||||||
|
text-shadow: none;
|
||||||
|
box-shadow: 0 0 .3em #bbb;
|
||||||
|
}
|
||||||
|
html.light #path a {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
html.light #path a:not(:last-child)::after {
|
||||||
|
border-color: #ccc;
|
||||||
|
background: none;
|
||||||
|
border-width: .1em .1em 0 0;
|
||||||
|
margin: -.2em .3em -.2em -.3em;
|
||||||
|
}
|
||||||
|
html.light #path a:hover {
|
||||||
|
background: none;
|
||||||
|
color: #60a;
|
||||||
|
}
|
||||||
|
html.light #files tbody div a {
|
||||||
|
color: #d38;
|
||||||
|
}
|
||||||
|
html.light #files a:hover,
|
||||||
|
html.light #files tr.sel a:hover {
|
||||||
|
color: #000;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
@@ -13,8 +13,8 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="ops">
|
<div id="ops">
|
||||||
<a href="#" data-dest="" data-desc="close submenu">---</a>
|
<a href="#" data-dest="" data-desc="close submenu">---</a>
|
||||||
<a href="#" data-perm="read" data-dest="search" data-desc="search for files by attributes, path/name, music tags, or any combination of those.<br /><br /><code>foo bar</code> = must contain both foo and bar,<br /><code>foo -bar</code> = must contain foo but not bar,<br /><code>^yana .opus$</code> = must start with yana and have the opus extension">🔎</a>
|
|
||||||
{%- if have_up2k_idx %}
|
{%- if have_up2k_idx %}
|
||||||
|
<a href="#" data-perm="read" data-dest="search" data-desc="search for files by attributes, path/name, music tags, or any combination of those.<br /><br /><code>foo bar</code> = must contain both foo and bar,<br /><code>foo -bar</code> = must contain foo but not bar,<br /><code>^yana .opus$</code> = must start with yana and have the opus extension">🔎</a>
|
||||||
<a href="#" data-dest="up2k" data-desc="up2k: upload files (if you have write-access) or toggle into the search-mode and drag files onto the search button to see if they exist somewhere on the server">🚀</a>
|
<a href="#" data-dest="up2k" data-desc="up2k: upload files (if you have write-access) or toggle into the search-mode and drag files onto the search button to see if they exist somewhere on the server">🚀</a>
|
||||||
{%- else %}
|
{%- else %}
|
||||||
<a href="#" data-perm="write" data-dest="up2k" data-desc="up2k: upload files with resume support (close your browser and drop the same files in later)">🚀</a>
|
<a href="#" data-perm="write" data-dest="up2k" data-desc="up2k: upload files with resume support (close your browser and drop the same files in later)">🚀</a>
|
||||||
@@ -39,14 +39,17 @@
|
|||||||
{%- include 'upload.html' %}
|
{%- include 'upload.html' %}
|
||||||
|
|
||||||
<div id="op_cfg" class="opview opbox">
|
<div id="op_cfg" class="opview opbox">
|
||||||
<h3>key notation</h3>
|
<h3>switches</h3>
|
||||||
<div id="key_notation"></div>
|
<div>
|
||||||
|
<a id="tooltips" class="tglbtn" href="#">tooltips</a>
|
||||||
|
<a id="lightmode" class="tglbtn" href="#">lightmode</a>
|
||||||
|
</div>
|
||||||
{%- if have_zip %}
|
{%- if have_zip %}
|
||||||
<h3>folder download</h3>
|
<h3>folder download</h3>
|
||||||
<div id="arc_fmt"></div>
|
<div id="arc_fmt"></div>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
<h3>tooltips</h3>
|
<h3>key notation</h3>
|
||||||
<div><a id="tooltips" class="tglbtn" href="#">enable</a></div>
|
<div id="key_notation"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 id="path">
|
<h1 id="path">
|
||||||
@@ -117,8 +120,8 @@
|
|||||||
<a href="#" id="selall">sel.<br />all</a>
|
<a href="#" id="selall">sel.<br />all</a>
|
||||||
<a href="#" id="selinv">sel.<br />inv.</a>
|
<a href="#" id="selinv">sel.<br />inv.</a>
|
||||||
<a href="#" id="selzip">zip</a>
|
<a href="#" id="selzip">zip</a>
|
||||||
</span>
|
</span><a
|
||||||
<a href="#" id="wtico">♫</a>
|
href="#" id="wtico">♫</a>
|
||||||
</div>
|
</div>
|
||||||
<div id="widgeti">
|
<div id="widgeti">
|
||||||
<div id="pctl"><a href="#" id="bprev">⏮</a><a href="#" id="bplay">▶</a><a href="#" id="bnext">⏭</a></div>
|
<div id="pctl"><a href="#" id="bprev">⏮</a><a href="#" id="bplay">▶</a><a href="#" id="bnext">⏭</a></div>
|
||||||
|
|||||||
@@ -314,8 +314,8 @@ function seek_au_sec(seek) {
|
|||||||
|
|
||||||
mp.au.currentTime = seek;
|
mp.au.currentTime = seek;
|
||||||
|
|
||||||
|
// ogv.js breaks on .play() during playback
|
||||||
if (mp.au === mp.au_native)
|
if (mp.au === mp.au_native)
|
||||||
// hack: ogv.js breaks on .play() during playback
|
|
||||||
mp.au.play();
|
mp.au.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,7 +427,7 @@ catch (ex) { }
|
|||||||
|
|
||||||
|
|
||||||
// plays the tid'th audio file on the page
|
// plays the tid'th audio file on the page
|
||||||
function play(tid, call_depth) {
|
function play(tid, seek, call_depth) {
|
||||||
if (mp.order.length == 0)
|
if (mp.order.length == 0)
|
||||||
return alert('no audio found wait what');
|
return alert('no audio found wait what');
|
||||||
|
|
||||||
@@ -449,7 +449,7 @@ function play(tid, call_depth) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ogv.js breaks on .play() unless directly user-triggered
|
// ogv.js breaks on .play() unless directly user-triggered
|
||||||
var hack_attempt_play = true;
|
var attempt_play = true;
|
||||||
|
|
||||||
var url = mp.tracks[tid];
|
var url = mp.tracks[tid];
|
||||||
if (need_ogv && /\.(ogg|opus)$/i.test(url)) {
|
if (need_ogv && /\.(ogg|opus)$/i.test(url)) {
|
||||||
@@ -458,7 +458,7 @@ function play(tid, call_depth) {
|
|||||||
}
|
}
|
||||||
else if (window['OGVPlayer']) {
|
else if (window['OGVPlayer']) {
|
||||||
mp.au = mp.au_ogvjs = new OGVPlayer();
|
mp.au = mp.au_ogvjs = new OGVPlayer();
|
||||||
hack_attempt_play = false;
|
attempt_play = false;
|
||||||
mp.au.addEventListener('error', evau_error, true);
|
mp.au.addEventListener('error', evau_error, true);
|
||||||
mp.au.addEventListener('progress', pbar.drawpos, false);
|
mp.au.addEventListener('progress', pbar.drawpos, false);
|
||||||
widget.open();
|
widget.open();
|
||||||
@@ -470,7 +470,7 @@ function play(tid, call_depth) {
|
|||||||
show_modal('<h1>loading ogv.js</h1><h2>thanks apple</h2>');
|
show_modal('<h1>loading ogv.js</h1><h2>thanks apple</h2>');
|
||||||
|
|
||||||
import_js('/.cpr/deps/ogv.js', function () {
|
import_js('/.cpr/deps/ogv.js', function () {
|
||||||
play(tid, 1);
|
play(tid, seek, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -498,12 +498,16 @@ function play(tid, call_depth) {
|
|||||||
ebi(oid).parentElement.parentElement.className += ' play';
|
ebi(oid).parentElement.parentElement.className += ' play';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (hack_attempt_play)
|
if (attempt_play)
|
||||||
mp.au.play();
|
mp.au.play();
|
||||||
|
|
||||||
if (mp.au.paused)
|
if (mp.au.paused)
|
||||||
autoplay_blocked();
|
autoplay_blocked(seek);
|
||||||
|
else if (seek) {
|
||||||
|
seek_au_sec(seek);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!seek) {
|
||||||
var o = ebi(oid);
|
var o = ebi(oid);
|
||||||
o.setAttribute('id', 'thx_js');
|
o.setAttribute('id', 'thx_js');
|
||||||
if (window.history && history.replaceState) {
|
if (window.history && history.replaceState) {
|
||||||
@@ -513,6 +517,7 @@ function play(tid, call_depth) {
|
|||||||
document.location.hash = oid;
|
document.location.hash = oid;
|
||||||
}
|
}
|
||||||
o.setAttribute('id', oid);
|
o.setAttribute('id', oid);
|
||||||
|
}
|
||||||
|
|
||||||
pbar.drawbuf();
|
pbar.drawbuf();
|
||||||
return true;
|
return true;
|
||||||
@@ -576,7 +581,7 @@ function unblocked() {
|
|||||||
|
|
||||||
|
|
||||||
// show ui to manually start playback of a linked song
|
// show ui to manually start playback of a linked song
|
||||||
function autoplay_blocked() {
|
function autoplay_blocked(seek) {
|
||||||
show_modal(
|
show_modal(
|
||||||
'<div id="blk_play"><a href="#" id="blk_go"></a></div>' +
|
'<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>');
|
'<div id="blk_abrt"><a href="#" id="blk_na">Cancel<br />(show file list)</a></div>');
|
||||||
@@ -592,6 +597,8 @@ function autoplay_blocked() {
|
|||||||
if (e) e.preventDefault();
|
if (e) e.preventDefault();
|
||||||
unblocked();
|
unblocked();
|
||||||
mp.au.play();
|
mp.au.play();
|
||||||
|
if (seek)
|
||||||
|
seek_au_sec(seek);
|
||||||
};
|
};
|
||||||
na.onclick = unblocked;
|
na.onclick = unblocked;
|
||||||
}
|
}
|
||||||
@@ -600,8 +607,20 @@ function autoplay_blocked() {
|
|||||||
// autoplay linked track
|
// autoplay linked track
|
||||||
(function () {
|
(function () {
|
||||||
var v = location.hash;
|
var v = location.hash;
|
||||||
if (v && v.length == 12 && v.indexOf('#af-') === 0)
|
if (v && v.indexOf('#af-') === 0) {
|
||||||
play(v.slice(2));
|
var id = v.slice(2).split('&');
|
||||||
|
if (id[0].length != 10)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (id.length == 1)
|
||||||
|
return play(id[0]);
|
||||||
|
|
||||||
|
var m = /^[Tt=0]*([0-9]+[Mm:])?0*([0-9]+)[Ss]?$/.exec(id[1]);
|
||||||
|
if (!m)
|
||||||
|
return play(id[0]);
|
||||||
|
|
||||||
|
return play(id[0], parseInt(m[1] || 0) * 60 + parseInt(m[2] || 0));
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
@@ -1549,8 +1568,11 @@ function addcrc() {
|
|||||||
ebi('unsearch') ? 'div>a:last-child' : 'a'));
|
ebi('unsearch') ? 'div>a:last-child' : 'a'));
|
||||||
|
|
||||||
for (var a = 0, aa = links.length; a < aa; a++)
|
for (var a = 0, aa = links.length; a < aa; a++)
|
||||||
if (!links[a].getAttribute('id'))
|
if (!links[a].getAttribute('id')) {
|
||||||
links[a].setAttribute('id', 'f-' + crc32(links[a].textContent || links[a].innerText));
|
var crc = crc32(links[a].textContent || links[a].innerText);
|
||||||
|
crc = ('00000000' + crc).slice(-8);
|
||||||
|
links[a].setAttribute('id', 'f-' + crc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1577,6 +1599,24 @@ function addcrc() {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var light = bcfg_get('lightmode', false);
|
||||||
|
|
||||||
|
function freshen() {
|
||||||
|
document.documentElement.setAttribute("class", light ? "light" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
ebi('lightmode').onclick = function (e) {
|
||||||
|
ev(e);
|
||||||
|
light = !light;
|
||||||
|
bcfg_set('lightmode', light);
|
||||||
|
freshen();
|
||||||
|
};
|
||||||
|
|
||||||
|
freshen();
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
var arcfmt = (function () {
|
var arcfmt = (function () {
|
||||||
if (!ebi('arc_fmt'))
|
if (!ebi('arc_fmt'))
|
||||||
return { "render": function () { } };
|
return { "render": function () { } };
|
||||||
|
|||||||
@@ -138,10 +138,10 @@ var md_opt = {
|
|||||||
document.documentElement.setAttribute("class", dark ? "dark" : "");
|
document.documentElement.setAttribute("class", dark ? "dark" : "");
|
||||||
btn.innerHTML = "go " + (dark ? "light" : "dark");
|
btn.innerHTML = "go " + (dark ? "light" : "dark");
|
||||||
if (window.localStorage)
|
if (window.localStorage)
|
||||||
localStorage.setItem('darkmode', dark ? 1 : 0);
|
localStorage.setItem('lightmode', dark ? 0 : 1);
|
||||||
};
|
};
|
||||||
btn.onclick = toggle;
|
btn.onclick = toggle;
|
||||||
if (window.localStorage && localStorage.getItem('darkmode') == 1)
|
if (window.localStorage && localStorage.getItem('lightmode') != 1)
|
||||||
toggle();
|
toggle();
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ var md_opt = {
|
|||||||
|
|
||||||
var lightswitch = (function () {
|
var lightswitch = (function () {
|
||||||
var fun = function () {
|
var fun = function () {
|
||||||
var dark = !!!document.documentElement.getAttribute("class");
|
var dark = !document.documentElement.getAttribute("class");
|
||||||
document.documentElement.setAttribute("class", dark ? "dark" : "");
|
document.documentElement.setAttribute("class", dark ? "dark" : "");
|
||||||
if (window.localStorage)
|
if (window.localStorage)
|
||||||
localStorage.setItem('darkmode', dark ? 1 : 0);
|
localStorage.setItem('lightmode', dark ? 0 : 1);
|
||||||
};
|
};
|
||||||
if (window.localStorage && localStorage.getItem('darkmode') == 1)
|
if (window.localStorage && localStorage.getItem('lightmode') != 1)
|
||||||
fun();
|
fun();
|
||||||
|
|
||||||
return fun;
|
return fun;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
if (window.localStorage && localStorage.getItem('darkmode') == 1)
|
if (window.localStorage && localStorage.getItem('lightmode') != 1)
|
||||||
document.documentElement.setAttribute("class", "dark");
|
document.documentElement.setAttribute("class", "dark");
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -47,6 +47,11 @@
|
|||||||
margin: -1.5em 0;
|
margin: -1.5em 0;
|
||||||
padding: .8em 0;
|
padding: .8em 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-width: 12em;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
#u2conf #u2btn_cw {
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
#u2notbtn {
|
#u2notbtn {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -72,6 +77,7 @@
|
|||||||
}
|
}
|
||||||
#u2tab td:nth-child(2) {
|
#u2tab td:nth-child(2) {
|
||||||
width: 5em;
|
width: 5em;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
#u2tab td:nth-child(3) {
|
#u2tab td:nth-child(3) {
|
||||||
width: 40%;
|
width: 40%;
|
||||||
@@ -83,6 +89,42 @@
|
|||||||
#u2tab tr+tr:hover td {
|
#u2tab tr+tr:hover td {
|
||||||
background: #222;
|
background: #222;
|
||||||
}
|
}
|
||||||
|
#u2cards {
|
||||||
|
padding: 1em 0 .3em 0;
|
||||||
|
margin: 1.5em auto -2.5em auto;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
#u2cards.w {
|
||||||
|
width: 45em;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
#u2cards a {
|
||||||
|
padding: .2em 1em;
|
||||||
|
border: 1px solid #777;
|
||||||
|
border-width: 0 0 1px 0;
|
||||||
|
background: linear-gradient(to bottom, #333, #222);
|
||||||
|
}
|
||||||
|
#u2cards a:first-child {
|
||||||
|
border-radius: .4em 0 0 0;
|
||||||
|
}
|
||||||
|
#u2cards a:last-child {
|
||||||
|
border-radius: 0 .4em 0 0;
|
||||||
|
}
|
||||||
|
#u2cards a.act {
|
||||||
|
padding-bottom: .5em;
|
||||||
|
border-width: 1px 1px .1em 1px;
|
||||||
|
border-radius: .3em .3em 0 0;
|
||||||
|
margin-left: -1px;
|
||||||
|
background: linear-gradient(to bottom, #464, #333 80%);
|
||||||
|
box-shadow: 0 -.17em .67em #280;
|
||||||
|
border-color: #7c5 #583 #333 #583;
|
||||||
|
position: relative;
|
||||||
|
color: #fd7;
|
||||||
|
}
|
||||||
|
#u2cards span {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
#u2conf {
|
#u2conf {
|
||||||
margin: 1em auto;
|
margin: 1em auto;
|
||||||
width: 30em;
|
width: 30em;
|
||||||
@@ -99,12 +141,16 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
#u2conf .txtbox {
|
#u2conf .txtbox {
|
||||||
width: 4em;
|
width: 3em;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: #444;
|
background: #444;
|
||||||
border: 1px solid #777;
|
border: 1px solid #777;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
padding: .15em 0;
|
padding: .15em 0;
|
||||||
|
height: 1.05em;
|
||||||
|
}
|
||||||
|
#u2conf .txtbox.err {
|
||||||
|
background: #922;
|
||||||
}
|
}
|
||||||
#u2conf a {
|
#u2conf a {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
@@ -113,13 +159,12 @@
|
|||||||
border-radius: .1em;
|
border-radius: .1em;
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
padding: .1em 0;
|
padding: .1em 0;
|
||||||
margin: 0 -.25em;
|
margin: 0 -1px;
|
||||||
width: 1.5em;
|
width: 1.5em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
line-height: 1em;
|
bottom: -0.08em;
|
||||||
bottom: -.08em;
|
|
||||||
}
|
}
|
||||||
#u2conf input+a {
|
#u2conf input+a {
|
||||||
background: #d80;
|
background: #d80;
|
||||||
@@ -170,12 +215,13 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: 0 -2em;
|
margin: 0 -2em;
|
||||||
height: 0;
|
|
||||||
padding: 0 1em;
|
padding: 0 1em;
|
||||||
|
height: 0;
|
||||||
opacity: .1;
|
opacity: .1;
|
||||||
transition: all 0.14s ease-in-out;
|
transition: all 0.14s ease-in-out;
|
||||||
border-radius: .4em;
|
|
||||||
box-shadow: 0 .2em .5em #222;
|
box-shadow: 0 .2em .5em #222;
|
||||||
|
border-radius: .4em;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
#u2cdesc.show {
|
#u2cdesc.show {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
@@ -193,24 +239,6 @@
|
|||||||
.prog {
|
.prog {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
.prog>div {
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
height: 1.1em;
|
|
||||||
margin-bottom: -.15em;
|
|
||||||
box-shadow: -1px -1px 0 inset rgba(255,255,255,0.1);
|
|
||||||
}
|
|
||||||
.prog>div>div {
|
|
||||||
width: 0%;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: #0a0;
|
|
||||||
}
|
|
||||||
#u2tab a>span {
|
#u2tab a>span {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
@@ -221,3 +249,38 @@
|
|||||||
float: right;
|
float: right;
|
||||||
margin-bottom: -.3em;
|
margin-bottom: -.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
html.light #u2btn {
|
||||||
|
box-shadow: .4em .4em 0 #ccc;
|
||||||
|
}
|
||||||
|
html.light #u2cards span {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
html.light #u2cards a {
|
||||||
|
background: linear-gradient(to bottom, #eee, #fff);
|
||||||
|
}
|
||||||
|
html.light #u2cards a.act {
|
||||||
|
color: #037;
|
||||||
|
background: inherit;
|
||||||
|
box-shadow: 0 -.17em .67em #0ad;
|
||||||
|
border-color: #09c #05a #eee #05a;
|
||||||
|
}
|
||||||
|
html.light #u2conf .txtbox {
|
||||||
|
background: #fff;
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
html.light #u2conf .txtbox.err {
|
||||||
|
background: #f96;
|
||||||
|
color: #300;
|
||||||
|
}
|
||||||
|
html.light #u2cdesc {
|
||||||
|
background: #fff;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
html.light #op_up2k.srch #u2btn {
|
||||||
|
border-color: #a80;
|
||||||
|
}
|
||||||
@@ -59,9 +59,9 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="#" id="nthread_sub">–</a>
|
<a href="#" id="nthread_sub">–</a><input
|
||||||
<input class="txtbox" id="nthread" value="2" />
|
class="txtbox" id="nthread" value="2"/><a
|
||||||
<a href="#" id="nthread_add">+</a>
|
href="#" id="nthread_add">+</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@@ -79,12 +79,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="u2cards">
|
||||||
|
<a href="#" act="ok">ok <span>0</span></a><a
|
||||||
|
href="#" act="ng">ng <span>0</span></a><a
|
||||||
|
href="#" act="done">done <span>0</span></a><a
|
||||||
|
href="#" act="bz" class="act">busy <span>0</span></a><a
|
||||||
|
href="#" act="q">que <span>0</span></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<table id="u2tab">
|
<table id="u2tab">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td>filename</td>
|
<td>filename</td>
|
||||||
<td>status</td>
|
<td>status</td>
|
||||||
<td>progress<a href="#" id="u2cleanup">cleanup</a></td>
|
<td>progress<a href="#" id="u2cleanup">cleanup</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p id="u2foot"></p>
|
<p id="u2foot"></p>
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ if (!window['console'])
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var clickev = window.Touch ? 'touchstart' : 'click';
|
var clickev = window.Touch ? 'touchstart' : 'click',
|
||||||
|
ANDROID = /(android)/i.test(navigator.userAgent);
|
||||||
|
|
||||||
|
|
||||||
// error handler for mobile devices
|
// error handler for mobile devices
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ CKSUM = None
|
|||||||
STAMP = None
|
STAMP = None
|
||||||
|
|
||||||
PY2 = sys.version_info[0] == 2
|
PY2 = sys.version_info[0] == 2
|
||||||
WINDOWS = sys.platform == "win32"
|
WINDOWS = sys.platform in ["win32", "msys"]
|
||||||
sys.dont_write_bytecode = True
|
sys.dont_write_bytecode = True
|
||||||
me = os.path.abspath(os.path.realpath(__file__))
|
me = os.path.abspath(os.path.realpath(__file__))
|
||||||
cpp = None
|
cpp = None
|
||||||
|
|||||||
Reference in New Issue
Block a user