Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5d822c70a | ||
|
|
9c09b4061a | ||
|
|
c26fb43ced | ||
|
|
deb8f20db6 | ||
|
|
50e18ed8ff | ||
|
|
31f3895f40 | ||
|
|
615929268a | ||
|
|
b8b15814cf | ||
|
|
7766fffe83 | ||
|
|
2a16c150d1 | ||
|
|
418c2166cc | ||
|
|
a4dd44f648 | ||
|
|
5352f7cda7 | ||
|
|
5533b47099 | ||
|
|
e9b14464ee | ||
|
|
4e986e5cd1 | ||
|
|
8a59b40c53 |
15
README.md
15
README.md
@@ -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/;}`
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<code>foo bar</code> = must contain both foo and bar,$N<code>foo -bar</code> = must contain foo but not bar,$N<code>^yana .opus$</code> = 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<code>foo bar</code> = must contain both foo and bar,$N<code>foo -bar</code> = must contain foo but not bar,$N<code>^yana .opus$</code> = 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<em>when active: doubleclick a file/folder to open it</em>$N$NHotkey: S">multiselect</a> <span>zoom: ' +
|
||||
'<a href="#" class="btn" z="-1.2" tt="Hotkey: shift-A">–</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)">–</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 (space-separated)", "34"]
|
||||
["path", "path", "path contains (space-separated)", "30"]
|
||||
],
|
||||
["name",
|
||||
["name", "name", "name contains (negate with -nope)", "34"]
|
||||
["name", "name", "name contains (negate with -nope)", "30"]
|
||||
]
|
||||
];
|
||||
var oldcfg = [];
|
||||
|
||||
if (QS('#srch_form.tags')) {
|
||||
sconf.push(["tags",
|
||||
["tags", "tags", "tags contains (^=start, end=$)", "34"]
|
||||
["tags", "tags", "tags contains (^=start, end=$)", "30"]
|
||||
]);
|
||||
sconf.push(["adv.",
|
||||
["adv", "adv", "key>=1A key<=2B .bpm>165", "34"]
|
||||
["adv", "adv", "key>=1A key<=2B .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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user