Compare commits

...

17 Commits

Author SHA1 Message Date
ed
c5d822c70a v1.1.2 2021-11-12 23:08:24 +01:00
ed
9c09b4061a prefer fpool on linux as well 2021-11-12 22:57:36 +01:00
ed
c26fb43ced more cleanup 2021-11-12 22:30:23 +01:00
ed
deb8f20db6 misc cleanup/unjank 2021-11-12 20:48:26 +01:00
ed
50e18ed8ff fix up2k layout in readonly folders 2021-11-12 19:18:52 +01:00
ed
31f3895f40 close misc views on escape 2021-11-12 19:18:29 +01:00
ed
615929268a cache monet 2021-11-12 02:00:44 +01:00
ed
b8b15814cf add traffic shaping, bump speeds on https/windows 2021-11-12 01:34:56 +01:00
ed
7766fffe83 mostly fix ogvjs preloading 2021-11-12 01:09:01 +01:00
ed
2a16c150d1 general preload improvements 2021-11-12 01:04:31 +01:00
ed
418c2166cc add cursed doubleclick-handler in gridsel mode 2021-11-11 01:03:14 +01:00
ed
a4dd44f648 textviewer initiable through hotkeys 2021-11-11 00:18:34 +01:00
ed
5352f7cda7 fix ctrl-a fencing in codeblocks 2021-11-11 00:11:29 +01:00
ed
5533b47099 handle crc collisions 2021-11-10 23:59:07 +01:00
ed
e9b14464ee terminate preloader if it can't finish in time 2021-11-10 22:53:02 +01:00
ed
4e986e5cd1 xhr preload is not gapless 2021-11-10 22:00:24 +01:00
ed
8a59b40c53 better clientside upload dedup 2021-11-10 20:57:45 +01:00
16 changed files with 268 additions and 153 deletions

View File

@@ -256,7 +256,10 @@ some improvement ideas
# accounts and volumes
per-folder, per-user permissions
per-folder, per-user permissions - if your setup is getting complex, consider making a [config file](./docs/example.conf) instead of using arguments
* much easier to manage, and you can modify the config at runtime with `systemctl reload copyparty` or more conveniently using the `[reload cfg]` button in the control-panel (if logged in as admin)
configuring accounts/volumes with arguments:
* `-a usr:pwd` adds account `usr` with password `pwd`
* `-v .::r` adds current-folder `.` as the webroot, `r`eadable by anyone
* the syntax is `-v src:dst:perm:perm:...` so local-path, url-path, and one or more permissions to set
@@ -473,8 +476,6 @@ the files will be hashed on the client-side, and each hash is sent to the server
files go into `[ok]` if they exist (and you get a link to where it is), otherwise they land in `[ng]`
* the main reason filesearch is combined with the uploader is cause the code was too spaghetti to separate it out somewhere else, this is no longer the case but now i've warmed up to the idea too much
adding the same file multiple times is blocked, so if you first search for a file and then decide to upload it, you have to click the `[cleanup]` button to discard `[done]` files (or just refresh the page)
### unpost
@@ -594,6 +595,8 @@ add the argument `-e2ts` to also scan/index tags from music files, which brings
using arguments or config files, or a mix of both:
* config files (`-c some.conf`) can set additional commandline arguments; see [./docs/example.conf](docs/example.conf)
* `kill -s USR1` (same as `systemctl reload copyparty`) to reload accounts and volumes from config files without restarting
* or click the `[reload cfg]` button in the control-panel when logged in as admin
## file indexing
@@ -632,7 +635,7 @@ if you set `--no-hash [...]` globally, you can enable hashing for specific volum
set upload rules using volume flags, some examples:
* `:c,sz=1k-3m` sets allowed filesize between 1 KiB and 3 MiB inclusive (suffixes: b, k, m, g)
* `:c,sz=1k-3m` sets allowed filesize between 1 KiB and 3 MiB inclusive (suffixes: `b`, `k`, `m`, `g`)
* `:c,nosub` disallow uploading into subdirectories; goes well with `rotn` and `rotf`:
* `:c,rotn=1000,2` moves uploads into subfolders, up to 1000 files in each folder before making a new one, two levels deep (must be at least 1)
* `:c,rotf=%Y/%m/%d/%H` enforces files to be uploaded into a structure of subfolders according to that date format
@@ -801,8 +804,8 @@ quick summary of more eccentric web-browsers trying to view a directory index:
interact with copyparty using non-browser clients
* javascript: dump some state into a file (two separate examples)
* `await fetch('https://127.0.0.1:3923/', {method:"PUT", body: JSON.stringify(foo)});`
* `var xhr = new XMLHttpRequest(); xhr.open('POST', 'https://127.0.0.1:3923/msgs?raw'); xhr.send('foo');`
* `await fetch('//127.0.0.1:3923/', {method:"PUT", body: JSON.stringify(foo)});`
* `var xhr = new XMLHttpRequest(); xhr.open('POST', '//127.0.0.1:3923/msgs?raw'); xhr.send('foo');`
* curl/wget: upload some files (post=file, chunk=stdin)
* `post(){ curl -b cppwd=wark -F act=bput -F f=@"$1" http://127.0.0.1:3923/;}`

View File

@@ -2,7 +2,7 @@
* command-line up2k client [(webm)](https://ocv.me/stuff/u2cli.webm)
* file uploads, file-search, autoresume of aborted/broken uploads
* faster than browsers
* early beta, if something breaks just restart it
* if something breaks just restart it

View File

@@ -12,7 +12,6 @@
# change '/mnt::rw' to another location or permission-set
# remove '-p 80,443,3923' to only listen on port 3923
# add '-i 127.0.0.1' to only allow local connections
# add '--use-fpool' if uploading into nfs locations
#
# with `Type=notify`, copyparty will signal systemd when it is ready to
# accept connections; correctly delaying units depending on copyparty.

View File

@@ -381,7 +381,9 @@ def run_argparse(argv, formatter):
ap2.add_argument("-i", metavar="IP", type=u, default="0.0.0.0", help="ip to bind (comma-sep.)")
ap2.add_argument("-p", metavar="PORT", type=u, default="3923", help="ports to bind (comma/range)")
ap2.add_argument("--rproxy", metavar="DEPTH", type=int, default=1, help="which ip to keep; 0 = tcp, 1 = origin (first x-fwd), 2 = cloudflare, 3 = nginx, -1 = closest proxy")
ap2.add_argument("--s-wr-sz", metavar="B", type=int, default=256*1024, help="socket write size in bytes")
ap2.add_argument("--s-wr-slp", metavar="SEC", type=float, default=0, help="socket write delay in seconds")
ap2 = ap.add_argument_group('SSL/TLS options')
ap2.add_argument("--http-only", action="store_true", help="disable ssl/tls")
ap2.add_argument("--https-only", action="store_true", help="disable plaintext")

View File

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

View File

@@ -532,11 +532,11 @@ class HttpCli(object):
raise Pebkac(405, "don't know how to handle POST({})".format(ctype))
def get_body_reader(self):
chunked = "chunked" in self.headers.get("transfer-encoding", "").lower()
if "chunked" in self.headers.get("transfer-encoding", "").lower():
return read_socket_chunked(self.sr), -1
remains = int(self.headers.get("content-length", -1))
if chunked:
return read_socket_chunked(self.sr), remains
elif remains == -1:
if remains == -1:
self.keepalive = False
return read_socket_unbounded(self.sr), remains
else:
@@ -1484,10 +1484,10 @@ class HttpCli(object):
ret = True
with open_func(*open_args) as f:
if use_sendfile:
remains = sendfile_kern(lower, upper, f, self.s)
else:
remains = sendfile_py(lower, upper, f, self.s)
sendfun = sendfile_kern if use_sendfile else sendfile_py
remains = sendfun(
lower, upper, f, self.s, self.args.s_wr_sz, self.args.s_wr_slp
)
if remains > 0:
logmsg += " \033[31m" + unicode(upper - remains) + "\033[0m"

View File

@@ -57,8 +57,10 @@ class SvcHub(object):
if args.log_thrs:
start_log_thrs(self.log, args.log_thrs, 0)
if not ANYWIN and not args.use_fpool:
if not args.use_fpool and args.j != 1:
args.no_fpool = True
m = "multithreading enabled with -j {}, so disabling fpool -- this can reduce upload performance on some filesystems"
self.log("root", m.format(args.j))
if not args.no_fpool and args.j != 1:
m = "WARNING: --use-fpool combined with multithreading is untested and can probably cause undefined behavior"

View File

@@ -1164,12 +1164,14 @@ def hashcopy(fin, fout):
return tlen, hashobj.hexdigest(), digest_b64
def sendfile_py(lower, upper, f, s):
def sendfile_py(lower, upper, f, s, bufsz, slp):
remains = upper - lower
f.seek(lower)
while remains > 0:
# time.sleep(0.01)
buf = f.read(min(1024 * 32, remains))
if slp:
time.sleep(slp)
buf = f.read(min(bufsz, remains))
if not buf:
return remains
@@ -1182,7 +1184,7 @@ def sendfile_py(lower, upper, f, s):
return 0
def sendfile_kern(lower, upper, f, s):
def sendfile_kern(lower, upper, f, s, bufsz, slp):
out_fd = s.fileno()
in_fd = f.fileno()
ofs = lower

View File

@@ -237,7 +237,7 @@ window.baguetteBox = (function () {
}
function keyDownHandler(e) {
if (e.ctrlKey || e.altKey || e.metaKey || e.isComposing)
if (e.ctrlKey || e.altKey || e.metaKey || e.isComposing || modal.busy)
return;
var k = e.code + '', v = vid();

View File

@@ -559,6 +559,11 @@ input[type="radio"]:checked+label,
input[type="checkbox"]:checked+label {
color: #fc5;
}
.opwide div>span>input+label {
padding: .3em 0 .3em .3em;
margin: 0 0 0 -.3em;
cursor: pointer;
}
.opview input.i {
width: calc(100% - 16.2em);
}
@@ -2096,10 +2101,6 @@ html.light #u2foot .warn span {
color: #fff;
padding-left: .2em;
}
#u2cleanup {
float: right;
margin-bottom: -.3em;
}
.fsearch_explain {
padding-left: .7em;
font-size: 1.1em;

View File

@@ -11,9 +11,9 @@ function dbg(msg) {
ebi('ops').innerHTML = (
'<a href="#" data-dest="" tt="close submenu">--</a>\n' +
(have_up2k_idx ? (
'<a href="#" data-perm="read" data-dest="search" tt="search for files by attributes, path/name, music tags, or any combination of those.$N$N&lt;code&gt;foo bar&lt;/code&gt; = must contain both foo and bar,$N&lt;code&gt;foo -bar&lt;/code&gt; = must contain foo but not bar,$N&lt;code&gt;^yana .opus$&lt;/code&gt; = must start with yana and have the opus extension">🔎</a>\n' +
'<a href="#" data-perm="read" data-dest="search" tt="search for files by attributes, path/name, music tags, or any combination of those$N$N&lt;code&gt;foo bar&lt;/code&gt; = must contain both foo and bar,$N&lt;code&gt;foo -bar&lt;/code&gt; = must contain foo but not bar,$N&lt;code&gt;^yana .opus$&lt;/code&gt; = must start with yana and have the opus extension">🔎</a>\n' +
(have_del && have_unpost ? '<a href="#" data-dest="unpost" tt="unpost: delete your recent uploads">🧯</a>\n' : '') +
'<a href="#" data-dest="up2k" tt="up2k: upload files (if you have write-access) or toggle into the search-mode and drag files onto the search button to see if they exist somewhere on the server">🚀</a>\n'
'<a href="#" data-dest="up2k" tt="up2k: upload files (if you have write-access) or toggle into the search-mode to see if they exist somewhere on the server">🚀</a>\n'
) : (
'<a href="#" data-perm="write" data-dest="up2k" tt="up2k: upload files with resume support (close your browser and drop the same files in later)">🚀</a>\n'
)) +
@@ -122,7 +122,7 @@ ebi('op_up2k').innerHTML = (
' <tr>\n' +
' <td>filename</td>\n' +
' <td>status</td>\n' +
' <td>progress<a href="#" id="u2cleanup" tt="remove completed uploads$N(makes it possible to upload a file after searching for it)">cleanup</a></td>\n' +
' <td>progress</td>\n' +
' </tr>\n' +
' </thead>\n' +
' <tbody></tbody>\n' +
@@ -245,8 +245,7 @@ function goto(dest) {
clmod(obj[a], 'act');
if (dest) {
var ui = ebi('op_' + dest),
lnk = QS('#ops>a[data-dest=' + dest + ']'),
var lnk = QS('#ops>a[data-dest=' + dest + ']'),
nps = lnk.getAttribute('data-perm');
nps = nps && nps.length ? nps.split(' ') : [];
@@ -259,8 +258,8 @@ function goto(dest) {
if (!has(perms, 'read') && !has(perms, 'write') && (dest == 'up2k'))
return;
clmod(ui, 'act', true);
lnk.className += " act";
clmod(ebi('op_' + dest), 'act', 1);
clmod(lnk, 'act', 1);
var fn = window['goto_' + dest];
if (fn)
@@ -334,7 +333,7 @@ var mpl = (function () {
) : '') +
'<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" />' +
'<input type="text" id="pb_tint" style="width:2.4em" 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>');
@@ -471,6 +470,19 @@ var mpl = (function () {
navigator.mediaSession.playbackState = "paused";
};
r.unbuffer = function (url) {
for (var a = 0; a < 2; a++) {
var au = a ? mp.au_native2 : mp.au_ogvjs2;
if (au && (!url || au.src == url)) {
au.src = '';
au.load();
}
}
if (!url)
mpl.preload_url = null;
}
return r;
})();
@@ -488,7 +500,6 @@ function MPlayer() {
r.au_native2 = null;
r.au_ogvjs = null;
r.au_ogvjs2 = null;
r.loading = false;
r.tracks = {};
r.order = [];
@@ -588,21 +599,30 @@ function MPlayer() {
setTimeout(fader, 10);
}
r.preload = function (url) {
r.preload = function (url, full) {
url = mpl.acode(url);
url += url.indexOf('?') < 0 ? '?cache' : '&cache';
if (mpl.fullpre)
url += (url.indexOf('?') < 0 ? '?' : '&') + 'cache=987';
mpl.preload_url = full ? url : null;
var t0 = Date.now();
if (full && !need_ogv_for(url))
return fetch(url).then(function (x) {
var rd = x.body.getReader(), n = 0;
function spd() {
return humansize(n / ((Date.now() + 1 - t0) / 1000)) + '/s';
}
function drop(x) {
if (x && x.done)
return console.log('xhr-preload finished, ' + spd());
if (x && x.value && x.value.length)
n += x.value.length;
if (n >= 128 * 1024 * 1024)
return console.log('aborting preload at 128 MiB');
if (mpl.preload_url !== url || n >= 128 * 1024 * 1024) {
console.log('xhr-preload aborted at ' + Math.floor(n / 1024) + ' KiB, ' + spd() + ' for ' + url);
return rd.cancel();
}
if (!x || !x.done)
return rd.read().then(drop);
return rd.read().then(drop);
}
drop();
});
@@ -610,21 +630,20 @@ function MPlayer() {
var au = null;
if (need_ogv_for(url)) {
au = mp.au_ogvjs2;
if (!au && window['OGVPlayer']) {
au = new OGVPlayer();
au.preload = "auto";
r.au_ogvjs2 = au;
}
if (!au && window['OGVPlayer'])
au = r.au_ogvjs2 = new OGVPlayer();
au.mdng = true;
bind_ogvjs();
} else {
au = mp.au_native2;
if (!au) {
au = new Audio();
au.preload = "auto";
r.au_native2 = au;
}
if (!au)
au = r.au_native2 = new Audio();
}
if (au)
if (au) {
au.preload = "auto";
au.src = url;
}
};
}
@@ -785,7 +804,7 @@ var pbar = (function () {
bctx.clearRect(0, 0, bc.w, bc.h);
if (!mp.au || mp.loading)
if (!mp.au || mp.au.mdng)
return;
var sm = bc.w * 1.0 / mp.au.duration,
@@ -812,7 +831,7 @@ var pbar = (function () {
pctx.clearRect(0, 0, pc.w, pc.h);
if (!mp.au || mp.loading || isNaN(adur = mp.au.duration) || isNaN(apos = mp.au.currentTime) || apos < 0 || adur < apos)
if (!mp.au || mp.au.mdng || isNaN(adur = mp.au.duration) || isNaN(apos = mp.au.currentTime) || apos < 0 || adur < apos)
return; // not-init || unsupp-codec
var sm = bc.w * 1.0 / adur;
@@ -1045,7 +1064,8 @@ function playpause(e) {
var mpui = (function () {
var r = {},
nth = 0,
preloaded = null;
preloaded = null,
fpreloaded = null;
r.progress_updater = function () {
timer.add(updater_impl, true);
@@ -1081,19 +1101,28 @@ var mpui = (function () {
}
// preload next song
if (mpl.preload && !mp.loading && preloaded != mp.au.src) {
if (mpl.preload && !mp.au.mdng && preloaded != mp.au.src) {
var pos = mp.au.currentTime,
len = mp.au.duration;
len = mp.au.duration,
rem = pos > 0 ? len - pos : 999,
full = null;
if (pos > 0 && pos > len - (mpl.fullpre ? 40 : 20)) {
preloaded = mp.au.src;
if (rem < (is_touch && IPHONE ? 34 : (mpl.fullpre ? 7 : 20))) {
preloaded = fpreloaded = mp.au.src;
full = false;
}
else if (rem < 40 && mpl.fullpre && fpreloaded != mp.au.src) {
fpreloaded = mp.au.src;
full = true;
}
if (full !== null)
try {
mp.preload(mp.tracks[mp.order[mp.order.indexOf(mp.au.tid) + 1]]);
mp.preload(mp.tracks[mp.order[mp.order.indexOf(mp.au.tid) + 1]], full);
}
catch (ex) {
console.log("preload failed", ex);
}
}
}
if (mp.au.paused)
@@ -1117,11 +1146,6 @@ function ev_play(e) {
}
function setclass(id, clas) {
ebi(id).setAttribute('class', clas);
}
var need_ogv = true;
try {
need_ogv = new Audio().canPlayType('audio/ogg; codecs=opus') !== 'probably';
@@ -1237,6 +1261,9 @@ var audio_eq = (function () {
if (!r.en && !mp.ac)
return;
if (mp.au === mp.au_ogvjs)
return toast.warn(10, "apple devices can't equalize ogg/opus audio");
if (mp.ac) {
for (var a = 0; a < r.filters.length; a++)
r.filters[a].disconnect();
@@ -1386,6 +1413,25 @@ var audio_eq = (function () {
})();
function bind_ogvjs() {
var a1 = mp.au_ogvjs,
a2 = mp.au_ogvjs2;
if (a2) {
a2.onerror = a2.onprogress = a2.onended = null;
a2.onloadedmetadata = a2.onloadeddata = function () {
a2.mdng = false;
};
}
a1.onerror = evau_error;
a1.onprogress = pbar.drawpos;
a1.onended = next_song;
a1.onloadedmetadata = a1.onloadeddata = function () {
a1.mdng = false;
};
}
// plays the tid'th audio file on the page
function play(tid, is_ev, seek, call_depth) {
if (mp.order.length == 0)
@@ -1394,6 +1440,7 @@ function play(tid, is_ev, seek, call_depth) {
if (crashed)
return;
mpl.preload_url = null;
mp.stopfade(true);
var tn = tid;
@@ -1427,7 +1474,7 @@ function play(tid, is_ev, seek, call_depth) {
if (mp.au) {
mp.au.pause();
setclass('a' + mp.au.tid, 'play');
clmod(ebi('a' + mp.au.tid), 'act');
}
// ogv.js breaks on .play() unless directly user-triggered
@@ -1442,7 +1489,6 @@ function play(tid, is_ev, seek, call_depth) {
mp.au = mp.au_ogvjs;
}
else if (window['OGVPlayer']) {
mp.loading = true;
try {
mp.au = mp.au_ogvjs = new OGVPlayer();
}
@@ -1451,12 +1497,8 @@ function play(tid, is_ev, seek, call_depth) {
'\n\n<a href="#" onclick="new OGVPlayer();">click here</a> for a full crash report');
}
attempt_play = is_ev;
mp.au.onerror = evau_error;
mp.au.onprogress = pbar.drawpos;
mp.au.onended = next_song;
mp.au.onloadedmetadata = mp.au.onloadeddata = function () {
mp.loading = false;
};
mp.au.mdng = true;
bind_ogvjs();
widget.open();
}
else if (safari < 14) {
@@ -1485,28 +1527,39 @@ function play(tid, is_ev, seek, call_depth) {
widget.open();
}
mp.au = mp.au_native;
mp.loading = false;
}
audio_eq.apply();
url += (url.indexOf('?') < 0 ? '?' : '&') + 'cache';
url += (url.indexOf('?') < 0 ? '?' : '&') + 'cache=987';
if (mp.au_ogvjs2 && mp.au_ogvjs2.src === url) {
mp.au = mp.au_ogvjs2;
mp.au_ogvjs2 = mp.au_ogvjs;
mp.au_ogvjs = mp.au;
bind_ogvjs();
}
if (mp.au.src == url)
mp.au.currentTime = 0;
else {
mp.loading = mp.au == mp.au_ogvjs;
mp.au.mdng = mp.au == mp.au_ogvjs;
mp.au.src = url;
}
setTimeout(function () {
mpl.unbuffer(url);
}, 500);
mp.au.tid = tid;
mp.au.volume = mp.expvol(mp.vol);
var oid = 'a' + tid;
setclass(oid, 'play act');
var trs = ebi('files').getElementsByTagName('tbody')[0].getElementsByTagName('tr');
for (var a = 0, aa = trs.length; a < aa; a++) {
var trs = QSA('#files tr.play');
for (var a = 0, aa = trs.length; a < aa; a++)
clmod(trs[a], 'play');
}
ebi(oid).parentElement.parentElement.className += ' play';
var oid = 'a' + tid;
clmod(ebi(oid), 'act', 1);
clmod(ebi(oid).closest('tr'), 'play', 1);
clmod(ebi('wtoggle'), 'np', mpl.clip);
if (window.thegrid)
thegrid.loadsel();
@@ -1536,7 +1589,7 @@ function play(tid, is_ev, seek, call_depth) {
catch (ex) {
toast.err(0, esc('playback failed: ' + basenames(ex)));
}
setclass(oid, 'play');
clmod(ebi(oid), 'act');
setTimeout(next_song, 500);
}
@@ -1592,7 +1645,11 @@ function autoplay_blocked(seek) {
play(tid, true, seek);
mp.fade_in();
}, null);
}, function () {
sethash('');
clmod(QS('#files tr.play'), 'play');
return reload_mp();
});
}
@@ -1603,7 +1660,7 @@ function eval_hash() {
if (v.indexOf('#af-') === 0) {
var id = v.slice(2).split('&');
if (id[0].length != 10)
if (id[0].length < 10)
return;
if (id.length == 1)
@@ -2494,6 +2551,7 @@ var showfile = (function () {
wr.appendChild(el);
wr.style.display = '';
set_tabindex();
document.documentElement.scrollTop = 0;
var hfun = no_push ? hist_replace : hist_push;
@@ -2583,7 +2641,7 @@ var thegrid = (function () {
gfiles.style.display = 'none';
gfiles.innerHTML = (
'<div id="ghead" class="ghead">' +
'<a href="#" class="tgl btn" id="gridsel" tt="enable file selection; ctrl-click a file to override$NHotkey: S">multiselect</a> <span>zoom: ' +
'<a href="#" class="tgl btn" id="gridsel" tt="enable file selection; ctrl-click a file to override$N$N&lt;em&gt;when active: doubleclick a file/folder to open it&lt;/em&gt;$N$NHotkey: S">multiselect</a> <span>zoom: ' +
'<a href="#" class="btn" z="-1.2" tt="Hotkey: shift-A">&ndash;</a> ' +
'<a href="#" class="btn" z="1.2" tt="Hotkey: shift-D">+</a></span> <span>chop: ' +
'<a href="#" class="btn" l="-1" tt="truncate filenames more (show less)">&ndash;</a> ' +
@@ -2685,10 +2743,21 @@ var thegrid = (function () {
}
setsz();
function gclick(e) {
function gclick1(e) {
if (ctrl(e))
return true;
return gclick.bind(this)(e, false);
}
function gclick2(e) {
if (ctrl(e) || !r.sel)
return true;
return gclick.bind(this)(e, true);
}
function gclick(e, dbl) {
var oth = ebi(this.getAttribute('ref')),
href = noq_href(this),
aplay = ebi('a' + oth.getAttribute('id')),
@@ -2701,7 +2770,7 @@ var thegrid = (function () {
if (href.endsWith('/'))
in_tree = treectl.find(oth.textContent.slice(0, -1));
if (r.sel) {
if (r.sel && !dbl) {
td.click();
clmod(this, 'sel', clgot(tr, 'sel'));
}
@@ -2714,7 +2783,16 @@ var thegrid = (function () {
else if (!is_img && have_sel)
window.open(href, '_blank');
else return true;
else {
if (!dbl)
return true;
setTimeout(function () {
r.sel = true;
}, 1);
r.sel = false;
this.click();
}
ev(e);
}
@@ -2829,8 +2907,10 @@ var thegrid = (function () {
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;
for (var a = 0, aa = ths.length; a < aa; a++) {
ths[a].ondblclick = gclick2;
ths[a].onclick = gclick1;
}
r.dirty = false;
r.bagit();
@@ -2915,7 +2995,7 @@ function tree_scrolltoo(q) {
function tree_neigh(n) {
var links = QSA(showfile.active() ? '#docul li>a' : '#treeul li>a+a');
var links = QSA(showfile.active() || treectl.texts ? '#docul li>a' : '#treeul li>a+a');
if (!links.length) {
treectl.dir_cb = function () {
tree_neigh(n);
@@ -2930,7 +3010,7 @@ function tree_neigh(n) {
break;
}
}
if (act == -1)
if (act == -1 && !treectl.texts)
return;
act += n;
@@ -2962,17 +3042,34 @@ function tree_up() {
document.onkeydown = function (e) {
var ae = document.activeElement, aet = '';
if (ae && ae != document.body)
aet = ae.nodeName.toLowerCase();
if (e.altKey || e.isComposing)
return;
if (QS('#bbox-overlay.visible'))
if (QS('#bbox-overlay.visible') || modal.busy)
return;
var k = e.code + '', pos = -1, n;
var k = e.code + '', pos = -1, n,
ae = document.activeElement,
aet = ae && ae != document.body ? ae.nodeName.toLowerCase() : '';
if (k == 'Escape') {
ae && ae.blur();
if (QS('.opview.act'))
return QS('#ops>a').click();
if (QS('#unsearch'))
return QS('#unsearch').click();
if (widget.is_open)
return widget.close();
if (!treectl.hidden)
return treectl.detree();
if (thegrid.en)
return ebi('griden').click();
}
if (aet == 'tr' && ae.closest('#files')) {
var d = '';
@@ -3109,28 +3206,28 @@ document.onkeydown = function (e) {
(function () {
var sconf = [
["size",
["szl", "sz_min", "minimum MiB", "16"],
["szu", "sz_max", "maximum MiB", "16"]
["szl", "sz_min", "minimum MiB", "14"],
["szu", "sz_max", "maximum MiB", "14"]
],
["date",
["dtl", "dt_min", "min. iso8601", "16"],
["dtu", "dt_max", "max. iso8601", "16"]
["dtl", "dt_min", "min. iso8601", "14"],
["dtu", "dt_max", "max. iso8601", "14"]
],
["path",
["path", "path", "path contains &nbsp; (space-separated)", "34"]
["path", "path", "path contains &nbsp; (space-separated)", "30"]
],
["name",
["name", "name", "name contains &nbsp; (negate with -nope)", "34"]
["name", "name", "name contains &nbsp; (negate with -nope)", "30"]
]
];
var oldcfg = [];
if (QS('#srch_form.tags')) {
sconf.push(["tags",
["tags", "tags", "tags contains &nbsp; (^=start, end=$)", "34"]
["tags", "tags", "tags contains &nbsp; (^=start, end=$)", "30"]
]);
sconf.push(["adv.",
["adv", "adv", "key>=1A&nbsp; key<=2B&nbsp; .bpm>165", "34"]
["adv", "adv", "key>=1A&nbsp; key<=2B&nbsp; .bpm>165", "30"]
]);
}
@@ -3821,7 +3918,8 @@ var treectl = (function () {
var top = this.top,
nodes = res.dirs.concat(res.files),
html = mk_files_header(res.taglist);
html = mk_files_header(res.taglist),
seen = {};
showfile.files = [];
html.push('<tbody>');
@@ -3835,6 +3933,10 @@ var treectl = (function () {
id = 'f-' + ('00000000' + crc32(fname)).slice(-8),
lang = showfile.getlang(fname);
while (seen[id])
id += 'a';
seen[id] = 1;
if (lang)
showfile.files.push({ 'id': id, 'name': fname });
@@ -4359,12 +4461,19 @@ function addcrc() {
'#files>tbody>tr>td:first-child+td>' + (
ebi('unsearch') ? 'div>a:last-child' : 'a'));
for (var a = 0, aa = links.length; a < aa; a++)
if (!links[a].getAttribute('id')) {
var seen = {}; // ejyefs ev69gg y9j8sg .opus
for (var a = 0, aa = links.length; a < aa; a++) {
var id = links[a].getAttribute('id');
if (!id) {
var crc = crc32(links[a].textContent || links[a].innerText);
crc = ('00000000' + crc).slice(-8);
links[a].setAttribute('id', 'f-' + crc);
id = 'f-' + ('00000000' + crc).slice(-8);
while (seen[id])
id += 'a';
links[a].setAttribute('id', id);
}
seen[id] = 1;
}
}
@@ -4707,14 +4816,12 @@ function show_md(md, name, div, url, depth) {
var els = QSA('#epi a');
for (var a = 0, aa = els.length; a < aa; a++) {
var href = els[a].getAttribute('href');
if (!href.startsWith('#'))
if (!href.startsWith('#') || href.startsWith('#md-'))
continue;
els[a].setAttribute('href', '#md-' + href.slice(1));
}
els = QSA('pre');
for (var a = 0, aa = els.length; a < aa; a++)
els[a].setAttribute('tabindex', '0');
set_tabindex();
}
catch (ex) {
toast.warn(10, errmsg + ex);
@@ -4722,6 +4829,13 @@ function show_md(md, name, div, url, depth) {
}
function set_tabindex() {
var els = QSA('pre');
for (var a = 0, aa = els.length; a < aa; a++)
els[a].setAttribute('tabindex', '0');
}
function show_readme(md) {
if (!treectl.ireadme)
return;
@@ -4946,6 +5060,7 @@ function reload_mp() {
for (var a = plays.length - 1; a >= 0; a--)
plays[a].parentNode.innerHTML = '-';
mpl.unbuffer();
mp = new MPlayer();
setTimeout(pbar.onresize, 1);
}

View File

@@ -37,7 +37,8 @@ a+a {
float: right;
margin: -.2em 0 0 .5em;
}
.logout {
.logout,
.btns a {
color: #c04;
border-color: #c7a;
}
@@ -94,7 +95,8 @@ html.dark a {
background: #057;
border-color: #37a;
}
html.dark .logout {
html.dark .logout,
html.dark .btns a {
background: #804;
border-color: #c28;
}

View File

@@ -133,7 +133,8 @@ html {
}
#modalc code,
#tt code {
background: #3c3c3c;
color: #eee;
background: #444;
padding: .1em .3em;
border-top: 1px solid #777;
border-radius: .3em;

View File

@@ -562,9 +562,9 @@ function fsearch_explain(n) {
return toast.inf(60, 'your access to this folder is Read-Only\n\n' + (acct == '*' ? 'you are currently not logged in' : 'you are currently logged in as "' + acct + '"'));
if (bcfg_get('fsearch', false))
return toast.inf(60, 'you are currently in file-search mode\n\nswitch to upload-mode by clicking the green magnifying glass (next to the big yellow search button), and then refresh\n\nsorry');
return toast.inf(60, 'you are currently in file-search mode\n\nswitch to upload-mode by clicking the green magnifying glass (next to the big yellow search button), and try uploading again\n\nsorry');
return toast.inf(60, 'refresh the page and try again, it should work now');
return toast.inf(60, 'try again, it should work now');
}
@@ -669,6 +669,7 @@ function up2k_init(subtle) {
var st = {
"files": [],
"seen": {},
"todo": {
"head": [],
"hash": [],
@@ -994,13 +995,9 @@ function up2k_init(subtle) {
}
function up_them(good_files) {
var seen = {},
evpath = get_evpath(),
var 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],
name = good_files[a][1],
@@ -1026,15 +1023,20 @@ function up2k_init(subtle) {
"bytes_uploaded": 0,
"hash": []
},
key = entry.name + '\n' + entry.size;
key = name + '\n' + entry.size + '\n' + lmod + '\n' + uc.fsearch;
if (uc.fsearch)
entry.srch = 1;
if (seen[key])
continue;
try {
if (st.seen[fdir][key])
continue;
}
catch (ex) {
st.seen[fdir] = {};
}
seen[key] = 1;
st.seen[fdir][key] = 1;
pvis.addfile([
uc.fsearch ? esc(entry.name) : linksplit(
@@ -1067,20 +1069,6 @@ function up2k_init(subtle) {
}
more_one_file();
function u2cleanup(e) {
ev(e);
for (var a = 0; a < st.files.length; a++) {
var t = st.files[a];
if (t.done && t.name) {
if (!qsr('#f' + t.n))
continue;
t.name = undefined;
}
}
}
ebi('u2cleanup').onclick = u2cleanup;
var etaref = 0, etaskip = 0, op_minh = 0;
function etafun() {
var nhash = st.busy.head.length + st.busy.hash.length + st.todo.head.length + st.todo.hash.length,
@@ -1911,8 +1899,8 @@ function up2k_init(subtle) {
wpx = window.innerWidth,
fpx = parseInt(getComputedStyle(bar)['font-size']),
wem = wpx * 1.0 / fpx,
wide = wem > 54 ? 'w' : '',
write = has(perms, 'write'),
wide = write && wem > 54 ? 'w' : '',
parent = ebi(wide && write ? 'u2btn_cw' : 'u2btn_ct'),
btn = ebi('u2btn');
@@ -1924,7 +1912,7 @@ function up2k_init(subtle) {
ebi('u2etaw').setAttribute('class', wide);
}
wide = wem > 78 ? 'ww' : wide;
wide = write && wem > 78 ? 'ww' : wide;
parent = ebi(wide == 'ww' && write ? 'u2c3w' : 'u2c3t');
var its = [ebi('u2etaw'), ebi('u2cards')];
if (its[0].parentNode !== parent) {

View File

@@ -9,7 +9,7 @@
#ops, #tree, #path, #wrap>h2:last-child, /* main tabs and navigators (tree/breadcrumbs) */
#u2cleanup, #u2conf tr:first-child>td[rowspan]:not(#u2btn_cw), /* most of the config options */
#u2conf tr:first-child>td[rowspan]:not(#u2btn_cw), /* most of the config options */
#srch_dz, #srch_zd, /* the filesearch dropzone */

View File

@@ -169,7 +169,7 @@ brew install python@2
pip install virtualenv
# readme toc
cat README.md | awk 'function pr() { if (!h) {return}; if (/^ *[*!#]/||!s) {printf "%s\n",h;h=0;return}; if (/.../) {printf "%s - %s\n",h,$0;h=0}; }; /^#/{s=1;pr()} /^#* *(file indexing|install on android|dev env setup|just the sfx|complete release|optional gpl stuff)|`$/{s=0} /^#/{lv=length($1);sub(/[^ ]+ /,"");bab=$0;gsub(/ /,"-",bab); h=sprintf("%" ((lv-1)*4+1) "s [%s](#%s)", "*",$0,bab);next} !h{next} {sub(/ .*/,"");sub(/[:,]$/,"")} {pr()}' > toc; grep -E '^## readme toc' -B1000 -A2 <README.md >p1; grep -E '^## quickstart' -B2 -A999999 <README.md >p2; (cat p1; grep quickstart -A1000 <toc; cat p2) >README.md; rm p1 p2 toc
cat README.md | awk 'function pr() { if (!h) {return}; if (/^ *[*!#|]/||!s) {printf "%s\n",h;h=0;return}; if (/.../) {printf "%s - %s\n",h,$0;h=0}; }; /^#/{s=1;pr()} /^#* *(file indexing|install on android|dev env setup|just the sfx|complete release|optional gpl stuff)|`$/{s=0} /^#/{lv=length($1);sub(/[^ ]+ /,"");bab=$0;gsub(/ /,"-",bab); h=sprintf("%" ((lv-1)*4+1) "s [%s](#%s)", "*",$0,bab);next} !h{next} {sub(/ .*/,"");sub(/[:,]$/,"")} {pr()}' > toc; grep -E '^## readme toc' -B1000 -A2 <README.md >p1; grep -E '^## quickstart' -B2 -A999999 <README.md >p2; (cat p1; grep quickstart -A1000 <toc; cat p2) >README.md; rm p1 p2 toc
# fix firefox phantom breakpoints,
# suggestions from bugtracker, doesnt work (debugger is not attachable)