Compare commits

..

40 Commits

Author SHA1 Message Date
ed
ff8313d0fb add mistake 2021-07-01 21:49:44 +02:00
ed
765294c263 ignore dupe-chunk warnings; handshake takes care of it 2021-07-01 20:22:12 +02:00
ed
d6b5351207 add cachebuster because chrome ignores no-cache 2021-07-01 20:10:02 +02:00
ed
a2009bcc6b up2k-cli: recover from tcp/dns issues on upload 2021-07-01 00:52:09 +02:00
ed
12709a8a0a up2k-cli: recover from antivirus yanking files mid-read 2021-07-01 00:11:40 +02:00
ed
c055baefd2 up2k-client: maybe fix busy-tab (assumed linear progress) 2021-06-30 23:17:07 +02:00
ed
56522599b5 up2k-client: way faster init on large filedrops 2021-06-30 21:26:13 +02:00
ed
664f53b75d chrome gets stuck iterating over aux.h on win10 2021-06-30 19:26:06 +02:00
ed
87200d9f10 make -nw apply to more stuff 2021-06-30 19:23:45 +02:00
ed
5c3d0b6520 catch errors in onloads 2021-06-30 17:09:37 +02:00
ed
bd49979f4a v0.11.29 2021-06-30 01:51:57 +02:00
ed
7e606cdd9f make search rate-control less visually confusing 2021-06-30 01:44:25 +02:00
ed
8b4b7fa794 allow opening tree nodes in a new tab 2021-06-30 01:08:20 +02:00
ed
05345ddf8b add per-connection request counting 2021-06-30 01:00:00 +02:00
ed
66adb470ad optional progressbar tint 2021-06-30 00:55:57 +02:00
ed
e15c8fd146 add upload pause 2021-06-30 00:34:33 +02:00
ed
0f09b98a39 scan for additional folder thumbnails 2021-06-30 00:19:39 +02:00
ed
b4d6f4e24d american-friendly upload limits (allow additional bypass using manual text entry) 2021-06-30 00:11:23 +02:00
ed
3217fa625b more todo 2021-06-29 23:59:15 +02:00
ed
e719ff8a47 make sfx kipu-proof 2021-06-29 23:53:57 +02:00
ed
9fcf528d45 update readme 2021-06-29 23:32:21 +02:00
ed
1ddbf5a158 update todo 2021-06-29 23:00:28 +02:00
ed
64bf4574b0 add todo maybe 2021-06-28 20:38:59 +02:00
ed
5649d26077 v0.11.28 2021-06-28 15:36:13 +02:00
ed
92f923effe hotkey for adjusting tree width 2021-06-28 15:34:10 +02:00
ed
0d46d548b9 fix panic when zero accounts 2021-06-28 15:20:40 +02:00
ed
062df3f0c3 point control-panel link to / 2021-06-27 00:52:15 +02:00
ed
789fb53b8e tweaks 2021-06-27 00:49:28 +02:00
ed
351db5a18f ah yes trailing whitespace as markup my good old friend we meet again 2021-06-27 00:20:42 +02:00
ed
aabbd271c8 add debian howto 2021-06-27 00:19:37 +02:00
ed
aae8e0171e v0.11.27 2021-06-25 22:23:21 +02:00
ed
45827a2458 fix exit-search button in gridview 2021-06-25 22:18:16 +02:00
ed
726030296f apparently the html dom-property is not normalized 2021-06-25 22:07:37 +02:00
ed
6659ab3881 ajax subfolders from gridview 2021-06-25 21:49:09 +02:00
ed
c6a103609e fix gridview selection/baguettebox order 2021-06-25 21:35:45 +02:00
ed
c6b3f035e5 gridview audio playback in search results too 2021-06-25 21:12:49 +02:00
ed
2b0a7e378e persist url-password as cookie 2021-06-25 20:39:55 +02:00
ed
b75ce909c8 audio seek with scrollbar on progressbar 2021-06-25 20:24:30 +02:00
ed
229c3f5dab play audio from grid when widget open 2021-06-25 20:04:19 +02:00
ed
ec73094506 v0.11.26 2021-06-25 03:10:43 +02:00
22 changed files with 612 additions and 165 deletions

View File

@@ -20,6 +20,7 @@ turn your phone or raspi into a portable file server with resumable uploads/down
* top
* [quickstart](#quickstart)
* [on debian](#on-debian)
* [notes](#notes)
* [status](#status)
* [bugs](#bugs)
@@ -68,6 +69,7 @@ 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`
* the syntax is `-v src:dst:perm:perm:...` so local-path, url-path, and one or more permissions to set
* 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
@@ -77,6 +79,19 @@ you may also want these, especially on servers:
* [contrib/nginx/copyparty.conf](contrib/nginx/copyparty.conf) to reverse-proxy behind nginx (for better https)
### on debian
recommended steps to enable audio metadata and thumbnails (from images and videos):
* as root, run the following:
`apt install python3 python3-pip python3-dev ffmpeg`
* then, as the user which will be running copyparty (so hopefully not root), run this:
`python3 -m pip install --user -U Pillow pillow-avif-plugin`
(skipped `pyheif-pillow-opener` because apparently debian is too old to build it)
## notes
general:
@@ -139,6 +154,8 @@ summary: all planned features work! now please enjoy the bloatening
* all volumes must exist / be available on startup; up2k (mtp especially) gets funky otherwise
* cannot mount something at `/d1/d2/d3` unless `d2` exists inside `d1`
* dupe files will not have metadata (audio tags etc) displayed in the file listing
* because they don't get `up` entries in the db (probably best fix) and `tx_browser` does not `lstat`
* probably more, pls let me know
## not my bugs
@@ -178,9 +195,11 @@ the browser has the following hotkeys
* `U/O` skip 10sec back/forward
* `J/L` prev/next song
* `P` play/pause (also starts playing the folder)
* when tree-sidebar is open:
* `A/D` adjust tree width
* in the grid view:
* `S` toggle multiselect
* `A/D` zoom
* shift+`A/D` zoom
## tree-mode
@@ -198,6 +217,8 @@ it does static images with Pillow and uses FFmpeg for video files, so you may wa
images named `folder.jpg` and `folder.png` become the thumbnail of the folder they're in
in the grid/thumbnail view, if the audio player panel is open, songs will start playing when clicked
## zip downloads
@@ -594,13 +615,14 @@ in the `scripts` folder:
roughly sorted by priority
* readme.md as epilogue
* single sha512 across all up2k chunks? maybe
* reduce up2k roundtrips
* start from a chunk index and just go
* terminate client on bad data
discarded ideas
* single sha512 across all up2k chunks?
* crypto.subtle cannot into streaming, would have to use hashwasm, expensive
* separate sqlite table per tag
* performance fixed by skipping some indexes (`+mt.k`)
* audio fingerprinting

View File

@@ -305,6 +305,7 @@ def run_argparse(argv, formatter):
ap2.add_argument("--th-poke", metavar="SEC", type=int, default=300, help="activity labeling cooldown")
ap2.add_argument("--th-clean", metavar="SEC", type=int, default=43200, help="cleanup interval")
ap2.add_argument("--th-maxage", metavar="SEC", type=int, default=604800, help="max folder age")
ap2.add_argument("--th-covers", metavar="N,N", type=str, default="folder.png,folder.jpg,cover.png,cover.jpg", help="folder thumbnails to stat for")
ap2 = ap.add_argument_group('database options')
ap2.add_argument("-e2d", action="store_true", help="enable up2k database")
@@ -323,6 +324,9 @@ def run_argparse(argv, formatter):
ap2.add_argument("-mtp", metavar="M=[f,]bin", action="append", type=str, help="read tag M using bin")
ap2.add_argument("--srch-time", metavar="SEC", type=int, default=30, help="search deadline")
ap2 = ap.add_argument_group('video streaming options')
ap2.add_argument("--vcr", action="store_true", help="enable video streaming")
ap2 = ap.add_argument_group('appearance options')
ap2.add_argument("--css-browser", metavar="L", help="URL to additional CSS to include")

View File

@@ -1,8 +1,8 @@
# coding: utf-8
VERSION = (0, 11, 25)
VERSION = (0, 11, 29)
CODENAME = "the grid"
BUILD_DT = (2021, 6, 25)
BUILD_DT = (2021, 6, 30)
S_VERSION = ".".join(map(str, VERSION))
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)

View File

@@ -693,8 +693,10 @@ class AuthSrv(object):
self.user = user
self.iuser = {v: k for k, v in user.items()}
self.re_pwd = None
pwds = [re.escape(x) for x in self.iuser.keys()]
self.re_pwd = re.compile("=(" + "|".join(pwds) + ")([]&; ]|$)")
if pwds:
self.re_pwd = re.compile("=(" + "|".join(pwds) + ")([]&; ]|$)")
# import pprint
# pprint.pprint({"usr": user, "rd": mread, "wr": mwrite, "mnt": mount})

View File

@@ -18,6 +18,8 @@ from .util import * # noqa # pylint: disable=unused-wildcard-import
from .authsrv import AuthSrv
from .szip import StreamZip
from .star import StreamTar
from .vcr import VCR_Direct
from .th_srv import FMT_FF
if not PY2:
unicode = str
@@ -55,7 +57,7 @@ class HttpCli(object):
def log(self, msg, c=0):
ptn = self.asrv.re_pwd
if ptn.search(msg):
if ptn and ptn.search(msg):
msg = ptn.sub(self.unpwd, msg)
self.log_func(self.log_src, msg, c)
@@ -72,9 +74,13 @@ class HttpCli(object):
if rem.startswith("/") or rem.startswith("../") or "/../" in rem:
raise Exception("that was close")
def j2(self, name, **kwargs):
def j2(self, name, **ka):
tpl = self.conn.hsrv.j2[name]
return tpl.render(**kwargs) if kwargs else tpl
if ka:
ka["ts"] = self.conn.hsrv.cachebuster()
return tpl.render(**ka)
return tpl
def run(self):
"""returns true if connection can be reused"""
@@ -181,6 +187,9 @@ class HttpCli(object):
self.rvol, self.wvol, self.avol = [[], [], []]
self.asrv.vfs.user_tree(self.uname, self.rvol, self.wvol, self.avol)
if pwd and "pw" in self.ouparam and pwd != cookies.get("cppwd"):
self.out_headers["Set-Cookie"] = self.get_pwd_cookie(pwd)[0]
ua = self.headers.get("user-agent", "")
self.is_rclone = ua.startswith("rclone/")
if self.is_rclone:
@@ -222,7 +231,9 @@ class HttpCli(object):
def send_headers(self, length, status=200, mime=None, headers={}):
response = ["{} {} {}".format(self.http_ver, status, HTTPCODE[status])]
if length is not None:
if length is None:
self.keepalive = False
else:
response.append("Content-Length: " + unicode(length))
# close if unknown length, otherwise take client's preference
@@ -496,7 +507,7 @@ class HttpCli(object):
spd1 = get_spd(nbytes, self.t0)
spd2 = get_spd(self.conn.nbyte, self.conn.t0)
return spd1 + " " + spd2
return "{} {} n{}".format(spd1, spd2, self.conn.nreq)
def handle_post_multipart(self):
self.parser = MultipartParser(self.log, self.sr, self.headers)
@@ -758,6 +769,12 @@ class HttpCli(object):
pwd = self.parser.require("cppwd", 64)
self.parser.drop()
ck, msg = self.get_pwd_cookie(pwd)
html = self.j2("msg", h1=msg, h2='<a href="/">ack</a>', redir="/")
self.reply(html.encode("utf-8"), headers={"Set-Cookie": ck})
return True
def get_pwd_cookie(self, pwd):
if pwd in self.asrv.iuser:
msg = "login ok"
dt = datetime.utcfromtimestamp(time.time() + 60 * 60 * 24 * 365)
@@ -768,9 +785,7 @@ class HttpCli(object):
exp = "Fri, 15 Aug 1997 01:00:00 GMT"
ck = "cppwd={}; Path=/; Expires={}; SameSite=Lax".format(pwd, exp)
html = self.j2("msg", h1=msg, h2='<a href="/">ack</a>', redir="/")
self.reply(html.encode("utf-8"), headers={"Set-Cookie": ck})
return True
return [ck, msg]
def handle_mkdir(self):
new_dir = self.parser.require("name", 512)
@@ -1376,6 +1391,7 @@ class HttpCli(object):
"md_plug": "true" if self.args.emp else "false",
"md_chk_rate": self.args.mcr,
"md": boundary,
"ts": self.conn.hsrv.cachebuster(),
}
html = template.render(**targs).encode("utf-8", "replace")
html = html.split(boundary.encode("utf-8"))
@@ -1551,11 +1567,20 @@ class HttpCli(object):
if rem.startswith(".hist/up2k."):
raise Pebkac(403)
if "vcr" in self.uparam:
ext = abspath.rsplit(".")[-1]
if not self.args.vcr or ext not in FMT_FF:
raise Pebkac(403)
vcr = VCR_Direct(self, abspath)
vcr.run()
return False
is_dir = stat.S_ISDIR(st.st_mode)
th_fmt = self.uparam.get("th")
if th_fmt is not None:
if is_dir:
for fn in ["folder.png", "folder.jpg"]:
for fn in self.args.th_covers.split(","):
fp = os.path.join(abspath, fn)
if os.path.exists(fp):
vrem = "{}/{}".format(vrem.rstrip("/"), fn)
@@ -1619,7 +1644,6 @@ class HttpCli(object):
url_suf = self.urlq()
is_ls = "ls" in self.uparam
ts = "" # "?{}".format(time.time())
tpl = "browser"
if "b" in self.uparam:
@@ -1644,7 +1668,6 @@ class HttpCli(object):
"vdir": quotep(self.vpath),
"vpnodes": vpnodes,
"files": [],
"ts": ts,
"perms": json.dumps(perms),
"taglist": [],
"tag_order": [],

View File

@@ -43,6 +43,7 @@ class HttpConn(object):
self.t0 = time.time()
self.stopping = False
self.nreq = 0
self.nbyte = 0
self.workload = 0
self.u2idx = None
@@ -188,6 +189,7 @@ class HttpConn(object):
if self.workload >= 2 ** 31:
self.workload = 100
self.nreq += 1
cli = HttpCli(self)
if not cli.run():
return

View File

@@ -4,6 +4,8 @@ from __future__ import print_function, unicode_literals
import os
import sys
import time
import base64
import struct
import socket
import threading
@@ -25,7 +27,6 @@ except ImportError:
sys.exit(1)
from .__init__ import E, MACOS
from .authsrv import AuthSrv
from .httpconn import HttpConn
@@ -48,6 +49,8 @@ class HttpSrv(object):
self.clients = {}
self.workload = 0
self.workload_thr_alive = False
self.cb_ts = 0
self.cb_v = 0
env = jinja2.Environment()
env.loader = jinja2.FileSystemLoader(os.path.join(E.mod, "web"))
@@ -138,11 +141,12 @@ class HttpSrv(object):
"shut({}): {}".format(fno, ex),
c="1;30",
)
if ex.errno not in [10038, 10054, 107, 57, 9]:
if ex.errno not in [10038, 10054, 107, 57, 49, 9]:
# 10038 No longer considered a socket
# 10054 Foribly closed by remote
# 107 Transport endpoint not connected
# 57 Socket is not connected
# 49 Can't assign requested address (wifi down)
# 9 Bad file descriptor
raise
finally:
@@ -177,3 +181,25 @@ class HttpSrv(object):
self.clients[cli] = now
self.workload = total
def cachebuster(self):
if time.time() - self.cb_ts < 1:
return self.cb_v
with self.mutex:
if time.time() - self.cb_ts < 1:
return self.cb_v
v = E.t0
try:
with os.scandir(os.path.join(E.mod, "web")) as dh:
for fh in dh:
inf = fh.stat(follow_symlinks=False)
v = max(v, inf.st_mtime)
except:
pass
v = base64.urlsafe_b64encode(struct.pack(">xxL", int(v)))
self.cb_v = v.decode("ascii")[-4:]
self.cb_ts = time.time()
return self.cb_v

View File

@@ -1019,7 +1019,8 @@ class Up2k(object):
break
except:
# missing; restart
job = None
if not self.args.nw:
job = None
break
else:
# file contents match, but not the path
@@ -1089,6 +1090,9 @@ class Up2k(object):
}
def _untaken(self, fdir, fname, ts, ip):
if self.args.nw:
return fname
# TODO broker which avoid this race and
# provides a new filename if taken (same as bup)
suffix = ".{:.6f}-{}".format(ts, ip)
@@ -1098,6 +1102,9 @@ class Up2k(object):
def _symlink(self, src, dst):
# TODO store this in linktab so we never delete src if there are links to it
self.log("linking dupe:\n {0}\n {1}".format(src, dst))
if self.args.nw:
return
try:
lsrc = src
ldst = dst
@@ -1175,6 +1182,10 @@ class Up2k(object):
if ret > 0:
return ret, src
if self.args.nw:
# del self.registry[ptop][wark]
return ret, dst
atomic_move(src, dst)
if ANYWIN:
@@ -1284,6 +1295,10 @@ class Up2k(object):
if self.args.dotpart:
tnam = "." + tnam
if self.args.nw:
job["tnam"] = tnam
return
suffix = ".{:.6f}-{}".format(job["t0"], job["addr"])
with ren_open(tnam, "wb", fdir=pdir, suffix=suffix) as f:
f, job["tnam"] = f["orz"]

80
copyparty/vcr.py Normal file
View File

@@ -0,0 +1,80 @@
# coding: utf-8
from __future__ import print_function, unicode_literals
import time
import shlex
import subprocess as sp
from .__init__ import PY2
from .util import fsenc
class VCR_Direct(object):
def __init__(self, cli, fpath):
self.cli = cli
self.fpath = fpath
self.log_func = cli.log_func
self.log_src = cli.log_src
def log(self, msg, c=0):
self.log_func(self.log_src, "vcr: {}".format(msg), c)
def run(self):
opts = self.cli.uparam
# fmt: off
cmd = [
"ffmpeg",
"-nostdin",
"-hide_banner",
"-v", "warning",
"-i", fsenc(self.fpath),
"-vf", "scale=640:-4",
"-c:a", "libopus",
"-b:a", "128k",
"-c:v", "libvpx",
"-deadline", "realtime",
"-row-mt", "1"
]
# fmt: on
if "ss" in opts:
cmd.extend(["-ss", opts["ss"]])
if "crf" in opts:
cmd.extend(["-b:v", "0", "-crf", opts["crf"]])
else:
cmd.extend(["-b:v", "{}M".format(opts.get("mbps", 1.2))])
cmd.extend(["-f", "webm", "-"])
comp = str if not PY2 else unicode
cmd = [x.encode("utf-8") if isinstance(x, comp) else x for x in cmd]
self.log(" ".join([shlex.quote(x.decode("utf-8", "replace")) for x in cmd]))
p = sp.Popen(cmd, stdout=sp.PIPE)
self.cli.send_headers(None, mime="video/webm")
fails = 0
while True:
self.log("read")
buf = p.stdout.read(1024 * 4)
if not buf:
fails += 1
if p.poll() is not None or fails > 30:
self.log("ffmpeg exited")
return
time.sleep(0.1)
continue
fails = 0
try:
self.cli.s.sendall(buf)
except:
self.log("client disconnected")
p.kill()
return

View File

@@ -119,7 +119,7 @@ window.baguetteBox = (function () {
var gallery = [];
[].forEach.call(tagsNodeList, function (imageElement, imageIndex) {
var imageElementClickHandler = function (event) {
if (event && event.ctrlKey)
if (event && (event.ctrlKey || event.metaKey))
return true;
event.preventDefault ? event.preventDefault() : event.returnValue = false;

View File

@@ -607,7 +607,7 @@ input.eq_gain {
#srch_q {
white-space: pre;
color: #f80;
height: 1em;
min-height: 1em;
margin: .2em 0 -1em 1.6em;
}
#tq_raw {

View File

@@ -6,10 +6,10 @@
<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 }}">
<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 }}">
<link rel="stylesheet" type="text/css" media="screen" href="{{ css }}?_={{ ts }}">
{%- endif %}
</head>
@@ -110,7 +110,7 @@
<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>
@@ -127,9 +127,9 @@
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>
<script src="/.cpr/util.js?_={{ ts }}"></script>
<script src="/.cpr/browser.js?_={{ ts }}"></script>
<script src="/.cpr/up2k.js?_={{ ts }}"></script>
</body>
</html>

View File

@@ -78,7 +78,7 @@ ebi('op_up2k').innerHTML = (
' <tr>\n' +
' <td>\n' +
' <a href="#" id="nthread_sub">&ndash;</a><input\n' +
' class="txtbox" id="nthread" value="2"/><a\n' +
' class="txtbox" id="nthread" value="2" tt="pause uploads by setting it to 0"/><a\n' +
' href="#" id="nthread_add">+</a><br />&nbsp;\n' +
' </td>\n' +
' </tr>\n' +
@@ -237,13 +237,17 @@ var mpl = (function () {
'<a href="#" class="tgl btn" tt="load the next folder and continue">📂 next-folder</a>' +
'</div></div>' +
'<div><h3>tint</h3><div>' +
'<input type="text" id="pb_tint" size="3" value="0" tt="background level (0-100) on the seekbar$Nto make buffering less distracting" />' +
'</div></div>' +
'<div><h3>audio equalizer</h3><div id="audio_eq"></div></div>');
var r = {
"pb_mode": sread('pb_mode') || 'loop-folder',
"preload": bcfg_get('au_preload', true),
"clip": bcfg_get('au_npclip', false),
"os_ctl": bcfg_get('au_os_ctl', true) && have_mctl,
"os_ctl": bcfg_get('au_os_ctl', have_mctl) && have_mctl,
"osd_cv": bcfg_get('au_osd_cv', true),
};
@@ -290,6 +294,19 @@ var mpl = (function () {
draw_pb_mode();
}
function set_tint() {
var tint = icfg_get('pb_tint', 0);
if (!tint)
ebi('barbuf').style.removeProperty('background');
else
ebi('barbuf').style.background = 'rgba(126,163,75,' + (tint / 100.0) + ')';
}
ebi('pb_tint').oninput = function (e) {
swrite('pb_tint', this.value);
set_tint();
};
set_tint();
r.pp = function () {
if (!r.os_ctl)
return;
@@ -522,37 +539,38 @@ function get_np() {
// toggle player widget
var widget = (function () {
var ret = {},
var r = {},
widget = ebi('widget'),
wtico = ebi('wtico'),
nptxt = ebi('nptxt'),
npirc = ebi('npirc'),
touchmode = false,
side_open = false,
was_paused = true;
ret.open = function () {
if (side_open)
r.is_open = false;
r.open = function () {
if (r.is_open)
return false;
widget.className = 'open';
side_open = true;
r.is_open = true;
return true;
};
ret.close = function () {
if (!side_open)
r.close = function () {
if (!r.is_open)
return false;
widget.className = '';
side_open = false;
r.is_open = false;
return true;
};
ret.toggle = function (e) {
ret.open() || ret.close();
r.toggle = function (e) {
r.open() || r.close();
ev(e);
return false;
};
ret.paused = function (paused) {
r.paused = function (paused) {
if (was_paused != paused) {
was_paused = paused;
ebi('bplay').innerHTML = paused ? '▶' : '⏸';
@@ -560,7 +578,7 @@ var widget = (function () {
};
wtico.onclick = function (e) {
if (!touchmode)
ret.toggle(e);
r.toggle(e);
return false;
};
@@ -591,7 +609,7 @@ var widget = (function () {
document.body.removeChild(o);
}, 500);
};
return ret;
return r;
})();
@@ -857,7 +875,10 @@ function playpause(e) {
ebi('bplay').onclick = playpause;
ebi('bprev').onclick = prev_song;
ebi('bnext').onclick = next_song;
ebi('barpos').onclick = function (e) {
var bar = ebi('barpos');
bar.onclick = function (e) {
if (!mp.au) {
play(0, true);
return mp.fade_in();
@@ -868,6 +889,19 @@ function playpause(e) {
seek_au_mul(x * 1.0 / rect.width);
};
if (!is_touch)
bar.onwheel = function (e) {
var dist = Math.sign(e.deltaY) * 10;
if (Math.abs(e.deltaY) < 30 && !e.deltaMode)
dist = e.deltaY;
if (!dist || !mp.au)
return true;
seek_au_rel(dist);
ev(e);
};
})();
@@ -1186,8 +1220,11 @@ function play(tid, is_ev, seek, call_depth) {
mp.stopfade(true);
var tn = tid;
if ((tn + '').indexOf('f-') === 0)
if ((tn + '').indexOf('f-') === 0) {
tn = mp.order.indexOf(tn);
if (tn < 0)
return;
}
if (tn >= mp.order.length) {
if (mpl.pb_mode == 'loop-folder') {
@@ -1385,8 +1422,7 @@ function autoplay_blocked(seek) {
}
// autoplay linked track
(function () {
function play_linked() {
var v = location.hash;
if (v && v.indexOf('#af-') === 0) {
var id = v.slice(2).split('&');
@@ -1402,7 +1438,7 @@ function autoplay_blocked(seek) {
return play(id[0], false, parseInt(m[1] || 0) * 60 + parseInt(m[2] || 0));
}
})();
};
var thegrid = (function () {
@@ -1508,35 +1544,61 @@ var thegrid = (function () {
}
setsz();
function seltgl(e) {
if (e && e.ctrlKey)
function gclick(e) {
if (e && (e.ctrlKey || e.metaKey))
return true;
ev(e);
var oth = ebi(this.getAttribute('ref')),
td = oth.parentNode.nextSibling,
href = this.getAttribute('href'),
aplay = ebi('a' + oth.getAttribute('id')),
is_img = /\.(gif|jpe?g|png|webp)(\?|$)/i.test(href),
is_vid = /\.(av1|asf|avi|flv|m4v|mkv|mjpeg|mjpg|mpg|mpeg|mpg2|mpeg2|h264|avc|h265|hevc|mov|3gp|mp4|ts|mpegts|nut|ogv|ogm|rm|vob|webm|wmv)(\?|$)/i.test(href),
in_tree = null,
have_sel = QS('#files tr.sel'),
td = oth.closest('td').nextSibling,
tr = td.parentNode;
td.click();
this.setAttribute('class', tr.getAttribute('class'));
}
if (/\/(\?|$)/.test(href)) {
var ta = QSA('#treeul a.hl+ul>li>a+a'),
txt = oth.textContent.slice(0, -1);
function bgopen(e) {
for (var a = 0, aa = ta.length; a < aa; a++) {
if (ta[a].textContent == txt) {
in_tree = ta[a];
break;
}
}
}
if (r.sel) {
td.click();
this.setAttribute('class', tr.getAttribute('class'));
}
else if (widget.is_open && aplay)
aplay.click();
else if (in_tree && !have_sel)
in_tree.click();
else if (is_vid)
window.open(href + (href.indexOf('?') === -1 ? '?' : '&') + 'vcr', '_blank');
else if (!is_img && have_sel)
window.open(href, '_blank');
else return true;
ev(e);
var url = this.getAttribute('href');
window.open(url, '_blank');
}
r.loadsel = function () {
if (r.dirty)
return;
var ths = QSA('#ggrid>a'),
have_sel = !!QS('#files tr.sel');
var ths = QSA('#ggrid>a');
for (var a = 0, aa = ths.length; a < aa; a++) {
ths[a].onclick = r.sel ? seltgl : have_sel ? bgopen : null;
ths[a].setAttribute('class', ebi(ths[a].getAttribute('ref')).parentNode.parentNode.getAttribute('class'));
var tr = ebi(ths[a].getAttribute('ref')).closest('tr');
ths[a].setAttribute('class', tr.getAttribute('class'));
}
var uns = QS('#ggrid a[ref="unsearch"]');
if (uns)
@@ -1567,6 +1629,8 @@ var thegrid = (function () {
if (r.thumbs) {
ihref += (ihref.indexOf('?') === -1 ? '?' : '&') + 'th=' + (have_webp ? 'w' : 'j');
if (href == "#")
ihref = '/.cpr/ico/⏏️';
}
else if (isdir) {
ihref = '/.cpr/ico/folder';
@@ -1594,6 +1658,11 @@ var thegrid = (function () {
ihref + '" /><span' + ac + '>' + ao.innerHTML + '</span></a>');
}
ebi('ggrid').innerHTML = html.join('\n');
var ths = QSA('#ggrid>a');
for (var a = 0, aa = ths.length; a < aa; a++)
ths[a].onclick = gclick;
r.dirty = false;
r.bagit();
r.loadsel();
@@ -1681,10 +1750,14 @@ document.onkeydown = function (e) {
if (!document.activeElement || document.activeElement != document.body && document.activeElement.nodeName.toLowerCase() != 'a')
return;
if (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey || e.isComposing)
if (e.ctrlKey || e.altKey || e.metaKey || e.isComposing)
return;
var k = (e.code + ''), pos = -1, n;
if (e.shiftKey && k != 'KeyA' && k != 'KeyD')
return;
if (k.indexOf('Digit') === 0)
pos = parseInt(k.slice(-1)) * 0.1;
@@ -1720,6 +1793,14 @@ document.onkeydown = function (e) {
if (k == 'KeyT')
return ebi('thumbs').click();
if (!treectl.hidden && (!e.shiftKey || !thegrid.en)) {
if (k == 'KeyA')
return QS('#twig').click();
if (k == 'KeyD')
return QS('#twobytwo').click();
}
if (thegrid.en) {
if (k == 'KeyS')
return ebi('gridsel').click();
@@ -1802,6 +1883,7 @@ document.onkeydown = function (e) {
}
var search_timeout,
defer_timeout,
search_in_progress = 0;
function ev_search_input() {
@@ -1816,9 +1898,29 @@ document.onkeydown = function (e) {
if (id != "q_raw")
encode_query();
clearTimeout(search_timeout);
if (Date.now() - search_in_progress > 30 * 1000)
set_vq();
clearTimeout(defer_timeout);
defer_timeout = setTimeout(try_search, 2000);
try_search();
}
function try_search() {
if (Date.now() - search_in_progress > 30 * 1000) {
clearTimeout(defer_timeout);
clearTimeout(search_timeout);
search_timeout = setTimeout(do_search, 200);
}
}
function set_vq() {
if (search_in_progress)
return;
var q = ebi('q_raw').value,
vq = ebi('files').getAttribute('q_raw');
srch_msg(false, (q == vq) ? '' : 'search results below are from a previous query:\n ' + (vq ? vq : '(*)'));
}
function encode_query() {
@@ -1888,7 +1990,8 @@ document.onkeydown = function (e) {
xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.onreadystatechange = xhr_search_results;
xhr.ts = Date.now();
xhr.send(JSON.stringify({ "q": ebi('q_raw').value }));
xhr.q_raw = ebi('q_raw').value;
xhr.send(JSON.stringify({ "q": xhr.q_raw }));
}
function xhr_search_results() {
@@ -1959,6 +2062,8 @@ document.onkeydown = function (e) {
ofiles.innerHTML = html.join('\n');
ofiles.setAttribute("ts", this.ts);
ofiles.setAttribute("q_raw", this.q_raw);
set_vq();
mukey.render();
reload_browser();
filecols.set_style(['File Name']);
@@ -1970,6 +2075,7 @@ document.onkeydown = function (e) {
ev(e);
treectl.show();
ebi('files').innerHTML = orig_html;
ebi('files').removeAttribute('q_raw');
orig_html = null;
msel.render();
reload_browser();
@@ -2188,6 +2294,9 @@ var treectl = (function () {
}
function treego(e) {
if (e && (e.ctrlKey || e.metaKey))
return true;
ev(e);
if (this.getAttribute('class') == 'hl' &&
this.previousSibling.textContent == '-') {
@@ -2965,3 +3074,4 @@ function reload_browser(not_mp) {
reload_browser(true);
mukey.render();
msel.render();
play_linked();

View File

@@ -54,7 +54,7 @@
<div>{{ logues[1] }}</div><br />
{%- endif %}
<h2><a href="{{ url_suf }}{{ url_suf and '&amp;' or '?' }}h">control-panel</a></h2>
<h2><a href="/{{ url_suf }}{{ url_suf and '&amp;' or '?' }}h">control-panel</a></h2>
</body>
</html>

View File

@@ -3,9 +3,9 @@
<title>📝🎉 {{ title }}</title> <!-- 📜 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=0.7">
<link href="/.cpr/md.css" rel="stylesheet">
<link href="/.cpr/md.css?_={{ ts }}" rel="stylesheet">
{%- if edit %}
<link href="/.cpr/md2.css" rel="stylesheet">
<link href="/.cpr/md2.css?_={{ ts }}" rel="stylesheet">
{%- endif %}
</head>
<body>
@@ -146,10 +146,10 @@ var md_opt = {
})();
</script>
<script src="/.cpr/util.js"></script>
<script src="/.cpr/deps/marked.js"></script>
<script src="/.cpr/md.js"></script>
<script src="/.cpr/util.js?_={{ ts }}"></script>
<script src="/.cpr/deps/marked.js?_={{ ts }}"></script>
<script src="/.cpr/md.js?_={{ ts }}"></script>
{%- if edit %}
<script src="/.cpr/md2.js"></script>
<script src="/.cpr/md2.js?_={{ ts }}"></script>
{%- endif %}
</body></html>

View File

@@ -3,9 +3,9 @@
<title>📝🎉 {{ title }}</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=0.7">
<link href="/.cpr/mde.css" rel="stylesheet">
<link href="/.cpr/deps/mini-fa.css" rel="stylesheet">
<link href="/.cpr/deps/easymde.css" rel="stylesheet">
<link href="/.cpr/mde.css?_={{ ts }}" rel="stylesheet">
<link href="/.cpr/deps/mini-fa.css?_={{ ts }}" rel="stylesheet">
<link href="/.cpr/deps/easymde.css?_={{ ts }}" rel="stylesheet">
</head>
<body>
<div id="mw">
@@ -43,7 +43,7 @@ var lightswitch = (function () {
})();
</script>
<script src="/.cpr/util.js"></script>
<script src="/.cpr/deps/easymde.js"></script>
<script src="/.cpr/mde.js"></script>
<script src="/.cpr/util.js?_={{ ts }}"></script>
<script src="/.cpr/deps/easymde.js?_={{ ts }}"></script>
<script src="/.cpr/mde.js?_={{ ts }}"></script>
</body></html>

View File

@@ -6,7 +6,7 @@
<title>copyparty</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/msg.css">
<link rel="stylesheet" type="text/css" media="screen" href="/.cpr/msg.css?_={{ ts }}">
</head>
<body>

View File

@@ -6,7 +6,7 @@
<title>copyparty</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/splash.css">
<link rel="stylesheet" type="text/css" media="screen" href="/.cpr/splash.css?_={{ ts }}">
</head>
<body>

View File

@@ -1,7 +1,5 @@
"use strict";
window.onerror = vis_exh;
function goto_up2k() {
if (up2k === false)
@@ -16,17 +14,19 @@ function goto_up2k() {
// chrome requires https to use crypto.subtle,
// usually it's undefined but some chromes throw on invoke
var up2k = null;
var sha_js = window.WebAssembly ? 'hw' : 'ac'; // ff53,c57,sa11
var up2k = null,
sha_js = window.WebAssembly ? 'hw' : 'ac', // ff53,c57,sa11
m = 'will use ' + sha_js + ' instead of native sha512 due to';
try {
var cf = crypto.subtle || crypto.webkitSubtle;
cf.digest('SHA-512', new Uint8Array(1)).then(
function (x) { console.log('sha-ok'); up2k = up2k_init(cf); },
function (x) { console.log('sha-ng:', x); up2k = up2k_init(false); }
function (x) { console.log(m, x); up2k = up2k_init(false); }
);
}
catch (ex) {
console.log('sha-na:', ex);
console.log(m, ex);
try {
up2k = up2k_init(false);
}
@@ -142,7 +142,7 @@ function U2pvis(act, btns) {
this.tail = -1;
this.wsz = 3;
this.addfile = function (entry, sz) {
this.addfile = function (entry, sz, draw) {
this.tab.push({
"hn": entry[0],
"ht": entry[1],
@@ -156,6 +156,9 @@ function U2pvis(act, btns) {
"bd0": 0 // upload start
});
this.ctr["q"]++;
if (!draw)
return;
this.drawcard("q");
if (this.act == "q") {
this.addrow(this.tab.length - 1);
@@ -256,6 +259,41 @@ function U2pvis(act, btns) {
var obj = ebi('f{0}p'.format(fobj.n)),
o1 = p[0] - 2, o2 = p[0] - 0.1, o3 = p[0];
if (!obj) { //} || true) {
var msg = [
"act", this.act,
"in", fo.in,
"is_act", this.is_act(fo.in),
"head", this.head,
"tail", this.tail,
"nfile", fobj.n,
"name", fobj.name,
"sz", fobj.size,
"bytesDelta", delta,
"bytesDone", fo.bd,
],
m2 = '',
ds = QSA("#u2tab>tbody>tr>td:first-child>a:last-child");
for (var a = 0; a < msg.length; a += 2)
m2 += msg[a] + '=' + msg[a + 1] + ', ';
console.log(m2);
for (var a = 0, aa = ds.length; a < aa; a++) {
var id = ds[a].parentNode.getAttribute('id').slice(1, -1);
console.log("dom %d/%d = [%s] in(%s) is_act(%s) %s",
a, aa, id, this.tab[id].in, this.is_act(fo.in), ds[a].textContent);
}
for (var a = 0, aa = this.tab.length; a < aa; a++)
if (this.is_act(this.tab[a].in))
console.log("tab %d/%d = sz %s", a, aa, this.tab[a].bt);
console.log("a");
throw 42;
}
obj.innerHTML = fo.hp;
obj.style.color = '#fff';
obj.style.background = 'linear-gradient(90deg, #050, #270 ' + o1 + '%, #4b0 ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)';
@@ -276,12 +314,14 @@ function U2pvis(act, btns) {
this.drawcard(oldcat);
this.drawcard(newcat);
if (this.is_act(newcat)) {
this.tail++;
this.tail = Math.max(this.tail, nfile + 1);
if (!ebi('f' + nfile))
this.addrow(nfile);
}
else if (this.is_act(oldcat)) {
this.head++;
while (this.head < Math.min(this.tab.length, this.tail) && (this.head == nfile || !this.is_act(this.tab[this.head].in)))
this.head++;
if (!bz_act) {
var tr = ebi("f" + nfile);
tr.parentNode.removeChild(tr);
@@ -350,8 +390,21 @@ function U2pvis(act, btns) {
}
}
if (this.head == -1) {
this.head = this.tab.length;
this.tail = this.head - 1;
var precard = has(["ok", "ng", "done"], this.act) ? {} : this.act == "bz" ? { "ok": 1, "ng": 1 } : { "ok": 1, "ng": 1, "bz": 1 },
postcard = has(["ok", "ng", "done"], this.act) ? { "bz": 1, "q": 1 } : this.act == "bz" ? { "q": 1 } : {};
for (var a = 0; a < this.tab.length; a++) {
var rt = this.tab[a].in;
if (precard[rt]) {
this.head = a + 1;
this.tail = a;
}
else if (postcard[rt]) {
this.head = a;
this.tail = a - 1;
break;
}
}
}
if (card == "bz") {
for (var a = this.head - 1; a >= this.head - this.wsz && a >= 0; a--) {
@@ -598,14 +651,50 @@ function up2k_init(subtle) {
}
}
function read_dirs(rd, pf, dirs, good, bad) {
function rd_flatten(pf, dirs) {
var ret = jcp(pf);
for (var a = 0; a < dirs.length; a++)
ret.push(dirs.fullPath || '');
ret.sort();
return ret;
}
var rd_missing_ref = [];
function read_dirs(rd, pf, dirs, good, bad, spins) {
spins = spins || 0;
if (++spins == 5)
rd_missing_ref = rd_flatten(pf, dirs);
if (spins == 200) {
var missing = rd_flatten(pf, dirs),
match = rd_missing_ref.length == missing.length,
aa = match ? missing.length : 0;
missing.sort();
for (var a = 0; a < aa; a++)
if (rd_missing_ref[a] != missing[a])
match = false;
if (match) {
var msg = ['directory iterator got stuck on the following {0} items; good chance your browser is about to spinlock:'.format(missing.length)];
for (var a = 0; a < Math.min(20, missing.length); a++)
msg.push(missing[a]);
alert(msg.join('\n-- '));
dirs = [];
pf = [];
}
spins = 0;
}
if (!dirs.length) {
if (!pf.length)
return gotallfiles(good, bad);
console.log("retry pf, " + pf.length);
setTimeout(function () {
read_dirs(rd, pf, dirs, good, bad);
read_dirs(rd, pf, dirs, good, bad, spins);
}, 50);
return;
}
@@ -645,7 +734,7 @@ function up2k_init(subtle) {
dirs.shift();
rd = null;
}
return read_dirs(rd, pf, dirs, good, bad);
return read_dirs(rd, pf, dirs, good, bad, spins);
});
}
@@ -670,41 +759,50 @@ function up2k_init(subtle) {
if (ask_up && !fsearch && !confirm(msg.join('\n')))
return;
var seen = {},
evpath = get_evpath(),
draw_each = good_files.length < 50;
for (var a = 0; a < st.files.length; a++)
seen[st.files[a].name + '\n' + st.files[a].size] = 1;
for (var a = 0; a < good_files.length; a++) {
var fobj = good_files[a][0],
now = Date.now(),
lmod = fobj.lastModified || now;
var entry = {
"n": parseInt(st.files.length.toString()),
"n": st.files.length,
"t0": now,
"fobj": fobj,
"name": good_files[a][1],
"size": fobj.size,
"lmod": lmod / 1000,
"purl": get_evpath(),
"purl": evpath,
"done": false,
"hash": []
};
},
key = entry.name + '\n' + entry.size;
var skip = false;
for (var b = 0; b < st.files.length; b++)
if (entry.name == st.files[b].name &&
entry.size == st.files[b].size)
skip = true;
if (skip)
if (seen[key])
continue;
seen[key] = 1;
pvis.addfile([
fsearch ? esc(entry.name) : linksplit(
uricom_dec(entry.purl)[0] + entry.name).join(' '),
'📐 hash',
''
], fobj.size);
], fobj.size, draw_each);
st.files.push(entry);
st.todo.hash.push(entry);
}
if (!draw_each) {
pvis.drawcard("q");
pvis.changecard(pvis.act);
}
}
ebi('u2btn').addEventListener('drop', gotfile, false);
@@ -781,7 +879,7 @@ function up2k_init(subtle) {
clearTimeout(tto);
running = true;
while (true) {
while (window['vis_exh']) {
var is_busy = 0 !=
st.todo.hash.length +
st.todo.handshake.length +
@@ -954,7 +1052,7 @@ function up2k_init(subtle) {
bpend += cdr - car;
reader.onload = function (e) {
function orz(e) {
if (!min_filebuf && nch == 1) {
min_filebuf = 1;
var td = Date.now() - t0;
@@ -964,9 +1062,30 @@ function up2k_init(subtle) {
}
}
hash_calc(nch, e.target.result);
}
reader.onload = function (e) {
try { orz(e); } catch (ex) { vis_exh(ex + '', '', '', '', ex); }
};
reader.onerror = function () {
alert('y o u b r o k e i t\nerror: ' + reader.error);
var err = reader.error + '';
var handled = false;
if (err.indexOf('NotReadableError') !== -1 || // win10-chrome defender
err.indexOf('NotFoundError') !== -1 // macos-firefox permissions
) {
pvis.seth(t.n, 1, 'OS-error');
pvis.seth(t.n, 2, err);
handled = true;
}
if (handled) {
pvis.move(t.n, 'ng');
st.busy.hash.splice(st.busy.hash.indexOf(t), 1);
st.bytes.uploaded += t.size;
return tasker();
}
alert('y o u b r o k e i t\nfile: ' + t.name + '\nerror: ' + err);
};
reader.readAsArrayBuffer(
bobslice.call(t.fobj, car, cdr));
@@ -1047,12 +1166,12 @@ function up2k_init(subtle) {
console.log('zombie handshake onerror,', t);
return;
}
console.log('handshake onerror, retrying');
console.log('handshake onerror, retrying', t);
st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1);
st.todo.handshake.unshift(t);
tasker();
};
xhr.onload = function (e) {
function orz(e) {
if (t.busied != me) {
console.log('zombie handshake onload,', t);
return;
@@ -1092,6 +1211,7 @@ function up2k_init(subtle) {
if (response.name !== t.name) {
// file exists; server renamed us
console.log("server-rename [" + t.name + "] to [" + response.name + "]");
t.name = response.name;
pvis.seth(t.n, 0, linksplit(t.purl + t.name).join(' '));
}
@@ -1196,6 +1316,9 @@ function up2k_init(subtle) {
(xhr.responseText && xhr.responseText) ||
"no further information"));
}
}
xhr.onload = function (e) {
try { orz(e); } catch (ex) { vis_exh(ex + '', '', '', '', ex); }
};
var req = {
@@ -1236,40 +1359,56 @@ function up2k_init(subtle) {
if (cdr >= t.size)
cdr = t.size;
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function (xev) {
pvis.prog(t, npart, xev.loaded);
};
xhr.onload = function (xev) {
function orz(xhr) {
var txt = ((xhr.response && xhr.response.err) || xhr.responseText) + '';
if (xhr.status == 200) {
pvis.prog(t, npart, cdr - car);
st.bytes.uploaded += cdr - car;
t.bytes_uploaded += cdr - car;
st.busy.upload.splice(st.busy.upload.indexOf(upt), 1);
t.postlist.splice(t.postlist.indexOf(npart), 1);
if (t.postlist.length == 0) {
t.t4 = Date.now();
pvis.seth(t.n, 1, 'verifying');
st.todo.handshake.unshift(t);
}
tasker();
}
else
else if (txt.indexOf('already got that') !== -1) {
console.log("ignoring dupe-segment error");
}
else {
alert("server broke; cu-err {0} on file [{1}]:\n".format(
xhr.status, t.name) + (
(xhr.response && xhr.response.err) ||
(xhr.responseText && xhr.responseText) ||
"no further information"));
};
xhr.open('POST', t.purl + 'chunkpit.php', true);
xhr.setRequestHeader("X-Up2k-Hash", t.hash[npart]);
xhr.setRequestHeader("X-Up2k-Wark", t.wark);
xhr.setRequestHeader('Content-Type', 'application/octet-stream');
if (xhr.overrideMimeType)
xhr.overrideMimeType('Content-Type', 'application/octet-stream');
xhr.status, t.name) + (txt || "no further information"));
return;
}
st.busy.upload.splice(st.busy.upload.indexOf(upt), 1);
t.postlist.splice(t.postlist.indexOf(npart), 1);
if (t.postlist.length == 0) {
t.t4 = Date.now();
pvis.seth(t.n, 1, 'verifying');
st.todo.handshake.unshift(t);
}
tasker();
}
function do_send() {
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function (xev) {
pvis.prog(t, npart, xev.loaded);
};
xhr.onload = function (xev) {
try { orz(xhr); } catch (ex) { vis_exh(ex + '', '', '', '', ex); }
};
xhr.onerror = function (xev) {
if (!window['vis_exh'])
return;
xhr.responseType = 'text';
xhr.send(bobslice.call(t.fobj, car, cdr));
console.log('chunkpit onerror, retrying', t);
do_send();
};
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');
xhr.responseType = 'text';
xhr.send(bobslice.call(t.fobj, car, cdr));
}
do_send();
}
/////
@@ -1309,6 +1448,17 @@ function up2k_init(subtle) {
}
tt.init();
function bumpthread2(e) {
if (e.ctrlKey || e.altKey || e.metaKey || e.isComposing)
return;
if (e.code == 'ArrowUp')
bumpthread(1);
if (e.code == 'ArrowDown')
bumpthread(-1);
}
function bumpthread(dir) {
try {
dir.stopPropagation();
@@ -1319,7 +1469,7 @@ function up2k_init(subtle) {
if (dir.target) {
clmod(obj, 'err', 1);
var v = Math.floor(parseInt(obj.value));
if (v < 1 || v > 8 || v !== v)
if (v < 0 || v > 64 || v !== v)
return;
parallel_uploads = v;
@@ -1330,11 +1480,11 @@ function up2k_init(subtle) {
parallel_uploads += dir;
if (parallel_uploads < 1)
parallel_uploads = 1;
if (parallel_uploads < 0)
parallel_uploads = 0;
if (parallel_uploads > 8)
parallel_uploads = 8;
if (parallel_uploads > 16)
parallel_uploads = 16;
obj.value = parallel_uploads;
bumpthread({ "target": 1 })
@@ -1430,6 +1580,7 @@ function up2k_init(subtle) {
bumpthread(-1);
};
ebi('nthread').onkeydown = bumpthread2;
ebi('nthread').addEventListener('input', bumpthread, false);
ebi('multitask').addEventListener('click', tgl_multitask, false);
ebi('ask_up').addEventListener('click', tgl_ask_up, false);
@@ -1443,7 +1594,10 @@ function up2k_init(subtle) {
nodes[a].addEventListener('touchend', nop, false);
set_fsearch();
bumpthread({ "target": 1 })
bumpthread({ "target": 1 });
if (parallel_uploads < 1)
bumpthread(1);
return { "init_deps": init_deps, "set_fsearch": set_fsearch }
}

View File

@@ -11,16 +11,6 @@ var is_touch = 'ontouchstart' in window,
// error handler for mobile devices
function hcroak(msg) {
document.body.innerHTML = msg;
window.onerror = undefined;
throw 'fatal_err';
}
function croak(msg) {
document.body.textContent = msg;
window.onerror = undefined;
throw msg;
}
function esc(txt) {
return txt.replace(/[&"<>]/g, function (c) {
return {
@@ -32,9 +22,12 @@ function esc(txt) {
});
}
function vis_exh(msg, url, lineNo, columnNo, error) {
if (!window.onerror)
return;
window.onerror = undefined;
window['vis_exh'] = null;
var html = ['<h1>you hit a bug!</h1><p>please screenshot this error and send me a copy arigathanks gozaimuch (ed/irc.rizon.net or ed#2644)</p><p>',
var html = ['<h1>you hit a bug!</h1><p>please send me a screenshot arigathanks gozaimuch: <code>ed/irc.rizon.net</code> or <code>ed#2644</code><br />&nbsp; (and if you can, press F12 and include the "Console" tab in the screenshot too)</p><p>',
esc(String(msg)), '</p><p>', esc(url + ' @' + lineNo + ':' + columnNo), '</p>'];
if (error) {
@@ -44,9 +37,13 @@ function vis_exh(msg, url, lineNo, columnNo, error) {
html.push('<h2>' + find[a] + '</h2>' +
esc(String(error[find[a]])).replace(/\n/g, '<br />\n'));
}
document.body.style.fontSize = '0.8em';
document.body.style.padding = '0 1em 1em 1em';
hcroak(html.join('\n'));
document.body.innerHTML = html.join('\n');
var s = mknod('style');
s.innerHTML = 'body{background:#333;color:#ddd;font-family:sans-serif;font-size:0.8em;padding:0 1em 1em 1em} code{color:#bf7;background:#222;padding:.1em;margin:.2em;font-size:1.1em;font-family:monospace,monospace} *{line-height:1.5em}';
document.head.appendChild(s);
throw 'fatal_err';
}
@@ -67,6 +64,9 @@ function ev(e) {
if (e.stopPropagation)
e.stopPropagation();
if (e.stopImmediatePropagation)
e.stopImmediatePropagation();
e.returnValue = false;
return e;
}
@@ -360,11 +360,11 @@ function get_vpath() {
function get_pwd() {
var pwd = ('; ' + document.cookie).split('; cppwd=');
if (pwd.length < 2)
return null;
var pwd = ('; ' + document.cookie).split('; cppwd=');
if (pwd.length < 2)
return null;
return pwd[1].split(';')[0];
return pwd[1].split(';')[0];
}
@@ -389,6 +389,11 @@ function has(haystack, needle) {
}
function jcp(obj) {
return JSON.parse(JSON.stringify(obj));
}
function sread(key) {
if (window.localStorage)
return localStorage.getItem(key);

View File

@@ -6,10 +6,10 @@ import re, os, sys, time, shutil, signal, threading, tarfile, hashlib, platform,
import subprocess as sp
"""
run me with any version of python, i will unpack and run copyparty
pls don't edit this file with a text editor,
it breaks the compressed stuff at the end
(but please don't edit this file with a text editor
since that would probably corrupt the binary stuff at the end)
run me with any version of python, i will unpack and run copyparty
there's zero binaries! just plaintext python scripts all the way down
so you can easily unpack the archive and inspect it for shady stuff

View File

@@ -108,6 +108,9 @@ class VHttpSrv(object):
aliases = ["splash", "browser", "browser2", "msg", "md", "mde"]
self.j2 = {x: J2_FILES for x in aliases}
def cachebuster(self):
return "a"
class VHttpConn(object):
def __init__(self, args, asrv, log, buf):
@@ -121,6 +124,7 @@ class VHttpConn(object):
self.log_src = "a"
self.lf_url = None
self.hsrv = VHttpSrv()
self.nreq = 0
self.nbyte = 0
self.workload = 0
self.ico = None