Compare commits

...

45 Commits

Author SHA1 Message Date
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
ed
9761b4e3e9 v0.10.7 2021-04-03 00:35:46 +02:00
ed
0cf6924dca v0.10.6 2021-04-02 03:11:40 +02:00
ed
5fd81e9f90 fix unreadable links when playing search results 2021-04-02 03:05:23 +02:00
ed
52bf6f892b more 2021-04-02 02:55:41 +02:00
ed
f3cce232a4 restore minimal support for old browsers 2021-04-02 02:43:07 +02:00
ed
53d3c8b28e decode urlform messages 2021-04-01 23:36:14 +02:00
24 changed files with 1191 additions and 335 deletions

2
.vscode/launch.json vendored
View File

@@ -14,6 +14,8 @@
"-emp", "-emp",
"-e2dsa", "-e2dsa",
"-e2ts", "-e2ts",
"-mtp",
".bpm=f,bin/mtag/audio-bpm.py",
"-a", "-a",
"ed:wark", "ed:wark",
"-v", "-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", "label": "no_dbg",
"type": "shell", "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" "command": "${config:python.pythonPath} .vscode/launch.py"
// -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
} }
] ]
} }

View File

@@ -27,6 +27,7 @@ turn your phone or raspi into a portable file server with resumable uploads/down
* [metadata from audio files](#metadata-from-audio-files) * [metadata from audio files](#metadata-from-audio-files)
* [file parser plugins](#file-parser-plugins) * [file parser plugins](#file-parser-plugins)
* [complete examples](#complete-examples) * [complete examples](#complete-examples)
* [browser support](#browser-support)
* [client examples](#client-examples) * [client examples](#client-examples)
* [dependencies](#dependencies) * [dependencies](#dependencies)
* [optional gpl stuff](#optional-gpl-stuff) * [optional gpl stuff](#optional-gpl-stuff)
@@ -110,6 +111,8 @@ the browser has the following hotkeys
* `I/K` prev/next folder * `I/K` prev/next folder
* `P` parent folder * `P` parent folder
you can link a particular timestamp in an audio file by adding it to the URL, such as `&20` / `&20s` / `&1m20` / `&1:20` after the `.../#af-c8960dab`
## zip downloads ## zip downloads
@@ -196,6 +199,41 @@ copyparty can invoke external programs to collect additional metadata for files
`python copyparty-sfx.py -v /mnt/nas/music:/music:r -e2dsa -e2ts -mtp .bpm=f,audio-bpm.py -mtp key=f,audio-key.py` `python copyparty-sfx.py -v /mnt/nas/music:/music:r -e2dsa -e2ts -mtp .bpm=f,audio-bpm.py -mtp key=f,audio-key.py`
# browser support
`ie` = internet-explorer, `ff` = firefox, `c` = chrome, `iOS` = iPhone/iPad, `Andr` = Android
| 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 # client examples
* javascript: dump some state into a file (two separate examples) * javascript: dump some state into a file (two separate examples)
@@ -303,15 +341,20 @@ in the `scripts` folder:
roughly sorted by priority roughly sorted by priority
* separate sqlite table per tag
* audio fingerprinting
* readme.md as epilogue
* reduce up2k roundtrips * reduce up2k roundtrips
* start from a chunk index and just go * start from a chunk index and just go
* terminate client on bad data * terminate client on bad data
* drop onto folders
* `os.copy_file_range` for up2k cloning * `os.copy_file_range` for up2k cloning
* up2k partials ui
* support pillow-simd * 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 * figure out the deal with pixel3a not being connectable as hotspot
* pixel3a having unpredictable 3sec latency in general :|||| * 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 os
import sys import sys
import time import time
import signal
import shutil import shutil
import filecmp import filecmp
import locale import locale
@@ -56,6 +55,12 @@ class RiceFormatter(argparse.HelpFormatter):
return "".join(indent + line + "\n" for line in text.splitlines()) 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): def warn(msg):
print("\033[1mwarning:\033[0;33m {}\033[0m\n".format(msg)) print("\033[1mwarning:\033[0;33m {}\033[0m\n".format(msg))
@@ -167,7 +172,7 @@ def configure_ssl_ciphers(al):
sys.exit(0) sys.exit(0)
def sighandler(signal=None, frame=None): def sighandler(sig=None, frame=None):
msg = [""] * 5 msg = [""] * 5
for th in threading.enumerate(): for th in threading.enumerate():
msg.append(str(th)) msg.append(str(th))
@@ -177,37 +182,9 @@ def sighandler(signal=None, frame=None):
print("\n".join(msg)) print("\n".join(msg))
def main(argv=None): def run_argparse(argv, formatter):
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)
ap = argparse.ArgumentParser( ap = argparse.ArgumentParser(
formatter_class=RiceFormatter, formatter_class=formatter,
prog="copyparty", prog="copyparty",
description="http file sharing hub v{} ({})".format(S_VERSION, S_BUILD_DT), description="http file sharing hub v{} ({})".format(S_VERSION, S_BUILD_DT),
epilog=dedent( epilog=dedent(
@@ -219,6 +196,9 @@ def main(argv=None):
list of cflags: list of cflags:
"cnodupe" rejects existing files (instead of symlinking them) "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 example:\033[35m
-a ed:hunter2 -v .::r:aed -v ../inc:dump:w:aed:cnodupe \033[36m -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-dbg", action="store_true", help="dump some tls info")
ap2.add_argument("--ssl-log", metavar="PATH", help="log master secrets") 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 # 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 # propagate implications
for k1, k2 in IMPLICATIONS: for k1, k2 in IMPLICATIONS:
if getattr(al, k1): if getattr(al, k1):

View File

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

View File

@@ -111,7 +111,27 @@ class VFS(object):
if rem: if rem:
rp += "/" + 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): def ls(self, rem, uname, scandir, lstat=False):
"""return user-readable [fsdir,real,virt] items at vpath""" """return user-readable [fsdir,real,virt] items at vpath"""
@@ -168,8 +188,13 @@ class VFS(object):
for vpath, apath, files, rd, vd in self.walk("", vrem, uname, dots, scandir): for vpath, apath, files, rd, vd in self.walk("", vrem, uname, dots, scandir):
if flt: if flt:
files = [x for x in files if x[0] in flt] files = [x for x in files if x[0] in flt]
rd = [x for x in rd if x[0] in flt]
vd = {x: y for x, y in vd.items() if x in flt} rm = [x for x in rd if x[0] not in flt]
[rd.remove(x) for x in rm]
rm = [x for x in vd.keys() if x not in flt]
[vd.pop(x) for x in rm]
flt = None flt = None
# print(repr([vpath, apath, [x[0] for x in files]])) # print(repr([vpath, apath, [x[0] for x in files]]))
@@ -228,12 +253,6 @@ class AuthSrv(object):
def log(self, msg, c=0): def log(self, msg, c=0):
self.log_func("auth", msg, c) 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): def laggy_iter(self, iterable):
"""returns [value,isFinalValue]""" """returns [value,isFinalValue]"""
it = iter(iterable) it = iter(iterable)
@@ -490,7 +509,7 @@ class AuthSrv(object):
with self.mutex: with self.mutex:
self.vfs = vfs self.vfs = vfs
self.user = user self.user = user
self.iuser = self.invert(user) self.iuser = {v: k for k, v in user.items()}
# import pprint # import pprint
# pprint.pprint({"usr": user, "rd": mread, "wr": mwrite, "mnt": mount}) # pprint.pprint({"usr": user, "rd": mread, "wr": mwrite, "mnt": mount})

View File

@@ -74,7 +74,7 @@ class HttpCli(object):
headerlines.pop(0) headerlines.pop(0)
try: try:
self.mode, self.req, _ = headerlines[0].split(" ") self.mode, self.req, self.http_ver = headerlines[0].split(" ")
except: except:
raise Pebkac(400, "bad headers:\n" + "\n".join(headerlines)) raise Pebkac(400, "bad headers:\n" + "\n".join(headerlines))
@@ -93,30 +93,13 @@ class HttpCli(object):
self.headers[k.lower()] = v.strip() self.headers[k.lower()] = v.strip()
v = self.headers.get("connection", "").lower() 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) v = self.headers.get("x-forwarded-for", None)
if v is not None and self.conn.addr[0] in ["127.0.0.1", "::1"]: if v is not None and self.conn.addr[0] in ["127.0.0.1", "::1"]:
self.ip = v.split(",")[0] self.ip = v.split(",")[0]
self.log_src = self.conn.set_rproxy(self.ip) 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 # split req into vpath + uparam
uparam = {} uparam = {}
if "?" not in self.req: if "?" not in self.req:
@@ -140,6 +123,22 @@ class HttpCli(object):
self.uparam = uparam self.uparam = uparam
self.vpath = unquotep(vpath) 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", "") ua = self.headers.get("user-agent", "")
if ua.startswith("rclone/"): if ua.startswith("rclone/"):
uparam["raw"] = False uparam["raw"] = False
@@ -160,7 +159,9 @@ class HttpCli(object):
except Pebkac as ex: except Pebkac as ex:
try: try:
# self.log("pebkac at httpcli.run #2: " + repr(ex)) # 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) self.log("{}\033[0m, {}".format(str(ex), self.vpath), 3)
msg = "<pre>{}\r\nURL: {}\r\n".format(str(ex), self.vpath) msg = "<pre>{}\r\nURL: {}\r\n".format(str(ex), self.vpath)
self.reply(msg.encode("utf-8", "replace"), status=ex.code) self.reply(msg.encode("utf-8", "replace"), status=ex.code)
@@ -169,7 +170,7 @@ class HttpCli(object):
return False return False
def send_headers(self, length, status=200, mime=None, headers={}): 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: if length is not None:
response.append("Content-Length: " + unicode(length)) response.append("Content-Length: " + unicode(length))
@@ -213,6 +214,20 @@ class HttpCli(object):
self.log(body.rstrip()) self.log(body.rstrip())
self.reply(b"<pre>" + body.encode("utf-8") + b"\r\n", *list(args), **kwargs) 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): def handle_get(self):
logmsg = "{:4} {}".format(self.mode, self.req) logmsg = "{:4} {}".format(self.mode, self.req)
@@ -321,8 +336,19 @@ class HttpCli(object):
elif "print" in opt: elif "print" in opt:
reader, _ = self.get_body_reader() reader, _ = self.get_body_reader()
for buf in reader: for buf in reader:
buf = buf.decode("utf-8", "replace") orig = buf.decode("utf-8", "replace")
self.log("urlform @ {}\n {}\n".format(self.vpath, buf)) m = "urlform_raw {} @ {}\n {}\n"
self.log(m.format(len(orig), self.vpath, orig))
try:
plain = unquote(buf.replace(b"+", b" "))
plain = plain.decode("utf-8", "replace")
if buf.startswith(b"msg="):
plain = plain[4:]
m = "urlform_dec {} @ {}\n {}\n"
self.log(m.format(len(plain), self.vpath, plain))
except Exception as ex:
self.log(repr(ex))
if "get" in opt: if "get" in opt:
return self.handle_get() return self.handle_get()
@@ -1202,9 +1228,10 @@ class HttpCli(object):
return True return True
def tx_mounts(self): def tx_mounts(self):
suf = self.urlq(rm=["h"])
rvol = [x + "/" if x else x for x in self.rvol] rvol = [x + "/" if x else x for x in self.rvol]
wvol = [x + "/" if x else x for x in self.wvol] 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")) self.reply(html.encode("utf-8"))
return True return True
@@ -1334,6 +1361,8 @@ class HttpCli(object):
idx = self.conn.get_u2idx() idx = self.conn.get_u2idx()
icur = idx.get_cur(vn.realpath) icur = idx.get_cur(vn.realpath)
url_suf = self.urlq()
dirs = [] dirs = []
files = [] files = []
for fn in vfs_ls: for fn in vfs_ls:
@@ -1486,8 +1515,12 @@ class HttpCli(object):
dirs.extend(files) dirs.extend(files)
tpl = "browser"
if "b" in self.uparam:
tpl = "browser2"
html = self.j2( html = self.j2(
"browser", tpl,
vdir=quotep(self.vpath), vdir=quotep(self.vpath),
vpnodes=vpnodes, vpnodes=vpnodes,
files=dirs, files=dirs,
@@ -1500,6 +1533,8 @@ class HttpCli(object):
have_up2k_idx=("e2d" in vn.flags), have_up2k_idx=("e2d" in vn.flags),
have_tags_idx=("e2t" in vn.flags), have_tags_idx=("e2t" in vn.flags),
have_zip=(not self.args.no_zip), have_zip=(not self.args.no_zip),
have_b_u=(self.writable and self.uparam.get("b") == "u"),
url_suf=url_suf,
logues=logues, logues=logues,
title=html_escape(self.vpath), title=html_escape(self.vpath),
srv_info=srv_info, srv_info=srv_info,

View File

@@ -52,7 +52,7 @@ class HttpSrv(object):
env.loader = jinja2.FileSystemLoader(os.path.join(E.mod, "web")) env.loader = jinja2.FileSystemLoader(os.path.join(E.mod, "web"))
self.j2 = { self.j2 = {
x: env.get_template(x + ".html") 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") cert_path = os.path.join(E.cfg, "cert.pem")

View File

@@ -101,17 +101,18 @@ class Up2k(object):
thr.daemon = True thr.daemon = True
thr.start() thr.start()
thr = threading.Thread(target=self._tagger)
thr.daemon = True
thr.start()
thr = threading.Thread(target=self._hasher) thr = threading.Thread(target=self._hasher)
thr.daemon = True thr.daemon = True
thr.start() thr.start()
thr = threading.Thread(target=self._run_all_mtp) if self.mtag:
thr.daemon = True thr = threading.Thread(target=self._tagger)
thr.start() thr.daemon = True
thr.start()
thr = threading.Thread(target=self._run_all_mtp)
thr.daemon = True
thr.start()
def log(self, msg, c=0): def log(self, msg, c=0):
self.log_func("up2k", msg + "\033[K", c) self.log_func("up2k", msg + "\033[K", c)
@@ -1068,6 +1069,8 @@ class Up2k(object):
with self.mutex: with self.mutex:
job = self.registry[ptop].get(wark, None) job = self.registry[ptop].get(wark, None)
if not job: if not job:
known = " ".join([x for x in self.registry[ptop].keys()])
self.log("unknown wark [{}], known: {}".format(wark, known))
raise Pebkac(400, "unknown wark") raise Pebkac(400, "unknown wark")
if chash not in job["need"]: if chash not in job["need"]:

View File

@@ -183,9 +183,9 @@ a, #files tbody div a:last-child {
text-shadow: 0 0 .3em #b80; text-shadow: 0 0 .3em #b80;
} }
#files tbody tr.sel td { #files tbody tr.sel td {
background: #80b;
color: #fff; color: #fff;
border-color: #a3d; background: #925;
border-color: #c37;
} }
#blocked { #blocked {
position: fixed; position: fixed;
@@ -243,7 +243,7 @@ a, #files tbody div a:last-child {
height: 100%; height: 100%;
background: #3c3c3c; background: #3c3c3c;
} }
#wtoggle { #wtico {
cursor: url(/.cpr/dd/1.png), pointer; cursor: url(/.cpr/dd/1.png), pointer;
animation: cursor 500ms infinite; animation: cursor 500ms infinite;
} }
@@ -273,24 +273,32 @@ a, #files tbody div a:last-child {
padding: .2em 0 0 .07em; padding: .2em 0 0 .07em;
color: #fff; color: #fff;
} }
#wtoggle>span { #wzip {
display: none; display: none;
margin-right: .3em;
padding-right: .3em;
border-right: .1em solid #555;
}
#wtoggle,
#wtoggle * {
line-height: 1em;
} }
#wtoggle.sel { #wtoggle.sel {
width: 4.27em; width: 6.4em;
} }
#wtoggle.sel>span { #wtoggle.sel #wzip {
display: inline-block; display: inline-block;
line-height: 0;
} }
#wtoggle.sel>span a { #wtoggle.sel #wzip a {
font-size: .4em; font-size: .4em;
margin: -.3em 0; padding: 0 .3em;
margin: -.3em .2em;
position: relative; position: relative;
display: inline-block; display: inline-block;
} }
#wtoggle.sel>span #selzip { #wtoggle.sel #wzip #selzip {
top: -.6em; top: -.6em;
padding: .4em .3em;
} }
#barpos, #barpos,
#barbuf { #barbuf {
@@ -487,7 +495,7 @@ input[type="checkbox"]:checked+label {
} }
#tree { #tree {
display: none; display: none;
position: fixed; position: absolute;
left: 0; left: 0;
bottom: 0; bottom: 0;
top: 7em; top: 7em;
@@ -622,7 +630,8 @@ input[type="checkbox"]:checked+label {
#files td.min a { #files td.min a {
display: none; display: none;
} }
#files tr.play td { #files tr.play td,
#files tr.play div a {
background: #fc4; background: #fc4;
border-color: transparent; border-color: transparent;
color: #400; color: #400;
@@ -676,3 +685,173 @@ input[type="checkbox"]:checked+label {
font-family: monospace, monospace; font-family: monospace, monospace;
line-height: 2em; line-height: 2em;
} }
html.light {
color: #333;
background: #eee;
text-shadow: none;
}
html.light #ops,
html.light .opbox,
html.light #srch_form {
background: #f7f7f7;
box-shadow: 0 0 .3em #ddd;
border-color: #f7f7f7;
}
html.light #ops a.act {
box-shadow: 0 .2em .2em #ccc;
background: #f7f7f7;
border-color: #07a;
padding-top: .4em;
}
html.light #op_cfg h3 {
border-color: #ccc;
}
html.light .tglbtn,
html.light #tree > a + a {
color: #666;
background: #ddd;
box-shadow: none;
}
html.light .tglbtn:hover,
html.light #tree > a + a:hover {
background: #caf;
}
html.light .tglbtn.on,
html.light #tree > a + a.on {
background: #4a0;
color: #fff;
}
html.light #srv_info {
color: #c83;
text-shadow: 1px 1px 0 #fff;
}
html.light #srv_info span {
color: #000;
}
html.light #treeul a+a {
background: inherit;
color: #06a;
}
html.light #treeul a.hl {
background: #07a;
color: #fff;
}
html.light #tree li {
border-color: #ddd #fff #f7f7f7 #fff;
}
html.light #tree ul {
border-color: #ccc;
}
html.light a,
html.light #ops a,
html.light #files tbody div a:last-child {
color: #06a;
}
html.light #files tbody {
background: #f7f7f7;
}
html.light #files {
box-shadow: 0 0 .3em #ccc;
}
html.light #files thead th {
background: #eee;
}
html.light #files tr+tr td {
border-top: 1px solid #ddd;
}
html.light #files td {
border-bottom: 1px solid #f7f7f7;
}
html.light #files tbody tr:last-child td {
border-bottom: .2em solid #ccc;
}
html.light #files td:nth-child(2n) {
color: #d38;
}
html.light #files tr:hover td {
background: #fff;
}
html.light #files tbody a.play {
color: #c0f;
}
html.light tr.play td {
background: #fc5;
}
html.light tr.play a {
color: #406;
}
html.light #files > thead > tr > th.min span {
background: linear-gradient(90deg, rgba(68,68,68,0), rgba(68,68,68,0.2) 70%, rgba(68,68,68,0.5));
}
html.light #blocked {
background: #eee;
}
html.light #blk_play a,
html.light #blk_abrt a {
background: #fff;
box-shadow: 0 .2em .4em #ddd;
}
html.light #widget a {
color: #fc5;
}
html.light #files tr.sel:hover td {
background: #c37;
}
html.light #files tr.sel td {
color: #fff;
}
html.light #files tr.sel a {
color: #fff;
}
html.light input[type="checkbox"] + label {
color: #333;
}
html.light .opview input[type="text"] {
background: #fff;
color: #333;
box-shadow: 0 0 2px #888;
border-color: #38d;
}
html.light #ops:hover #opdesc {
background: #fff;
box-shadow: 0 .3em 1em #ccc;
}
html.light #opdesc code {
background: #060;
color: #fff;
}
html.light #u2tab a>span,
html.light #files td div span {
color: #000;
}
html.light #path {
background: #f7f7f7;
text-shadow: none;
box-shadow: 0 0 .3em #bbb;
}
html.light #path a {
color: #333;
}
html.light #path a:not(:last-child)::after {
border-color: #ccc;
background: none;
border-width: .1em .1em 0 0;
margin: -.2em .3em -.2em -.3em;
}
html.light #path a:hover {
background: none;
color: #60a;
}
html.light #files tbody div a {
color: #d38;
}
html.light #files a:hover,
html.light #files tr.sel a:hover {
color: #000;
background: #fff;
}

View File

@@ -13,8 +13,8 @@
<body> <body>
<div id="ops"> <div id="ops">
<a href="#" data-dest="" data-desc="close submenu">---</a> <a href="#" data-dest="" data-desc="close submenu">---</a>
<a href="#" data-perm="read" data-dest="search" data-desc="search for files by attributes, path/name, music tags, or any combination of those.&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 %} {%- 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> <a href="#" data-dest="up2k" data-desc="up2k: upload files (if you have write-access) or toggle into the search-mode and drag files onto the search button to see if they exist somewhere on the server">🚀</a>
{%- else %} {%- else %}
<a href="#" data-perm="write" data-dest="up2k" data-desc="up2k: upload files with resume support (close your browser and drop the same files in later)">🚀</a> <a href="#" data-perm="write" data-dest="up2k" data-desc="up2k: upload files with resume support (close your browser and drop the same files in later)">🚀</a>
@@ -39,14 +39,17 @@
{%- include 'upload.html' %} {%- include 'upload.html' %}
<div id="op_cfg" class="opview opbox"> <div id="op_cfg" class="opview opbox">
<h3>key notation</h3> <h3>switches</h3>
<div id="key_notation"></div> <div>
<a id="tooltips" class="tglbtn" href="#">tooltips</a>
<a id="lightmode" class="tglbtn" href="#">lightmode</a>
</div>
{%- if have_zip %} {%- if have_zip %}
<h3>folder download</h3> <h3>folder download</h3>
<div id="arc_fmt"></div> <div id="arc_fmt"></div>
{%- endif %} {%- endif %}
<h3>tooltips</h3> <h3>key notation</h3>
<div><a id="tooltips" class="tglbtn" href="#">enable</a></div> <div id="key_notation"></div>
</div> </div>
<h1 id="path"> <h1 id="path">
@@ -113,12 +116,12 @@
<div id="widget"> <div id="widget">
<div id="wtoggle"> <div id="wtoggle">
<span> <span id="wzip">
<a href="#" id="selall">sel.<br />all</a> <a href="#" id="selall">sel.<br />all</a>
<a href="#" id="selinv">sel.<br />inv.</a> <a href="#" id="selinv">sel.<br />inv.</a>
<a href="#" id="selzip">zip</a> <a href="#" id="selzip">zip</a>
</span> </span><a
href="#" id="wtico"></a>
</div> </div>
<div id="widgeti"> <div id="widgeti">
<div id="pctl"><a href="#" id="bprev"></a><a href="#" id="bplay"></a><a href="#" id="bnext"></a></div> <div id="pctl"><a href="#" id="bprev"></a><a href="#" id="bplay"></a><a href="#" id="bnext"></a></div>

View File

@@ -75,7 +75,7 @@ makeSortable(ebi('files'), mp.read_order.bind(mp));
var widget = (function () { var widget = (function () {
var ret = {}; var ret = {};
var widget = ebi('widget'); var widget = ebi('widget');
var wtoggle = ebi('wtoggle'); var wtico = ebi('wtico');
var touchmode = false; var touchmode = false;
var side_open = false; var side_open = false;
var was_paused = true; var was_paused = true;
@@ -113,14 +113,7 @@ var widget = (function () {
return false; return false;
}; };
if (window.Touch) { wtico.onclick = click_handler;
var touch_handler = function (e) {
touchmode = true;
return ret.toggle(e);
};
wtoggle.addEventListener('touchstart', touch_handler, false);
}
wtoggle.onclick = click_handler;
return ret; return ret;
})(); })();
@@ -321,10 +314,10 @@ function seek_au_sec(seek) {
mp.au.currentTime = seek; mp.au.currentTime = seek;
// ogv.js breaks on .play() during playback
if (mp.au === mp.au_native) if (mp.au === mp.au_native)
// hack: ogv.js breaks on .play() during playback
mp.au.play(); mp.au.play();
}; }
function song_skip(n) { function song_skip(n) {
@@ -336,7 +329,7 @@ function song_skip(n) {
play(mp.order.indexOf(tid) + n); play(mp.order.indexOf(tid) + n);
else else
play(mp.order[0]); play(mp.order[0]);
}; }
// hook up the widget buttons // hook up the widget buttons
@@ -434,7 +427,7 @@ catch (ex) { }
// plays the tid'th audio file on the page // plays the tid'th audio file on the page
function play(tid, call_depth) { function play(tid, seek, call_depth) {
if (mp.order.length == 0) if (mp.order.length == 0)
return alert('no audio found wait what'); return alert('no audio found wait what');
@@ -456,7 +449,7 @@ function play(tid, call_depth) {
} }
// ogv.js breaks on .play() unless directly user-triggered // ogv.js breaks on .play() unless directly user-triggered
var hack_attempt_play = true; var attempt_play = true;
var url = mp.tracks[tid]; var url = mp.tracks[tid];
if (need_ogv && /\.(ogg|opus)$/i.test(url)) { if (need_ogv && /\.(ogg|opus)$/i.test(url)) {
@@ -465,7 +458,7 @@ function play(tid, call_depth) {
} }
else if (window['OGVPlayer']) { else if (window['OGVPlayer']) {
mp.au = mp.au_ogvjs = new OGVPlayer(); mp.au = mp.au_ogvjs = new OGVPlayer();
hack_attempt_play = false; attempt_play = false;
mp.au.addEventListener('error', evau_error, true); mp.au.addEventListener('error', evau_error, true);
mp.au.addEventListener('progress', pbar.drawpos, false); mp.au.addEventListener('progress', pbar.drawpos, false);
widget.open(); widget.open();
@@ -477,7 +470,7 @@ function play(tid, call_depth) {
show_modal('<h1>loading ogv.js</h1><h2>thanks apple</h2>'); show_modal('<h1>loading ogv.js</h1><h2>thanks apple</h2>');
import_js('/.cpr/deps/ogv.js', function () { import_js('/.cpr/deps/ogv.js', function () {
play(tid, 1); play(tid, seek, 1);
}); });
return; return;
@@ -500,26 +493,31 @@ function play(tid, call_depth) {
setclass(oid, 'play act'); setclass(oid, 'play act');
var trs = ebi('files').getElementsByTagName('tbody')[0].getElementsByTagName('tr'); var trs = ebi('files').getElementsByTagName('tbody')[0].getElementsByTagName('tr');
for (var a = 0, aa = trs.length; a < aa; a++) { for (var a = 0, aa = trs.length; a < aa; a++) {
trs[a].className = trs[a].className.replace(/ *play */, ""); clmod(trs[a], 'play');
} }
ebi(oid).parentElement.parentElement.className += ' play'; ebi(oid).parentElement.parentElement.className += ' play';
try { try {
if (hack_attempt_play) if (attempt_play)
mp.au.play(); mp.au.play();
if (mp.au.paused) if (mp.au.paused)
autoplay_blocked(); autoplay_blocked(seek);
else if (seek) {
seek_au_sec(seek);
}
var o = ebi(oid); if (!seek) {
o.setAttribute('id', 'thx_js'); var o = ebi(oid);
if (window.history && history.replaceState) { o.setAttribute('id', 'thx_js');
hist_replace(document.location.pathname + '#' + oid); 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(); pbar.drawbuf();
return true; return true;
@@ -583,7 +581,7 @@ function unblocked() {
// show ui to manually start playback of a linked song // show ui to manually start playback of a linked song
function autoplay_blocked() { function autoplay_blocked(seek) {
show_modal( show_modal(
'<div id="blk_play"><a href="#" id="blk_go"></a></div>' + '<div id="blk_play"><a href="#" id="blk_go"></a></div>' +
'<div id="blk_abrt"><a href="#" id="blk_na">Cancel<br />(show file list)</a></div>'); '<div id="blk_abrt"><a href="#" id="blk_na">Cancel<br />(show file list)</a></div>');
@@ -599,6 +597,8 @@ function autoplay_blocked() {
if (e) e.preventDefault(); if (e) e.preventDefault();
unblocked(); unblocked();
mp.au.play(); mp.au.play();
if (seek)
seek_au_sec(seek);
}; };
na.onclick = unblocked; na.onclick = unblocked;
} }
@@ -607,8 +607,20 @@ function autoplay_blocked() {
// autoplay linked track // autoplay linked track
(function () { (function () {
var v = location.hash; var v = location.hash;
if (v && v.length == 12 && v.indexOf('#af-') === 0) if (v && v.indexOf('#af-') === 0) {
play(v.slice(2)); var id = v.slice(2).split('&');
if (id[0].length != 10)
return;
if (id.length == 1)
return play(id[0]);
var m = /^[Tt=0]*([0-9]+[Mm:])?0*([0-9]+)[Ss]?$/.exec(id[1]);
if (!m)
return play(id[0]);
return play(id[0], parseInt(m[1] || 0) * 60 + parseInt(m[2] || 0));
}
})(); })();
@@ -625,13 +637,16 @@ function tree_neigh(n) {
break; break;
} }
} }
a += n; if (act == -1)
if (a < 0) return;
a = links.length - 1;
if (a >= links.length)
a = 0;
links[a].click(); act += n;
if (act < 0)
act = links.length - 1;
if (act >= links.length)
act = 0;
links[act].click();
} }
@@ -649,10 +664,13 @@ function tree_up() {
document.onkeydown = function (e) { document.onkeydown = function (e) {
if (document.activeElement != document.body && document.activeElement.nodeName.toLowerCase() != 'a') if (!document.activeElement || document.activeElement != document.body && document.activeElement.nodeName.toLowerCase() != 'a')
return; return;
var k = e.code, pos = -1; if (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey || e.isComposing)
return;
var k = (e.code + ''), pos = -1;
if (k.indexOf('Digit') === 0) if (k.indexOf('Digit') === 0)
pos = parseInt(k.slice(-1)) * 0.1; pos = parseInt(k.slice(-1)) * 0.1;
@@ -772,6 +790,7 @@ document.onkeydown = function (e) {
// ebi('srch_q').textContent = JSON.stringify(params, null, 4); // ebi('srch_q').textContent = JSON.stringify(params, null, 4);
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.open('POST', '/?srch', true); xhr.open('POST', '/?srch', true);
xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.onreadystatechange = xhr_search_results; xhr.onreadystatechange = xhr_search_results;
xhr.ts = new Date().getTime(); xhr.ts = new Date().getTime();
xhr.send(JSON.stringify(params)); xhr.send(JSON.stringify(params));
@@ -869,6 +888,7 @@ document.onkeydown = function (e) {
oldcfg = []; oldcfg = [];
ebi('files').innerHTML = orig_html; ebi('files').innerHTML = orig_html;
orig_html = null; orig_html = null;
msel.render();
reload_browser(); reload_browser();
} }
})(); })();
@@ -877,12 +897,16 @@ document.onkeydown = function (e) {
var treectl = (function () { var treectl = (function () {
var treectl = { var treectl = {
"hidden": false "hidden": false
}; },
var dyn = bcfg_get('dyntree', true); entreed = false,
var treesz = icfg_get('treesz', 16); 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); treesz = Math.min(Math.max(treesz, 4), 50);
console.log('treesz [' + treesz + ']'); console.log('treesz [' + treesz + ']');
var entreed = false;
function entree(e) { function entree(e) {
ev(e); ev(e);
@@ -914,13 +938,43 @@ var treectl = (function () {
if (!entreed || treectl.hidden) if (!entreed || treectl.hidden)
return; return;
var top = ebi('wrap').getBoundingClientRect().top; var tree = ebi('tree'),
ebi('tree').style.top = Math.max(0, parseInt(top)) + 'px'; 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() { function periodic() {
onscroll(); onscroll();
setTimeout(periodic, document.visibilityState ? 200 : 5000); setTimeout(periodic, document.visibilityState ? 100 : 5000);
} }
periodic(); periodic();
@@ -997,8 +1051,6 @@ var treectl = (function () {
var o = links[a].parentNode; var o = links[a].parentNode;
if (!o.getElementsByTagName('li').length) if (!o.getElementsByTagName('li').length)
o.innerHTML = html; o.innerHTML = html;
//else
// links[a].previousSibling.textContent = '-';
} }
} }
} }
@@ -1282,7 +1334,7 @@ function find_file_col(txt) {
function mk_files_header(taglist) { function mk_files_header(taglist) {
var html = [ var html = [
'<thead>', '<thead><tr>',
'<th name="lead"><span>c</span></th>', '<th name="lead"><span>c</span></th>',
'<th name="href"><span>File Name</span></th>', '<th name="href"><span>File Name</span></th>',
'<th name="sz" sort="int"><span>Size</span></th>' '<th name="sz" sort="int"><span>Size</span></th>'
@@ -1301,7 +1353,7 @@ function mk_files_header(taglist) {
html = html.concat([ html = html.concat([
'<th name="ext"><span>T</span></th>', '<th name="ext"><span>T</span></th>',
'<th name="ts"><span>Date</span></th>', '<th name="ts"><span>Date</span></th>',
'</thead>', '</tr></thead>',
]); ]);
return html; return html;
} }
@@ -1335,13 +1387,13 @@ var filecols = (function () {
continue; continue;
var name = span[0].textContent, var name = span[0].textContent,
cls = ''; cls = false;
if (has(hidden, name)) { if (has(hidden, name)) {
ohidden.push(a); ohidden.push(a);
cls = ' min'; cls = true;
} }
ths[a].className = ths[a].className.replace(/ *min */, " ") + cls; clmod(ths[a], 'min', cls)
} }
for (var a = 0; a < ncols; a++) { for (var a = 0; a < ncols; a++) {
var cls = has(ohidden, a) ? 'min' : ''; var cls = has(ohidden, a) ? 'min' : '';
@@ -1512,12 +1564,15 @@ var mukey = (function () {
function addcrc() { function addcrc() {
var links = document.querySelectorAll( var links = document.querySelectorAll(
'#files>tbody>tr>td:nth-child(2)>' + ( '#files>tbody>tr>td:first-child+td>' + (
ebi('unsearch') ? 'div>a:last-child' : 'a')); ebi('unsearch') ? 'div>a:last-child' : 'a'));
for (var a = 0, aa = links.length; a < aa; a++) for (var a = 0, aa = links.length; a < aa; a++)
if (!links[a].getAttribute('id')) if (!links[a].getAttribute('id')) {
links[a].setAttribute('id', 'f-' + crc32(links[a].textContent)); 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 () { var arcfmt = (function () {
if (!ebi('arc_fmt')) if (!ebi('arc_fmt'))
return { "render": function () { } }; return { "render": function () { } };
@@ -1620,27 +1693,26 @@ var msel = (function () {
return names; return names;
} }
function selui() { function selui() {
var fun = getsel().length ? "add" : "remove"; clmod(ebi('wtoggle'), 'sel', getsel().length);
ebi('wtoggle').classList[fun]('sel');
} }
function seltgl(e) { function seltgl(e) {
ev(e); ev(e);
var tr = this.parentNode; var tr = this.parentNode;
tr.classList.toggle('sel'); clmod(tr, 'sel', 't');
selui(); selui();
} }
function evsel(e, fun) { function evsel(e, fun) {
ev(e); ev(e);
var trs = document.querySelectorAll('#files tbody tr'); var trs = document.querySelectorAll('#files tbody tr');
for (var a = 0, aa = trs.length; a < aa; a++) for (var a = 0, aa = trs.length; a < aa; a++)
trs[a].classList[fun]('sel'); clmod(trs[a], 'sel', fun);
selui(); selui();
} }
ebi('selall').onclick = function (e) { ebi('selall').onclick = function (e) {
evsel(e, "add"); evsel(e, "add");
}; };
ebi('selinv').onclick = function (e) { ebi('selinv').onclick = function (e) {
evsel(e, "toggle"); evsel(e, "t");
}; };
ebi('selzip').onclick = function (e) { ebi('selzip').onclick = function (e) {
ev(e); ev(e);

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" : ""); document.documentElement.setAttribute("class", dark ? "dark" : "");
btn.innerHTML = "go " + (dark ? "light" : "dark"); btn.innerHTML = "go " + (dark ? "light" : "dark");
if (window.localStorage) if (window.localStorage)
localStorage.setItem('darkmode', dark ? 1 : 0); localStorage.setItem('lightmode', dark ? 0 : 1);
}; };
btn.onclick = toggle; btn.onclick = toggle;
if (window.localStorage && localStorage.getItem('darkmode') == 1) if (window.localStorage && localStorage.getItem('lightmode') != 1)
toggle(); toggle();
})(); })();

View File

@@ -31,12 +31,12 @@ var md_opt = {
var lightswitch = (function () { var lightswitch = (function () {
var fun = function () { var fun = function () {
var dark = !!!document.documentElement.getAttribute("class"); var dark = !document.documentElement.getAttribute("class");
document.documentElement.setAttribute("class", dark ? "dark" : ""); document.documentElement.setAttribute("class", dark ? "dark" : "");
if (window.localStorage) if (window.localStorage)
localStorage.setItem('darkmode', dark ? 1 : 0); localStorage.setItem('lightmode', dark ? 0 : 1);
}; };
if (window.localStorage && localStorage.getItem('darkmode') == 1) if (window.localStorage && localStorage.getItem('lightmode') != 1)
fun(); fun();
return fun; return fun;

View File

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

View File

@@ -31,6 +31,7 @@ catch (ex) {
} }
catch (ex) { } catch (ex) { }
} }
treectl.onscroll();
function up2k_flagbus() { function up2k_flagbus() {
@@ -131,6 +132,283 @@ function up2k_flagbus() {
return flag; 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) { function up2k_init(have_crypto) {
//have_crypto = false; //have_crypto = false;
var need_filereader_cache = undefined; var need_filereader_cache = undefined;
@@ -215,10 +493,6 @@ function up2k_init(have_crypto) {
var flag_en = bcfg_get('flag_en', false); var flag_en = bcfg_get('flag_en', false);
var fsearch = bcfg_get('fsearch', 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 fdom_ctr = 0;
var st = { var st = {
"files": [], "files": [],
@@ -238,6 +512,8 @@ function up2k_init(have_crypto) {
} }
}; };
var pvis = new U2pvis("bz", '#u2cards');
var bobslice = null; var bobslice = null;
if (window.File) if (window.File)
bobslice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice; bobslice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
@@ -374,7 +650,7 @@ function up2k_init(have_crypto) {
for (var a = 0, aa = Math.min(20, bad_files.length); a < aa; a++) for (var a = 0, aa = Math.min(20, bad_files.length); a < aa; a++)
msg += '-- ' + bad_files[a] + '\n'; 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'; 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); alert(msg);
@@ -412,11 +688,12 @@ function up2k_init(have_crypto) {
if (skip) if (skip)
continue; continue;
var tr = document.createElement('tr'); pvis.addfile([
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); fsearch ? esc(entry.name) : linksplit(
tr.getElementsByTagName('td')[0].innerHTML = fsearch ? entry.name : linksplit(esc(entry.purl + entry.name)).join(' '); esc(uricom_dec(entry.purl)[0] + entry.name)).join(' '),
ebi('u2tab').appendChild(tr); '📐 hash',
''
], fobj.size);
st.files.push(entry); st.files.push(entry);
st.todo.hash.push(entry); st.todo.hash.push(entry);
} }
@@ -437,7 +714,10 @@ function up2k_init(have_crypto) {
for (var a = 0; a < st.files.length; a++) { for (var a = 0; a < st.files.length; a++) {
var t = st.files[a]; var t = st.files[a];
if (t.done && t.name) { 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); tr.parentNode.removeChild(tr);
t.name = undefined; t.name = undefined;
} }
@@ -460,7 +740,8 @@ function up2k_init(have_crypto) {
function hashing_permitted() { function hashing_permitted() {
if (multitask) { if (multitask) {
var ahead = st.bytes.hashed - st.bytes.uploaded; var ahead = st.bytes.hashed - st.bytes.uploaded;
return ahead < 1024 * 1024 * 128; return ahead < 1024 * 1024 * 128 &&
st.todo.handshake.length + st.busy.handshake.length < 16;
} }
return handshakes_permitted() && 0 == return handshakes_permitted() && 0 ==
st.todo.handshake.length + st.todo.handshake.length +
@@ -524,7 +805,7 @@ function up2k_init(have_crypto) {
if (st.todo.handshake.length > 0 && if (st.todo.handshake.length > 0 &&
st.busy.handshake.length == 0 && ( st.busy.handshake.length == 0 && (
st.todo.handshake[0].t3 || ( st.todo.handshake[0].t4 || (
handshakes_permitted() && handshakes_permitted() &&
st.busy.upload.length < parallel_uploads st.busy.upload.length < parallel_uploads
) )
@@ -693,13 +974,8 @@ function up2k_init(have_crypto) {
if (!need_filereader_cache) if (!need_filereader_cache)
subchunks = 1; subchunks = 1;
var pb_html = ''; pvis.setab(t.n, nchunks);
var pb_perc = 99.9 / nchunks; pvis.move(t.n, 'bz');
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(); var reader = new FileReader();
@@ -717,8 +993,6 @@ function up2k_init(have_crypto) {
reader.readAsArrayBuffer( reader.readAsArrayBuffer(
bobslice.call(t.fobj, car, cdr)); bobslice.call(t.fobj, car, cdr));
prog(t.n, nchunk, col_hashing);
}; };
var segm_load = function (e) { var segm_load = function (e) {
@@ -762,9 +1036,8 @@ function up2k_init(have_crypto) {
var b64str = buf2b64(hslice).replace(/=$/, ''); var b64str = buf2b64(hslice).replace(/=$/, '');
t.hash.push(b64str); t.hash.push(b64str);
prog(t.n, nchunk, col_hashed); pvis.hashed(t);
if (++nchunk < nchunks) { if (++nchunk < nchunks) {
prog(t.n, nchunk, col_hashing);
return segm_next(); return segm_next();
} }
@@ -774,13 +1047,14 @@ function up2k_init(have_crypto) {
alert('{0} ms, {1} MB/s\n'.format(t.t2 - t.t1, spd.toFixed(3)) + t.hash.join('\n')); 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'; pvis.seth(t.n, 2, 'hashing done');
pvis.seth(t.n, 1, '📦 wait');
st.busy.hash.splice(st.busy.hash.indexOf(t), 1); st.busy.hash.splice(st.busy.hash.indexOf(t), 1);
st.todo.handshake.push(t); st.todo.handshake.push(t);
}; };
var segm_err = function () { 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(); segm_next();
@@ -810,7 +1084,7 @@ function up2k_init(have_crypto) {
else { else {
smsg = 'found'; smsg = 'found';
var hit = response.hits[0], var hit = response.hits[0],
msg = linksplit(hit.rp).join(''), msg = linksplit(esc(hit.rp)).join(''),
tr = unix2iso(hit.ts), tr = unix2iso(hit.ts),
tu = unix2iso(t.lmod), tu = unix2iso(t.lmod),
diff = parseInt(t.lmod) - parseInt(hit.ts), diff = parseInt(t.lmod) - parseInt(hit.ts),
@@ -819,8 +1093,9 @@ function up2k_init(have_crypto) {
msg += '<br /><small>' + tr + ' (srv), ' + tu + ' (You), ' + sdiff + '</span></span>'; msg += '<br /><small>' + tr + ' (srv), ' + tu + ' (You), ' + sdiff + '</span></span>';
} }
ebi('f{0}p'.format(t.n)).innerHTML = msg; pvis.seth(t.n, 2, msg);
ebi('f{0}t'.format(t.n)).innerHTML = smsg; 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.busy.handshake.splice(st.busy.handshake.indexOf(t), 1);
st.bytes.uploaded += t.size; st.bytes.uploaded += t.size;
t.done = true; t.done = true;
@@ -831,7 +1106,15 @@ function up2k_init(have_crypto) {
if (response.name !== t.name) { if (response.name !== t.name) {
// file exists; server renamed us // file exists; server renamed us
t.name = response.name; 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 = []; t.postlist = [];
@@ -844,10 +1127,11 @@ function up2k_init(have_crypto) {
missing[a], JSON.stringify(t))); missing[a], JSON.stringify(t)));
t.postlist.push(idx); 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) pvis.setat(t.n, cbd);
? col_uploaded : col_hashed); pvis.prog(t, 0, cbd[0]);
var done = true; var done = true;
var msg = '&#x1f3b7;&#x1f41b;'; var msg = '&#x1f3b7;&#x1f41b;';
@@ -861,49 +1145,56 @@ function up2k_init(have_crypto) {
msg = 'uploading'; msg = 'uploading';
done = false; 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); st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1);
if (done) { if (done) {
t.done = true; t.done = true;
st.bytes.uploaded += t.size - t.bytes_uploaded; st.bytes.uploaded += t.size - t.bytes_uploaded;
var spd1 = (t.size / ((t.t2 - t.t1) / 1000.)) / (1024 * 1024.); var spd1 = (t.size / ((t.t2 - t.t1) / 1000.)) / (1024 * 1024.);
var spd2 = (t.size / ((t.t3 - t.t2) / 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( pvis.seth(t.n, 2, 'hash {0}, up {1} MB/s'.format(
spd1.toFixed(2), spd2.toFixed(2)); spd1.toFixed(2), spd2.toFixed(2)));
pvis.move(t.n, 'ok');
} }
else t.t3 = undefined; else t.t4 = undefined;
tasker(); tasker();
} }
else { else {
var err = ""; var err = "",
var rsp = (xhr.responseText + ''); rsp = (xhr.responseText + ''),
ofs = rsp.lastIndexOf('\nURL: ');
if (ofs !== -1)
rsp = rsp.slice(0, ofs);
if (rsp.indexOf('<pre>') === 0)
rsp = rsp.slice(5);
st.bytes.uploaded += t.size;
if (rsp.indexOf('partial upload exists') !== -1 || if (rsp.indexOf('partial upload exists') !== -1 ||
rsp.indexOf('file already exists') !== -1) { rsp.indexOf('file already exists') !== -1) {
err = rsp; err = rsp;
var ofs = err.lastIndexOf(' : ');
if (ofs > 0)
err = err.slice(0, ofs);
ofs = err.indexOf('\n/'); ofs = err.indexOf('\n/');
if (ofs !== -1) { if (ofs !== -1) {
err = err.slice(0, ofs + 1) + linksplit(err.slice(ofs + 2, -1)).join(' '); err = err.slice(0, ofs + 1) + linksplit(err.slice(ofs + 2)).join(' ');
} }
} }
if (err != "") { if (err != "") {
ebi('f{0}t'.format(t.n)).innerHTML = "ERROR"; pvis.seth(t.n, 1, "ERROR");
ebi('f{0}p'.format(t.n)).innerHTML = err; pvis.seth(t.n, 2, err);
pvis.move(t.n, 'ng');
st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1); st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1);
tasker(); tasker();
return; return;
} }
alert("server broke (error {0}):\n\"{1}\"\n".format( alert("server broke; hs-err {0} on file [{1}]:\n".format(
xhr.status, xhr.status, t.name) + (
(xhr.response && xhr.response.err) || (xhr.response && xhr.response.err) ||
(xhr.responseText && xhr.responseText) || (xhr.responseText && xhr.responseText) ||
"no further information")); "no further information"));
} }
}; };
@@ -932,8 +1223,10 @@ function up2k_init(have_crypto) {
var npart = upt.npart; var npart = upt.npart;
var t = st.files[upt.nfile]; 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 chunksize = get_chunksize(t.size);
var car = npart * chunksize; var car = npart * chunksize;
@@ -941,68 +1234,40 @@ function up2k_init(have_crypto) {
if (cdr >= t.size) if (cdr >= t.size)
cdr = t.size; cdr = t.size;
var reader = new FileReader(); var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function (xev) {
reader.onerror = function () { pvis.prog(t, npart, xev.loaded);
alert('y o u b r o k e i t\n\n(was that a folder? just files please)');
}; };
xhr.onload = function (xev) {
reader.onload = function (e) { if (xhr.status == 200) {
var xhr = new XMLHttpRequest(); pvis.prog(t, npart, cdr - car);
xhr.upload.onprogress = function (xev) { st.bytes.uploaded += cdr - car;
var perc = xev.loaded / (cdr - car) * 100; t.bytes_uploaded += cdr - car;
prog(t.n, npart, '', perc); st.busy.upload.splice(st.busy.upload.indexOf(upt), 1);
}; t.postlist.splice(t.postlist.indexOf(npart), 1);
xhr.onload = function (xev) { if (t.postlist.length == 0) {
if (xhr.status == 200) { t.t4 = new Date().getTime();
prog(t.n, npart, col_uploaded); pvis.seth(t.n, 1, 'verifying');
st.bytes.uploaded += cdr - car; st.todo.handshake.unshift(t);
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.t3 = new Date().getTime();
ebi('f{0}t'.format(t.n)).innerHTML = 'verifying';
st.todo.handshake.unshift(t);
}
tasker();
} }
else tasker();
alert("server broke (error {0}):\n\"{1}\"\n".format( }
xhr.status, else
alert("server broke; cu-err {0} on file [{1}]:\n".format(
xhr.status, t.name) + (
(xhr.response && xhr.response.err) || (xhr.response && xhr.response.err) ||
(xhr.responseText && xhr.responseText) || (xhr.responseText && xhr.responseText) ||
"no further information")); "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');
xhr.overrideMimeType('Content-Type', 'application/octet-stream');
xhr.responseType = 'text';
xhr.send(e.target.result);
}; };
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)); xhr.responseType = 'text';
} xhr.send(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';
}
} }
///// /////
@@ -1023,6 +1288,7 @@ function up2k_init(have_crypto) {
if (btn.parentNode !== parent) { if (btn.parentNode !== parent) {
parent.appendChild(btn); parent.appendChild(btn);
ebi('u2conf').setAttribute('class', wide ? 'has_btn' : ''); ebi('u2conf').setAttribute('class', wide ? 'has_btn' : '');
ebi('u2cards').setAttribute('class', wide ? 'w' : '');
} }
} }
window.addEventListener('resize', onresize); window.addEventListener('resize', onresize);
@@ -1058,14 +1324,14 @@ function up2k_init(have_crypto) {
var obj = ebi('nthread'); var obj = ebi('nthread');
if (dir.target) { if (dir.target) {
obj.style.background = '#922'; clmod(obj, 'err', 1);
var v = Math.floor(parseInt(obj.value)); var v = Math.floor(parseInt(obj.value));
if (v < 1 || v > 8 || v !== v) if (v < 1 || v > 8 || v !== v)
return; return;
parallel_uploads = v; parallel_uploads = v;
swrite('nthread', v); swrite('nthread', v);
obj.style.background = '#444'; clmod(obj, 'err');
return; return;
} }

View File

@@ -47,6 +47,11 @@
margin: -1.5em 0; margin: -1.5em 0;
padding: .8em 0; padding: .8em 0;
width: 100%; width: 100%;
max-width: 12em;
display: inline-block;
}
#u2conf #u2btn_cw {
text-align: right;
} }
#u2notbtn { #u2notbtn {
display: none; display: none;
@@ -72,6 +77,7 @@
} }
#u2tab td:nth-child(2) { #u2tab td:nth-child(2) {
width: 5em; width: 5em;
white-space: nowrap;
} }
#u2tab td:nth-child(3) { #u2tab td:nth-child(3) {
width: 40%; width: 40%;
@@ -83,6 +89,35 @@
#u2tab tr+tr:hover td { #u2tab tr+tr:hover td {
background: #222; 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 { #u2conf {
margin: 1em auto; margin: 1em auto;
width: 30em; width: 30em;
@@ -106,6 +141,9 @@
font-size: 1.2em; font-size: 1.2em;
padding: .15em 0; padding: .15em 0;
} }
#u2conf .txtbox.err {
background: #922;
}
#u2conf a { #u2conf a {
color: #fff; color: #fff;
background: #c38; background: #c38;
@@ -193,24 +231,6 @@
.prog { .prog {
font-family: monospace; font-family: monospace;
} }
.prog>div {
display: inline-block;
position: relative;
overflow: hidden;
margin: 0;
padding: 0;
height: 1.1em;
margin-bottom: -.15em;
box-shadow: -1px -1px 0 inset rgba(255,255,255,0.1);
}
.prog>div>div {
width: 0%;
position: absolute;
left: 0;
top: 0;
bottom: 0;
background: #0a0;
}
#u2tab a>span { #u2tab a>span {
font-weight: bold; font-weight: bold;
font-style: italic; font-style: italic;
@@ -221,3 +241,35 @@
float: right; float: right;
margin-bottom: -.3em; margin-bottom: -.3em;
} }
html.light #u2btn {
box-shadow: .4em .4em 0 #ccc;
}
html.light #u2cards span {
color: #000;
}
html.light #u2cards a {
background: linear-gradient(to bottom, #eee, #fff);
}
html.light #u2cards a.act {
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="op_bup" class="opview opbox act">
<div id="u2err"></div> <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="hidden" name="act" value="bput" />
<input type="file" name="f" multiple><br /> <input type="file" name="f" multiple><br />
<input type="submit" value="start upload"> <input type="submit" value="start upload">
@@ -9,7 +9,7 @@
</div> </div>
<div id="op_mkdir" class="opview opbox act"> <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="hidden" name="act" value="mkdir" />
<input type="text" name="name" size="30"> <input type="text" name="name" size="30">
<input type="submit" value="mkdir"> <input type="submit" value="mkdir">
@@ -17,15 +17,15 @@
</div> </div>
<div id="op_new_md" class="opview opbox"> <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="hidden" name="act" value="new_md" />
<input type="text" name="name" size="30"> <input type="text" name="name" size="30">
<input type="submit" value="create doc"> <input type="submit" value="create doc">
</form> </form>
</div> </div>
<div id="op_msg" class="opview opbox"> <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="text" name="msg" size="30">
<input type="submit" value="send msg"> <input type="submit" value="send msg">
</form> </form>
@@ -79,12 +79,23 @@
</div> </div>
</div> </div>
<div id="u2cards">
<a href="#" act="ok">ok <span>0</span></a><a
href="#" act="ng">ng <span>0</span></a><a
href="#" act="done">done <span>0</span></a><a
href="#" act="bz" class="act">busy <span>0</span></a><a
href="#" act="q">que <span>0</span></a>
</div>
<table id="u2tab"> <table id="u2tab">
<tr> <thead>
<td>filename</td> <tr>
<td>status</td> <td>filename</td>
<td>progress<a href="#" id="u2cleanup">cleanup</a></td> <td>status</td>
</tr> <td>progress<a href="#" id="u2cleanup">cleanup</a></td>
</tr>
</thead>
<tbody></tbody>
</table> </table>
<p id="u2foot"></p> <p id="u2foot"></p>

View File

@@ -1,5 +1,14 @@
"use strict"; "use strict";
if (!window['console'])
window['console'] = {
"log": function (msg) { }
};
var clickev = window.Touch ? 'touchstart' : 'click';
// error handler for mobile devices // error handler for mobile devices
function hcroak(msg) { function hcroak(msg) {
document.body.innerHTML = msg; document.body.innerHTML = msg;
@@ -110,7 +119,16 @@ function crc32(str) {
crc = (crc >>> 8) ^ crctab[(crc ^ str.charCodeAt(i)) & 0xFF]; crc = (crc >>> 8) ^ crctab[(crc ^ str.charCodeAt(i)) & 0xFF];
} }
return ((crc ^ (-1)) >>> 0).toString(16); return ((crc ^ (-1)) >>> 0).toString(16);
}; }
function clmod(obj, cls, add) {
var re = new RegExp('\\s*\\b' + cls + '\\s*\\b', 'g');
if (add == 't')
add = !re.test(obj.className);
obj.className = obj.className.replace(re, ' ') + (add ? ' ' + cls : '');
}
function sortfiles(nodes) { function sortfiles(nodes) {
@@ -149,7 +167,7 @@ function sortfiles(nodes) {
} }
} }
var onodes = nodes.map((x) => x); var onodes = nodes.map(function (x) { return x; });
nodes.sort(function (n1, n2) { nodes.sort(function (n1, n2) {
var v1 = n1._sv, var v1 = n1._sv,
v2 = n2._sv; v2 = n2._sv;
@@ -280,16 +298,16 @@ function opclick(e) {
function goto(dest) { function goto(dest) {
var obj = document.querySelectorAll('.opview.act'); var obj = document.querySelectorAll('.opview.act');
for (var a = obj.length - 1; a >= 0; a--) for (var a = obj.length - 1; a >= 0; a--)
obj[a].classList.remove('act'); clmod(obj[a], 'act');
obj = document.querySelectorAll('#ops>a'); obj = document.querySelectorAll('#ops>a');
for (var a = obj.length - 1; a >= 0; a--) for (var a = obj.length - 1; a >= 0; a--)
obj[a].classList.remove('act'); clmod(obj[a], 'act');
if (dest) { if (dest) {
var ui = ebi('op_' + dest); var ui = ebi('op_' + dest);
ui.classList.add('act'); clmod(ui, 'act', true);
document.querySelector('#ops>a[data-dest=' + dest + ']').classList.add('act'); document.querySelector('#ops>a[data-dest=' + dest + ']').className += " act";
var fn = window['goto_' + dest]; var fn = window['goto_' + dest];
if (fn) if (fn)
@@ -476,8 +494,7 @@ function bcfg_upd_ui(name, val) {
if (o.getAttribute('type') == 'checkbox') if (o.getAttribute('type') == 'checkbox')
o.checked = val; o.checked = val;
else if (o) { else if (o) {
var fun = val ? 'add' : 'remove'; clmod(o, 'on', val);
o.classList[fun]('on');
} }
} }

View File

@@ -73,6 +73,13 @@ shab64() { sp=$1; f="$2"; v=0; sz=$(stat -c%s "$f"); while true; do w=$((v+sp*10
command -v gdate && date() { gdate "$@"; }; while true; do t=$(date +%s.%N); (time wget http://127.0.0.1:3923/?ls -qO- | jq -C '.files[]|{sz:.sz,ta:.tags.artist,tb:.tags.".bpm"}|del(.[]|select(.==null))' | awk -F\" '/"/{t[$2]++} END {for (k in t){v=t[k];p=sprintf("%" (v+1) "s",v);gsub(/ /,"#",p);printf "\033[36m%s\033[33m%s ",k,p}}') 2>&1 | awk -v ts=$t 'NR==1{t1=$0} NR==2{sub(/.*0m/,"");sub(/s$/,"");t2=$0;c=2; if(t2>0.3){c=3} if(t2>0.8){c=1} } END{sub(/[0-9]{6}$/,"",ts);printf "%s \033[3%dm%s %s\033[0m\n",ts,c,t2,t1}'; sleep 0.1 || break; done command -v gdate && date() { gdate "$@"; }; while true; do t=$(date +%s.%N); (time wget http://127.0.0.1:3923/?ls -qO- | jq -C '.files[]|{sz:.sz,ta:.tags.artist,tb:.tags.".bpm"}|del(.[]|select(.==null))' | awk -F\" '/"/{t[$2]++} END {for (k in t){v=t[k];p=sprintf("%" (v+1) "s",v);gsub(/ /,"#",p);printf "\033[36m%s\033[33m%s ",k,p}}') 2>&1 | awk -v ts=$t 'NR==1{t1=$0} NR==2{sub(/.*0m/,"");sub(/s$/,"");t2=$0;c=2; if(t2>0.3){c=3} if(t2>0.8){c=1} } END{sub(/[0-9]{6}$/,"",ts);printf "%s \033[3%dm%s %s\033[0m\n",ts,c,t2,t1}'; sleep 0.1 || break; done
##
## js oneliners
# get all up2k search result URLs
var t=[]; var b=document.location.href.split('#')[0].slice(0, -1); document.querySelectorAll('#u2tab .prog a').forEach((x) => {t.push(b+encodeURI(x.getAttribute("href")))}); console.log(t.join("\n"));
## ##
## sqlite3 stuff ## sqlite3 stuff
@@ -139,6 +146,7 @@ dbg.asyncStore.pendingBreakpoints = {}
# fix firefox phantom breakpoints # fix firefox phantom breakpoints
about:config >> devtools.debugger.prefs-schema-version = -1 about:config >> devtools.debugger.prefs-schema-version = -1
## ##
## http 206 ## http 206

View File

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

View File

@@ -2,7 +2,7 @@
# coding: latin-1 # coding: latin-1
from __future__ import print_function, unicode_literals 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 run me with any version of python, i will unpack and run copyparty
@@ -26,6 +26,7 @@ CKSUM = None
STAMP = None STAMP = None
PY2 = sys.version_info[0] == 2 PY2 = sys.version_info[0] == 2
WINDOWS = sys.platform == "win32"
sys.dont_write_bytecode = True sys.dont_write_bytecode = True
me = os.path.abspath(os.path.realpath(__file__)) me = os.path.abspath(os.path.realpath(__file__))
cpp = None cpp = None
@@ -343,6 +344,21 @@ def get_payload():
break 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): def confirm(rv):
msg() msg()
msg(traceback.format_exc()) msg(traceback.format_exc())
@@ -362,15 +378,20 @@ def run(tmp, j2ver):
msg("sfxdir:", tmp) msg("sfxdir:", tmp)
msg() msg()
# "systemd-tmpfiles-clean.timer"?? HOW do you even come up with this shit # block systemd-tmpfiles-clean.timer
try: try:
import fcntl import fcntl
fd = os.open(tmp, os.O_RDONLY) fd = os.open(tmp, os.O_RDONLY)
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
tmp = os.readlink(tmp) # can't flock a symlink, even with O_NOFOLLOW tmp = os.readlink(tmp) # can't flock a symlink, even with O_NOFOLLOW
except: except Exception as ex:
pass 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")] ld = [tmp, os.path.join(tmp, "dep-j2")]
if j2ver: if j2ver:
@@ -380,7 +401,10 @@ def run(tmp, j2ver):
sys.path.insert(0, x) sys.path.insert(0, x)
try: try:
runpy.run_module(str("copyparty"), run_name=str("__main__")) from copyparty.__main__ import main as copyparty
copyparty()
except SystemExit as ex: except SystemExit as ex:
if ex.code: if ex.code:
confirm(ex.code) confirm(ex.code)