Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
456f575637 | ||
|
|
51546c9e64 | ||
|
|
83b4b70ef4 | ||
|
|
a5120d4f6f | ||
|
|
c95941e14f | ||
|
|
0dd531149d | ||
|
|
67da1b5219 | ||
|
|
919bd16437 | ||
|
|
ecead109ab | ||
|
|
765294c263 | ||
|
|
d6b5351207 | ||
|
|
a2009bcc6b | ||
|
|
12709a8a0a | ||
|
|
c055baefd2 | ||
|
|
56522599b5 | ||
|
|
664f53b75d | ||
|
|
87200d9f10 | ||
|
|
5c3d0b6520 |
12
README.md
12
README.md
@@ -23,6 +23,7 @@ turn your phone or raspi into a portable file server with resumable uploads/down
|
|||||||
* [on debian](#on-debian)
|
* [on debian](#on-debian)
|
||||||
* [notes](#notes)
|
* [notes](#notes)
|
||||||
* [status](#status)
|
* [status](#status)
|
||||||
|
* [testimonials](#testimonials)
|
||||||
* [bugs](#bugs)
|
* [bugs](#bugs)
|
||||||
* [general bugs](#general-bugs)
|
* [general bugs](#general-bugs)
|
||||||
* [not my bugs](#not-my-bugs)
|
* [not my bugs](#not-my-bugs)
|
||||||
@@ -143,6 +144,13 @@ summary: all planned features work! now please enjoy the bloatening
|
|||||||
* ☑ editor (sure why not)
|
* ☑ editor (sure why not)
|
||||||
|
|
||||||
|
|
||||||
|
## testimonials
|
||||||
|
|
||||||
|
small collection of user feedback
|
||||||
|
|
||||||
|
`good enough`, `surprisingly correct`, `certified good software`, `just works`, `why`
|
||||||
|
|
||||||
|
|
||||||
# bugs
|
# bugs
|
||||||
|
|
||||||
* Windows: python 3.7 and older cannot read tags with ffprobe, so use mutagen or upgrade
|
* Windows: python 3.7 and older cannot read tags with ffprobe, so use mutagen or upgrade
|
||||||
@@ -618,6 +626,7 @@ roughly sorted by priority
|
|||||||
* reduce up2k roundtrips
|
* reduce up2k roundtrips
|
||||||
* start from a chunk index and just go
|
* start from a chunk index and just go
|
||||||
* terminate client on bad data
|
* terminate client on bad data
|
||||||
|
* logging to file
|
||||||
|
|
||||||
discarded ideas
|
discarded ideas
|
||||||
|
|
||||||
@@ -637,3 +646,6 @@ discarded ideas
|
|||||||
* nah
|
* nah
|
||||||
* look into android thumbnail cache file format
|
* look into android thumbnail cache file format
|
||||||
* absolutely not
|
* absolutely not
|
||||||
|
* indexedDB for hashes, cfg enable/clear/sz, 2gb avail, ~9k for 1g, ~4k for 100m, 500k items before autoeviction
|
||||||
|
* blank hashlist when up-ok to skip handshake
|
||||||
|
* too many confusing side-effects
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
VERSION = (0, 11, 29)
|
VERSION = (0, 11, 31)
|
||||||
CODENAME = "the grid"
|
CODENAME = "the grid"
|
||||||
BUILD_DT = (2021, 6, 30)
|
BUILD_DT = (2021, 7, 4)
|
||||||
|
|
||||||
S_VERSION = ".".join(map(str, VERSION))
|
S_VERSION = ".".join(map(str, VERSION))
|
||||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||||
|
|||||||
@@ -72,9 +72,13 @@ class HttpCli(object):
|
|||||||
if rem.startswith("/") or rem.startswith("../") or "/../" in rem:
|
if rem.startswith("/") or rem.startswith("../") or "/../" in rem:
|
||||||
raise Exception("that was close")
|
raise Exception("that was close")
|
||||||
|
|
||||||
def j2(self, name, **kwargs):
|
def j2(self, name, **ka):
|
||||||
tpl = self.conn.hsrv.j2[name]
|
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):
|
def run(self):
|
||||||
"""returns true if connection can be reused"""
|
"""returns true if connection can be reused"""
|
||||||
@@ -603,13 +607,14 @@ class HttpCli(object):
|
|||||||
os.makedirs(fsenc(dst))
|
os.makedirs(fsenc(dst))
|
||||||
except OSError as ex:
|
except OSError as ex:
|
||||||
self.log("makedirs failed [{}]".format(dst))
|
self.log("makedirs failed [{}]".format(dst))
|
||||||
if ex.errno == 13:
|
if not os.path.isdir(fsenc(dst)):
|
||||||
raise Pebkac(500, "the server OS denied write-access")
|
if ex.errno == 13:
|
||||||
|
raise Pebkac(500, "the server OS denied write-access")
|
||||||
|
|
||||||
if ex.errno == 17:
|
if ex.errno == 17:
|
||||||
raise Pebkac(400, "some file got your folder name")
|
raise Pebkac(400, "some file got your folder name")
|
||||||
|
|
||||||
raise Pebkac(500, min_ex())
|
raise Pebkac(500, min_ex())
|
||||||
except:
|
except:
|
||||||
raise Pebkac(500, min_ex())
|
raise Pebkac(500, min_ex())
|
||||||
|
|
||||||
@@ -1383,6 +1388,7 @@ class HttpCli(object):
|
|||||||
"md_plug": "true" if self.args.emp else "false",
|
"md_plug": "true" if self.args.emp else "false",
|
||||||
"md_chk_rate": self.args.mcr,
|
"md_chk_rate": self.args.mcr,
|
||||||
"md": boundary,
|
"md": boundary,
|
||||||
|
"ts": self.conn.hsrv.cachebuster(),
|
||||||
}
|
}
|
||||||
html = template.render(**targs).encode("utf-8", "replace")
|
html = template.render(**targs).encode("utf-8", "replace")
|
||||||
html = html.split(boundary.encode("utf-8"))
|
html = html.split(boundary.encode("utf-8"))
|
||||||
@@ -1626,7 +1632,6 @@ class HttpCli(object):
|
|||||||
|
|
||||||
url_suf = self.urlq()
|
url_suf = self.urlq()
|
||||||
is_ls = "ls" in self.uparam
|
is_ls = "ls" in self.uparam
|
||||||
ts = "" # "?{}".format(time.time())
|
|
||||||
|
|
||||||
tpl = "browser"
|
tpl = "browser"
|
||||||
if "b" in self.uparam:
|
if "b" in self.uparam:
|
||||||
@@ -1651,7 +1656,6 @@ class HttpCli(object):
|
|||||||
"vdir": quotep(self.vpath),
|
"vdir": quotep(self.vpath),
|
||||||
"vpnodes": vpnodes,
|
"vpnodes": vpnodes,
|
||||||
"files": [],
|
"files": [],
|
||||||
"ts": ts,
|
|
||||||
"perms": json.dumps(perms),
|
"perms": json.dumps(perms),
|
||||||
"taglist": [],
|
"taglist": [],
|
||||||
"tag_order": [],
|
"tag_order": [],
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ from __future__ import print_function, unicode_literals
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import base64
|
||||||
|
import struct
|
||||||
import socket
|
import socket
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
@@ -25,7 +27,6 @@ except ImportError:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
from .__init__ import E, MACOS
|
from .__init__ import E, MACOS
|
||||||
from .authsrv import AuthSrv
|
|
||||||
from .httpconn import HttpConn
|
from .httpconn import HttpConn
|
||||||
|
|
||||||
|
|
||||||
@@ -48,6 +49,8 @@ class HttpSrv(object):
|
|||||||
self.clients = {}
|
self.clients = {}
|
||||||
self.workload = 0
|
self.workload = 0
|
||||||
self.workload_thr_alive = False
|
self.workload_thr_alive = False
|
||||||
|
self.cb_ts = 0
|
||||||
|
self.cb_v = 0
|
||||||
|
|
||||||
env = jinja2.Environment()
|
env = jinja2.Environment()
|
||||||
env.loader = jinja2.FileSystemLoader(os.path.join(E.mod, "web"))
|
env.loader = jinja2.FileSystemLoader(os.path.join(E.mod, "web"))
|
||||||
@@ -138,11 +141,12 @@ class HttpSrv(object):
|
|||||||
"shut({}): {}".format(fno, ex),
|
"shut({}): {}".format(fno, ex),
|
||||||
c="1;30",
|
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
|
# 10038 No longer considered a socket
|
||||||
# 10054 Foribly closed by remote
|
# 10054 Foribly closed by remote
|
||||||
# 107 Transport endpoint not connected
|
# 107 Transport endpoint not connected
|
||||||
# 57 Socket is not connected
|
# 57 Socket is not connected
|
||||||
|
# 49 Can't assign requested address (wifi down)
|
||||||
# 9 Bad file descriptor
|
# 9 Bad file descriptor
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
@@ -177,3 +181,25 @@ class HttpSrv(object):
|
|||||||
self.clients[cli] = now
|
self.clients[cli] = now
|
||||||
|
|
||||||
self.workload = total
|
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
|
||||||
|
|||||||
@@ -1019,7 +1019,8 @@ class Up2k(object):
|
|||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
# missing; restart
|
# missing; restart
|
||||||
job = None
|
if not self.args.nw:
|
||||||
|
job = None
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# file contents match, but not the path
|
# file contents match, but not the path
|
||||||
@@ -1046,8 +1047,9 @@ class Up2k(object):
|
|||||||
pdir = os.path.join(cj["ptop"], cj["prel"])
|
pdir = os.path.join(cj["ptop"], cj["prel"])
|
||||||
job["name"] = self._untaken(pdir, cj["name"], now, cj["addr"])
|
job["name"] = self._untaken(pdir, cj["name"], now, cj["addr"])
|
||||||
dst = os.path.join(job["ptop"], job["prel"], job["name"])
|
dst = os.path.join(job["ptop"], job["prel"], job["name"])
|
||||||
os.unlink(fsenc(dst)) # TODO ed pls
|
if not self.args.nw:
|
||||||
self._symlink(src, dst)
|
os.unlink(fsenc(dst)) # TODO ed pls
|
||||||
|
self._symlink(src, dst)
|
||||||
|
|
||||||
if not job:
|
if not job:
|
||||||
job = {
|
job = {
|
||||||
@@ -1089,6 +1091,9 @@ class Up2k(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _untaken(self, fdir, fname, ts, ip):
|
def _untaken(self, fdir, fname, ts, ip):
|
||||||
|
if self.args.nw:
|
||||||
|
return fname
|
||||||
|
|
||||||
# TODO broker which avoid this race and
|
# TODO broker which avoid this race and
|
||||||
# provides a new filename if taken (same as bup)
|
# provides a new filename if taken (same as bup)
|
||||||
suffix = ".{:.6f}-{}".format(ts, ip)
|
suffix = ".{:.6f}-{}".format(ts, ip)
|
||||||
@@ -1098,6 +1103,9 @@ class Up2k(object):
|
|||||||
def _symlink(self, src, dst):
|
def _symlink(self, src, dst):
|
||||||
# TODO store this in linktab so we never delete src if there are links to it
|
# 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))
|
self.log("linking dupe:\n {0}\n {1}".format(src, dst))
|
||||||
|
if self.args.nw:
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
lsrc = src
|
lsrc = src
|
||||||
ldst = dst
|
ldst = dst
|
||||||
@@ -1175,6 +1183,10 @@ class Up2k(object):
|
|||||||
if ret > 0:
|
if ret > 0:
|
||||||
return ret, src
|
return ret, src
|
||||||
|
|
||||||
|
if self.args.nw:
|
||||||
|
# del self.registry[ptop][wark]
|
||||||
|
return ret, dst
|
||||||
|
|
||||||
atomic_move(src, dst)
|
atomic_move(src, dst)
|
||||||
|
|
||||||
if ANYWIN:
|
if ANYWIN:
|
||||||
@@ -1284,6 +1296,10 @@ class Up2k(object):
|
|||||||
if self.args.dotpart:
|
if self.args.dotpart:
|
||||||
tnam = "." + tnam
|
tnam = "." + tnam
|
||||||
|
|
||||||
|
if self.args.nw:
|
||||||
|
job["tnam"] = tnam
|
||||||
|
return
|
||||||
|
|
||||||
suffix = ".{:.6f}-{}".format(job["t0"], job["addr"])
|
suffix = ".{:.6f}-{}".format(job["t0"], job["addr"])
|
||||||
with ren_open(tnam, "wb", fdir=pdir, suffix=suffix) as f:
|
with ren_open(tnam, "wb", fdir=pdir, suffix=suffix) as f:
|
||||||
f, job["tnam"] = f["orz"]
|
f, job["tnam"] = f["orz"]
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
<title>⇆🎉 {{ title }}</title>
|
<title>⇆🎉 {{ title }}</title>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
<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/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/upload.css?_={{ ts }}">
|
||||||
{%- if css %}
|
{%- 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 %}
|
{%- endif %}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@@ -127,9 +127,9 @@
|
|||||||
have_tags_idx = {{ have_tags_idx|tojson }},
|
have_tags_idx = {{ have_tags_idx|tojson }},
|
||||||
have_zip = {{ have_zip|tojson }};
|
have_zip = {{ have_zip|tojson }};
|
||||||
</script>
|
</script>
|
||||||
<script src="/.cpr/util.js{{ ts }}"></script>
|
<script src="/.cpr/util.js?_={{ ts }}"></script>
|
||||||
<script src="/.cpr/browser.js{{ ts }}"></script>
|
<script src="/.cpr/browser.js?_={{ ts }}"></script>
|
||||||
<script src="/.cpr/up2k.js{{ ts }}"></script>
|
<script src="/.cpr/up2k.js?_={{ ts }}"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
<title>📝🎉 {{ title }}</title> <!-- 📜 -->
|
<title>📝🎉 {{ title }}</title> <!-- 📜 -->
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=0.7">
|
<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 %}
|
{%- if edit %}
|
||||||
<link href="/.cpr/md2.css" rel="stylesheet">
|
<link href="/.cpr/md2.css?_={{ ts }}" rel="stylesheet">
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -146,10 +146,10 @@ var md_opt = {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script src="/.cpr/util.js"></script>
|
<script src="/.cpr/util.js?_={{ ts }}"></script>
|
||||||
<script src="/.cpr/deps/marked.js"></script>
|
<script src="/.cpr/deps/marked.js?_={{ ts }}"></script>
|
||||||
<script src="/.cpr/md.js"></script>
|
<script src="/.cpr/md.js?_={{ ts }}"></script>
|
||||||
{%- if edit %}
|
{%- if edit %}
|
||||||
<script src="/.cpr/md2.js"></script>
|
<script src="/.cpr/md2.js?_={{ ts }}"></script>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
</body></html>
|
</body></html>
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
<title>📝🎉 {{ title }}</title>
|
<title>📝🎉 {{ title }}</title>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=0.7">
|
<meta name="viewport" content="width=device-width, initial-scale=0.7">
|
||||||
<link href="/.cpr/mde.css" rel="stylesheet">
|
<link href="/.cpr/mde.css?_={{ ts }}" rel="stylesheet">
|
||||||
<link href="/.cpr/deps/mini-fa.css" rel="stylesheet">
|
<link href="/.cpr/deps/mini-fa.css?_={{ ts }}" rel="stylesheet">
|
||||||
<link href="/.cpr/deps/easymde.css" rel="stylesheet">
|
<link href="/.cpr/deps/easymde.css?_={{ ts }}" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="mw">
|
<div id="mw">
|
||||||
@@ -43,7 +43,7 @@ var lightswitch = (function () {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script src="/.cpr/util.js"></script>
|
<script src="/.cpr/util.js?_={{ ts }}"></script>
|
||||||
<script src="/.cpr/deps/easymde.js"></script>
|
<script src="/.cpr/deps/easymde.js?_={{ ts }}"></script>
|
||||||
<script src="/.cpr/mde.js"></script>
|
<script src="/.cpr/mde.js?_={{ ts }}"></script>
|
||||||
</body></html>
|
</body></html>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<title>copyparty</title>
|
<title>copyparty</title>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
<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>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<title>copyparty</title>
|
<title>copyparty</title>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
<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>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -14,17 +14,19 @@ function goto_up2k() {
|
|||||||
|
|
||||||
// chrome requires https to use crypto.subtle,
|
// chrome requires https to use crypto.subtle,
|
||||||
// usually it's undefined but some chromes throw on invoke
|
// usually it's undefined but some chromes throw on invoke
|
||||||
var up2k = null;
|
var up2k = null,
|
||||||
var sha_js = window.WebAssembly ? 'hw' : 'ac'; // ff53,c57,sa11
|
sha_js = window.WebAssembly ? 'hw' : 'ac', // ff53,c57,sa11
|
||||||
|
m = 'will use ' + sha_js + ' instead of native sha512 due to';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var cf = crypto.subtle || crypto.webkitSubtle;
|
var cf = crypto.subtle || crypto.webkitSubtle;
|
||||||
cf.digest('SHA-512', new Uint8Array(1)).then(
|
cf.digest('SHA-512', new Uint8Array(1)).then(
|
||||||
function (x) { console.log('sha-ok'); up2k = up2k_init(cf); },
|
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) {
|
catch (ex) {
|
||||||
console.log('sha-na:', ex);
|
console.log(m, ex);
|
||||||
try {
|
try {
|
||||||
up2k = up2k_init(false);
|
up2k = up2k_init(false);
|
||||||
}
|
}
|
||||||
@@ -140,7 +142,7 @@ function U2pvis(act, btns) {
|
|||||||
this.tail = -1;
|
this.tail = -1;
|
||||||
this.wsz = 3;
|
this.wsz = 3;
|
||||||
|
|
||||||
this.addfile = function (entry, sz) {
|
this.addfile = function (entry, sz, draw) {
|
||||||
this.tab.push({
|
this.tab.push({
|
||||||
"hn": entry[0],
|
"hn": entry[0],
|
||||||
"ht": entry[1],
|
"ht": entry[1],
|
||||||
@@ -154,6 +156,9 @@ function U2pvis(act, btns) {
|
|||||||
"bd0": 0 // upload start
|
"bd0": 0 // upload start
|
||||||
});
|
});
|
||||||
this.ctr["q"]++;
|
this.ctr["q"]++;
|
||||||
|
if (!draw)
|
||||||
|
return;
|
||||||
|
|
||||||
this.drawcard("q");
|
this.drawcard("q");
|
||||||
if (this.act == "q") {
|
if (this.act == "q") {
|
||||||
this.addrow(this.tab.length - 1);
|
this.addrow(this.tab.length - 1);
|
||||||
@@ -220,7 +225,7 @@ function U2pvis(act, btns) {
|
|||||||
this.hashed = function (fobj) {
|
this.hashed = function (fobj) {
|
||||||
var fo = this.tab[fobj.n],
|
var fo = this.tab[fobj.n],
|
||||||
nb = fo.bt * (++fo.nh / fo.cb.length),
|
nb = fo.bt * (++fo.nh / fo.cb.length),
|
||||||
p = this.perc(nb, 0, fobj.size, fobj.t1);
|
p = this.perc(nb, 0, fobj.size, fobj.t_hashing);
|
||||||
|
|
||||||
fo.hp = '{0}%, {1}, {2} MB/s'.format(
|
fo.hp = '{0}%, {1}, {2} MB/s'.format(
|
||||||
p[0].toFixed(2), p[1], p[2].toFixed(2)
|
p[0].toFixed(2), p[1], p[2].toFixed(2)
|
||||||
@@ -243,7 +248,7 @@ function U2pvis(act, btns) {
|
|||||||
fo.cb[nchunk] = cbd;
|
fo.cb[nchunk] = cbd;
|
||||||
fo.bd += delta;
|
fo.bd += delta;
|
||||||
|
|
||||||
var p = this.perc(fo.bd, fo.bd0, fo.bt, fobj.t3);
|
var p = this.perc(fo.bd, fo.bd0, fo.bt, fobj.t_uploading);
|
||||||
fo.hp = '{0}%, {1}, {2} MB/s'.format(
|
fo.hp = '{0}%, {1}, {2} MB/s'.format(
|
||||||
p[0].toFixed(2), p[1], p[2].toFixed(2)
|
p[0].toFixed(2), p[1], p[2].toFixed(2)
|
||||||
);
|
);
|
||||||
@@ -254,6 +259,41 @@ function U2pvis(act, btns) {
|
|||||||
var obj = ebi('f{0}p'.format(fobj.n)),
|
var obj = ebi('f{0}p'.format(fobj.n)),
|
||||||
o1 = p[0] - 2, o2 = p[0] - 0.1, o3 = p[0];
|
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.innerHTML = fo.hp;
|
||||||
obj.style.color = '#fff';
|
obj.style.color = '#fff';
|
||||||
obj.style.background = 'linear-gradient(90deg, #050, #270 ' + o1 + '%, #4b0 ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)';
|
obj.style.background = 'linear-gradient(90deg, #050, #270 ' + o1 + '%, #4b0 ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)';
|
||||||
@@ -274,12 +314,14 @@ function U2pvis(act, btns) {
|
|||||||
this.drawcard(oldcat);
|
this.drawcard(oldcat);
|
||||||
this.drawcard(newcat);
|
this.drawcard(newcat);
|
||||||
if (this.is_act(newcat)) {
|
if (this.is_act(newcat)) {
|
||||||
this.tail++;
|
this.tail = Math.max(this.tail, nfile + 1);
|
||||||
if (!ebi('f' + nfile))
|
if (!ebi('f' + nfile))
|
||||||
this.addrow(nfile);
|
this.addrow(nfile);
|
||||||
}
|
}
|
||||||
else if (this.is_act(oldcat)) {
|
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) {
|
if (!bz_act) {
|
||||||
var tr = ebi("f" + nfile);
|
var tr = ebi("f" + nfile);
|
||||||
tr.parentNode.removeChild(tr);
|
tr.parentNode.removeChild(tr);
|
||||||
@@ -348,8 +390,21 @@ function U2pvis(act, btns) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.head == -1) {
|
if (this.head == -1) {
|
||||||
this.head = this.tab.length;
|
var precard = has(["ok", "ng", "done"], this.act) ? {} : this.act == "bz" ? { "ok": 1, "ng": 1 } : { "ok": 1, "ng": 1, "bz": 1 },
|
||||||
this.tail = this.head - 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") {
|
if (card == "bz") {
|
||||||
for (var a = this.head - 1; a >= this.head - this.wsz && a >= 0; a--) {
|
for (var a = this.head - 1; a >= this.head - this.wsz && a >= 0; a--) {
|
||||||
@@ -557,7 +612,7 @@ function up2k_init(subtle) {
|
|||||||
}
|
}
|
||||||
else files = e.target.files;
|
else files = e.target.files;
|
||||||
|
|
||||||
if (!files || files.length == 0)
|
if (!files || !files.length)
|
||||||
return alert('no files selected??');
|
return alert('no files selected??');
|
||||||
|
|
||||||
more_one_file();
|
more_one_file();
|
||||||
@@ -596,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 (!dirs.length) {
|
||||||
if (!pf.length)
|
if (!pf.length)
|
||||||
return gotallfiles(good, bad);
|
return gotallfiles(good, bad);
|
||||||
|
|
||||||
console.log("retry pf, " + pf.length);
|
console.log("retry pf, " + pf.length);
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
read_dirs(rd, pf, dirs, good, bad);
|
read_dirs(rd, pf, dirs, good, bad, spins);
|
||||||
}, 50);
|
}, 50);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -624,8 +715,7 @@ function up2k_init(subtle) {
|
|||||||
|
|
||||||
pf.push(name);
|
pf.push(name);
|
||||||
dn.file(function (fobj) {
|
dn.file(function (fobj) {
|
||||||
var idx = pf.indexOf(name);
|
apop(pf, name);
|
||||||
pf.splice(idx, 1);
|
|
||||||
try {
|
try {
|
||||||
if (fobj.size > 0) {
|
if (fobj.size > 0) {
|
||||||
good.push([fobj, name]);
|
good.push([fobj, name]);
|
||||||
@@ -643,12 +733,12 @@ function up2k_init(subtle) {
|
|||||||
dirs.shift();
|
dirs.shift();
|
||||||
rd = null;
|
rd = null;
|
||||||
}
|
}
|
||||||
return read_dirs(rd, pf, dirs, good, bad);
|
return read_dirs(rd, pf, dirs, good, bad, spins);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function gotallfiles(good_files, bad_files) {
|
function gotallfiles(good_files, bad_files) {
|
||||||
if (bad_files.length > 0) {
|
if (bad_files.length) {
|
||||||
var ntot = bad_files.length + good_files.length,
|
var ntot = bad_files.length + good_files.length,
|
||||||
msg = 'These {0} files (of {1} total) were skipped because they are empty:\n'.format(bad_files.length, ntot);
|
msg = 'These {0} files (of {1} total) were skipped because they are empty:\n'.format(bad_files.length, ntot);
|
||||||
|
|
||||||
@@ -668,41 +758,50 @@ function up2k_init(subtle) {
|
|||||||
if (ask_up && !fsearch && !confirm(msg.join('\n')))
|
if (ask_up && !fsearch && !confirm(msg.join('\n')))
|
||||||
return;
|
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++) {
|
for (var a = 0; a < good_files.length; a++) {
|
||||||
var fobj = good_files[a][0],
|
var fobj = good_files[a][0],
|
||||||
now = Date.now(),
|
now = Date.now(),
|
||||||
lmod = fobj.lastModified || now;
|
lmod = fobj.lastModified || now;
|
||||||
|
|
||||||
var entry = {
|
var entry = {
|
||||||
"n": parseInt(st.files.length.toString()),
|
"n": st.files.length,
|
||||||
"t0": now,
|
"t0": now,
|
||||||
"fobj": fobj,
|
"fobj": fobj,
|
||||||
"name": good_files[a][1],
|
"name": good_files[a][1],
|
||||||
"size": fobj.size,
|
"size": fobj.size,
|
||||||
"lmod": lmod / 1000,
|
"lmod": lmod / 1000,
|
||||||
"purl": get_evpath(),
|
"purl": evpath,
|
||||||
"done": false,
|
"done": false,
|
||||||
"hash": []
|
"hash": []
|
||||||
};
|
},
|
||||||
|
key = entry.name + '\n' + entry.size;
|
||||||
|
|
||||||
var skip = false;
|
if (seen[key])
|
||||||
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)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
seen[key] = 1;
|
||||||
|
|
||||||
pvis.addfile([
|
pvis.addfile([
|
||||||
fsearch ? esc(entry.name) : linksplit(
|
fsearch ? esc(entry.name) : linksplit(
|
||||||
uricom_dec(entry.purl)[0] + entry.name).join(' '),
|
uricom_dec(entry.purl)[0] + entry.name).join(' '),
|
||||||
'📐 hash',
|
'📐 hash',
|
||||||
''
|
''
|
||||||
], fobj.size);
|
], fobj.size, draw_each);
|
||||||
|
|
||||||
st.files.push(entry);
|
st.files.push(entry);
|
||||||
st.todo.hash.push(entry);
|
st.todo.hash.push(entry);
|
||||||
}
|
}
|
||||||
|
if (!draw_each) {
|
||||||
|
pvis.drawcard("q");
|
||||||
|
pvis.changecard(pvis.act);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ebi('u2btn').addEventListener('drop', gotfile, false);
|
ebi('u2btn').addEventListener('drop', gotfile, false);
|
||||||
|
|
||||||
@@ -737,15 +836,25 @@ function up2k_init(subtle) {
|
|||||||
//
|
//
|
||||||
|
|
||||||
function handshakes_permitted() {
|
function handshakes_permitted() {
|
||||||
var lim = multitask ? 1 : 0;
|
if (!st.todo.handshake.length)
|
||||||
|
return true;
|
||||||
|
|
||||||
if (lim <
|
var cd = st.todo.handshake[0].cooldown;
|
||||||
st.todo.upload.length +
|
if (cd && cd - Date.now() > 0)
|
||||||
st.busy.upload.length)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var cd = st.todo.handshake.length ? st.todo.handshake[0].cooldown : 0;
|
// keepalive or verify
|
||||||
if (cd && cd - Date.now() > 0)
|
if (st.todo.handshake[0].keepalive ||
|
||||||
|
st.todo.handshake[0].t_uploaded)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (parallel_uploads <
|
||||||
|
st.busy.handshake.length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((multitask ? parallel_uploads : 0) <
|
||||||
|
st.todo.upload.length +
|
||||||
|
st.busy.upload.length)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -779,14 +888,15 @@ function up2k_init(subtle) {
|
|||||||
|
|
||||||
clearTimeout(tto);
|
clearTimeout(tto);
|
||||||
running = true;
|
running = true;
|
||||||
while (true) {
|
while (window['vis_exh']) {
|
||||||
var is_busy = 0 !=
|
var now = Date.now(),
|
||||||
st.todo.hash.length +
|
is_busy = 0 !=
|
||||||
st.todo.handshake.length +
|
st.todo.hash.length +
|
||||||
st.todo.upload.length +
|
st.todo.handshake.length +
|
||||||
st.busy.hash.length +
|
st.todo.upload.length +
|
||||||
st.busy.handshake.length +
|
st.busy.hash.length +
|
||||||
st.busy.upload.length;
|
st.busy.handshake.length +
|
||||||
|
st.busy.upload.length;
|
||||||
|
|
||||||
if (was_busy != is_busy) {
|
if (was_busy != is_busy) {
|
||||||
was_busy = is_busy;
|
was_busy = is_busy;
|
||||||
@@ -797,7 +907,6 @@ function up2k_init(subtle) {
|
|||||||
|
|
||||||
if (flag) {
|
if (flag) {
|
||||||
if (is_busy) {
|
if (is_busy) {
|
||||||
var now = Date.now();
|
|
||||||
flag.take(now);
|
flag.take(now);
|
||||||
if (!flag.ours)
|
if (!flag.ours)
|
||||||
return defer();
|
return defer();
|
||||||
@@ -809,43 +918,46 @@ function up2k_init(subtle) {
|
|||||||
|
|
||||||
var mou_ikkai = false;
|
var mou_ikkai = false;
|
||||||
|
|
||||||
if (st.busy.handshake.length > 0 &&
|
if (st.busy.handshake.length &&
|
||||||
st.busy.handshake[0].busied < Date.now() - 30 * 1000
|
st.busy.handshake[0].t_busied < now - 30 * 1000
|
||||||
) {
|
) {
|
||||||
console.log("retrying stuck handshake");
|
console.log("retrying stuck handshake");
|
||||||
var t = st.busy.handshake.shift();
|
var t = st.busy.handshake.shift();
|
||||||
st.todo.handshake.unshift(t);
|
st.todo.handshake.unshift(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (st.todo.handshake.length > 0 &&
|
var nprev = -1;
|
||||||
st.busy.handshake.length == 0 && (
|
for (var a = 0; a < st.todo.upload.length; a++) {
|
||||||
st.todo.handshake[0].t4 || (
|
var nf = st.todo.upload[a].nfile;
|
||||||
handshakes_permitted() &&
|
if (nprev == nf)
|
||||||
st.busy.upload.length < parallel_uploads
|
continue;
|
||||||
)
|
|
||||||
)
|
nprev = nf;
|
||||||
) {
|
var t = st.files[nf];
|
||||||
exec_handshake();
|
if (now - t.t_busied > 1000 * 30 &&
|
||||||
mou_ikkai = true;
|
now - t.t_handshake > 1000 * (21600 - 1800)
|
||||||
|
) {
|
||||||
|
apop(st.todo.handshake, t);
|
||||||
|
st.todo.handshake.unshift(t);
|
||||||
|
t.keepalive = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handshakes_permitted() &&
|
if (handshakes_permitted() &&
|
||||||
st.todo.handshake.length > 0 &&
|
st.todo.handshake.length) {
|
||||||
st.busy.handshake.length == 0 &&
|
|
||||||
st.busy.upload.length < parallel_uploads) {
|
|
||||||
exec_handshake();
|
exec_handshake();
|
||||||
mou_ikkai = true;
|
mou_ikkai = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (st.todo.upload.length > 0 &&
|
if (st.todo.upload.length &&
|
||||||
st.busy.upload.length < parallel_uploads) {
|
st.busy.upload.length < parallel_uploads) {
|
||||||
exec_upload();
|
exec_upload();
|
||||||
mou_ikkai = true;
|
mou_ikkai = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hashing_permitted() &&
|
if (hashing_permitted() &&
|
||||||
st.todo.hash.length > 0 &&
|
st.todo.hash.length &&
|
||||||
st.busy.hash.length == 0) {
|
!st.busy.hash.length) {
|
||||||
exec_hash();
|
exec_hash();
|
||||||
mou_ikkai = true;
|
mou_ikkai = true;
|
||||||
}
|
}
|
||||||
@@ -952,7 +1064,7 @@ function up2k_init(subtle) {
|
|||||||
|
|
||||||
bpend += cdr - car;
|
bpend += cdr - car;
|
||||||
|
|
||||||
reader.onload = function (e) {
|
function orz(e) {
|
||||||
if (!min_filebuf && nch == 1) {
|
if (!min_filebuf && nch == 1) {
|
||||||
min_filebuf = 1;
|
min_filebuf = 1;
|
||||||
var td = Date.now() - t0;
|
var td = Date.now() - t0;
|
||||||
@@ -962,9 +1074,30 @@ function up2k_init(subtle) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
hash_calc(nch, e.target.result);
|
hash_calc(nch, e.target.result);
|
||||||
|
}
|
||||||
|
reader.onload = function (e) {
|
||||||
|
try { orz(e); } catch (ex) { vis_exh(ex + '', '', '', '', ex); }
|
||||||
};
|
};
|
||||||
reader.onerror = function () {
|
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');
|
||||||
|
apop(st.busy.hash, t);
|
||||||
|
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(
|
reader.readAsArrayBuffer(
|
||||||
bobslice.call(t.fobj, car, cdr));
|
bobslice.call(t.fobj, car, cdr));
|
||||||
@@ -992,15 +1125,15 @@ function up2k_init(subtle) {
|
|||||||
t.hash.push(hashtab[a]);
|
t.hash.push(hashtab[a]);
|
||||||
}
|
}
|
||||||
|
|
||||||
t.t2 = Date.now();
|
t.t_hashed = Date.now();
|
||||||
if (t.n == 0 && window.location.hash == '#dbg') {
|
if (t.n == 0 && window.location.hash == '#dbg') {
|
||||||
var spd = (t.size / ((t.t2 - t.t1) / 1000.)) / (1024 * 1024.);
|
var spd = (t.size / ((t.t_hashed - t.t_hashing) / 1000.)) / (1024 * 1024.);
|
||||||
alert('{0} ms, {1} MB/s\n'.format(t.t2 - t.t1, spd.toFixed(3)) + t.hash.join('\n'));
|
alert('{0} ms, {1} MB/s\n'.format(t.t_hashed - t.t_hashing, spd.toFixed(3)) + t.hash.join('\n'));
|
||||||
}
|
}
|
||||||
|
|
||||||
pvis.seth(t.n, 2, 'hashing done');
|
pvis.seth(t.n, 2, 'hashing done');
|
||||||
pvis.seth(t.n, 1, '📦 wait');
|
pvis.seth(t.n, 1, '📦 wait');
|
||||||
st.busy.hash.splice(st.busy.hash.indexOf(t), 1);
|
apop(st.busy.hash, t);
|
||||||
st.todo.handshake.push(t);
|
st.todo.handshake.push(t);
|
||||||
tasker();
|
tasker();
|
||||||
};
|
};
|
||||||
@@ -1023,7 +1156,7 @@ function up2k_init(subtle) {
|
|||||||
}, 1);
|
}, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
t.t1 = Date.now();
|
t.t_hashing = Date.now();
|
||||||
segm_next();
|
segm_next();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1034,30 +1167,41 @@ function up2k_init(subtle) {
|
|||||||
|
|
||||||
function exec_handshake() {
|
function exec_handshake() {
|
||||||
var t = st.todo.handshake.shift(),
|
var t = st.todo.handshake.shift(),
|
||||||
|
keepalive = t.keepalive,
|
||||||
me = Date.now();
|
me = Date.now();
|
||||||
|
|
||||||
st.busy.handshake.push(t);
|
st.busy.handshake.push(t);
|
||||||
t.busied = me;
|
t.keepalive = undefined;
|
||||||
|
t.t_busied = me;
|
||||||
|
|
||||||
|
if (keepalive)
|
||||||
|
console.log("sending keepalive handshake", t);
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.onerror = function () {
|
xhr.onerror = function () {
|
||||||
if (t.busied != me) {
|
if (t.t_busied != me) {
|
||||||
console.log('zombie handshake onerror,', t);
|
console.log('zombie handshake onerror,', t);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('handshake onerror, retrying');
|
console.log('handshake onerror, retrying', t);
|
||||||
st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1);
|
apop(st.busy.handshake, t);
|
||||||
st.todo.handshake.unshift(t);
|
st.todo.handshake.unshift(t);
|
||||||
|
t.keepalive = keepalive;
|
||||||
tasker();
|
tasker();
|
||||||
};
|
};
|
||||||
xhr.onload = function (e) {
|
function orz(e) {
|
||||||
if (t.busied != me) {
|
if (t.t_busied != me) {
|
||||||
console.log('zombie handshake onload,', t);
|
console.log('zombie handshake onload,', t);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (xhr.status == 200) {
|
if (xhr.status == 200) {
|
||||||
var response = JSON.parse(xhr.responseText);
|
t.t_handshake = Date.now();
|
||||||
|
if (keepalive) {
|
||||||
|
apop(st.busy.handshake, t);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = JSON.parse(xhr.responseText);
|
||||||
if (!response.name) {
|
if (!response.name) {
|
||||||
var msg = '',
|
var msg = '',
|
||||||
smsg = '';
|
smsg = '';
|
||||||
@@ -1081,7 +1225,7 @@ function up2k_init(subtle) {
|
|||||||
pvis.seth(t.n, 2, msg);
|
pvis.seth(t.n, 2, msg);
|
||||||
pvis.seth(t.n, 1, smsg);
|
pvis.seth(t.n, 1, smsg);
|
||||||
pvis.move(t.n, smsg == '404' ? 'ng' : 'ok');
|
pvis.move(t.n, smsg == '404' ? 'ng' : 'ok');
|
||||||
st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1);
|
apop(st.busy.handshake, t);
|
||||||
st.bytes.uploaded += t.size;
|
st.bytes.uploaded += t.size;
|
||||||
t.done = true;
|
t.done = true;
|
||||||
tasker();
|
tasker();
|
||||||
@@ -1090,6 +1234,7 @@ function up2k_init(subtle) {
|
|||||||
|
|
||||||
if (response.name !== t.name) {
|
if (response.name !== t.name) {
|
||||||
// file exists; server renamed us
|
// file exists; server renamed us
|
||||||
|
console.log("server-rename [" + t.name + "] to [" + response.name + "]");
|
||||||
t.name = response.name;
|
t.name = response.name;
|
||||||
pvis.seth(t.n, 0, linksplit(t.purl + t.name).join(' '));
|
pvis.seth(t.n, 0, linksplit(t.purl + t.name).join(' '));
|
||||||
}
|
}
|
||||||
@@ -1122,7 +1267,7 @@ function up2k_init(subtle) {
|
|||||||
var done = true,
|
var done = true,
|
||||||
msg = '🎷🐛';
|
msg = '🎷🐛';
|
||||||
|
|
||||||
if (t.postlist.length > 0) {
|
if (t.postlist.length) {
|
||||||
for (var a = 0; a < t.postlist.length; a++)
|
for (var a = 0; a < t.postlist.length; a++)
|
||||||
st.todo.upload.push({
|
st.todo.upload.push({
|
||||||
'nfile': t.n,
|
'nfile': t.n,
|
||||||
@@ -1133,20 +1278,20 @@ function up2k_init(subtle) {
|
|||||||
done = false;
|
done = false;
|
||||||
}
|
}
|
||||||
pvis.seth(t.n, 1, msg);
|
pvis.seth(t.n, 1, msg);
|
||||||
st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1);
|
apop(st.busy.handshake, t);
|
||||||
|
|
||||||
if (done) {
|
if (done) {
|
||||||
t.done = true;
|
t.done = true;
|
||||||
st.bytes.uploaded += t.size - t.bytes_uploaded;
|
st.bytes.uploaded += t.size - t.bytes_uploaded;
|
||||||
var spd1 = (t.size / ((t.t2 - t.t1) / 1000.)) / (1024 * 1024.),
|
var spd1 = (t.size / ((t.t_hashed - t.t_hashing) / 1000.)) / (1024 * 1024.),
|
||||||
spd2 = (t.size / ((t.t4 - t.t3) / 1000.)) / (1024 * 1024.);
|
spd2 = (t.size / ((t.t_uploaded - t.t_uploading) / 1000.)) / (1024 * 1024.);
|
||||||
|
|
||||||
pvis.seth(t.n, 2, 'hash {0}, up {1} MB/s'.format(
|
pvis.seth(t.n, 2, 'hash {0}, up {1} MB/s'.format(
|
||||||
spd1.toFixed(2), spd2.toFixed(2)));
|
spd1.toFixed(2), spd2.toFixed(2)));
|
||||||
|
|
||||||
pvis.move(t.n, 'ok');
|
pvis.move(t.n, 'ok');
|
||||||
}
|
}
|
||||||
else t.t4 = undefined;
|
else t.t_uploaded = undefined;
|
||||||
|
|
||||||
tasker();
|
tasker();
|
||||||
}
|
}
|
||||||
@@ -1165,7 +1310,7 @@ function up2k_init(subtle) {
|
|||||||
var penalty = rsp.replace(/.*rate-limit /, "").split(' ')[0];
|
var penalty = rsp.replace(/.*rate-limit /, "").split(' ')[0];
|
||||||
console.log("rate-limit: " + penalty);
|
console.log("rate-limit: " + penalty);
|
||||||
t.cooldown = Date.now() + parseFloat(penalty) * 1000;
|
t.cooldown = Date.now() + parseFloat(penalty) * 1000;
|
||||||
st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1);
|
apop(st.busy.handshake, t);
|
||||||
st.todo.handshake.unshift(t);
|
st.todo.handshake.unshift(t);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1184,7 +1329,7 @@ function up2k_init(subtle) {
|
|||||||
pvis.seth(t.n, 2, err);
|
pvis.seth(t.n, 2, err);
|
||||||
pvis.move(t.n, 'ng');
|
pvis.move(t.n, 'ng');
|
||||||
|
|
||||||
st.busy.handshake.splice(st.busy.handshake.indexOf(t), 1);
|
apop(st.busy.handshake, t);
|
||||||
tasker();
|
tasker();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1194,6 +1339,9 @@ function up2k_init(subtle) {
|
|||||||
(xhr.responseText && xhr.responseText) ||
|
(xhr.responseText && xhr.responseText) ||
|
||||||
"no further information"));
|
"no further information"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
xhr.onload = function (e) {
|
||||||
|
try { orz(e); } catch (ex) { vis_exh(ex + '', '', '', '', ex); }
|
||||||
};
|
};
|
||||||
|
|
||||||
var req = {
|
var req = {
|
||||||
@@ -1222,8 +1370,8 @@ function up2k_init(subtle) {
|
|||||||
var npart = upt.npart,
|
var npart = upt.npart,
|
||||||
t = st.files[upt.nfile];
|
t = st.files[upt.nfile];
|
||||||
|
|
||||||
if (!t.t3)
|
if (!t.t_uploading)
|
||||||
t.t3 = Date.now();
|
t.t_uploading = Date.now();
|
||||||
|
|
||||||
pvis.seth(t.n, 1, "🚀 send");
|
pvis.seth(t.n, 1, "🚀 send");
|
||||||
|
|
||||||
@@ -1234,40 +1382,56 @@ function up2k_init(subtle) {
|
|||||||
if (cdr >= t.size)
|
if (cdr >= t.size)
|
||||||
cdr = t.size;
|
cdr = t.size;
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
function orz(xhr) {
|
||||||
xhr.upload.onprogress = function (xev) {
|
var txt = ((xhr.response && xhr.response.err) || xhr.responseText) + '';
|
||||||
pvis.prog(t, npart, xev.loaded);
|
|
||||||
};
|
|
||||||
xhr.onload = function (xev) {
|
|
||||||
if (xhr.status == 200) {
|
if (xhr.status == 200) {
|
||||||
pvis.prog(t, npart, cdr - car);
|
pvis.prog(t, npart, cdr - car);
|
||||||
st.bytes.uploaded += cdr - car;
|
st.bytes.uploaded += cdr - car;
|
||||||
t.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", t);
|
||||||
|
}
|
||||||
|
else {
|
||||||
alert("server broke; cu-err {0} on file [{1}]:\n".format(
|
alert("server broke; cu-err {0} on file [{1}]:\n".format(
|
||||||
xhr.status, t.name) + (
|
xhr.status, t.name) + (txt || "no further information"));
|
||||||
(xhr.response && xhr.response.err) ||
|
return;
|
||||||
(xhr.responseText && xhr.responseText) ||
|
}
|
||||||
"no further information"));
|
apop(st.busy.upload, upt);
|
||||||
};
|
apop(t.postlist, npart);
|
||||||
xhr.open('POST', t.purl + 'chunkpit.php', true);
|
if (!t.postlist.length) {
|
||||||
xhr.setRequestHeader("X-Up2k-Hash", t.hash[npart]);
|
t.t_uploaded = Date.now();
|
||||||
xhr.setRequestHeader("X-Up2k-Wark", t.wark);
|
pvis.seth(t.n, 1, 'verifying');
|
||||||
xhr.setRequestHeader('Content-Type', 'application/octet-stream');
|
st.todo.handshake.unshift(t);
|
||||||
if (xhr.overrideMimeType)
|
}
|
||||||
xhr.overrideMimeType('Content-Type', 'application/octet-stream');
|
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';
|
console.log('chunkpit onerror, retrying', t);
|
||||||
xhr.send(bobslice.call(t.fobj, car, cdr));
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/////
|
/////
|
||||||
|
|||||||
@@ -11,16 +11,6 @@ var is_touch = 'ontouchstart' in window,
|
|||||||
|
|
||||||
|
|
||||||
// error handler for mobile devices
|
// 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) {
|
function esc(txt) {
|
||||||
return txt.replace(/[&"<>]/g, function (c) {
|
return txt.replace(/[&"<>]/g, function (c) {
|
||||||
return {
|
return {
|
||||||
@@ -37,7 +27,7 @@ function vis_exh(msg, url, lineNo, columnNo, error) {
|
|||||||
|
|
||||||
window.onerror = undefined;
|
window.onerror = undefined;
|
||||||
window['vis_exh'] = null;
|
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 /> (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>'];
|
esc(String(msg)), '</p><p>', esc(url + ' @' + lineNo + ':' + columnNo), '</p>'];
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -47,9 +37,13 @@ function vis_exh(msg, url, lineNo, columnNo, error) {
|
|||||||
html.push('<h2>' + find[a] + '</h2>' +
|
html.push('<h2>' + find[a] + '</h2>' +
|
||||||
esc(String(error[find[a]])).replace(/\n/g, '<br />\n'));
|
esc(String(error[find[a]])).replace(/\n/g, '<br />\n'));
|
||||||
}
|
}
|
||||||
document.body.style.fontSize = '0.8em';
|
document.body.innerHTML = html.join('\n');
|
||||||
document.body.style.padding = '0 1em 1em 1em';
|
|
||||||
hcroak(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';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -395,6 +389,18 @@ function has(haystack, needle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function apop(arr, v) {
|
||||||
|
var ofs = arr.indexOf(v);
|
||||||
|
if (ofs !== -1)
|
||||||
|
arr.splice(ofs, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function jcp(obj) {
|
||||||
|
return JSON.parse(JSON.stringify(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function sread(key) {
|
function sread(key) {
|
||||||
if (window.localStorage)
|
if (window.localStorage)
|
||||||
return localStorage.getItem(key);
|
return localStorage.getItem(key);
|
||||||
|
|||||||
51
docs/hls.html
Normal file
51
docs/hls.html
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>hls-test</title>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
</head><body>
|
||||||
|
|
||||||
|
<video id="vid" controls></video>
|
||||||
|
<script src="hls.light.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var video = document.getElementById('vid');
|
||||||
|
var hls = new Hls({
|
||||||
|
debug: true,
|
||||||
|
autoStartLoad: false
|
||||||
|
});
|
||||||
|
hls.loadSource('live/v.m3u8');
|
||||||
|
hls.attachMedia(video);
|
||||||
|
hls.on(Hls.Events.MANIFEST_PARSED, function() {
|
||||||
|
hls.startLoad(0);
|
||||||
|
});
|
||||||
|
hls.on(Hls.Events.MEDIA_ATTACHED, function() {
|
||||||
|
video.muted = true;
|
||||||
|
video.play();
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
general good news:
|
||||||
|
- doesn't need fixed-length segments; ok to let x264 pick optimal keyframes and slice on those
|
||||||
|
- hls.js polls the m3u8 for new segments, scales the duration accordingly, seeking works great
|
||||||
|
- the sfx will grow by 66 KiB since that's how small hls.js can get, wait thats not good
|
||||||
|
|
||||||
|
# vod, creates m3u8 at the end, fixed keyframes, v bad
|
||||||
|
ffmpeg -hide_banner -threads 0 -flags -global_header -i ..\CowboyBebopMovie-OP1.webm -vf scale=1280:-4,format=yuv420p -ac 2 -c:a libopus -b:a 128k -c:v libx264 -preset slow -crf 24 -maxrate:v 5M -bufsize:v 10M -g 120 -keyint_min 120 -sc_threshold 0 -hls_time 4 -hls_playlist_type vod -hls_segment_filename v%05d.ts v.m3u8
|
||||||
|
|
||||||
|
# live, updates m3u8 as it goes, dynamic keyframes, streamable with hls.js
|
||||||
|
ffmpeg -hide_banner -threads 0 -flags -global_header -i ..\..\CowboyBebopMovie-OP1.webm -vf scale=1280:-4,format=yuv420p -ac 2 -c:a libopus -b:a 128k -c:v libx264 -preset slow -crf 24 -maxrate:v 5M -bufsize:v 10M -f segment -segment_list v.m3u8 -segment_format mpegts -segment_list_flags live v%05d.ts
|
||||||
|
|
||||||
|
# fmp4 (fragmented mp4), doesn't work with hls.js, gets duratoin 149:07:51 (536871s), probably the tkhd/mdhd 0xffffffff (timebase 8000? ok)
|
||||||
|
ffmpeg -re -hide_banner -threads 0 -flags +cgop -i ..\..\CowboyBebopMovie-OP1.webm -vf scale=1280:-4,format=yuv420p -ac 2 -c:a libopus -b:a 128k -c:v libx264 -preset slow -crf 24 -maxrate:v 5M -bufsize:v 10M -f segment -segment_list v.m3u8 -segment_format fmp4 -segment_list_flags live v%05d.mp4
|
||||||
|
|
||||||
|
# try 2, works, uses tempfiles for m3u8 updates, good, 6% smaller
|
||||||
|
ffmpeg -re -hide_banner -threads 0 -flags +cgop -i ..\..\CowboyBebopMovie-OP1.webm -vf scale=1280:-4,format=yuv420p -ac 2 -c:a libopus -b:a 128k -c:v libx264 -preset slow -crf 24 -maxrate:v 5M -bufsize:v 10M -f hls -hls_segment_type fmp4 -hls_list_size 0 -hls_segment_filename v%05d.mp4 v.m3u8
|
||||||
|
|
||||||
|
more notes
|
||||||
|
- adding -hls_flags single_file makes duration wack during playback (for both fmp4 and ts), ok once finalized and refreshed, gives no size reduction anyways
|
||||||
|
- bebop op has good keyframe spacing for testing hls.js, in particular it hops one seg back and immediately resumes if it hits eof with the explicit hls.startLoad(0); otherwise it jumps into the middle of a seg and becomes art
|
||||||
|
- can probably -c:v copy most of the time, is there a way to check for cgop? todo
|
||||||
|
|
||||||
|
*/
|
||||||
|
</script>
|
||||||
|
</body></html>
|
||||||
@@ -108,6 +108,9 @@ class VHttpSrv(object):
|
|||||||
aliases = ["splash", "browser", "browser2", "msg", "md", "mde"]
|
aliases = ["splash", "browser", "browser2", "msg", "md", "mde"]
|
||||||
self.j2 = {x: J2_FILES for x in aliases}
|
self.j2 = {x: J2_FILES for x in aliases}
|
||||||
|
|
||||||
|
def cachebuster(self):
|
||||||
|
return "a"
|
||||||
|
|
||||||
|
|
||||||
class VHttpConn(object):
|
class VHttpConn(object):
|
||||||
def __init__(self, args, asrv, log, buf):
|
def __init__(self, args, asrv, log, buf):
|
||||||
|
|||||||
Reference in New Issue
Block a user