Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
264b497681 | ||
|
|
372b949622 | ||
|
|
789a602914 | ||
|
|
093e955100 | ||
|
|
c32a89bebf | ||
|
|
c0bebe9f9f | ||
|
|
57579b2fe5 | ||
|
|
51d14a6b4d | ||
|
|
c50f1b64e5 | ||
|
|
98aaab02c5 | ||
|
|
0fc7973d8b | ||
|
|
10362aa02e | ||
|
|
0a8e759fe6 | ||
|
|
d70981cdd1 | ||
|
|
e08c03b886 | ||
|
|
56086e8984 | ||
|
|
1aa9033022 | ||
|
|
076e103d53 | ||
|
|
38c00ea8fc | ||
|
|
415757af43 | ||
|
|
e72ed8c0ed | ||
|
|
32f9c6b5bb | ||
|
|
6251584ef6 | ||
|
|
f3e413bc28 | ||
|
|
6f6cc8f3f8 | ||
|
|
8b081e9e69 | ||
|
|
c8a510d10e | ||
|
|
6f834f6679 | ||
|
|
cf2d6650ac | ||
|
|
cd52dea488 | ||
|
|
6ea75df05d | ||
|
|
4846e1e8d6 |
14
README.md
14
README.md
@@ -62,6 +62,14 @@ download [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/do
|
||||
|
||||
running the sfx without arguments (for example doubleclicking it on Windows) will give everyone full access to the current folder; see `-h` for help if you want accounts and volumes etc
|
||||
|
||||
some recommended options:
|
||||
* `-e2dsa` enables general file indexing, see [search configuration](#search-configuration)
|
||||
* `-e2ts` enables audio metadata indexing (needs either FFprobe or mutagen), see [optional dependencies](#optional-dependencies)
|
||||
* `-v /mnt/music:/music:r:afoo -a foo:bar` shares `/mnt/music` as `/music`, `r`eadable by anyone, with user `foo` as `a`dmin (read/write), password `bar`
|
||||
* replace `:r:afoo` with `:rfoo` to only make the folder readable by `foo` and nobody else
|
||||
* in addition to `r`ead and `a`dmin, `w`rite makes a folder write-only, so cannot list/access files in it
|
||||
* `--ls '**,*,ln,p,r'` to crash on startup if any of the volumes contain a symlink which point outside the volume, as that could give users unintended access
|
||||
|
||||
you may also want these, especially on servers:
|
||||
* [contrib/systemd/copyparty.service](contrib/systemd/copyparty.service) to run copyparty as a systemd service
|
||||
* [contrib/nginx/copyparty.conf](contrib/nginx/copyparty.conf) to reverse-proxy behind nginx (for better https)
|
||||
@@ -101,7 +109,7 @@ summary: all planned features work! now please enjoy the bloatening
|
||||
* ☑ FUSE client (read-only)
|
||||
* browser
|
||||
* ☑ tree-view
|
||||
* ☑ media player
|
||||
* ☑ audio player
|
||||
* ☑ thumbnails
|
||||
* ☑ images using Pillow
|
||||
* ☑ videos using FFmpeg
|
||||
@@ -163,7 +171,7 @@ the browser has the following hotkeys
|
||||
* `0..9` jump to 10%..90%
|
||||
* `U/O` skip 10sec back/forward
|
||||
* `J/L` prev/next song
|
||||
* `J` also starts playing the folder
|
||||
* `M` play/pause (also starts playing the folder)
|
||||
* in the grid view:
|
||||
* `S` toggle multiselect
|
||||
* `A/D` zoom
|
||||
@@ -301,7 +309,7 @@ the same arguments can be set as volume flags, in addition to `d2d` and `d2t` fo
|
||||
* `-v ~/music::r:cd2t` disables all `-e2t*` (tags), does not affect `-e2d*`
|
||||
|
||||
note:
|
||||
* `e2tsr` is probably always overkill, since `e2ds`/`e2dsa` would pick up any file modifications and cause `e2ts` to reindex those
|
||||
* `e2tsr` is probably always overkill, since `e2ds`/`e2dsa` would pick up any file modifications and `e2ts` would then reindex those
|
||||
* the rescan button in the admin panel has no effect unless the volume has `-e2ds` or higher
|
||||
|
||||
you can choose to only index filename/path/size/last-modified (and not the hash of the file contents) by setting `--no-hash` or the volume-flag `cdhash`, this has the following consequences:
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# when running copyparty behind a reverse-proxy,
|
||||
# make sure that copyparty allows at least as many clients as the proxy does,
|
||||
# so run copyparty with -nc 512 if your nginx has the default limits
|
||||
# (worker_processes 1, worker_connections 512)
|
||||
|
||||
upstream cpp {
|
||||
server 127.0.0.1:3923;
|
||||
keepalive 120;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# coding: utf-8
|
||||
|
||||
VERSION = (0, 11, 14)
|
||||
VERSION = (0, 11, 19)
|
||||
CODENAME = "the grid"
|
||||
BUILD_DT = (2021, 6, 14)
|
||||
BUILD_DT = (2021, 6, 19)
|
||||
|
||||
S_VERSION = ".".join(map(str, VERSION))
|
||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||
|
||||
@@ -68,6 +68,7 @@ class MpWorker(object):
|
||||
|
||||
# self.logw("work: [{}]".format(d[0]))
|
||||
if dest == "shutdown":
|
||||
self.httpsrv.shutdown()
|
||||
self.logw("ok bye")
|
||||
sys.exit(0)
|
||||
return
|
||||
|
||||
@@ -25,6 +25,7 @@ class BrokerThr(object):
|
||||
|
||||
def shutdown(self):
|
||||
# self.log("broker", "shutting down")
|
||||
self.httpsrv.shutdown()
|
||||
pass
|
||||
|
||||
def put(self, want_retval, dest, *args):
|
||||
|
||||
@@ -256,10 +256,11 @@ class HttpCli(object):
|
||||
if self.is_rclone:
|
||||
return ""
|
||||
|
||||
cmap = {"pw": "cppwd"}
|
||||
kv = {
|
||||
k: v
|
||||
for k, v in self.uparam.items()
|
||||
if k not in rm and self.cookies.get(k) != v
|
||||
if k not in rm and self.cookies.get(cmap.get(k, k)) != v
|
||||
}
|
||||
kv.update(add)
|
||||
if not kv:
|
||||
@@ -581,10 +582,17 @@ class HttpCli(object):
|
||||
try:
|
||||
dst = os.path.join(vfs.realpath, rem)
|
||||
os.makedirs(fsenc(dst))
|
||||
except:
|
||||
if not os.path.isdir(fsenc(dst)):
|
||||
except OSError as ex:
|
||||
if ex.errno == 13:
|
||||
raise Pebkac(500, "the server OS denied write-access")
|
||||
|
||||
if ex.errno == 17:
|
||||
raise Pebkac(400, "some file got your folder name")
|
||||
|
||||
raise Pebkac(500, min_ex())
|
||||
except:
|
||||
raise Pebkac(500, min_ex())
|
||||
|
||||
x = self.conn.hsrv.broker.put(True, "up2k.handle_json", body)
|
||||
ret = x.get()
|
||||
if sub:
|
||||
@@ -769,8 +777,13 @@ class HttpCli(object):
|
||||
|
||||
try:
|
||||
os.mkdir(fsenc(fn))
|
||||
except OSError as ex:
|
||||
if ex.errno == 13:
|
||||
raise Pebkac(500, "the server OS denied write-access")
|
||||
|
||||
raise Pebkac(500, "mkdir failed:\n" + min_ex())
|
||||
except:
|
||||
raise Pebkac(500, "mkdir failed, check the logs")
|
||||
raise Pebkac(500, min_ex())
|
||||
|
||||
vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/")
|
||||
self.redirect(vpath)
|
||||
@@ -1187,7 +1200,7 @@ class HttpCli(object):
|
||||
#
|
||||
# send reply
|
||||
|
||||
if not is_compressed:
|
||||
if not is_compressed and "cache" not in self.uparam:
|
||||
self.out_headers.update(NO_CACHE)
|
||||
|
||||
self.out_headers["Accept-Ranges"] = "bytes"
|
||||
|
||||
@@ -43,6 +43,7 @@ class HttpConn(object):
|
||||
self.ico = Ico(self.args)
|
||||
|
||||
self.t0 = time.time()
|
||||
self.stopping = False
|
||||
self.nbyte = 0
|
||||
self.workload = 0
|
||||
self.u2idx = None
|
||||
@@ -50,6 +51,14 @@ class HttpConn(object):
|
||||
self.lf_url = re.compile(self.args.lf_url) if self.args.lf_url else None
|
||||
self.set_rproxy()
|
||||
|
||||
def shutdown(self):
|
||||
self.stopping = True
|
||||
try:
|
||||
self.s.shutdown(socket.SHUT_RDWR)
|
||||
self.s.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
def set_rproxy(self, ip=None):
|
||||
if ip is None:
|
||||
color = 36
|
||||
@@ -163,7 +172,7 @@ class HttpConn(object):
|
||||
self.log("client rejected our certificate (nice)")
|
||||
|
||||
elif "ALERT_CERTIFICATE_UNKNOWN" in em:
|
||||
# chrome-android keeps doing this
|
||||
# android-chrome keeps doing this
|
||||
pass
|
||||
|
||||
else:
|
||||
@@ -174,7 +183,7 @@ class HttpConn(object):
|
||||
if not self.sr:
|
||||
self.sr = Unrecv(self.s)
|
||||
|
||||
while True:
|
||||
while not self.stopping:
|
||||
if self.is_mp:
|
||||
self.workload += 50
|
||||
if self.workload >= 2 ** 31:
|
||||
|
||||
@@ -80,7 +80,14 @@ class HttpSrv(object):
|
||||
return len(self.clients)
|
||||
|
||||
def shutdown(self):
|
||||
self.log("ok bye")
|
||||
clients = list(self.clients.keys())
|
||||
for cli in clients:
|
||||
try:
|
||||
cli.shutdown()
|
||||
except:
|
||||
pass
|
||||
|
||||
self.log("httpsrv-n", "ok bye")
|
||||
|
||||
def thr_client(self, sck, addr):
|
||||
"""thread managing one tcp client"""
|
||||
@@ -100,25 +107,35 @@ class HttpSrv(object):
|
||||
thr.daemon = True
|
||||
thr.start()
|
||||
|
||||
fno = sck.fileno()
|
||||
try:
|
||||
if self.args.log_conn:
|
||||
self.log("%s %s" % addr, "|%sC-crun" % ("-" * 6,), c="1;30")
|
||||
|
||||
cli.run()
|
||||
|
||||
except (OSError, socket.error) as ex:
|
||||
if ex.errno not in [10038, 10054, 107, 57, 9]:
|
||||
self.log(
|
||||
"%s %s" % addr,
|
||||
"run({}): {}".format(fno, ex),
|
||||
c=6,
|
||||
)
|
||||
|
||||
finally:
|
||||
sck = cli.s
|
||||
if self.args.log_conn:
|
||||
self.log("%s %s" % addr, "|%sC-cdone" % ("-" * 7,), c="1;30")
|
||||
|
||||
try:
|
||||
fno = sck.fileno()
|
||||
sck.shutdown(socket.SHUT_RDWR)
|
||||
sck.close()
|
||||
except (OSError, socket.error) as ex:
|
||||
if not MACOS:
|
||||
self.log(
|
||||
"%s %s" % addr,
|
||||
"shut({}): {}".format(sck.fileno(), ex),
|
||||
"shut({}): {}".format(fno, ex),
|
||||
c="1;30",
|
||||
)
|
||||
if ex.errno not in [10038, 10054, 107, 57, 9]:
|
||||
|
||||
@@ -16,6 +16,7 @@ if not PY2:
|
||||
|
||||
def have_ff(cmd):
|
||||
if PY2:
|
||||
print("# checking {}".format(cmd))
|
||||
cmd = (cmd + " -version").encode("ascii").split(b" ")
|
||||
try:
|
||||
sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE).communicate()
|
||||
|
||||
@@ -21,6 +21,7 @@ class TcpSrv(object):
|
||||
self.log = hub.log
|
||||
|
||||
self.num_clients = Counter()
|
||||
self.stopping = False
|
||||
|
||||
ip = "127.0.0.1"
|
||||
eps = {ip: "local only"}
|
||||
@@ -67,7 +68,7 @@ class TcpSrv(object):
|
||||
ip, port = srv.getsockname()
|
||||
self.log("tcpsrv", "listening @ {0}:{1}".format(ip, port))
|
||||
|
||||
while True:
|
||||
while not self.stopping:
|
||||
if self.args.log_conn:
|
||||
self.log("tcpsrv", "|%sC-ncli" % ("-" * 1,), c="1;30")
|
||||
|
||||
@@ -80,6 +81,9 @@ class TcpSrv(object):
|
||||
|
||||
ready, _, _ = select.select(self.srv, [], [])
|
||||
for srv in ready:
|
||||
if self.stopping:
|
||||
break
|
||||
|
||||
sck, addr = srv.accept()
|
||||
sip, sport = srv.getsockname()
|
||||
if self.args.log_conn:
|
||||
@@ -95,6 +99,13 @@ class TcpSrv(object):
|
||||
self.hub.broker.put(False, "httpconn", sck, addr)
|
||||
|
||||
def shutdown(self):
|
||||
self.stopping = True
|
||||
try:
|
||||
for srv in self.srv:
|
||||
srv.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
self.log("tcpsrv", "ok bye")
|
||||
|
||||
def detect_interfaces(self, listen_ips):
|
||||
|
||||
@@ -339,29 +339,32 @@ class ThumbSrv(object):
|
||||
interval = self.args.th_clean
|
||||
while True:
|
||||
time.sleep(interval)
|
||||
ndirs = 0
|
||||
for vol, histpath in self.asrv.vfs.histtab.items():
|
||||
if histpath.startswith(vol):
|
||||
self.log("\033[Jcln {}/\033[A".format(histpath))
|
||||
else:
|
||||
self.log("\033[Jcln {} ({})/\033[A".format(histpath, vol))
|
||||
|
||||
self.clean(histpath)
|
||||
ndirs += self.clean(histpath)
|
||||
|
||||
self.log("\033[Jcln ok")
|
||||
self.log("\033[Jcln ok; rm {} dirs".format(ndirs))
|
||||
|
||||
def clean(self, histpath):
|
||||
# self.log("cln {}".format(histpath))
|
||||
thumbpath = os.path.join(histpath, "th")
|
||||
# self.log("cln {}".format(thumbpath))
|
||||
maxage = self.args.th_maxage
|
||||
now = time.time()
|
||||
prev_b64 = None
|
||||
prev_fp = None
|
||||
try:
|
||||
ents = os.listdir(histpath)
|
||||
ents = os.listdir(thumbpath)
|
||||
except:
|
||||
return
|
||||
return 0
|
||||
|
||||
ndirs = 0
|
||||
for f in sorted(ents):
|
||||
fp = os.path.join(histpath, f)
|
||||
fp = os.path.join(thumbpath, f)
|
||||
cmp = fp.lower().replace("\\", "/")
|
||||
|
||||
# "top" or b64 prefix/full (a folder)
|
||||
@@ -376,10 +379,11 @@ class ThumbSrv(object):
|
||||
break
|
||||
|
||||
if safe:
|
||||
ndirs += 1
|
||||
self.log("rm -rf [{}]".format(fp))
|
||||
shutil.rmtree(fp, ignore_errors=True)
|
||||
else:
|
||||
self.clean(fp)
|
||||
ndirs += self.clean(fp)
|
||||
continue
|
||||
|
||||
# thumb file
|
||||
@@ -401,3 +405,5 @@ class ThumbSrv(object):
|
||||
|
||||
prev_b64 = b64
|
||||
prev_fp = fp
|
||||
|
||||
return ndirs
|
||||
|
||||
@@ -16,7 +16,7 @@ import traceback
|
||||
import subprocess as sp
|
||||
from copy import deepcopy
|
||||
|
||||
from .__init__ import WINDOWS, ANYWIN
|
||||
from .__init__ import WINDOWS, ANYWIN, PY2
|
||||
from .util import (
|
||||
Pebkac,
|
||||
Queue,
|
||||
@@ -134,7 +134,7 @@ class Up2k(object):
|
||||
def get_state(self):
|
||||
mtpq = 0
|
||||
q = "select count(w) from mt where k = 't:mtp'"
|
||||
got_lock = self.mutex.acquire(timeout=0.5)
|
||||
got_lock = False if PY2 else self.mutex.acquire(timeout=0.5)
|
||||
if got_lock:
|
||||
for cur in self.cur.values():
|
||||
try:
|
||||
|
||||
@@ -25,6 +25,35 @@ html, body {
|
||||
body {
|
||||
padding-bottom: 5em;
|
||||
}
|
||||
#tt {
|
||||
position: fixed;
|
||||
max-width: 34em;
|
||||
background: #222;
|
||||
border: 0 solid #555;
|
||||
overflow: hidden;
|
||||
margin-top: 1em;
|
||||
padding: 0 1em;
|
||||
height: 0;
|
||||
opacity: .1;
|
||||
transition: opacity 0.14s, height 0.14s, padding 0.14s;
|
||||
box-shadow: 0 .2em .5em #222;
|
||||
border-radius: .4em;
|
||||
z-index: 9001;
|
||||
}
|
||||
#tt.show {
|
||||
padding: 1em;
|
||||
height: auto;
|
||||
border-width: .2em 0;
|
||||
opacity: 1;
|
||||
}
|
||||
#tt code {
|
||||
background: #3c3c3c;
|
||||
padding: .2em .3em;
|
||||
border-top: 1px solid #777;
|
||||
border-radius: .3em;
|
||||
font-family: monospace, monospace;
|
||||
line-height: 2em;
|
||||
}
|
||||
#path,
|
||||
#path * {
|
||||
font-size: 1em;
|
||||
@@ -53,6 +82,7 @@ body {
|
||||
#files tbody a {
|
||||
display: block;
|
||||
padding: .3em 0;
|
||||
scroll-margin-top: 45vh;
|
||||
}
|
||||
#files tbody div a {
|
||||
color: #f5a;
|
||||
@@ -68,7 +98,6 @@ a, #files tbody div a:last-child {
|
||||
text-decoration: underline;
|
||||
}
|
||||
#files thead {
|
||||
background: #333;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
@@ -76,29 +105,30 @@ a, #files tbody div a:last-child {
|
||||
color: #999;
|
||||
font-weight: normal;
|
||||
}
|
||||
#files tr:hover {
|
||||
#files tr:hover td {
|
||||
background: #1c1c1c;
|
||||
}
|
||||
#files thead th {
|
||||
padding: .5em 1.3em .3em 1.3em;
|
||||
padding: .5em .3em .3em .3em;
|
||||
border-right: 2px solid #3c3c3c;
|
||||
border-bottom: 2px solid #444;
|
||||
background: #333;
|
||||
cursor: pointer;
|
||||
}
|
||||
#files thead th+th {
|
||||
border-left: 2px solid #2a2a2a;
|
||||
}
|
||||
#files thead th:last-child {
|
||||
background: #444;
|
||||
border-radius: .7em .7em 0 0;
|
||||
border-right: none;
|
||||
}
|
||||
#files thead th:first-child {
|
||||
#files tbody {
|
||||
background: #222;
|
||||
}
|
||||
#files tbody,
|
||||
#files thead th:nth-child(2) {
|
||||
background: #222;
|
||||
border-radius: 0 .7em 0 0;
|
||||
}
|
||||
#files td {
|
||||
margin: 0;
|
||||
padding: 0 .5em;
|
||||
border-bottom: 1px solid #111;
|
||||
border-left: 1px solid #2c2c2c;
|
||||
}
|
||||
#files td+td+td {
|
||||
max-width: 30em;
|
||||
@@ -185,9 +215,17 @@ a, #files tbody div a:last-child {
|
||||
margin: -.2em;
|
||||
}
|
||||
#files tbody a.play.act {
|
||||
color: #840;
|
||||
color: #720;
|
||||
text-shadow: 0 0 .3em #b80;
|
||||
}
|
||||
#ggrid a.play,
|
||||
html.light #ggrid a.play {
|
||||
color: #fff;
|
||||
background: #750;
|
||||
border-color: #c90;
|
||||
border-top: 1px solid #da4;
|
||||
box-shadow: 0 .1em 1.2em #b83;
|
||||
}
|
||||
#files tbody tr.sel td,
|
||||
#ggrid a.sel,
|
||||
html.light #ggrid a.sel {
|
||||
@@ -209,11 +247,17 @@ html.light #ggrid a.sel {
|
||||
box-shadow: 0 .1em 1.2em #b36;
|
||||
transition: all 0.2s cubic-bezier(.2, 2.2, .5, 1); /* https://cubic-bezier.com/#.4,2,.7,1 */
|
||||
}
|
||||
#ggrid a.sel img {
|
||||
#ggrid a.sel img,
|
||||
#ggrid a.play img {
|
||||
opacity: .7;
|
||||
box-shadow: 0 0 1em #b36;
|
||||
filter: contrast(130%) brightness(107%);
|
||||
}
|
||||
#ggrid a.sel img {
|
||||
box-shadow: 0 0 1em #b36;
|
||||
}
|
||||
#ggrid a.play img {
|
||||
box-shadow: 0 0 1em #b83;
|
||||
}
|
||||
#files tr.sel a {
|
||||
color: #fff;
|
||||
}
|
||||
@@ -267,6 +311,7 @@ html.light #ggrid a.sel {
|
||||
height: 6em;
|
||||
width: 100%;
|
||||
z-index: 3;
|
||||
touch-action: none;
|
||||
transition: bottom 0.15s;
|
||||
}
|
||||
#widget.open {
|
||||
@@ -483,20 +528,56 @@ html.light #ggrid a.sel {
|
||||
margin: .5em;
|
||||
}
|
||||
.opview input[type=text] {
|
||||
color: #fff;
|
||||
background: #383838;
|
||||
color: #fff;
|
||||
border: none;
|
||||
box-shadow: 0 0 .3em #222;
|
||||
border-bottom: 1px solid #fc5;
|
||||
border-radius: .2em;
|
||||
padding: .2em .3em;
|
||||
}
|
||||
.opview input.err,
|
||||
html.light .opview input[type="text"].err {
|
||||
color: #fff;
|
||||
background: #a20;
|
||||
border-color: #f00;
|
||||
box-shadow: 0 0 .7em #f00;
|
||||
text-shadow: 1px 1px 0 #500;
|
||||
outline: none;
|
||||
}
|
||||
input[type="checkbox"]+label {
|
||||
color: #f5a;
|
||||
}
|
||||
input[type="checkbox"]:checked+label {
|
||||
color: #fc5;
|
||||
}
|
||||
input[type="radio"]:checked+label {
|
||||
color: #fc0;
|
||||
}
|
||||
html.light input[type="radio"]:checked+label {
|
||||
color: #07c;
|
||||
}
|
||||
input.eq_gain {
|
||||
width: 3em;
|
||||
text-align: center;
|
||||
margin: 0 .6em;
|
||||
}
|
||||
#audio_eq table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
#audio_eq td {
|
||||
text-align: center;
|
||||
}
|
||||
#audio_eq a.eq_step {
|
||||
font-size: 1.5em;
|
||||
display: block;
|
||||
padding: 0;
|
||||
}
|
||||
#au_eq {
|
||||
display: block;
|
||||
margin-top: .5em;
|
||||
padding: 1.3em .3em;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -563,6 +644,7 @@ input[type="checkbox"]:checked+label {
|
||||
}
|
||||
#wrap {
|
||||
margin-top: 2em;
|
||||
min-height: 90vh;
|
||||
}
|
||||
#tree {
|
||||
display: none;
|
||||
@@ -575,8 +657,15 @@ input[type="checkbox"]:checked+label {
|
||||
overscroll-behavior-y: none;
|
||||
scrollbar-color: #eb0 #333;
|
||||
}
|
||||
#treeh {
|
||||
background: #333;
|
||||
position: sticky;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
}
|
||||
#thx_ff {
|
||||
padding: 5em 0;
|
||||
/* widget */
|
||||
}
|
||||
#tree::-webkit-scrollbar-track,
|
||||
#tree::-webkit-scrollbar {
|
||||
@@ -600,6 +689,7 @@ input[type="checkbox"]:checked+label {
|
||||
box-shadow: 0 .1em .2em #222 inset;
|
||||
border-radius: .3em;
|
||||
margin: .2em;
|
||||
white-space: pre;
|
||||
position: relative;
|
||||
top: -.2em;
|
||||
}
|
||||
@@ -636,10 +726,10 @@ input[type="checkbox"]:checked+label {
|
||||
#treeul a.hl {
|
||||
color: #400;
|
||||
background: #fc4;
|
||||
border-radius: .3em;
|
||||
text-shadow: none;
|
||||
}
|
||||
#treeul a {
|
||||
border-radius: .3em;
|
||||
display: inline-block;
|
||||
}
|
||||
#treeul a+a {
|
||||
@@ -667,34 +757,20 @@ input[type="checkbox"]:checked+label {
|
||||
font-size: 2em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#files th:hover .cfg,
|
||||
#files th.min .cfg {
|
||||
#files th:hover .cfg {
|
||||
display: block;
|
||||
width: 1em;
|
||||
border-radius: .2em;
|
||||
margin: -1.3em auto 0 auto;
|
||||
background: #444;
|
||||
}
|
||||
#files th.min .cfg {
|
||||
margin: -.6em;
|
||||
}
|
||||
#files>thead>tr>th.min span {
|
||||
position: absolute;
|
||||
transform: rotate(270deg);
|
||||
background: linear-gradient(90deg, rgba(68,68,68,0), rgba(68,68,68,0.5) 70%, #444);
|
||||
margin-left: -4.6em;
|
||||
padding: .4em;
|
||||
top: 5.4em;
|
||||
width: 8em;
|
||||
text-align: right;
|
||||
letter-spacing: .04em;
|
||||
#files>thead>tr>th.min,
|
||||
#files td.min {
|
||||
display: none;
|
||||
}
|
||||
#files td:nth-child(2n) {
|
||||
color: #f5a;
|
||||
}
|
||||
#files td.min a {
|
||||
display: none;
|
||||
}
|
||||
#files tr.play td,
|
||||
#files tr.play div a {
|
||||
background: #fc4;
|
||||
@@ -709,47 +785,32 @@ input[type="checkbox"]:checked+label {
|
||||
color: #300;
|
||||
background: #fea;
|
||||
}
|
||||
#op_cfg {
|
||||
.opwide {
|
||||
max-width: none;
|
||||
margin-right: 1.5em;
|
||||
}
|
||||
#op_cfg>div>a {
|
||||
.opwide>div {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
border-left: .2em solid #4c4c4c;
|
||||
margin-left: .5em;
|
||||
padding-left: .5em;
|
||||
}
|
||||
.opwide>div.fill {
|
||||
display: block;
|
||||
}
|
||||
.opwide>div>div>a {
|
||||
line-height: 2em;
|
||||
}
|
||||
#op_cfg>div>span {
|
||||
#op_cfg>div>div>span {
|
||||
display: inline-block;
|
||||
padding: .2em .4em;
|
||||
}
|
||||
#op_cfg h3 {
|
||||
.opbox h3 {
|
||||
margin: .8em 0 0 .6em;
|
||||
padding: 0;
|
||||
border-bottom: 1px solid #555;
|
||||
}
|
||||
#opdesc {
|
||||
display: none;
|
||||
}
|
||||
#ops:hover #opdesc {
|
||||
display: block;
|
||||
background: linear-gradient(0deg,#555, #4c4c4c 80%, #444);
|
||||
box-shadow: 0 .3em 1em #222;
|
||||
padding: 1em;
|
||||
border-radius: .3em;
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
top: 6em;
|
||||
right: 1.5em;
|
||||
}
|
||||
#ops:hover #opdesc.off {
|
||||
display: none;
|
||||
}
|
||||
#opdesc code {
|
||||
background: #3c3c3c;
|
||||
padding: .2em .3em;
|
||||
border-top: 1px solid #777;
|
||||
border-radius: .3em;
|
||||
font-family: monospace, monospace;
|
||||
line-height: 2em;
|
||||
}
|
||||
#thumbs {
|
||||
opacity: .3;
|
||||
}
|
||||
@@ -856,6 +917,15 @@ html.light {
|
||||
background: #eee;
|
||||
text-shadow: none;
|
||||
}
|
||||
html.light #tt {
|
||||
background: #fff;
|
||||
border-color: #888;
|
||||
box-shadow: 0 .3em 1em rgba(0,0,0,0.4);
|
||||
}
|
||||
html.light #tt code {
|
||||
background: #060;
|
||||
color: #fff;
|
||||
}
|
||||
html.light #ops,
|
||||
html.light .opbox,
|
||||
html.light #srch_form {
|
||||
@@ -900,7 +970,10 @@ html.light #treeul a.hl {
|
||||
color: #fff;
|
||||
}
|
||||
html.light #tree li {
|
||||
border-color: #ddd #fff #f7f7f7 #fff;
|
||||
border-color: #f7f7f7 #fff #ddd #fff;
|
||||
}
|
||||
html.light #tree a:hover {
|
||||
background: #fff;
|
||||
}
|
||||
html.light #tree ul {
|
||||
border-color: #ccc;
|
||||
@@ -918,13 +991,14 @@ html.light #files {
|
||||
}
|
||||
html.light #files thead th {
|
||||
background: #eee;
|
||||
border-radius: 0;
|
||||
border: 1px solid #ccc;
|
||||
border-top: none;
|
||||
}
|
||||
html.light #files tr td {
|
||||
border-top: 1px solid #ddd;
|
||||
html.light #files thead th+th {
|
||||
border-left: 1px solid #f7f7f7;
|
||||
}
|
||||
html.light #files td {
|
||||
border-bottom: 1px solid #f7f7f7;
|
||||
border-color: #fff #fff #ddd #ddd;
|
||||
}
|
||||
html.light #files tbody tr:last-child td {
|
||||
border-bottom: .2em solid #ccc;
|
||||
@@ -932,25 +1006,28 @@ html.light #files tbody tr:last-child td {
|
||||
html.light #files td:nth-child(2n) {
|
||||
color: #d38;
|
||||
}
|
||||
html.light #files tr:hover td {
|
||||
background: #fff;
|
||||
html.light #files tr.play td:nth-child(2n) {
|
||||
color: #c16;
|
||||
}
|
||||
html.light #files tbody a.play {
|
||||
color: #c0f;
|
||||
}
|
||||
html.light tr.play td {
|
||||
html.light #files tbody a.play.act {
|
||||
color: #90c;
|
||||
}
|
||||
html.light #files tr.play td {
|
||||
background: #fc5;
|
||||
border-color: #eb1;
|
||||
}
|
||||
html.light #files tr:hover td {
|
||||
background: #fff;
|
||||
}
|
||||
html.light tr.play a {
|
||||
color: #406;
|
||||
}
|
||||
html.light #files th:hover .cfg,
|
||||
html.light #files th.min .cfg {
|
||||
html.light #files th:hover .cfg {
|
||||
background: #ccc;
|
||||
}
|
||||
html.light #files > thead > tr > th.min span {
|
||||
background: linear-gradient(90deg, rgba(204,204,204,0), rgba(204,204,204,0.5) 70%, #ccc);
|
||||
}
|
||||
html.light #blocked {
|
||||
background: #eee;
|
||||
}
|
||||
@@ -960,7 +1037,24 @@ html.light #blk_abrt a {
|
||||
box-shadow: 0 .2em .4em #ddd;
|
||||
}
|
||||
html.light #widget a {
|
||||
color: #fc5;
|
||||
color: #06a;
|
||||
}
|
||||
html.light #wtoggle,
|
||||
html.light #widgeti {
|
||||
background: #eee;
|
||||
}
|
||||
html.light #wtoggle {
|
||||
box-shadow: 0 0 .5em #bbb;
|
||||
}
|
||||
html.light #widget.open {
|
||||
border-top: .2em solid #f7f7f7;
|
||||
}
|
||||
html.light #wzip,
|
||||
html.light #wnp {
|
||||
border-color: #ccc;
|
||||
}
|
||||
html.light #barbuf {
|
||||
background: none;
|
||||
}
|
||||
html.light #files tr.sel:hover td {
|
||||
background: #c37;
|
||||
@@ -977,20 +1071,15 @@ html.light #files tr.sel a.play.act {
|
||||
html.light input[type="checkbox"] + label {
|
||||
color: #333;
|
||||
}
|
||||
html.light .opwide>div {
|
||||
border-color: #ccc;
|
||||
}
|
||||
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;
|
||||
@@ -1000,9 +1089,6 @@ html.light #path {
|
||||
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;
|
||||
@@ -1011,7 +1097,7 @@ html.light #path a:not(:last-child)::after {
|
||||
}
|
||||
html.light #path a:hover {
|
||||
background: none;
|
||||
color: #60a;
|
||||
color: #90d;
|
||||
}
|
||||
html.light #files tbody div a {
|
||||
color: #d38;
|
||||
@@ -1021,6 +1107,9 @@ html.light #files tr.sel a:hover {
|
||||
color: #000;
|
||||
background: #fff;
|
||||
}
|
||||
html.light #treeh {
|
||||
background: #eee;
|
||||
}
|
||||
html.light #tree {
|
||||
scrollbar-color: #a70 #ddd;
|
||||
}
|
||||
@@ -1039,6 +1128,7 @@ html.light #tree::-webkit-scrollbar {
|
||||
opacity: 0;
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
touch-action: none;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
|
||||
@@ -2,134 +2,134 @@
|
||||
<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">
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="/.cpr/browser.css{{ ts }}">
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="/.cpr/upload.css{{ ts }}">
|
||||
{%- if css %}
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="{{ css }}{{ ts }}">
|
||||
{%- endif %}
|
||||
<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">
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="/.cpr/browser.css{{ ts }}">
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="/.cpr/upload.css{{ ts }}">
|
||||
{%- if css %}
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="{{ css }}{{ ts }}">
|
||||
{%- endif %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="ops">
|
||||
<a href="#" data-dest="" data-desc="close submenu">---</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>
|
||||
{%- endif %}
|
||||
<a href="#" data-perm="write" data-dest="bup" data-desc="bup: basic uploader, even supports netscape 4.0">🎈</a>
|
||||
<a href="#" data-perm="write" data-dest="mkdir" data-desc="mkdir: create a new directory">📂</a>
|
||||
<a href="#" data-perm="read write" data-dest="new_md" data-desc="new-md: create a new markdown document">📝</a>
|
||||
<a href="#" data-perm="write" data-dest="msg" data-desc="msg: send a message to the server log">📟</a>
|
||||
<a href="#" data-dest="cfg" data-desc="configuration options">⚙️</a>
|
||||
<div id="opdesc"></div>
|
||||
</div>
|
||||
<div id="ops"></div>
|
||||
|
||||
<div id="op_search" class="opview">
|
||||
{%- if have_tags_idx %}
|
||||
<div id="srch_form" class="tags"></div>
|
||||
{%- else %}
|
||||
<div id="srch_form"></div>
|
||||
{%- endif %}
|
||||
<div id="srch_q"></div>
|
||||
</div>
|
||||
<div id="op_search" class="opview">
|
||||
{%- if have_tags_idx %}
|
||||
<div id="srch_form" class="tags"></div>
|
||||
{%- else %}
|
||||
<div id="srch_form"></div>
|
||||
{%- endif %}
|
||||
<div id="srch_q"></div>
|
||||
</div>
|
||||
|
||||
{%- include 'upload.html' %}
|
||||
<div id="op_player" class="opview opbox opwide"></div>
|
||||
|
||||
<div id="op_cfg" class="opview opbox">
|
||||
<h3>switches</h3>
|
||||
<div>
|
||||
<a id="tooltips" class="tgl btn" href="#">tooltips</a>
|
||||
<a id="lightmode" class="tgl btn" href="#">lightmode</a>
|
||||
<a id="griden" class="tgl btn" href="#">the grid</a>
|
||||
<a id="thumbs" class="tgl btn" href="#">thumbs</a>
|
||||
</div>
|
||||
{%- if have_zip %}
|
||||
<h3>folder download</h3>
|
||||
<div id="arc_fmt"></div>
|
||||
{%- endif %}
|
||||
<h3>key notation</h3>
|
||||
<div id="key_notation"></div>
|
||||
</div>
|
||||
<div id="op_bup" class="opview opbox act">
|
||||
<div id="u2err"></div>
|
||||
<form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}">
|
||||
<input type="hidden" name="act" value="bput" />
|
||||
<input type="file" name="f" multiple><br />
|
||||
<input type="submit" value="start upload">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<h1 id="path">
|
||||
<a href="#" id="entree">🌲</a>
|
||||
{%- for n in vpnodes %}
|
||||
<a href="/{{ n[0] }}">{{ n[1] }}</a>
|
||||
{%- endfor %}
|
||||
</h1>
|
||||
<div id="op_mkdir" class="opview opbox act">
|
||||
<form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}">
|
||||
<input type="hidden" name="act" value="mkdir" />
|
||||
<input type="text" name="name" size="30">
|
||||
<input type="submit" value="mkdir">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="tree">
|
||||
<a href="#" id="detree">🍞...</a>
|
||||
<a href="#" class="btn" step="2" id="twobytwo">+</a>
|
||||
<a href="#" class="btn" step="-2" id="twig">–</a>
|
||||
<a href="#" class="tgl btn" id="dyntree">a</a>
|
||||
<ul id="treeul"></ul>
|
||||
<div id="thx_ff"> </div>
|
||||
</div>
|
||||
<div id="op_new_md" class="opview opbox">
|
||||
<form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}">
|
||||
<input type="hidden" name="act" value="new_md" />
|
||||
<input type="text" name="name" size="30">
|
||||
<input type="submit" value="create doc">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="op_msg" class="opview opbox act">
|
||||
<form method="post" enctype="application/x-www-form-urlencoded" accept-charset="utf-8" action="{{ url_suf }}">
|
||||
<input type="text" name="msg" size="30">
|
||||
<input type="submit" value="send msg">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="op_up2k" class="opview"></div>
|
||||
|
||||
<div id="op_cfg" class="opview opbox opwide"></div>
|
||||
|
||||
<h1 id="path">
|
||||
<a href="#" id="entree" tt="show directory tree$NHotkey: B">🌲</a>
|
||||
{%- for n in vpnodes %}
|
||||
<a href="/{{ n[0] }}">{{ n[1] }}</a>
|
||||
{%- endfor %}
|
||||
</h1>
|
||||
|
||||
<div id="tree"></div>
|
||||
|
||||
<div id="wrap">
|
||||
|
||||
<div id="pro" class="logue">{{ logues[0] }}</div>
|
||||
<div id="pro" class="logue">{{ logues[0] }}</div>
|
||||
|
||||
<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>
|
||||
{%- for k in taglist %}
|
||||
{%- if k.startswith('.') %}
|
||||
<th name="tags/{{ k }}" sort="int"><span>{{ k[1:] }}</span></th>
|
||||
{%- else %}
|
||||
<th name="tags/{{ k }}"><span>{{ k[0]|upper }}{{ k[1:] }}</span></th>
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
<th name="ext"><span>T</span></th>
|
||||
<th name="ts"><span>Date</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<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>
|
||||
{%- for k in taglist %}
|
||||
{%- if k.startswith('.') %}
|
||||
<th name="tags/{{ k }}" sort="int"><span>{{ k[1:] }}</span></th>
|
||||
{%- else %}
|
||||
<th name="tags/{{ k }}"><span>{{ k[0]|upper }}{{ k[1:] }}</span></th>
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
<th name="ext"><span>T</span></th>
|
||||
<th name="ts"><span>Date</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{%- for f in files %}
|
||||
<tr><td>{{ f.lead }}</td><td><a href="{{ f.href }}">{{ f.name|e }}</a></td><td>{{ f.sz }}</td>
|
||||
{%- if f.tags is defined %}
|
||||
{%- for k in taglist %}
|
||||
<td>{{ f.tags[k] }}</td>
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
<td>{{ f.ext }}</td><td>{{ f.dt }}</td></tr>
|
||||
<tr><td>{{ f.lead }}</td><td><a href="{{ f.href }}">{{ f.name|e }}</a></td><td>{{ f.sz }}</td>
|
||||
{%- if f.tags is defined %}
|
||||
{%- for k in taglist %}
|
||||
<td>{{ f.tags[k] }}</td>
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
<td>{{ f.ext }}</td><td>{{ f.dt }}</td></tr>
|
||||
{%- endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div id="epi" class="logue">{{ logues[1] }}</div>
|
||||
<div id="epi" class="logue">{{ logues[1] }}</div>
|
||||
|
||||
<h2><a href="?h">control-panel</a></h2>
|
||||
<h2><a href="?h">control-panel</a></h2>
|
||||
|
||||
</div>
|
||||
|
||||
{%- if srv_info %}
|
||||
<div id="srv_info"><span>{{ srv_info }}</span></div>
|
||||
{%- endif %}
|
||||
{%- if srv_info %}
|
||||
<div id="srv_info"><span>{{ srv_info }}</span></div>
|
||||
{%- endif %}
|
||||
|
||||
<div id="widget"></div>
|
||||
<div id="widget"></div>
|
||||
|
||||
<script>
|
||||
var tag_order_cfg = {{ tag_order }};
|
||||
</script>
|
||||
<script src="/.cpr/util.js{{ ts }}"></script>
|
||||
<script src="/.cpr/browser.js{{ ts }}"></script>
|
||||
<script src="/.cpr/up2k.js{{ ts }}"></script>
|
||||
<script>
|
||||
apply_perms({{ perms }});
|
||||
</script>
|
||||
<script>
|
||||
var perms = {{ perms }},
|
||||
tag_order_cfg = {{ tag_order }},
|
||||
have_up2k_idx = {{ have_up2k_idx|tojson }},
|
||||
have_tags_idx = {{ have_tags_idx|tojson }},
|
||||
have_zip = {{ have_zip|tojson }};
|
||||
</script>
|
||||
<script src="/.cpr/util.js{{ ts }}"></script>
|
||||
<script src="/.cpr/browser.js{{ ts }}"></script>
|
||||
<script src="/.cpr/up2k.js{{ ts }}"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,59 +2,59 @@
|
||||
<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">
|
||||
<style>
|
||||
html{font-family:sans-serif}
|
||||
td{border:1px solid #999;border-width:1px 1px 0 0;padding:0 5px}
|
||||
a{display:block}
|
||||
</style>
|
||||
<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">
|
||||
<style>
|
||||
html{font-family:sans-serif}
|
||||
td{border:1px solid #999;border-width:1px 1px 0 0;padding:0 5px}
|
||||
a{display:block}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{%- if srv_info %}
|
||||
<p><span>{{ srv_info }}</span></p>
|
||||
{%- endif %}
|
||||
{%- 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 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 %}
|
||||
{%- 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>
|
||||
<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>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{%- if logues[1] %}
|
||||
<div>{{ logues[1] }}</div><br />
|
||||
{%- endif %}
|
||||
{%- if logues[1] %}
|
||||
<div>{{ logues[1] }}</div><br />
|
||||
{%- endif %}
|
||||
|
||||
<h2><a href="{{ url_suf }}{{ url_suf and '&' or '?' }}h">control-panel</a></h2>
|
||||
<h2><a href="{{ url_suf }}{{ url_suf and '&' or '?' }}h">control-panel</a></h2>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
61
copyparty/web/dbg-audio.js
Normal file
61
copyparty/web/dbg-audio.js
Normal file
@@ -0,0 +1,61 @@
|
||||
var ofun = audio_eq.apply.bind(audio_eq);
|
||||
audio_eq.apply = function () {
|
||||
var ac1 = mp.ac;
|
||||
ofun();
|
||||
var ac = mp.ac,
|
||||
w = 2048,
|
||||
h = 256;
|
||||
|
||||
if (!audio_eq.filters.length) {
|
||||
audio_eq.ana = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var can = ebi('fft_can');
|
||||
if (!can) {
|
||||
can = mknod('canvas');
|
||||
can.setAttribute('id', 'fft_can');
|
||||
can.style.cssText = 'position:absolute;left:0;bottom:5em;width:' + w + 'px;height:' + h + 'px;z-index:9001';
|
||||
document.body.appendChild(can);
|
||||
can.width = w;
|
||||
can.height = h;
|
||||
}
|
||||
var cc = can.getContext('2d');
|
||||
if (!ac)
|
||||
return;
|
||||
|
||||
var ana = ac.createAnalyser();
|
||||
ana.smoothingTimeConstant = 0;
|
||||
ana.fftSize = 8192;
|
||||
|
||||
audio_eq.filters[0].connect(ana);
|
||||
audio_eq.ana = ana;
|
||||
|
||||
var buf = new Uint8Array(ana.frequencyBinCount),
|
||||
colw = can.width / buf.length;
|
||||
|
||||
cc.fillStyle = '#fc0';
|
||||
function draw() {
|
||||
if (ana == audio_eq.ana)
|
||||
requestAnimationFrame(draw);
|
||||
|
||||
ana.getByteFrequencyData(buf);
|
||||
|
||||
cc.clearRect(0, 0, can.width, can.height);
|
||||
|
||||
/*var x = 0, w = 1;
|
||||
for (var a = 0; a < buf.length; a++) {
|
||||
cc.fillRect(x, h - buf[a], w, h);
|
||||
x += w;
|
||||
}*/
|
||||
var mul = Math.pow(w, 4) / buf.length;
|
||||
for (var x = 0; x < w; x++) {
|
||||
var a = Math.floor(Math.pow(x, 4) / mul),
|
||||
v = buf[a];
|
||||
|
||||
cc.fillRect(x, h - v, 1, v);
|
||||
}
|
||||
}
|
||||
draw();
|
||||
};
|
||||
audio_eq.apply();
|
||||
@@ -444,8 +444,7 @@ function up2k_init(subtle) {
|
||||
}
|
||||
|
||||
// show uploader if the user only has write-access
|
||||
var perms = document.body.getAttribute('perms');
|
||||
if (perms && !has(perms.split(' '), 'read'))
|
||||
if (perms.length && !has(perms, 'read'))
|
||||
goto('up2k');
|
||||
|
||||
// shows or clears a message in the basic uploader ui
|
||||
@@ -1265,7 +1264,7 @@ function up2k_init(subtle) {
|
||||
fpx = parseInt(getComputedStyle(bar)['font-size']),
|
||||
wem = wpx * 1.0 / fpx,
|
||||
wide = wem > 54,
|
||||
parent = ebi(wide ? 'u2btn_cw' : 'u2btn_ct'),
|
||||
parent = ebi(wide && has(perms, 'write') ? 'u2btn_cw' : 'u2btn_ct'),
|
||||
btn = ebi('u2btn');
|
||||
|
||||
//console.log([wpx, fpx, wem]);
|
||||
@@ -1278,31 +1277,18 @@ function up2k_init(subtle) {
|
||||
window.addEventListener('resize', onresize);
|
||||
onresize();
|
||||
|
||||
function desc_show(e) {
|
||||
var cfg = sread('tooltips');
|
||||
if (cfg !== null && cfg != '1')
|
||||
return;
|
||||
|
||||
var msg = this.getAttribute('alt'),
|
||||
cdesc = ebi('u2cdesc');
|
||||
|
||||
cdesc.innerHTML = msg.replace(/\$N/g, "<br />");
|
||||
cdesc.setAttribute('class', 'show');
|
||||
if (is_touch) {
|
||||
// android-chrome wobbles for a bit; firefox / iOS-safari are OK
|
||||
setTimeout(onresize, 20);
|
||||
setTimeout(onresize, 100);
|
||||
setTimeout(onresize, 500);
|
||||
}
|
||||
function desc_hide(e) {
|
||||
ebi('u2cdesc').setAttribute('class', '');
|
||||
}
|
||||
var o = QSA('#u2conf *[alt]');
|
||||
|
||||
var o = QSA('#u2conf *[tt]');
|
||||
for (var a = o.length - 1; a >= 0; a--) {
|
||||
o[a].parentNode.getElementsByTagName('input')[0].setAttribute('alt', o[a].getAttribute('alt'));
|
||||
}
|
||||
var o = QSA('#u2conf *[alt]');
|
||||
for (var a = 0; a < o.length; a++) {
|
||||
o[a].onfocus = desc_show;
|
||||
o[a].onblur = desc_hide;
|
||||
o[a].onmouseenter = desc_show;
|
||||
o[a].onmouseleave = desc_hide;
|
||||
o[a].parentNode.getElementsByTagName('input')[0].setAttribute('tt', o[a].getAttribute('tt'));
|
||||
}
|
||||
tt.init();
|
||||
|
||||
function bumpthread(dir) {
|
||||
try {
|
||||
@@ -1350,14 +1336,12 @@ function up2k_init(subtle) {
|
||||
}
|
||||
|
||||
function set_fsearch(new_state) {
|
||||
var perms = document.body.getAttribute('perms'),
|
||||
fixed = false;
|
||||
var fixed = false;
|
||||
|
||||
if (!ebi('fsearch')) {
|
||||
new_state = false;
|
||||
}
|
||||
else if (perms) {
|
||||
perms = perms.split(' ');
|
||||
else if (perms.length) {
|
||||
if (!has(perms, 'write')) {
|
||||
new_state = true;
|
||||
fixed = true;
|
||||
@@ -1387,6 +1371,8 @@ function up2k_init(subtle) {
|
||||
ebi('u2bm').innerHTML = ico + ' <sup>' + desc + '</sup>';
|
||||
}
|
||||
catch (ex) { }
|
||||
|
||||
onresize();
|
||||
}
|
||||
|
||||
function tgl_flag_en() {
|
||||
@@ -1450,5 +1436,9 @@ function warn_uploader_busy(e) {
|
||||
}
|
||||
|
||||
|
||||
tt.init();
|
||||
|
||||
if (QS('#op_up2k.act'))
|
||||
goto_up2k();
|
||||
|
||||
apply_perms(perms);
|
||||
|
||||
@@ -211,29 +211,6 @@
|
||||
box-shadow: none;
|
||||
opacity: .2;
|
||||
}
|
||||
#u2cdesc {
|
||||
position: absolute;
|
||||
width: 34em;
|
||||
left: calc(50% - 15em);
|
||||
background: #222;
|
||||
border: 0 solid #555;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
margin: 0 -2em;
|
||||
padding: 0 1em;
|
||||
height: 0;
|
||||
opacity: .1;
|
||||
transition: all 0.14s ease-in-out;
|
||||
box-shadow: 0 .2em .5em #222;
|
||||
border-radius: .4em;
|
||||
z-index: 1;
|
||||
}
|
||||
#u2cdesc.show {
|
||||
padding: 1em;
|
||||
height: auto;
|
||||
border-width: .2em 0;
|
||||
opacity: 1;
|
||||
}
|
||||
#u2foot {
|
||||
color: #fff;
|
||||
font-style: italic;
|
||||
@@ -286,10 +263,6 @@ 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,103 +0,0 @@
|
||||
|
||||
<div id="op_bup" class="opview opbox act">
|
||||
<div id="u2err"></div>
|
||||
<form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}">
|
||||
<input type="hidden" name="act" value="bput" />
|
||||
<input type="file" name="f" multiple><br />
|
||||
<input type="submit" value="start upload">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="op_mkdir" class="opview opbox act">
|
||||
<form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}">
|
||||
<input type="hidden" name="act" value="mkdir" />
|
||||
<input type="text" name="name" size="30">
|
||||
<input type="submit" value="mkdir">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="op_new_md" class="opview opbox">
|
||||
<form method="post" enctype="multipart/form-data" accept-charset="utf-8" action="{{ url_suf }}">
|
||||
<input type="hidden" name="act" value="new_md" />
|
||||
<input type="text" name="name" size="30">
|
||||
<input type="submit" value="create doc">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="op_msg" class="opview opbox act">
|
||||
<form method="post" enctype="application/x-www-form-urlencoded" accept-charset="utf-8" action="{{ url_suf }}">
|
||||
<input type="text" name="msg" size="30">
|
||||
<input type="submit" value="send msg">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="op_up2k" class="opview">
|
||||
<form id="u2form" method="post" enctype="multipart/form-data" onsubmit="return false;"></form>
|
||||
|
||||
<table id="u2conf">
|
||||
<tr>
|
||||
<td><br />parallel uploads:</td>
|
||||
<td rowspan="2">
|
||||
<input type="checkbox" id="multitask" />
|
||||
<label for="multitask" alt="continue hashing other files while uploading">🏃</label>
|
||||
</td>
|
||||
<td rowspan="2">
|
||||
<input type="checkbox" id="ask_up" />
|
||||
<label for="ask_up" alt="ask for confirmation befofre upload starts">💭</label>
|
||||
</td>
|
||||
<td rowspan="2">
|
||||
<input type="checkbox" id="flag_en" />
|
||||
<label for="flag_en" alt="ensure only one tab is uploading at a time $N (other tabs must have this enabled too)">💤</label>
|
||||
</td>
|
||||
{%- if have_up2k_idx %}
|
||||
<td data-perm="read" rowspan="2">
|
||||
<input type="checkbox" id="fsearch" />
|
||||
<label for="fsearch" alt="don't actually upload, instead check if the files already $N exist on the server (will scan all folders you can read)">🔎</label>
|
||||
</td>
|
||||
{%- endif %}
|
||||
<td data-perm="read" rowspan="2" id="u2btn_cw"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="#" id="nthread_sub">–</a><input
|
||||
class="txtbox" id="nthread" value="2"/><a
|
||||
href="#" id="nthread_add">+</a><br />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div id="u2cdesc"></div>
|
||||
|
||||
<div id="u2notbtn"></div>
|
||||
|
||||
<div id="u2btn_ct">
|
||||
<div id="u2btn">
|
||||
<span id="u2bm"></span><br />
|
||||
drag/drop files<br />
|
||||
and folders here<br />
|
||||
(or click me)
|
||||
</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">
|
||||
<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>
|
||||
<p id="u2footfoot" data-perm="write">( you can use the <a href="#" id="u2nope">basic uploader</a> if you don't need lastmod timestamps, resumable uploads, or progress bars )</p>
|
||||
</div>
|
||||
@@ -6,7 +6,7 @@ if (!window['console'])
|
||||
};
|
||||
|
||||
|
||||
var clickev = window.Touch ? 'touchstart' : 'click',
|
||||
var is_touch = 'ontouchstart' in window,
|
||||
ANDROID = /(android)/i.test(navigator.userAgent);
|
||||
|
||||
|
||||
@@ -285,63 +285,6 @@ function makeSortable(table, cb) {
|
||||
}
|
||||
|
||||
|
||||
(function () {
|
||||
var ops = QSA('#ops>a');
|
||||
for (var a = 0; a < ops.length; a++) {
|
||||
ops[a].onclick = opclick;
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
function opclick(e) {
|
||||
ev(e);
|
||||
|
||||
var dest = this.getAttribute('data-dest');
|
||||
goto(dest);
|
||||
|
||||
swrite('opmode', dest || null);
|
||||
|
||||
var input = QS('.opview.act input:not([type="hidden"])')
|
||||
if (input)
|
||||
input.focus();
|
||||
}
|
||||
|
||||
|
||||
function goto(dest) {
|
||||
var obj = QSA('.opview.act');
|
||||
for (var a = obj.length - 1; a >= 0; a--)
|
||||
clmod(obj[a], 'act');
|
||||
|
||||
obj = QSA('#ops>a');
|
||||
for (var a = obj.length - 1; a >= 0; a--)
|
||||
clmod(obj[a], 'act');
|
||||
|
||||
if (dest) {
|
||||
var ui = ebi('op_' + dest);
|
||||
clmod(ui, 'act', true);
|
||||
QS('#ops>a[data-dest=' + dest + ']').className += " act";
|
||||
|
||||
var fn = window['goto_' + dest];
|
||||
if (fn)
|
||||
fn();
|
||||
}
|
||||
|
||||
if (window['treectl'])
|
||||
treectl.onscroll();
|
||||
}
|
||||
|
||||
|
||||
(function () {
|
||||
goto();
|
||||
var op = sread('opmode');
|
||||
if (op !== null && op !== '.')
|
||||
try {
|
||||
goto(op);
|
||||
}
|
||||
catch (ex) { }
|
||||
})();
|
||||
|
||||
|
||||
function linksplit(rp) {
|
||||
var ret = [];
|
||||
var apath = '/';
|
||||
@@ -528,3 +471,67 @@ function hist_replace(url) {
|
||||
console.log("h-repl " + url);
|
||||
history.replaceState(url, url, url);
|
||||
}
|
||||
|
||||
|
||||
var tt = (function () {
|
||||
var r = {
|
||||
"tt": mknod("div"),
|
||||
"en": true
|
||||
};
|
||||
|
||||
r.tt.setAttribute('id', 'tt');
|
||||
document.body.appendChild(r.tt);
|
||||
|
||||
function show() {
|
||||
var cfg = sread('tooltips');
|
||||
if (cfg !== null && cfg != '1')
|
||||
return;
|
||||
|
||||
var msg = this.getAttribute('tt');
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
var pos = this.getBoundingClientRect(),
|
||||
left = pos.left < window.innerWidth / 2,
|
||||
top = pos.top < window.innerHeight / 2;
|
||||
|
||||
r.tt.style.top = top ? pos.bottom + 'px' : 'auto';
|
||||
r.tt.style.bottom = top ? 'auto' : (window.innerHeight - pos.top) + 'px';
|
||||
r.tt.style.left = left ? pos.left + 'px' : 'auto';
|
||||
r.tt.style.right = left ? 'auto' : (window.innerWidth - pos.right) + 'px';
|
||||
|
||||
r.tt.innerHTML = msg.replace(/\$N/g, "<br />");
|
||||
clmod(r.tt, 'show', 1);
|
||||
}
|
||||
|
||||
function hide() {
|
||||
clmod(r.tt, 'show');
|
||||
}
|
||||
|
||||
r.init = function () {
|
||||
var ttb = ebi('tooltips');
|
||||
if (ttb) {
|
||||
ttb.onclick = function (e) {
|
||||
ev(e);
|
||||
r.en = !r.en;
|
||||
bcfg_set('tooltips', r.en);
|
||||
r.init();
|
||||
};
|
||||
r.en = bcfg_get('tooltips', true)
|
||||
}
|
||||
|
||||
var _show = r.en ? show : null,
|
||||
_hide = r.en ? hide : null;
|
||||
|
||||
var o = QSA('*[tt]');
|
||||
for (var a = o.length - 1; a >= 0; a--) {
|
||||
o[a].onfocus = _show;
|
||||
o[a].onblur = _hide;
|
||||
o[a].onmouseenter = _show;
|
||||
o[a].onmouseleave = _hide;
|
||||
}
|
||||
hide();
|
||||
};
|
||||
|
||||
return r;
|
||||
})();
|
||||
|
||||
95
docs/biquad.html
Normal file
95
docs/biquad.html
Normal file
@@ -0,0 +1,95 @@
|
||||
<!DOCTYPE html><html><head></head><body><script>
|
||||
|
||||
setTimeout(location.reload.bind(location), 700);
|
||||
document.documentElement.scrollLeft = 0;
|
||||
|
||||
var can = document.createElement('canvas'),
|
||||
cc = can.getContext('2d'),
|
||||
w = 2048,
|
||||
h = 1024;
|
||||
|
||||
w = 2048;
|
||||
|
||||
can.width = w;
|
||||
can.height = h;
|
||||
document.body.appendChild(can);
|
||||
can.style.cssText = 'width:' + w + 'px;height:' + h + 'px';
|
||||
|
||||
cc.fillStyle = '#000';
|
||||
cc.fillRect(0, 0, w, h);
|
||||
|
||||
var cfg = [ // hz, q, g
|
||||
[31.25 * 0.88, 0, 1.4], // shelf
|
||||
[31.25 * 1.04, 0.7, 0.96], // peak
|
||||
[62.5, 0.7, 1],
|
||||
[125, 0.8, 1],
|
||||
[250, 0.9, 1.03],
|
||||
[500, 0.9, 1.1],
|
||||
[1000, 0.9, 1.1],
|
||||
[2000, 0.9, 1.105],
|
||||
[4000, 0.88, 1.05],
|
||||
[8000 * 1.006, 0.73, 1.24],
|
||||
//[16000 * 1.00, 0.5, 1.75], // peak.v1
|
||||
//[16000 * 1.19, 0, 1.8] // shelf.v1
|
||||
[16000 * 0.89, 0.7, 1.26], // peak
|
||||
[16000 * 1.13, 0.82, 1.09], // peak
|
||||
[16000 * 1.205, 0, 1.9] // shelf
|
||||
];
|
||||
|
||||
var freqs = new Float32Array(22000),
|
||||
sum = new Float32Array(freqs.length),
|
||||
ac = new AudioContext(),
|
||||
step = w / freqs.length,
|
||||
colors = [
|
||||
'rgba(255, 0, 0, 0.7)',
|
||||
'rgba(0, 224, 0, 0.7)',
|
||||
'rgba(0, 64, 255, 0.7)'
|
||||
];
|
||||
|
||||
var order = [];
|
||||
|
||||
for (var a = 0; a < cfg.length; a += 2)
|
||||
order.push(a);
|
||||
|
||||
for (var a = 1; a < cfg.length; a += 2)
|
||||
order.push(a);
|
||||
|
||||
for (var ia = 0; ia < order.length; ia++) {
|
||||
var a = order[ia],
|
||||
fi = ac.createBiquadFilter(),
|
||||
mag = new Float32Array(freqs.length),
|
||||
phase = new Float32Array(freqs.length);
|
||||
|
||||
for (var b = 0; b < freqs.length; b++)
|
||||
freqs[b] = b;
|
||||
|
||||
fi.type = a == 0 ? 'lowshelf' : a == cfg.length - 1 ? 'highshelf' : 'peaking';
|
||||
fi.frequency.value = cfg[a][0];
|
||||
fi.Q.value = cfg[a][1];
|
||||
fi.gain.value = 1;
|
||||
|
||||
fi.getFrequencyResponse(freqs, mag, phase);
|
||||
cc.fillStyle = colors[a % colors.length];
|
||||
for (var b = 0; b < sum.length; b++) {
|
||||
mag[b] -= 1;
|
||||
sum[b] += mag[b] * cfg[a][2];
|
||||
var y = h - (mag[b] * h * 3);
|
||||
cc.fillRect(b * step, y, step, h - y);
|
||||
cc.fillRect(b * step - 1, y - 1, 3, 3);
|
||||
}
|
||||
}
|
||||
|
||||
var min = 999999, max = 0;
|
||||
for (var a = 0; a < sum.length; a++) {
|
||||
min = Math.min(min, sum[a]);
|
||||
max = Math.max(max, sum[a]);
|
||||
}
|
||||
cc.fillStyle = 'rgba(255,255,255,1)';
|
||||
for (var a = 0; a < sum.length; a++) {
|
||||
var v = (sum[a] - min) / (max - min);
|
||||
cc.fillRect(a * step, 0, step, v * h / 2);
|
||||
}
|
||||
|
||||
cc.fillRect(0, 460, w, 1);
|
||||
|
||||
</script></body></html>
|
||||
32
docs/tcp-debug.sh
Normal file
32
docs/tcp-debug.sh
Normal file
@@ -0,0 +1,32 @@
|
||||
(cd ~/dev/copyparty && strace -Tttyyvfs 256 -o strace.strace python3 -um copyparty -i 127.0.0.1 --http-only --stackmon /dev/shm/cpps,10 ) 2>&1 | tee /dev/stderr > ~/log-copyparty-$(date +%Y-%m%d-%H%M%S).txt
|
||||
|
||||
14/Jun/2021:16:34:02 1623688447.212405 death
|
||||
14/Jun/2021:16:35:02 1623688502.420860 back
|
||||
|
||||
tcpdump -nni lo -w /home/ed/lo.pcap
|
||||
|
||||
# 16:35:25.324662 IP 127.0.0.1.48632 > 127.0.0.1.3920: Flags [F.], seq 849, ack 544, win 359, options [nop,nop,TS val 809396796 ecr 809396796], length 0
|
||||
|
||||
tcpdump -nnr /home/ed/lo.pcap | awk '/ > 127.0.0.1.3920: /{sub(/ > .*/,"");sub(/.*\./,"");print}' | sort -n | uniq | while IFS= read -r port; do echo; tcpdump -nnr /home/ed/lo.pcap 2>/dev/null | grep -E "\.$port( > |: F)" | sed -r 's/ > .*, /, /'; done | grep -E '^16:35:0.*length [^0]' -C50
|
||||
|
||||
16:34:02.441732 IP 127.0.0.1.48638, length 0
|
||||
16:34:02.441738 IP 127.0.0.1.3920, length 0
|
||||
16:34:02.441744 IP 127.0.0.1.48638, length 0
|
||||
16:34:02.441756 IP 127.0.0.1.48638, length 791
|
||||
16:34:02.441759 IP 127.0.0.1.3920, length 0
|
||||
16:35:02.445529 IP 127.0.0.1.48638, length 0
|
||||
16:35:02.489194 IP 127.0.0.1.3920, length 0
|
||||
16:35:02.515595 IP 127.0.0.1.3920, length 216
|
||||
16:35:02.515600 IP 127.0.0.1.48638, length 0
|
||||
|
||||
grep 48638 "$(find ~ -maxdepth 1 -name log-copyparty-\*.txt | sort | tail -n 1)"
|
||||
|
||||
1623688502.510380 48638 rh
|
||||
1623688502.511291 48638 Unrecv direct ...
|
||||
1623688502.511827 48638 rh = 791
|
||||
16:35:02.518 127.0.0.1 48638 shut(8): [Errno 107] Socket not connected
|
||||
Exception in thread httpsrv-0.1-48638:
|
||||
|
||||
grep 48638 ~/dev/copyparty/strace.strace
|
||||
14561 16:35:02.506310 <... accept4 resumed> {sa_family=AF_INET, sin_port=htons(48638), sin_addr=inet_addr("127.0.0.1")}, [16], SOCK_CLOEXEC) = 8<TCP:[127.0.0.1:3920->127.0.0.1:48638]> <0.000012>
|
||||
15230 16:35:02.510725 write(1<pipe:[256639555]>, "1623688502.510380 48638 rh\n", 27 <unfinished ...>
|
||||
@@ -92,20 +92,34 @@ chmod 755 \
|
||||
copyparty-extras/copyparty-*/{scripts,bin}/*
|
||||
|
||||
|
||||
# extract and repack the sfx with less features enabled
|
||||
# extract the sfx
|
||||
( cd copyparty-extras/sfx-full/
|
||||
./copyparty-sfx.py -h
|
||||
cd ../copyparty-*/
|
||||
./scripts/make-sfx.sh re no-ogv no-cm
|
||||
)
|
||||
|
||||
|
||||
# put new sfx into copyparty-extras/sfx-lite/,
|
||||
# fuse client into copyparty-extras/,
|
||||
repack() {
|
||||
|
||||
# do the repack
|
||||
(cd copyparty-extras/copyparty-*/
|
||||
./scripts/make-sfx.sh $2
|
||||
)
|
||||
|
||||
# put new sfx into copyparty-extras/$name/,
|
||||
( cd copyparty-extras/
|
||||
mv copyparty-*/dist/* $1/
|
||||
)
|
||||
}
|
||||
|
||||
repack sfx-full "re gz no-sh"
|
||||
repack sfx-lite "re no-ogv no-cm"
|
||||
repack sfx-lite "re no-ogv no-cm gz no-sh"
|
||||
|
||||
|
||||
# move fuse client into copyparty-extras/,
|
||||
# copy lite-sfx.py to ./copyparty,
|
||||
# delete extracted source code
|
||||
( cd copyparty-extras/
|
||||
mv copyparty-*/dist/* sfx-lite/
|
||||
mv copyparty-*/bin/copyparty-fuse.py .
|
||||
cp -pv sfx-lite/copyparty-sfx.py ../copyparty
|
||||
rm -rf copyparty-{0..9}*.*.*{0..9}
|
||||
@@ -119,6 +133,7 @@ true
|
||||
|
||||
|
||||
# create the bundle
|
||||
printf '\n\n'
|
||||
fn=copyparty-$(date +%Y-%m%d-%H%M%S).tgz
|
||||
tar -czvf "$od/$fn" *
|
||||
cd "$od"
|
||||
|
||||
@@ -11,6 +11,10 @@ echo
|
||||
# `re` does a repack of an sfx which you already executed once
|
||||
# (grabs files from the sfx-created tempdir), overrides `clean`
|
||||
#
|
||||
# `gz` creates a gzip-compressed python sfx instead of bzip2
|
||||
#
|
||||
# `no-sh` makes just the python sfx, skips the sh/unix sfx
|
||||
#
|
||||
# `no-ogv` saves ~500k by removing the opus/vorbis audio codecs
|
||||
# (only affects apple devices; everything else has native support)
|
||||
#
|
||||
@@ -167,7 +171,7 @@ find .. -type f \( -name .DS_Store -or -name ._.DS_Store \) -delete
|
||||
find .. -type f -name ._\* | while IFS= read -r f; do cmp <(printf '\x00\x05\x16') <(head -c 3 -- "$f") && rm -f -- "$f"; done
|
||||
|
||||
echo use smol web deps
|
||||
rm -f copyparty/web/deps/*.full.* copyparty/web/Makefile
|
||||
rm -f copyparty/web/deps/*.full.* copyparty/web/dbg-* copyparty/web/Makefile
|
||||
|
||||
# it's fine dw
|
||||
grep -lE '\.full\.(js|css)' copyparty/web/* |
|
||||
|
||||
105
scripts/test/race.py
Normal file
105
scripts/test/race.py
Normal file
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import json
|
||||
import threading
|
||||
import http.client
|
||||
|
||||
|
||||
class Conn(object):
|
||||
def __init__(self, ip, port):
|
||||
self.s = http.client.HTTPConnection(ip, port, timeout=260)
|
||||
self.st = []
|
||||
|
||||
def get(self, vpath):
|
||||
self.st = [time.time()]
|
||||
|
||||
self.s.request("GET", vpath)
|
||||
self.st.append(time.time())
|
||||
|
||||
ret = self.s.getresponse()
|
||||
self.st.append(time.time())
|
||||
|
||||
if ret.status < 200 or ret.status >= 400:
|
||||
raise Exception(ret.status)
|
||||
|
||||
ret = ret.read()
|
||||
self.st.append(time.time())
|
||||
|
||||
return ret
|
||||
|
||||
def get_json(self, vpath):
|
||||
ret = self.get(vpath)
|
||||
return json.loads(ret)
|
||||
|
||||
|
||||
class CState(threading.Thread):
|
||||
def __init__(self, cs):
|
||||
threading.Thread.__init__(self)
|
||||
self.daemon = True
|
||||
self.cs = cs
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
colors = [5, 1, 3, 2, 7]
|
||||
remotes = []
|
||||
remotes_ok = False
|
||||
while True:
|
||||
time.sleep(0.001)
|
||||
if not remotes_ok:
|
||||
remotes = []
|
||||
remotes_ok = True
|
||||
for conn in self.cs:
|
||||
try:
|
||||
remotes.append(conn.s.sock.getsockname()[1])
|
||||
except:
|
||||
remotes.append("?")
|
||||
remotes_ok = False
|
||||
|
||||
m = []
|
||||
for conn, remote in zip(self.cs, remotes):
|
||||
stage = len(conn.st)
|
||||
m.append(f"\033[3{colors[stage]}m{remote}")
|
||||
|
||||
m = " ".join(m)
|
||||
print(f"{m}\033[0m\n\033[A", end="")
|
||||
|
||||
|
||||
def allget(cs, urls):
|
||||
thrs = []
|
||||
for c, url in zip(cs, urls):
|
||||
t = threading.Thread(target=c.get, args=(url,))
|
||||
t.start()
|
||||
thrs.append(t)
|
||||
|
||||
for t in thrs:
|
||||
t.join()
|
||||
|
||||
|
||||
def main():
|
||||
os.system("")
|
||||
|
||||
ip, port = sys.argv[1].split(":")
|
||||
port = int(port)
|
||||
|
||||
cs = []
|
||||
for _ in range(64):
|
||||
cs.append(Conn(ip, 3923))
|
||||
|
||||
CState(cs)
|
||||
|
||||
urlbase = "/doujin/c95"
|
||||
j = cs[0].get_json(f"{urlbase}?ls")
|
||||
urls = []
|
||||
for d in j["dirs"]:
|
||||
urls.append(f"{urlbase}/{d['href']}?th=w")
|
||||
|
||||
for n in range(100):
|
||||
print(n)
|
||||
allget(cs, urls)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user