mirror of
https://github.com/9001/copyparty.git
synced 2025-10-24 00:24:04 +00:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04f1b2cf3a | ||
|
|
c06d928bb5 | ||
|
|
ab09927e7b | ||
|
|
779437db67 | ||
|
|
28cbdb652e | ||
|
|
2b2415a7d8 | ||
|
|
746a8208aa | ||
|
|
a2a041a98a | ||
|
|
10b436e449 | ||
|
|
4d62b34786 | ||
|
|
0546210687 | ||
|
|
f8c11faada | ||
|
|
16d6e9be1f | ||
|
|
aff8185f2e | ||
|
|
217d15fe81 | ||
|
|
171e93c201 | ||
|
|
acc1d2e9e3 | ||
|
|
49c2f37154 | ||
|
|
69e54497aa | ||
|
|
9aa1885669 | ||
|
|
4418508513 | ||
|
|
e897df3b34 | ||
|
|
8cd97ab0e7 | ||
|
|
bf4949353d | ||
|
|
98a944f7cc | ||
|
|
7c10f81c92 | ||
|
|
126ecc55c3 | ||
|
|
1034a51bd2 | ||
|
|
a2657887cc | ||
|
|
c14b17bfaf | ||
|
|
59ebc795e7 | ||
|
|
8e128d917e | ||
|
|
ea762b05e0 | ||
|
|
db374b19f1 | ||
|
|
ab3839ef36 | ||
|
|
9886c442f2 | ||
|
|
c8d1926d52 | ||
|
|
a6bd699e52 | ||
|
|
12143f2702 | ||
|
|
480705dee9 | ||
|
|
781d5094f4 | ||
|
|
5615cb94cd | ||
|
|
302302a2ac |
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -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
35
.vscode/launch.py
vendored
Normal 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
4
.vscode/tasks.json
vendored
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
60
README.md
60
README.md
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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 "?" + "&".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,
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"]:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.<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 %}
|
||||
<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>
|
||||
{%- 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>
|
||||
|
||||
@@ -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 () { } };
|
||||
|
||||
55
copyparty/web/browser2.html
Normal file
55
copyparty/web/browser2.html
Normal 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 }}&h">control-panel</a></h2>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -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();
|
||||
})();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 = '🎷🐛';
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user