Compare commits

...

43 Commits

Author SHA1 Message Date
ed
04f1b2cf3a v0.10.13 2021-04-21 01:19:22 +02:00
ed
c06d928bb5 sorry android 2021-04-21 01:10:18 +02:00
ed
ab09927e7b v0.10.12 2021-04-19 21:58:49 +02:00
ed
779437db67 up2k: more runahead 2021-04-19 21:58:30 +02:00
ed
28cbdb652e v0.10.11 2021-04-19 21:43:08 +02:00
ed
2b2415a7d8 up2k: gotta go faster 2021-04-19 21:29:43 +02:00
ed
746a8208aa v0.10.10 2021-04-19 17:17:07 +02:00
ed
a2a041a98a optimize 2021-04-19 16:54:38 +02:00
ed
10b436e449 browser: add media fragment uris 2021-04-19 16:41:06 +02:00
ed
4d62b34786 browser: add light mode 2021-04-19 15:40:32 +02:00
ed
0546210687 fix up2k progressbars 2021-04-19 13:18:29 +02:00
ed
f8c11faada don't start 2t stuff if there's no backend avail 2021-04-19 13:17:34 +02:00
ed
16d6e9be1f tweaks 2021-04-17 09:24:25 +02:00
ed
aff8185f2e v0.10.9 2021-04-17 01:29:27 +02:00
ed
217d15fe81 up2k: cheap progress bars 2021-04-17 00:57:35 +02:00
ed
171e93c201 up2k: show realtime speeds 2021-04-17 00:01:03 +02:00
ed
acc1d2e9e3 up2k: show some context in the busy-tab 2021-04-16 23:49:57 +02:00
ed
49c2f37154 up2k: replace progressbars with text 2021-04-16 21:23:53 +02:00
ed
69e54497aa yes good 2021-04-14 16:03:15 +02:00
ed
9aa1885669 hide search tab when d2d 2021-04-14 15:23:25 +02:00
ed
4418508513 dodge cpython bug 2021-04-14 14:37:44 +02:00
ed
e897df3b34 v0.10.8 2021-04-11 21:26:39 +02:00
ed
8cd97ab0e7 much better 2021-04-11 21:07:41 +02:00
ed
bf4949353d support url-pwd on mounts page 2021-04-11 20:43:35 +02:00
ed
98a944f7cc no bopping 2021-04-11 20:23:38 +02:00
ed
7c10f81c92 stop eating browser hotkeys 2021-04-11 20:01:03 +02:00
ed
126ecc55c3 listen to the linter 2021-04-11 19:51:51 +02:00
ed
1034a51bd2 support ~ paths 2021-04-11 17:36:38 +02:00
ed
a2657887cc vscode: get no-dbg args from launch.json 2021-04-11 17:22:42 +02:00
ed
c14b17bfaf whoops 2021-04-10 20:22:33 +02:00
ed
59ebc795e7 tree scroll snapping 2021-04-10 19:30:30 +02:00
ed
8e128d917e sfx: support non-bz2 py 2021-04-10 18:30:58 +02:00
ed
ea762b05e0 guess they stole it from win10, sausage 2021-04-10 18:16:57 +02:00
ed
db374b19f1 mention the new cflags in -h 2021-04-07 21:13:45 +02:00
ed
ab3839ef36 w/a argparser bug fixed 2018-06-08 2021-04-07 20:31:29 +02:00
ed
9886c442f2 add missing uridecode 2021-04-03 23:58:51 +02:00
ed
c8d1926d52 h 2021-04-03 08:26:42 +02:00
ed
a6bd699e52 safari funny 2021-04-03 08:08:43 +02:00
ed
12143f2702 http/1.0, minimal dir listing, pw in url 2021-04-03 07:56:35 +02:00
ed
480705dee9 more todo 2021-04-03 04:41:10 +02:00
ed
781d5094f4 update todo 2021-04-03 04:13:51 +02:00
ed
5615cb94cd adj browser support table 2021-04-03 02:58:50 +02:00
ed
302302a2ac fix zip touch events on iOS 2021-04-03 02:52:19 +02:00
23 changed files with 1150 additions and 418 deletions

2
.vscode/launch.json vendored
View File

@@ -14,6 +14,8 @@
"-emp",
"-e2dsa",
"-e2ts",
"-mtp",
".bpm=f,bin/mtag/audio-bpm.py",
"-a",
"ed:wark",
"-v",

35
.vscode/launch.py vendored Normal file
View File

@@ -0,0 +1,35 @@
# takes arguments from launch.json
# is used by no_dbg in tasks.json
# launches 10x faster than mspython debugpy
# and is stoppable with ^C
import os
import sys
import shlex
sys.path.insert(0, os.getcwd())
import jstyleson
from copyparty.__main__ import main as copyparty
with open(".vscode/launch.json", "r") as f:
tj = f.read()
oj = jstyleson.loads(tj)
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]
try:
copyparty(["a"] + argv)
except SystemExit as ex:
if ex.code:
raise
print("\n\033[32mokke\033[0m")
sys.exit(1)

4
.vscode/tasks.json vendored
View File

@@ -9,9 +9,7 @@
{
"label": "no_dbg",
"type": "shell",
"command": "${config:python.pythonPath} -m copyparty -ed -emp -e2dsa -e2ts -a ed:wark -v srv::r:aed:cnodupe -v dist:dist:r ;exit 1"
// -v ~/Music/mt:mt:r:cmtp=.bpm=~/dev/copyparty/bin/mtag/audio-bpm.py:cmtp=key=~/dev/copyparty/bin/mtag/audio-key.py:ce2tsr
// -v ~/Music/mt:mt:r:cmtp=.bpm=~/dev/copyparty/bin/mtag/audio-bpm.py:ce2tsr
"command": "${config:python.pythonPath} .vscode/launch.py"
}
]
}

View File

@@ -111,6 +111,8 @@ the browser has the following hotkeys
* `I/K` prev/next 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
@@ -199,26 +201,38 @@ copyparty can invoke external programs to collect additional metadata for files
# browser support
| feature | ie6 | ie9 | ie10 | ie11 | ff 52+ | chr 49+ |
| --------------- | --- | --- | ---- | ---- | ------ | ------- |
| browse files | yep | yep | yep | yep | yep | yep |
| basic uploader | yep | yep | yep | yep | yep | yep |
| make directory | yep | yep | yep | yep | yep | yep |
| send message | yep | yep | yep | yep | yep | yep |
| set sort order | - | yep | yep | yep | yep | yep |
| zip selection | - | yep | yep | yep | yep | yep |
| directory tree | - | - | `*1` | yep | yep | yep |
| up2k | - | - | yep | yep | yep | yep |
| icons work | - | - | yep | yep | yep | yep |
| markdown editor | - | - | yep | yep | yep | yep |
| markdown viewer | - | - | yep | yep | yep | yep |
| play mp3/mp4 | - | yep | yep | yep | yep | yep |
| play ogg/opus | - | - | - | - | yep | yep |
`ie` = internet-explorer, `ff` = firefox, `c` = chrome, `iOS` = iPhone/iPad, `Andr` = Android
* internet explorer 6 to 8 (and netscape 4.0) behave the same
| feature | ie6 | ie9 | ie10 | ie11 | ff 52 | c 49 | iOS | Andr |
| --------------- | --- | --- | ---- | ---- | ----- | ---- | --- | ---- |
| browse files | yep | yep | yep | yep | yep | yep | yep | yep |
| basic uploader | yep | yep | yep | yep | yep | yep | yep | yep |
| make directory | yep | yep | yep | yep | yep | yep | yep | yep |
| send message | yep | yep | yep | yep | yep | yep | yep | yep |
| set sort order | - | yep | yep | yep | yep | yep | yep | yep |
| zip selection | - | yep | yep | yep | yep | yep | yep | yep |
| directory tree | - | - | `*1` | yep | yep | yep | yep | yep |
| up2k | - | - | yep | yep | yep | yep | yep | yep |
| icons work | - | - | yep | yep | yep | yep | yep | yep |
| markdown editor | - | - | yep | yep | yep | yep | yep | yep |
| markdown viewer | - | - | yep | yep | yep | yep | yep | yep |
| play mp3/m4a | - | yep | yep | yep | yep | yep | yep | yep |
| play ogg/opus | - | - | - | - | yep | yep | `*2` | yep |
* internet explorer 6 to 8 behave the same
* firefox 52 and chrome 49 are the last winxp versions
* `*1` only public folders (login session is dropped) and no history / back-button
* `*2` using a wasm decoder which can sometimes get stuck and consumes a bit more power
quick summary of more eccentric web-browsers trying to view a directory index:
* safari (14.0.3/macos) is chrome with janky wasm, so playing opus can deadlock the javascript engine
* safari (14.0.1/iOS) same as macos, except it recovers from the deadlocks if you poke it a bit
* links (2.21/macports) can browse, login, upload/mkdir/msg
* lynx (2.8.9/macports) can browse, login, upload/mkdir/msg
* w3m (0.5.3/macports) can browse, login, upload at 100kB/s, mkdir/msg
* netsurf (3.10/arch) is basically ie6 with much better css (javascript has almost no effect)
* netscape 4.0 and 4.5 can browse (text is yellow on white), upload with `?b=u`
* SerenityOS (22d13d8) hits a page fault, works with `?b=u`, file input not-impl, url params are multiplying
# client examples
@@ -327,14 +341,20 @@ in the `scripts` folder:
roughly sorted by priority
* separate sqlite table per tag
* audio fingerprinting
* readme.md as epilogue
* reduce up2k roundtrips
* start from a chunk index and just go
* terminate client on bad data
* `os.copy_file_range` for up2k cloning
* up2k partials ui
* support pillow-simd
* cache sha512 chunks on client
* comment field
* ~~look into android thumbnail cache file format~~ bad idea
* figure out the deal with pixel3a not being connectable as hotspot
* pixel3a having unpredictable 3sec latency in general :||||
discarded ideas
* up2k partials ui
* cache sha512 chunks on client
* comment field
* look into android thumbnail cache file format

View File

@@ -12,7 +12,6 @@ import re
import os
import sys
import time
import signal
import shutil
import filecmp
import locale
@@ -56,6 +55,12 @@ class RiceFormatter(argparse.HelpFormatter):
return "".join(indent + line + "\n" for line in text.splitlines())
class Dodge11874(RiceFormatter):
def __init__(self, *args, **kwargs):
kwargs["width"] = 9003
super(Dodge11874, self).__init__(*args, **kwargs)
def warn(msg):
print("\033[1mwarning:\033[0;33m {}\033[0m\n".format(msg))
@@ -167,7 +172,7 @@ def configure_ssl_ciphers(al):
sys.exit(0)
def sighandler(signal=None, frame=None):
def sighandler(sig=None, frame=None):
msg = [""] * 5
for th in threading.enumerate():
msg.append(str(th))
@@ -177,37 +182,9 @@ def sighandler(signal=None, frame=None):
print("\n".join(msg))
def main(argv=None):
time.strptime("19970815", "%Y%m%d") # python#7980
if WINDOWS:
os.system("rem") # enables colors
if argv is None:
argv = sys.argv
desc = py_desc().replace("[", "\033[1;30m[")
f = '\033[36mcopyparty v{} "\033[35m{}\033[36m" ({})\n{}\033[0m\n'
print(f.format(S_VERSION, CODENAME, S_BUILD_DT, desc))
ensure_locale()
if HAVE_SSL:
ensure_cert()
deprecated = [["-e2s", "-e2ds"]]
for dk, nk in deprecated:
try:
idx = argv.index(dk)
except:
continue
msg = "\033[1;31mWARNING:\033[0;1m\n {} \033[0;33mwas replaced with\033[0;1m {} \033[0;33mand will be removed\n\033[0m"
print(msg.format(dk, nk))
argv[idx] = nk
time.sleep(2)
def run_argparse(argv, formatter):
ap = argparse.ArgumentParser(
formatter_class=RiceFormatter,
formatter_class=formatter,
prog="copyparty",
description="http file sharing hub v{} ({})".format(S_VERSION, S_BUILD_DT),
epilog=dedent(
@@ -219,6 +196,9 @@ def main(argv=None):
list of cflags:
"cnodupe" rejects existing files (instead of symlinking them)
"ce2d" sets -e2d (all -e2* args can be set using ce2* cflags)
"cd2t" disables metadata collection, overrides -e2t*
"cd2d" disables all database stuff, overrides -e2*
example:\033[35m
-a ed:hunter2 -v .::r:aed -v ../inc:dump:w:aed:cnodupe \033[36m
@@ -293,9 +273,44 @@ def main(argv=None):
ap2.add_argument("--ssl-dbg", action="store_true", help="dump some tls info")
ap2.add_argument("--ssl-log", metavar="PATH", help="log master secrets")
al = ap.parse_args(args=argv[1:])
return ap.parse_args(args=argv[1:])
# fmt: on
def main(argv=None):
time.strptime("19970815", "%Y%m%d") # python#7980
if WINDOWS:
os.system("rem") # enables colors
if argv is None:
argv = sys.argv
desc = py_desc().replace("[", "\033[1;30m[")
f = '\033[36mcopyparty v{} "\033[35m{}\033[36m" ({})\n{}\033[0m\n'
print(f.format(S_VERSION, CODENAME, S_BUILD_DT, desc))
ensure_locale()
if HAVE_SSL:
ensure_cert()
deprecated = [["-e2s", "-e2ds"]]
for dk, nk in deprecated:
try:
idx = argv.index(dk)
except:
continue
msg = "\033[1;31mWARNING:\033[0;1m\n {} \033[0;33mwas replaced with\033[0;1m {} \033[0;33mand will be removed\n\033[0m"
print(msg.format(dk, nk))
argv[idx] = nk
time.sleep(2)
try:
al = run_argparse(argv, RiceFormatter)
except AssertionError:
al = run_argparse(argv, Dodge11874)
# propagate implications
for k1, k2 in IMPLICATIONS:
if getattr(al, k1):

View File

@@ -1,8 +1,8 @@
# coding: utf-8
VERSION = (0, 10, 7)
VERSION = (0, 10, 13)
CODENAME = "zip it"
BUILD_DT = (2021, 4, 3)
BUILD_DT = (2021, 4, 20)
S_VERSION = ".".join(map(str, VERSION))
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)

View File

@@ -111,7 +111,27 @@ class VFS(object):
if rem:
rp += "/" + rem
return fsdec(os.path.realpath(fsenc(rp)))
try:
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):
"""return user-readable [fsdir,real,virt] items at vpath"""
@@ -233,12 +253,6 @@ class AuthSrv(object):
def log(self, msg, c=0):
self.log_func("auth", msg, c)
def invert(self, orig):
if PY2:
return {v: k for k, v in orig.iteritems()}
else:
return {v: k for k, v in orig.items()}
def laggy_iter(self, iterable):
"""returns [value,isFinalValue]"""
it = iter(iterable)
@@ -495,7 +509,7 @@ class AuthSrv(object):
with self.mutex:
self.vfs = vfs
self.user = user
self.iuser = self.invert(user)
self.iuser = {v: k for k, v in user.items()}
# import pprint
# pprint.pprint({"usr": user, "rd": mread, "wr": mwrite, "mnt": mount})

View File

@@ -74,7 +74,7 @@ class HttpCli(object):
headerlines.pop(0)
try:
self.mode, self.req, _ = headerlines[0].split(" ")
self.mode, self.req, self.http_ver = headerlines[0].split(" ")
except:
raise Pebkac(400, "bad headers:\n" + "\n".join(headerlines))
@@ -93,30 +93,13 @@ class HttpCli(object):
self.headers[k.lower()] = v.strip()
v = self.headers.get("connection", "").lower()
self.keepalive = not v.startswith("close")
self.keepalive = not v.startswith("close") and self.http_ver != "HTTP/1.0"
v = self.headers.get("x-forwarded-for", None)
if v is not None and self.conn.addr[0] in ["127.0.0.1", "::1"]:
self.ip = v.split(",")[0]
self.log_src = self.conn.set_rproxy(self.ip)
self.uname = "*"
if "cookie" in self.headers:
cookies = self.headers["cookie"].split(";")
for k, v in [x.split("=", 1) for x in cookies]:
if k.strip() != "cppwd":
continue
v = unescape_cookie(v)
if v in self.auth.iuser:
self.uname = self.auth.iuser[v]
break
if self.uname:
self.rvol = self.auth.vfs.user_tree(self.uname, readable=True)
self.wvol = self.auth.vfs.user_tree(self.uname, writable=True)
# split req into vpath + uparam
uparam = {}
if "?" not in self.req:
@@ -140,6 +123,22 @@ class HttpCli(object):
self.uparam = uparam
self.vpath = unquotep(vpath)
pwd = None
if "cookie" in self.headers:
cookies = self.headers["cookie"].split(";")
for k, v in [x.split("=", 1) for x in cookies]:
if k.strip() != "cppwd":
continue
pwd = unescape_cookie(v)
break
pwd = uparam.get("pw", pwd)
self.uname = self.auth.iuser.get(pwd, "*")
if self.uname:
self.rvol = self.auth.vfs.user_tree(self.uname, readable=True)
self.wvol = self.auth.vfs.user_tree(self.uname, writable=True)
ua = self.headers.get("user-agent", "")
if ua.startswith("rclone/"):
uparam["raw"] = False
@@ -160,7 +159,9 @@ class HttpCli(object):
except Pebkac as ex:
try:
# self.log("pebkac at httpcli.run #2: " + repr(ex))
self.keepalive = self._check_nonfatal(ex)
if not self._check_nonfatal(ex):
self.keepalive = False
self.log("{}\033[0m, {}".format(str(ex), self.vpath), 3)
msg = "<pre>{}\r\nURL: {}\r\n".format(str(ex), self.vpath)
self.reply(msg.encode("utf-8", "replace"), status=ex.code)
@@ -169,7 +170,7 @@ class HttpCli(object):
return False
def send_headers(self, length, status=200, mime=None, headers={}):
response = ["HTTP/1.1 {} {}".format(status, HTTPCODE[status])]
response = ["{} {} {}".format(self.http_ver, status, HTTPCODE[status])]
if length is not None:
response.append("Content-Length: " + unicode(length))
@@ -213,6 +214,20 @@ class HttpCli(object):
self.log(body.rstrip())
self.reply(b"<pre>" + body.encode("utf-8") + b"\r\n", *list(args), **kwargs)
def urlq(self, add={}, rm=[]):
"""
generates url query based on uparam (b, pw, all others)
removing anything in rm, adding pairs in add
"""
kv = {k: v for k, v in self.uparam.items() if k not in rm}
kv.update(add)
if not kv:
return ""
r = ["{}={}".format(k, quotep(v)) if v else k for k, v in kv.items()]
return "?" + "&amp;".join(r)
def handle_get(self):
logmsg = "{:4} {}".format(self.mode, self.req)
@@ -1213,9 +1228,10 @@ class HttpCli(object):
return True
def tx_mounts(self):
suf = self.urlq(rm=["h"])
rvol = [x + "/" if x else x for x in self.rvol]
wvol = [x + "/" if x else x for x in self.wvol]
html = self.j2("splash", this=self, rvol=rvol, wvol=wvol)
html = self.j2("splash", this=self, rvol=rvol, wvol=wvol, url_suf=suf)
self.reply(html.encode("utf-8"))
return True
@@ -1345,6 +1361,8 @@ class HttpCli(object):
idx = self.conn.get_u2idx()
icur = idx.get_cur(vn.realpath)
url_suf = self.urlq()
dirs = []
files = []
for fn in vfs_ls:
@@ -1497,8 +1515,12 @@ class HttpCli(object):
dirs.extend(files)
tpl = "browser"
if "b" in self.uparam:
tpl = "browser2"
html = self.j2(
"browser",
tpl,
vdir=quotep(self.vpath),
vpnodes=vpnodes,
files=dirs,
@@ -1511,6 +1533,8 @@ class HttpCli(object):
have_up2k_idx=("e2d" in vn.flags),
have_tags_idx=("e2t" in vn.flags),
have_zip=(not self.args.no_zip),
have_b_u=(self.writable and self.uparam.get("b") == "u"),
url_suf=url_suf,
logues=logues,
title=html_escape(self.vpath),
srv_info=srv_info,

View File

@@ -52,7 +52,7 @@ class HttpSrv(object):
env.loader = jinja2.FileSystemLoader(os.path.join(E.mod, "web"))
self.j2 = {
x: env.get_template(x + ".html")
for x in ["splash", "browser", "msg", "md", "mde"]
for x in ["splash", "browser", "browser2", "msg", "md", "mde"]
}
cert_path = os.path.join(E.cfg, "cert.pem")

View File

@@ -101,17 +101,18 @@ class Up2k(object):
thr.daemon = True
thr.start()
thr = threading.Thread(target=self._tagger)
thr.daemon = True
thr.start()
thr = threading.Thread(target=self._hasher)
thr.daemon = True
thr.start()
thr = threading.Thread(target=self._run_all_mtp)
thr.daemon = True
thr.start()
if self.mtag:
thr = threading.Thread(target=self._tagger)
thr.daemon = True
thr.start()
thr = threading.Thread(target=self._run_all_mtp)
thr.daemon = True
thr.start()
def log(self, msg, c=0):
self.log_func("up2k", msg + "\033[K", c)
@@ -1068,6 +1069,8 @@ class Up2k(object):
with self.mutex:
job = self.registry[ptop].get(wark, None)
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")
if chash not in job["need"]:

View File

@@ -183,9 +183,9 @@ a, #files tbody div a:last-child {
text-shadow: 0 0 .3em #b80;
}
#files tbody tr.sel td {
background: #80b;
color: #fff;
border-color: #a3d;
background: #925;
border-color: #c37;
}
#blocked {
position: fixed;
@@ -243,7 +243,7 @@ a, #files tbody div a:last-child {
height: 100%;
background: #3c3c3c;
}
#wtoggle {
#wtico {
cursor: url(/.cpr/dd/1.png), pointer;
animation: cursor 500ms infinite;
}
@@ -273,24 +273,32 @@ a, #files tbody div a:last-child {
padding: .2em 0 0 .07em;
color: #fff;
}
#wtoggle>span {
#wzip {
display: none;
margin-right: .3em;
padding-right: .3em;
border-right: .1em solid #555;
}
#wtoggle,
#wtoggle * {
line-height: 1em;
}
#wtoggle.sel {
width: 4.27em;
width: 6.4em;
}
#wtoggle.sel>span {
#wtoggle.sel #wzip {
display: inline-block;
line-height: 0;
}
#wtoggle.sel>span a {
#wtoggle.sel #wzip a {
font-size: .4em;
margin: -.3em 0;
padding: 0 .3em;
margin: -.3em .2em;
position: relative;
display: inline-block;
}
#wtoggle.sel>span #selzip {
#wtoggle.sel #wzip #selzip {
top: -.6em;
padding: .4em .3em;
}
#barpos,
#barbuf {
@@ -487,7 +495,7 @@ input[type="checkbox"]:checked+label {
}
#tree {
display: none;
position: fixed;
position: absolute;
left: 0;
bottom: 0;
top: 7em;
@@ -677,3 +685,173 @@ input[type="checkbox"]:checked+label {
font-family: monospace, monospace;
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;
}

View File

@@ -13,8 +13,8 @@
<body>
<div id="ops">
<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.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;foo bar&lt;/code&gt; = must contain both foo and bar,&lt;br /&gt;&lt;code&gt;foo -bar&lt;/code&gt; = must contain foo but not bar,&lt;br /&gt;&lt;code&gt;^yana .opus$&lt;/code&gt; = must start with yana and have the opus extension">🔎</a>
{%- 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.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;foo bar&lt;/code&gt; = must contain both foo and bar,&lt;br /&gt;&lt;code&gt;foo -bar&lt;/code&gt; = must contain foo but not bar,&lt;br /&gt;&lt;code&gt;^yana .opus$&lt;/code&gt; = 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>
{%- 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>
@@ -39,14 +39,17 @@
{%- include 'upload.html' %}
<div id="op_cfg" class="opview opbox">
<h3>key notation</h3>
<div id="key_notation"></div>
<h3>switches</h3>
<div>
<a id="tooltips" class="tglbtn" href="#">tooltips</a>
<a id="lightmode" class="tglbtn" href="#">lightmode</a>
</div>
{%- if have_zip %}
<h3>folder download</h3>
<div id="arc_fmt"></div>
{%- endif %}
<h3>tooltips</h3>
<div><a id="tooltips" class="tglbtn" href="#">enable</a></div>
<h3>key notation</h3>
<div id="key_notation"></div>
</div>
<h1 id="path">
@@ -113,12 +116,12 @@
<div id="widget">
<div id="wtoggle">
<span>
<span id="wzip">
<a href="#" id="selall">sel.<br />all</a>
<a href="#" id="selinv">sel.<br />inv.</a>
<a href="#" id="selzip">zip</a>
</span>
</span><a
href="#" id="wtico"></a>
</div>
<div id="widgeti">
<div id="pctl"><a href="#" id="bprev"></a><a href="#" id="bplay"></a><a href="#" id="bnext"></a></div>

View File

@@ -75,7 +75,7 @@ makeSortable(ebi('files'), mp.read_order.bind(mp));
var widget = (function () {
var ret = {};
var widget = ebi('widget');
var wtoggle = ebi('wtoggle');
var wtico = ebi('wtico');
var touchmode = false;
var side_open = false;
var was_paused = true;
@@ -113,14 +113,7 @@ var widget = (function () {
return false;
};
if (window.Touch) {
var touch_handler = function (e) {
touchmode = true;
return ret.toggle(e);
};
wtoggle.addEventListener('touchstart', touch_handler, false);
}
wtoggle.onclick = click_handler;
wtico.onclick = click_handler;
return ret;
})();
@@ -321,10 +314,10 @@ function seek_au_sec(seek) {
mp.au.currentTime = seek;
// ogv.js breaks on .play() during playback
if (mp.au === mp.au_native)
// hack: ogv.js breaks on .play() during playback
mp.au.play();
};
}
function song_skip(n) {
@@ -336,7 +329,7 @@ function song_skip(n) {
play(mp.order.indexOf(tid) + n);
else
play(mp.order[0]);
};
}
// hook up the widget buttons
@@ -434,7 +427,7 @@ catch (ex) { }
// 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)
return alert('no audio found wait what');
@@ -456,7 +449,7 @@ function play(tid, call_depth) {
}
// ogv.js breaks on .play() unless directly user-triggered
var hack_attempt_play = true;
var attempt_play = true;
var url = mp.tracks[tid];
if (need_ogv && /\.(ogg|opus)$/i.test(url)) {
@@ -465,7 +458,7 @@ function play(tid, call_depth) {
}
else if (window['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('progress', pbar.drawpos, false);
widget.open();
@@ -477,7 +470,7 @@ function play(tid, call_depth) {
show_modal('<h1>loading ogv.js</h1><h2>thanks apple</h2>');
import_js('/.cpr/deps/ogv.js', function () {
play(tid, 1);
play(tid, seek, 1);
});
return;
@@ -505,21 +498,26 @@ function play(tid, call_depth) {
ebi(oid).parentElement.parentElement.className += ' play';
try {
if (hack_attempt_play)
if (attempt_play)
mp.au.play();
if (mp.au.paused)
autoplay_blocked();
autoplay_blocked(seek);
else if (seek) {
seek_au_sec(seek);
}
var o = ebi(oid);
o.setAttribute('id', 'thx_js');
if (window.history && history.replaceState) {
hist_replace(document.location.pathname + '#' + oid);
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;
}
o.setAttribute('id', oid);
}
else {
document.location.hash = oid;
}
o.setAttribute('id', oid);
pbar.drawbuf();
return true;
@@ -583,7 +581,7 @@ function unblocked() {
// show ui to manually start playback of a linked song
function autoplay_blocked() {
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>');
@@ -599,6 +597,8 @@ function autoplay_blocked() {
if (e) e.preventDefault();
unblocked();
mp.au.play();
if (seek)
seek_au_sec(seek);
};
na.onclick = unblocked;
}
@@ -607,8 +607,20 @@ function autoplay_blocked() {
// autoplay linked track
(function () {
var v = location.hash;
if (v && v.length == 12 && v.indexOf('#af-') === 0)
play(v.slice(2));
if (v && v.indexOf('#af-') === 0) {
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));
}
})();
@@ -625,13 +637,16 @@ function tree_neigh(n) {
break;
}
}
a += n;
if (a < 0)
a = links.length - 1;
if (a >= links.length)
a = 0;
if (act == -1)
return;
links[a].click();
act += n;
if (act < 0)
act = links.length - 1;
if (act >= links.length)
act = 0;
links[act].click();
}
@@ -652,6 +667,9 @@ document.onkeydown = function (e) {
if (!document.activeElement || document.activeElement != document.body && document.activeElement.nodeName.toLowerCase() != 'a')
return;
if (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey || e.isComposing)
return;
var k = (e.code + ''), pos = -1;
if (k.indexOf('Digit') === 0)
pos = parseInt(k.slice(-1)) * 0.1;
@@ -879,12 +897,16 @@ document.onkeydown = function (e) {
var treectl = (function () {
var treectl = {
"hidden": false
};
var dyn = bcfg_get('dyntree', true);
var treesz = icfg_get('treesz', 16);
},
entreed = false,
fixedpos = false,
prev_atop = null,
prev_winh = null,
dyn = bcfg_get('dyntree', true),
treesz = icfg_get('treesz', 16);
treesz = Math.min(Math.max(treesz, 4), 50);
console.log('treesz [' + treesz + ']');
var entreed = false;
function entree(e) {
ev(e);
@@ -916,13 +938,43 @@ var treectl = (function () {
if (!entreed || treectl.hidden)
return;
var top = ebi('wrap').getBoundingClientRect().top;
ebi('tree').style.top = Math.max(0, parseInt(top)) + 'px';
var tree = ebi('tree'),
wrap = ebi('wrap'),
atop = wrap.getBoundingClientRect().top,
winh = window.innerHeight;
if (atop === prev_atop && winh === prev_winh)
return;
prev_atop = atop;
prev_winh = winh;
if (fixedpos && atop >= 0) {
tree.style.position = 'absolute';
tree.style.bottom = '';
fixedpos = false;
}
else if (!fixedpos && atop < 0) {
tree.style.position = 'fixed';
tree.style.height = 'auto';
fixedpos = true;
}
if (fixedpos) {
tree.style.top = Math.max(0, parseInt(atop)) + 'px';
}
else {
var top = Math.max(0, parseInt(wrap.offsetTop)),
treeh = (winh - atop) - 4;
tree.style.top = top + 'px';
tree.style.height = treeh < 10 ? '' : treeh + 'px';
}
}
function periodic() {
onscroll();
setTimeout(periodic, document.visibilityState ? 200 : 5000);
setTimeout(periodic, document.visibilityState ? 100 : 5000);
}
periodic();
@@ -1516,8 +1568,11 @@ function addcrc() {
ebi('unsearch') ? 'div>a:last-child' : 'a'));
for (var a = 0, aa = links.length; a < aa; a++)
if (!links[a].getAttribute('id'))
links[a].setAttribute('id', 'f-' + crc32(links[a].textContent || links[a].innerText));
if (!links[a].getAttribute('id')) {
var crc = crc32(links[a].textContent || links[a].innerText);
crc = ('00000000' + crc).slice(-8);
links[a].setAttribute('id', 'f-' + crc);
}
}
@@ -1544,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 () {
if (!ebi('arc_fmt'))
return { "render": function () { } };

View File

@@ -0,0 +1,55 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{ title }}</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=0.8">
</head>
<body>
{%- if srv_info %}
<p><span>{{ srv_info }}</span></p>
{%- endif %}
{%- if have_b_u %}
<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="submit" value="start upload" />
</form>
<br />
{%- endif %}
{%- if logues[0] %}
<div>{{ logues[0] }}</div><br />
{%- endif %}
<table id="files">
<thead>
<tr>
<th name="lead"><span>c</span></th>
<th name="href"><span>File Name</span></th>
<th name="sz" sort="int"><span>Size</span></th>
<th name="ts"><span>Date</span></th>
</tr>
</thead>
<tbody>
<tr><td></td><td><a href="../{{ url_suf }}">parent folder</a></td><td>-</td><td>-</td></tr>
{%- for f in files %}
<tr><td>{{ f.lead }}</td><td><a href="{{ f.href }}{{ url_suf }}">{{ f.name|e }}</a></td><td>{{ f.sz }}</td><td>{{ f.dt }}</td></tr>
{%- endfor %}
</tbody>
</table>
{%- if logues[1] %}
<div>{{ logues[1] }}</div><br />
{%- endif %}
<h2><a href="{{ url_suf }}&amp;h">control-panel</a></h2>
</body>
</html>

View File

@@ -138,10 +138,10 @@ var md_opt = {
document.documentElement.setAttribute("class", dark ? "dark" : "");
btn.innerHTML = "go " + (dark ? "light" : "dark");
if (window.localStorage)
localStorage.setItem('darkmode', dark ? 1 : 0);
localStorage.setItem('lightmode', dark ? 0 : 1);
};
btn.onclick = toggle;
if (window.localStorage && localStorage.getItem('darkmode') == 1)
if (window.localStorage && localStorage.getItem('lightmode') != 1)
toggle();
})();

View File

@@ -31,12 +31,12 @@ var md_opt = {
var lightswitch = (function () {
var fun = function () {
var dark = !!!document.documentElement.getAttribute("class");
var dark = !document.documentElement.getAttribute("class");
document.documentElement.setAttribute("class", dark ? "dark" : "");
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();
return fun;

View File

@@ -16,20 +16,20 @@
<h1>you can browse these:</h1>
<ul>
{% for mp in rvol %}
<li><a href="/{{ mp }}">/{{ mp }}</a></li>
<li><a href="/{{ mp }}{{ url_suf }}">/{{ mp }}</a></li>
{% endfor %}
</ul>
<h1>you can upload to:</h1>
<ul>
{% for mp in wvol %}
<li><a href="/{{ mp }}">/{{ mp }}</a></li>
<li><a href="/{{ mp }}{{ url_suf }}">/{{ mp }}</a></li>
{% endfor %}
</ul>
<h1>login for more:</h1>
<ul>
<form method="post" enctype="multipart/form-data" action="/">
<form method="post" enctype="multipart/form-data" action="/{{ url_suf }}">
<input type="hidden" name="act" value="login" />
<input type="password" name="cppwd" />
<input type="submit" value="Login" />
@@ -38,7 +38,7 @@
</div>
<script>
if (window.localStorage && localStorage.getItem('darkmode') == 1)
if (window.localStorage && localStorage.getItem('lightmode') != 1)
document.documentElement.setAttribute("class", "dark");
</script>

View File

@@ -31,6 +31,7 @@ catch (ex) {
}
catch (ex) { }
}
treectl.onscroll();
function up2k_flagbus() {
@@ -131,9 +132,285 @@ function up2k_flagbus() {
return flag;
}
function U2pvis(act, btns) {
this.act = act;
this.ctr = { "ok": 0, "ng": 0, "bz": 0, "q": 0 };
this.tab = [];
this.head = 0;
this.tail = -1;
this.wsz = 3;
this.addfile = function (entry, sz) {
this.tab.push({
"hn": entry[0],
"ht": entry[1],
"hp": entry[2],
"in": 'q',
"nh": 0, //hashed
"nd": 0, //done
"cb": [], // bytes done in chunk
"bt": sz, // bytes total
"bd": 0, // bytes done
"bd0": 0 // upload start
});
this.ctr["q"]++;
this.drawcard("q");
if (this.act == "q") {
this.addrow(this.tab.length - 1);
}
if (this.act == "bz") {
this.bzw();
}
};
this.is_act = function (card) {
if (this.act == "done")
return card == "ok" || card == "ng";
return this.act == card;
}
this.seth = function (nfile, field, html) {
var fo = this.tab[nfile];
field = ['hn', 'ht', 'hp'][field];
if (fo[field] === html)
return;
fo[field] = html;
if (!this.is_act(fo.in))
return;
var obj = ebi('f{0}{1}'.format(nfile, field.slice(1)));
obj.innerHTML = html;
if (field == 'hp') {
obj.style.color = '';
obj.style.background = '';
}
};
this.setab = function (nfile, nblocks) {
var t = [];
for (var a = 0; a < nblocks; a++)
t.push(0);
this.tab[nfile].cb = t;
};
this.setat = function (nfile, blocktab) {
this.tab[nfile].cb = blocktab;
var bd = 0;
for (var a = 0; a < blocktab.length; a++)
bd += blocktab[a];
this.tab[nfile].bd = bd;
this.tab[nfile].bd0 = bd;
};
this.perc = function (bd, bd0, sz, t0) {
var td = new Date().getTime() - t0,
p = bd * 100.0 / sz,
nb = bd - bd0,
spd = nb / (td / 1000),
eta = (sz - bd) / spd;
return [p, s2ms(eta), spd / (1024 * 1024)];
};
this.hashed = function (fobj) {
var fo = this.tab[fobj.n];
var nb = fo.bt * (++fo.nh / fo.cb.length);
var p = this.perc(nb, 0, fobj.size, fobj.t1);
fo.hp = '{0}%, {1}, {2} MB/s'.format(
p[0].toFixed(2), p[1], p[2].toFixed(2)
);
if (!this.is_act(fo.in))
return;
var obj = ebi('f{0}p'.format(fobj.n));
obj.innerHTML = fo.hp;
obj.style.color = '#fff';
var o1 = p[0] - 2, o2 = p[0] - 0.1, o3 = p[0];
obj.style.background = 'linear-gradient(90deg, #025, #06a ' + o1 + '%, #09d ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)';
};
this.prog = function (fobj, nchunk, cbd) {
var fo = this.tab[fobj.n];
var delta = cbd - fo.cb[nchunk];
fo.cb[nchunk] = cbd;
fo.bd += delta;
var p = this.perc(fo.bd, fo.bd0, fo.bt, fobj.t3);
fo.hp = '{0}%, {1}, {2} MB/s'.format(
p[0].toFixed(2), p[1], p[2].toFixed(2)
);
if (!this.is_act(fo.in))
return;
var obj = ebi('f{0}p'.format(fobj.n));
obj.innerHTML = fo.hp;
obj.style.color = '#fff';
var o1 = p[0] - 2, o2 = p[0] - 0.1, o3 = p[0];
obj.style.background = 'linear-gradient(90deg, #050, #270 ' + o1 + '%, #4b0 ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)';
};
this.move = function (nfile, newcat) {
var fo = this.tab[nfile],
oldcat = fo.in,
bz_act = this.act == "bz";
if (oldcat == newcat) {
throw 42;
}
fo.in = newcat;
this.ctr[oldcat]--;
this.ctr[newcat]++;
this.drawcard(oldcat);
this.drawcard(newcat);
if (this.is_act(newcat)) {
this.tail++;
if (!ebi('f' + nfile))
this.addrow(nfile);
}
else if (this.is_act(oldcat)) {
this.head++;
if (!bz_act) {
var tr = ebi("f" + nfile);
tr.parentNode.removeChild(tr);
}
}
if (bz_act) {
this.bzw();
}
};
this.bzw_log = function (first, last) {
console.log("first %d head %d tail %d last %d", first, this.head, this.tail, last);
var trs = document.querySelectorAll('#u2tab>tbody>tr'), msg = [];
for (var a = 0; a < trs.length; a++)
msg.push(trs[a].getAttribute('id'));
console.log(msg.join(' '));
}
this.bzw = function () {
var first = document.querySelector('#u2tab>tbody>tr:first-child');
if (!first)
return;
var last = document.querySelector('#u2tab>tbody>tr:last-child');
first = parseInt(first.getAttribute('id').slice(1));
last = parseInt(last.getAttribute('id').slice(1));
//this.bzw_log(first, last);
while (this.head - first > this.wsz) {
var obj = ebi('f' + (first++));
obj.parentNode.removeChild(obj);
}
while (last - this.tail < this.wsz && last < this.tab.length - 2) {
var obj = ebi('f' + (++last));
if (!obj)
this.addrow(last);
}
//this.bzw_log(first, last);
//console.log('--');
};
this.drawcard = function (cat) {
var cards = document.querySelectorAll('#u2cards>a>span');
if (cat == "q") {
cards[4].innerHTML = this.ctr[cat];
return;
}
if (cat == "bz") {
cards[3].innerHTML = this.ctr[cat];
return;
}
cards[2].innerHTML = this.ctr["ok"] + this.ctr["ng"];
if (cat == "ng") {
cards[1].innerHTML = this.ctr[cat];
}
if (cat == "ok") {
cards[0].innerHTML = this.ctr[cat];
}
};
this.changecard = function (card) {
this.act = card;
var html = [];
this.head = -1;
this.tail = -1;
for (var a = 0; a < this.tab.length; a++) {
var rt = this.tab[a].in;
if (this.is_act(rt)) {
html.push(this.genrow(a, true));
this.tail = a;
if (this.head == -1)
this.head = a;
}
}
if (this.head == -1) {
this.head = this.tab.length;
this.tail = this.head - 1;
}
if (card == "bz") {
for (var a = this.head - 1; a >= this.head - this.wsz && a >= 0; a--) {
html.unshift(this.genrow(a, true).replace(/><td>/, "><td>a "));
}
for (var a = this.tail + 1; a <= this.tail + this.wsz && a < this.tab.length; a++) {
html.push(this.genrow(a, true).replace(/><td>/, "><td>b "));
}
}
ebi('u2tab').tBodies[0].innerHTML = html.join('\n');
};
this.genrow = function (nfile, as_html) {
var r = this.tab[nfile],
td1 = '<td id="f' + nfile,
td = '</td>' + td1,
ret = td1 + 'n">' + r.hn +
td + 't">' + r.ht +
td + 'p" class="prog">' + r.hp + '</td>';
if (as_html)
return '<tr id="f' + nfile + '">' + ret + '</tr>';
var obj = document.createElement('tr');
obj.setAttribute('id', 'f' + nfile);
obj.innerHTML = ret;
return obj;
};
this.addrow = function (nfile) {
var tr = this.genrow(nfile);
ebi('u2tab').tBodies[0].appendChild(tr);
};
var that = this;
btns = document.querySelectorAll(btns + '>a[act]');
for (var a = 0; a < btns.length; a++) {
btns[a].onclick = function (e) {
ev(e);
var newtab = this.getAttribute('act');
for (var b = 0; b < btns.length; b++) {
btns[b].className = (
btns[b].getAttribute('act') == newtab) ? 'act' : '';
}
that.changecard(newtab);
};
}
}
function up2k_init(have_crypto) {
//have_crypto = false;
var need_filereader_cache = undefined;
// show modal message
function showmodal(msg) {
@@ -215,10 +492,6 @@ function up2k_init(have_crypto) {
var flag_en = bcfg_get('flag_en', false);
var fsearch = bcfg_get('fsearch', false);
var col_hashing = '#00bbff';
var col_hashed = '#004466';
var col_uploading = '#ffcc44';
var col_uploaded = '#00bb00';
var fdom_ctr = 0;
var st = {
"files": [],
@@ -238,6 +511,8 @@ function up2k_init(have_crypto) {
}
};
var pvis = new U2pvis("bz", '#u2cards');
var bobslice = null;
if (window.File)
bobslice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
@@ -374,7 +649,7 @@ function up2k_init(have_crypto) {
for (var a = 0, aa = Math.min(20, bad_files.length); a < aa; a++)
msg += '-- ' + bad_files[a] + '\n';
if (files.length - bad_files.length <= 1 && /(android)/i.test(navigator.userAgent))
if (good_files.length - bad_files.length <= 1 && /(android)/i.test(navigator.userAgent))
msg += '\nFirefox-Android has a bug which prevents selecting multiple files. Try selecting one file at a time. For more info, see firefox bug 1456557';
alert(msg);
@@ -412,11 +687,12 @@ function up2k_init(have_crypto) {
if (skip)
continue;
var tr = document.createElement('tr');
tr.innerHTML = '<td id="f{0}n"></td><td id="f{0}t">hashing</td><td id="f{0}p" class="prog"></td>'.format(st.files.length);
tr.getElementsByTagName('td')[0].innerHTML = fsearch ? entry.name : linksplit(esc(entry.purl + entry.name)).join(' ');
ebi('u2tab').appendChild(tr);
pvis.addfile([
fsearch ? esc(entry.name) : linksplit(
esc(uricom_dec(entry.purl)[0] + entry.name)).join(' '),
'📐 hash',
''
], fobj.size);
st.files.push(entry);
st.todo.hash.push(entry);
}
@@ -437,7 +713,10 @@ function up2k_init(have_crypto) {
for (var a = 0; a < st.files.length; a++) {
var t = st.files[a];
if (t.done && t.name) {
var tr = ebi('f{0}p'.format(t.n)).parentNode;
var tr = ebi('f' + t.n);
if (!tr)
continue;
tr.parentNode.removeChild(tr);
t.name = undefined;
}
@@ -460,7 +739,8 @@ function up2k_init(have_crypto) {
function hashing_permitted() {
if (multitask) {
var ahead = st.bytes.hashed - st.bytes.uploaded;
return ahead < 1024 * 1024 * 128;
return ahead < 1024 * 1024 * 1024 * 4 &&
st.todo.handshake.length + st.busy.handshake.length < 16;
}
return handshakes_permitted() && 0 ==
st.todo.handshake.length +
@@ -626,31 +906,6 @@ function up2k_init(have_crypto) {
}
}
function test_filereader_speed(segm_err) {
var f = st.todo.hash[0].fobj,
sz = Math.min(2, f.size),
reader = new FileReader(),
t0, ctr = 0;
var segm_next = function () {
var t = new Date().getTime(),
td = t - t0;
if (++ctr > 2) {
need_filereader_cache = td > 50;
st.busy.hash.pop();
return;
}
t0 = t;
reader.onload = segm_next;
reader.onerror = segm_err;
reader.readAsArrayBuffer(
bobslice.call(f, 0, sz));
};
segm_next();
}
function ensure_rendered(func) {
var hidden = false;
var keys = ['hidden', 'msHidden', 'webkitHidden'];
@@ -665,122 +920,88 @@ function up2k_init(have_crypto) {
}
function exec_hash() {
if (need_filereader_cache === undefined) {
st.busy.hash.push(1);
return test_filereader_speed(segm_err);
}
var t = st.todo.hash.shift();
st.busy.hash.push(t);
st.bytes.hashed += t.size;
t.bytes_uploaded = 0;
t.t1 = new Date().getTime();
var nchunk = 0;
var chunksize = get_chunksize(t.size);
var nchunks = Math.ceil(t.size / chunksize);
var bpend = 0,
nchunk = 0,
chunksize = get_chunksize(t.size),
nchunks = Math.ceil(t.size / chunksize),
hashtab = {};
// android-chrome has 180ms latency on FileReader calls,
// detect this and do 32MB at a time
var cache_buf = undefined,
cache_ofs = 0,
subchunks = 2;
while (subchunks * chunksize <= 32 * 1024 * 1024)
subchunks++;
subchunks--;
if (!need_filereader_cache)
subchunks = 1;
var pb_html = '';
var pb_perc = 99.9 / nchunks;
for (var a = 0; a < nchunks; a++)
pb_html += '<div id="f{0}p{1}" style="width:{2}%"><div></div></div>'.format(
t.n, a, pb_perc);
ebi('f{0}p'.format(t.n)).innerHTML = pb_html;
var reader = new FileReader();
pvis.setab(t.n, nchunks);
pvis.move(t.n, 'bz');
var segm_next = function () {
if (cache_buf) {
return hash_calc();
}
reader.onload = segm_load;
reader.onerror = segm_err;
if (nchunk >= nchunks || (bpend > chunksize && bpend >= 32 * 1024 * 1024))
return false;
var reader = new FileReader(),
nch = nchunk++,
car = nch * chunksize,
cdr = car + chunksize;
var car = nchunk * chunksize;
var cdr = car + chunksize * subchunks;
if (cdr >= t.size)
cdr = t.size;
bpend += cdr - car;
reader.onload = function (e) {
hash_calc(nch, e.target.result);
};
reader.onerror = segm_err;
reader.readAsArrayBuffer(
bobslice.call(t.fobj, car, cdr));
prog(t.n, nchunk, col_hashing);
return true;
};
var segm_load = function (e) {
cache_buf = e.target.result;
cache_ofs = 0;
hash_calc();
};
var hash_calc = function (nch, buf) {
while (segm_next());
var hash_calc = function () {
var buf = cache_buf;
if (chunksize >= buf.byteLength)
cache_buf = undefined;
else {
var ofs = cache_ofs;
var ofs2 = ofs + Math.min(chunksize, cache_buf.byteLength - cache_ofs);
cache_ofs = ofs2;
buf = new Uint8Array(cache_buf).subarray(ofs, ofs2);
if (ofs2 >= cache_buf.byteLength)
cache_buf = undefined;
}
var hash_done = function (hashbuf) {
var hslice = new Uint8Array(hashbuf).subarray(0, 32);
var b64str = buf2b64(hslice).replace(/=$/, '');
hashtab[nch] = b64str;
t.hash.push(nch);
pvis.hashed(t);
var func = function () {
if (have_crypto)
crypto.subtle.digest('SHA-512', buf).then(hash_done);
else {
var hasher = new asmCrypto.Sha512();
hasher.process(new Uint8Array(buf));
hasher.finish();
hash_done(hasher.result);
bpend -= buf.byteLength;
if (t.hash.length < nchunks) {
return segm_next();
}
t.hash = [];
for (var a = 0; a < nchunks; a++) {
t.hash.push(hashtab[a]);
}
t.t2 = new Date().getTime();
if (t.n == 0 && window.location.hash == '#dbg') {
var spd = (t.size / ((t.t2 - t.t1) / 1000.)) / (1024 * 1024.);
alert('{0} ms, {1} MB/s\n'.format(t.t2 - t.t1, spd.toFixed(3)) + t.hash.join('\n'));
}
pvis.seth(t.n, 2, 'hashing done');
pvis.seth(t.n, 1, '📦 wait');
st.busy.hash.splice(st.busy.hash.indexOf(t), 1);
st.todo.handshake.push(t);
};
if (cache_buf)
ensure_rendered(func);
else
func();
};
var hash_done = function (hashbuf) {
var hslice = new Uint8Array(hashbuf).subarray(0, 32);
var b64str = buf2b64(hslice).replace(/=$/, '');
t.hash.push(b64str);
prog(t.n, nchunk, col_hashed);
if (++nchunk < nchunks) {
prog(t.n, nchunk, col_hashing);
return segm_next();
if (have_crypto)
crypto.subtle.digest('SHA-512', buf).then(hash_done);
else {
var hasher = new asmCrypto.Sha512();
hasher.process(new Uint8Array(buf));
hasher.finish();
hash_done(hasher.result);
}
t.t2 = new Date().getTime();
if (t.n == 0 && window.location.hash == '#dbg') {
var spd = (t.size / ((t.t2 - t.t1) / 1000.)) / (1024 * 1024.);
alert('{0} ms, {1} MB/s\n'.format(t.t2 - t.t1, spd.toFixed(3)) + t.hash.join('\n'));
}
ebi('f{0}t'.format(t.n)).innerHTML = 'connecting';
st.busy.hash.splice(st.busy.hash.indexOf(t), 1);
st.todo.handshake.push(t);
};
var segm_err = function () {
alert('y o u b r o k e i t\n\n(was that a folder? just files please)');
alert('y o u b r o k e i t\nerror: ' + reader.error);
};
segm_next();
@@ -810,7 +1031,7 @@ function up2k_init(have_crypto) {
else {
smsg = 'found';
var hit = response.hits[0],
msg = linksplit(hit.rp).join(''),
msg = linksplit(esc(hit.rp)).join(''),
tr = unix2iso(hit.ts),
tu = unix2iso(t.lmod),
diff = parseInt(t.lmod) - parseInt(hit.ts),
@@ -819,8 +1040,9 @@ function up2k_init(have_crypto) {
msg += '<br /><small>' + tr + ' (srv), ' + tu + ' (You), ' + sdiff + '</span></span>';
}
ebi('f{0}p'.format(t.n)).innerHTML = msg;
ebi('f{0}t'.format(t.n)).innerHTML = smsg;
pvis.seth(t.n, 2, msg);
pvis.seth(t.n, 1, smsg);
pvis.move(t.n, smsg == '404' ? 'ng' : 'ok');
st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1);
st.bytes.uploaded += t.size;
t.done = true;
@@ -831,7 +1053,15 @@ function up2k_init(have_crypto) {
if (response.name !== t.name) {
// file exists; server renamed us
t.name = response.name;
ebi('f{0}n'.format(t.n)).innerHTML = linksplit(esc(t.purl + t.name)).join(' ');
pvis.seth(t.n, 0, linksplit(esc(t.purl + t.name)).join(' '));
}
var chunksize = get_chunksize(t.size);
var cdr_idx = Math.ceil(t.size / chunksize) - 1;
var cdr_sz = (t.size % chunksize) || chunksize;
var cbd = [];
for (var a = 0; a <= cdr_idx; a++) {
cbd.push(a == cdr_idx ? cdr_sz : chunksize);
}
t.postlist = [];
@@ -844,10 +1074,11 @@ function up2k_init(have_crypto) {
missing[a], JSON.stringify(t)));
t.postlist.push(idx);
cbd[idx] = 0;
}
for (var a = 0; a < t.hash.length; a++)
prog(t.n, a, (t.postlist.indexOf(a) == -1)
? col_uploaded : col_hashed);
pvis.setat(t.n, cbd);
pvis.prog(t, 0, cbd[0]);
var done = true;
var msg = '&#x1f3b7;&#x1f41b;';
@@ -861,7 +1092,7 @@ function up2k_init(have_crypto) {
msg = 'uploading';
done = false;
}
ebi('f{0}t'.format(t.n)).innerHTML = msg;
pvis.seth(t.n, 1, msg);
st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1);
if (done) {
@@ -869,8 +1100,9 @@ function up2k_init(have_crypto) {
st.bytes.uploaded += t.size - t.bytes_uploaded;
var spd1 = (t.size / ((t.t2 - t.t1) / 1000.)) / (1024 * 1024.);
var spd2 = (t.size / ((t.t4 - t.t3) / 1000.)) / (1024 * 1024.);
ebi('f{0}p'.format(t.n)).innerHTML = 'hash {0}, up {1} MB/s'.format(
spd1.toFixed(2), spd2.toFixed(2));
pvis.seth(t.n, 2, 'hash {0}, up {1} MB/s'.format(
spd1.toFixed(2), spd2.toFixed(2)));
pvis.move(t.n, 'ok');
}
else t.t4 = undefined;
@@ -897,18 +1129,19 @@ function up2k_init(have_crypto) {
}
}
if (err != "") {
ebi('f{0}t'.format(t.n)).innerHTML = "ERROR";
ebi('f{0}p'.format(t.n)).innerHTML = err;
pvis.seth(t.n, 1, "ERROR");
pvis.seth(t.n, 2, err);
pvis.move(t.n, 'ng');
st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1);
tasker();
return;
}
alert("server broke (error {0}):\n\"{1}\"\n".format(
xhr.status,
(xhr.response && xhr.response.err) ||
(xhr.responseText && xhr.responseText) ||
"no further information"));
alert("server broke; hs-err {0} on file [{1}]:\n".format(
xhr.status, t.name) + (
(xhr.response && xhr.response.err) ||
(xhr.responseText && xhr.responseText) ||
"no further information"));
}
};
@@ -937,8 +1170,10 @@ function up2k_init(have_crypto) {
var npart = upt.npart;
var t = st.files[upt.nfile];
if (!t.t3)
t.t3 = new Date().getTime();
prog(t.n, npart, col_uploading);
pvis.seth(t.n, 1, "🚀 send");
var chunksize = get_chunksize(t.size);
var car = npart * chunksize;
@@ -946,73 +1181,40 @@ function up2k_init(have_crypto) {
if (cdr >= t.size)
cdr = t.size;
var reader = new FileReader();
reader.onerror = function () {
alert('y o u b r o k e i t\n\n(was that a folder? just files please)');
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function (xev) {
pvis.prog(t, npart, xev.loaded);
};
reader.onload = function (e) {
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function (xev) {
var perc = xev.loaded / (cdr - car) * 100;
prog(t.n, npart, '', perc);
};
xhr.onload = function (xev) {
if (xhr.status == 200) {
prog(t.n, npart, col_uploaded);
st.bytes.uploaded += cdr - car;
t.bytes_uploaded += cdr - car;
st.busy.upload.splice(st.busy.upload.indexOf(upt), 1);
t.postlist.splice(t.postlist.indexOf(npart), 1);
if (t.postlist.length == 0) {
t.t4 = new Date().getTime();
ebi('f{0}t'.format(t.n)).innerHTML = 'verifying';
st.todo.handshake.unshift(t);
}
tasker();
xhr.onload = function (xev) {
if (xhr.status == 200) {
pvis.prog(t, npart, cdr - car);
st.bytes.uploaded += cdr - car;
t.bytes_uploaded += cdr - car;
st.busy.upload.splice(st.busy.upload.indexOf(upt), 1);
t.postlist.splice(t.postlist.indexOf(npart), 1);
if (t.postlist.length == 0) {
t.t4 = new Date().getTime();
pvis.seth(t.n, 1, 'verifying');
st.todo.handshake.unshift(t);
}
else
alert("server broke (error {0}):\n\"{1}\"\n".format(
xhr.status,
tasker();
}
else
alert("server broke; cu-err {0} on file [{1}]:\n".format(
xhr.status, t.name) + (
(xhr.response && xhr.response.err) ||
(xhr.responseText && xhr.responseText) ||
"no further information"));
};
xhr.open('POST', t.purl + 'chunkpit.php', true);
//xhr.setRequestHeader("X-Up2k-Hash", t.hash[npart].substr(1) + "x");
xhr.setRequestHeader("X-Up2k-Hash", t.hash[npart]);
xhr.setRequestHeader("X-Up2k-Wark", t.wark);
xhr.setRequestHeader('Content-Type', 'application/octet-stream');
if (xhr.overrideMimeType)
xhr.overrideMimeType('Content-Type', 'application/octet-stream');
xhr.responseType = 'text';
xhr.send(e.target.result);
if (!t.t3)
t.t3 = new Date().getTime();
};
xhr.open('POST', t.purl + 'chunkpit.php', true);
xhr.setRequestHeader("X-Up2k-Hash", t.hash[npart]);
xhr.setRequestHeader("X-Up2k-Wark", t.wark);
xhr.setRequestHeader('Content-Type', 'application/octet-stream');
if (xhr.overrideMimeType)
xhr.overrideMimeType('Content-Type', 'application/octet-stream');
reader.readAsArrayBuffer(bobslice.call(t.fobj, car, cdr));
}
/////
////
/// progress bar
//
function prog(nfile, nchunk, color, percent) {
var n1 = ebi('f{0}p{1}'.format(nfile, nchunk));
var n2 = n1.getElementsByTagName('div')[0];
if (percent === undefined) {
n1.style.background = color;
n2.style.display = 'none';
}
else {
n2.style.width = percent + '%';
n2.style.display = 'block';
}
xhr.responseType = 'text';
xhr.send(bobslice.call(t.fobj, car, cdr));
}
/////
@@ -1033,6 +1235,7 @@ function up2k_init(have_crypto) {
if (btn.parentNode !== parent) {
parent.appendChild(btn);
ebi('u2conf').setAttribute('class', wide ? 'has_btn' : '');
ebi('u2cards').setAttribute('class', wide ? 'w' : '');
}
}
window.addEventListener('resize', onresize);
@@ -1068,14 +1271,14 @@ function up2k_init(have_crypto) {
var obj = ebi('nthread');
if (dir.target) {
obj.style.background = '#922';
clmod(obj, 'err', 1);
var v = Math.floor(parseInt(obj.value));
if (v < 1 || v > 8 || v !== v)
return;
parallel_uploads = v;
swrite('nthread', v);
obj.style.background = '#444';
clmod(obj, 'err');
return;
}

View File

@@ -47,6 +47,11 @@
margin: -1.5em 0;
padding: .8em 0;
width: 100%;
max-width: 12em;
display: inline-block;
}
#u2conf #u2btn_cw {
text-align: right;
}
#u2notbtn {
display: none;
@@ -72,6 +77,7 @@
}
#u2tab td:nth-child(2) {
width: 5em;
white-space: nowrap;
}
#u2tab td:nth-child(3) {
width: 40%;
@@ -83,6 +89,35 @@
#u2tab tr+tr:hover td {
background: #222;
}
#u2cards {
margin: 2.5em auto -2.5em auto;
text-align: center;
}
#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 {
border-width: 1px 1px 0 1px;
border-radius: .3em .3em 0 0;
margin-left: -1px;
background: transparent;
}
#u2cards span {
color: #fff;
}
#u2conf {
margin: 1em auto;
width: 30em;
@@ -106,6 +141,9 @@
font-size: 1.2em;
padding: .15em 0;
}
#u2conf .txtbox.err {
background: #922;
}
#u2conf a {
color: #fff;
background: #c38;
@@ -193,24 +231,6 @@
.prog {
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 {
font-weight: bold;
font-style: italic;
@@ -221,3 +241,35 @@
float: right;
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 {
background: inherit;
}
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;
}

View File

@@ -1,7 +1,7 @@
<div id="op_bup" class="opview opbox act">
<div id="u2err"></div>
<form method="post" enctype="multipart/form-data" accept-charset="utf-8">
<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="submit" value="start upload">
@@ -9,7 +9,7 @@
</div>
<div id="op_mkdir" class="opview opbox act">
<form method="post" enctype="multipart/form-data" accept-charset="utf-8">
<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="submit" value="mkdir">
@@ -17,7 +17,7 @@
</div>
<div id="op_new_md" class="opview opbox">
<form method="post" enctype="multipart/form-data" accept-charset="utf-8">
<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="submit" value="create doc">
@@ -25,7 +25,7 @@
</div>
<div id="op_msg" class="opview opbox act">
<form method="post" enctype="application/x-www-form-urlencoded" accept-charset="utf-8">
<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">
</form>
@@ -79,12 +79,23 @@
</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">
<tr>
<td>filename</td>
<td>status</td>
<td>progress<a href="#" id="u2cleanup">cleanup</a></td>
</tr>
<thead>
<tr>
<td>filename</td>
<td>status</td>
<td>progress<a href="#" id="u2cleanup">cleanup</a></td>
</tr>
</thead>
<tbody></tbody>
</table>
<p id="u2foot"></p>

View File

@@ -6,6 +6,9 @@ if (!window['console'])
};
var clickev = window.Touch ? 'touchstart' : 'click';
// error handler for mobile devices
function hcroak(msg) {
document.body.innerHTML = msg;
@@ -116,7 +119,7 @@ function crc32(str) {
crc = (crc >>> 8) ^ crctab[(crc ^ str.charCodeAt(i)) & 0xFF];
}
return ((crc ^ (-1)) >>> 0).toString(16);
};
}
function clmod(obj, cls, add) {

View File

@@ -45,11 +45,13 @@ pybin=$(command -v python3 || command -v python) || {
exit 1
}
use_gz=
do_sh=1
do_py=1
while [ ! -z "$1" ]; do
[ "$1" = clean ] && clean=1 && shift && continue
[ "$1" = re ] && repack=1 && shift && continue
[ "$1" = gz ] && use_gz=1 && shift && continue
[ "$1" = no-ogv ] && no_ogv=1 && shift && continue
[ "$1" = no-cm ] && no_cm=1 && shift && continue
[ "$1" = no-sh ] && do_sh= && shift && continue
@@ -204,16 +206,20 @@ args=(--owner=1000 --group=1000)
tar -cf tar "${args[@]}" --numeric-owner copyparty dep-j2
pc=bzip2
pe=bz2
[ $use_gz ] && pc=gzip && pe=gz
echo compressing tar
# detect best level; bzip2 -7 is usually better than -9
[ $do_py ] && { for n in {2..9}; do cp tar t.$n; bzip2 -$n t.$n & done; wait; mv -v $(ls -1S t.*.bz2 | tail -n 1) tar.bz2; }
[ $do_sh ] && { for n in {2..9}; do cp tar t.$n; xz -ze$n t.$n & done; wait; mv -v $(ls -1S t.*.xz | tail -n 1) tar.xz; }
[ $do_py ] && { for n in {2..9}; do cp tar t.$n; $pc -$n t.$n & done; wait; mv -v $(ls -1S t.*.$pe | tail -n 1) tar.bz2; }
[ $do_sh ] && { for n in {2..9}; do cp tar t.$n; xz -ze$n t.$n & done; wait; mv -v $(ls -1S t.*.xz | tail -n 1) tar.xz; }
rm t.* || true
exts=()
[ $do_sh ] && {
exts+=(sh)
exts+=(.sh)
echo creating unix sfx
(
sed "s/PACK_TS/$ts/; s/PACK_HTS/$hts/; s/CPP_VER/$ver/" <../scripts/sfx.sh |
@@ -224,17 +230,30 @@ echo creating unix sfx
[ $do_py ] && {
exts+=(py)
echo creating generic sfx
$pybin ../scripts/sfx.py --sfx-make tar.bz2 $ver $ts
mv sfx.out $sfx_out.py
chmod 755 $sfx_out.*
echo creating generic sfx
py=../scripts/sfx.py
suf=
[ $use_gz ] && {
sed -r 's/"r:bz2"/"r:gz"/' <$py >$py.t
py=$py.t
suf=-gz
}
$pybin $py --sfx-make tar.bz2 $ver $ts
mv sfx.out $sfx_out$suf.py
exts+=($suf.py)
[ $use_gz ] &&
rm $py
}
chmod 755 $sfx_out*
printf "done:\n"
for ext in ${exts[@]}; do
printf " %s\n" "$(realpath $sfx_out)."$ext
printf " %s\n" "$(realpath $sfx_out)"$ext
done
# apk add bash python3 tar xz bzip2

View File

@@ -2,7 +2,7 @@
# coding: latin-1
from __future__ import print_function, unicode_literals
import os, sys, time, shutil, runpy, tarfile, hashlib, platform, tempfile, traceback
import os, sys, time, shutil, threading, tarfile, hashlib, platform, tempfile, traceback
"""
run me with any version of python, i will unpack and run copyparty
@@ -26,6 +26,7 @@ CKSUM = None
STAMP = None
PY2 = sys.version_info[0] == 2
WINDOWS = sys.platform == "win32"
sys.dont_write_bytecode = True
me = os.path.abspath(os.path.realpath(__file__))
cpp = None
@@ -343,6 +344,21 @@ def get_payload():
break
def utime(top):
i = 0
files = [os.path.join(dp, p) for dp, dd, df in os.walk(top) for p in dd + df]
while WINDOWS:
t = int(time.time())
if i:
msg("utime {}, {}".format(i, t))
for f in files:
os.utime(f, (t, t))
i += 1
time.sleep(78123)
def confirm(rv):
msg()
msg(traceback.format_exc())
@@ -362,15 +378,20 @@ def run(tmp, j2ver):
msg("sfxdir:", tmp)
msg()
# "systemd-tmpfiles-clean.timer"?? HOW do you even come up with this shit
# block systemd-tmpfiles-clean.timer
try:
import fcntl
fd = os.open(tmp, os.O_RDONLY)
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
tmp = os.readlink(tmp) # can't flock a symlink, even with O_NOFOLLOW
except:
pass
except Exception as ex:
if not WINDOWS:
msg("\033[31mflock:", repr(ex))
t = threading.Thread(target=utime, args=(tmp,))
t.daemon = True
t.start()
ld = [tmp, os.path.join(tmp, "dep-j2")]
if j2ver:
@@ -380,7 +401,10 @@ def run(tmp, j2ver):
sys.path.insert(0, x)
try:
runpy.run_module(str("copyparty"), run_name=str("__main__"))
from copyparty.__main__ import main as copyparty
copyparty()
except SystemExit as ex:
if ex.code:
confirm(ex.code)