Compare commits

...

30 Commits

Author SHA1 Message Date
ed
bd49979f4a v0.11.29 2021-06-30 01:51:57 +02:00
ed
7e606cdd9f make search rate-control less visually confusing 2021-06-30 01:44:25 +02:00
ed
8b4b7fa794 allow opening tree nodes in a new tab 2021-06-30 01:08:20 +02:00
ed
05345ddf8b add per-connection request counting 2021-06-30 01:00:00 +02:00
ed
66adb470ad optional progressbar tint 2021-06-30 00:55:57 +02:00
ed
e15c8fd146 add upload pause 2021-06-30 00:34:33 +02:00
ed
0f09b98a39 scan for additional folder thumbnails 2021-06-30 00:19:39 +02:00
ed
b4d6f4e24d american-friendly upload limits (allow additional bypass using manual text entry) 2021-06-30 00:11:23 +02:00
ed
3217fa625b more todo 2021-06-29 23:59:15 +02:00
ed
e719ff8a47 make sfx kipu-proof 2021-06-29 23:53:57 +02:00
ed
9fcf528d45 update readme 2021-06-29 23:32:21 +02:00
ed
1ddbf5a158 update todo 2021-06-29 23:00:28 +02:00
ed
64bf4574b0 add todo maybe 2021-06-28 20:38:59 +02:00
ed
5649d26077 v0.11.28 2021-06-28 15:36:13 +02:00
ed
92f923effe hotkey for adjusting tree width 2021-06-28 15:34:10 +02:00
ed
0d46d548b9 fix panic when zero accounts 2021-06-28 15:20:40 +02:00
ed
062df3f0c3 point control-panel link to / 2021-06-27 00:52:15 +02:00
ed
789fb53b8e tweaks 2021-06-27 00:49:28 +02:00
ed
351db5a18f ah yes trailing whitespace as markup my good old friend we meet again 2021-06-27 00:20:42 +02:00
ed
aabbd271c8 add debian howto 2021-06-27 00:19:37 +02:00
ed
aae8e0171e v0.11.27 2021-06-25 22:23:21 +02:00
ed
45827a2458 fix exit-search button in gridview 2021-06-25 22:18:16 +02:00
ed
726030296f apparently the html dom-property is not normalized 2021-06-25 22:07:37 +02:00
ed
6659ab3881 ajax subfolders from gridview 2021-06-25 21:49:09 +02:00
ed
c6a103609e fix gridview selection/baguettebox order 2021-06-25 21:35:45 +02:00
ed
c6b3f035e5 gridview audio playback in search results too 2021-06-25 21:12:49 +02:00
ed
2b0a7e378e persist url-password as cookie 2021-06-25 20:39:55 +02:00
ed
b75ce909c8 audio seek with scrollbar on progressbar 2021-06-25 20:24:30 +02:00
ed
229c3f5dab play audio from grid when widget open 2021-06-25 20:04:19 +02:00
ed
ec73094506 v0.11.26 2021-06-25 03:10:43 +02:00
15 changed files with 228 additions and 68 deletions

View File

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

View File

@@ -305,6 +305,7 @@ def run_argparse(argv, formatter):
ap2.add_argument("--th-poke", metavar="SEC", type=int, default=300, help="activity labeling cooldown") ap2.add_argument("--th-poke", metavar="SEC", type=int, default=300, help="activity labeling cooldown")
ap2.add_argument("--th-clean", metavar="SEC", type=int, default=43200, help="cleanup interval") ap2.add_argument("--th-clean", metavar="SEC", type=int, default=43200, help="cleanup interval")
ap2.add_argument("--th-maxage", metavar="SEC", type=int, default=604800, help="max folder age") ap2.add_argument("--th-maxage", metavar="SEC", type=int, default=604800, help="max folder age")
ap2.add_argument("--th-covers", metavar="N,N", type=str, default="folder.png,folder.jpg,cover.png,cover.jpg", help="folder thumbnails to stat for")
ap2 = ap.add_argument_group('database options') ap2 = ap.add_argument_group('database options')
ap2.add_argument("-e2d", action="store_true", help="enable up2k database") ap2.add_argument("-e2d", action="store_true", help="enable up2k database")

View File

@@ -1,8 +1,8 @@
# coding: utf-8 # coding: utf-8
VERSION = (0, 11, 25) VERSION = (0, 11, 29)
CODENAME = "the grid" CODENAME = "the grid"
BUILD_DT = (2021, 6, 25) BUILD_DT = (2021, 6, 30)
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)

View File

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

View File

@@ -55,7 +55,7 @@ class HttpCli(object):
def log(self, msg, c=0): def log(self, msg, c=0):
ptn = self.asrv.re_pwd ptn = self.asrv.re_pwd
if ptn.search(msg): if ptn and ptn.search(msg):
msg = ptn.sub(self.unpwd, msg) msg = ptn.sub(self.unpwd, msg)
self.log_func(self.log_src, msg, c) self.log_func(self.log_src, msg, c)
@@ -181,6 +181,9 @@ class HttpCli(object):
self.rvol, self.wvol, self.avol = [[], [], []] self.rvol, self.wvol, self.avol = [[], [], []]
self.asrv.vfs.user_tree(self.uname, self.rvol, self.wvol, self.avol) self.asrv.vfs.user_tree(self.uname, self.rvol, self.wvol, self.avol)
if pwd and "pw" in self.ouparam and pwd != cookies.get("cppwd"):
self.out_headers["Set-Cookie"] = self.get_pwd_cookie(pwd)[0]
ua = self.headers.get("user-agent", "") ua = self.headers.get("user-agent", "")
self.is_rclone = ua.startswith("rclone/") self.is_rclone = ua.startswith("rclone/")
if self.is_rclone: if self.is_rclone:
@@ -496,7 +499,7 @@ class HttpCli(object):
spd1 = get_spd(nbytes, self.t0) spd1 = get_spd(nbytes, self.t0)
spd2 = get_spd(self.conn.nbyte, self.conn.t0) spd2 = get_spd(self.conn.nbyte, self.conn.t0)
return spd1 + " " + spd2 return "{} {} n{}".format(spd1, spd2, self.conn.nreq)
def handle_post_multipart(self): def handle_post_multipart(self):
self.parser = MultipartParser(self.log, self.sr, self.headers) self.parser = MultipartParser(self.log, self.sr, self.headers)
@@ -758,6 +761,12 @@ class HttpCli(object):
pwd = self.parser.require("cppwd", 64) pwd = self.parser.require("cppwd", 64)
self.parser.drop() self.parser.drop()
ck, msg = self.get_pwd_cookie(pwd)
html = self.j2("msg", h1=msg, h2='<a href="/">ack</a>', redir="/")
self.reply(html.encode("utf-8"), headers={"Set-Cookie": ck})
return True
def get_pwd_cookie(self, pwd):
if pwd in self.asrv.iuser: if pwd in self.asrv.iuser:
msg = "login ok" msg = "login ok"
dt = datetime.utcfromtimestamp(time.time() + 60 * 60 * 24 * 365) dt = datetime.utcfromtimestamp(time.time() + 60 * 60 * 24 * 365)
@@ -768,9 +777,7 @@ class HttpCli(object):
exp = "Fri, 15 Aug 1997 01:00:00 GMT" exp = "Fri, 15 Aug 1997 01:00:00 GMT"
ck = "cppwd={}; Path=/; Expires={}; SameSite=Lax".format(pwd, exp) ck = "cppwd={}; Path=/; Expires={}; SameSite=Lax".format(pwd, exp)
html = self.j2("msg", h1=msg, h2='<a href="/">ack</a>', redir="/") return [ck, msg]
self.reply(html.encode("utf-8"), headers={"Set-Cookie": ck})
return True
def handle_mkdir(self): def handle_mkdir(self):
new_dir = self.parser.require("name", 512) new_dir = self.parser.require("name", 512)
@@ -1555,7 +1562,7 @@ class HttpCli(object):
th_fmt = self.uparam.get("th") th_fmt = self.uparam.get("th")
if th_fmt is not None: if th_fmt is not None:
if is_dir: if is_dir:
for fn in ["folder.png", "folder.jpg"]: for fn in self.args.th_covers.split(","):
fp = os.path.join(abspath, fn) fp = os.path.join(abspath, fn)
if os.path.exists(fp): if os.path.exists(fp):
vrem = "{}/{}".format(vrem.rstrip("/"), fn) vrem = "{}/{}".format(vrem.rstrip("/"), fn)

View File

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

View File

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

View File

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

View File

@@ -110,7 +110,7 @@
<div id="epi" class="logue">{{ logues[1] }}</div> <div id="epi" class="logue">{{ logues[1] }}</div>
<h2><a href="?h">control-panel</a></h2> <h2><a href="/?h">control-panel</a></h2>
</div> </div>

View File

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

View File

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

View File

@@ -1,7 +1,5 @@
"use strict"; "use strict";
window.onerror = vis_exh;
function goto_up2k() { function goto_up2k() {
if (up2k === false) if (up2k === false)
@@ -1309,6 +1307,17 @@ function up2k_init(subtle) {
} }
tt.init(); tt.init();
function bumpthread2(e) {
if (e.ctrlKey || e.altKey || e.metaKey || e.isComposing)
return;
if (e.code == 'ArrowUp')
bumpthread(1);
if (e.code == 'ArrowDown')
bumpthread(-1);
}
function bumpthread(dir) { function bumpthread(dir) {
try { try {
dir.stopPropagation(); dir.stopPropagation();
@@ -1319,7 +1328,7 @@ function up2k_init(subtle) {
if (dir.target) { if (dir.target) {
clmod(obj, 'err', 1); clmod(obj, 'err', 1);
var v = Math.floor(parseInt(obj.value)); var v = Math.floor(parseInt(obj.value));
if (v < 1 || v > 8 || v !== v) if (v < 0 || v > 64 || v !== v)
return; return;
parallel_uploads = v; parallel_uploads = v;
@@ -1330,11 +1339,11 @@ function up2k_init(subtle) {
parallel_uploads += dir; parallel_uploads += dir;
if (parallel_uploads < 1) if (parallel_uploads < 0)
parallel_uploads = 1; parallel_uploads = 0;
if (parallel_uploads > 8) if (parallel_uploads > 16)
parallel_uploads = 8; parallel_uploads = 16;
obj.value = parallel_uploads; obj.value = parallel_uploads;
bumpthread({ "target": 1 }) bumpthread({ "target": 1 })
@@ -1430,6 +1439,7 @@ function up2k_init(subtle) {
bumpthread(-1); bumpthread(-1);
}; };
ebi('nthread').onkeydown = bumpthread2;
ebi('nthread').addEventListener('input', bumpthread, false); ebi('nthread').addEventListener('input', bumpthread, false);
ebi('multitask').addEventListener('click', tgl_multitask, false); ebi('multitask').addEventListener('click', tgl_multitask, false);
ebi('ask_up').addEventListener('click', tgl_ask_up, false); ebi('ask_up').addEventListener('click', tgl_ask_up, false);
@@ -1443,7 +1453,10 @@ function up2k_init(subtle) {
nodes[a].addEventListener('touchend', nop, false); nodes[a].addEventListener('touchend', nop, false);
set_fsearch(); set_fsearch();
bumpthread({ "target": 1 }) bumpthread({ "target": 1 });
if (parallel_uploads < 1)
bumpthread(1);
return { "init_deps": init_deps, "set_fsearch": set_fsearch } return { "init_deps": init_deps, "set_fsearch": set_fsearch }
} }

View File

@@ -32,6 +32,9 @@ function esc(txt) {
}); });
} }
function vis_exh(msg, url, lineNo, columnNo, error) { function vis_exh(msg, url, lineNo, columnNo, error) {
if (!window.onerror)
return;
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 screenshot this error and send me a copy arigathanks gozaimuch (ed/irc.rizon.net or ed#2644)</p><p>',
@@ -67,6 +70,9 @@ function ev(e) {
if (e.stopPropagation) if (e.stopPropagation)
e.stopPropagation(); e.stopPropagation();
if (e.stopImmediatePropagation)
e.stopImmediatePropagation();
e.returnValue = false; e.returnValue = false;
return e; return e;
} }
@@ -360,11 +366,11 @@ function get_vpath() {
function get_pwd() { function get_pwd() {
var pwd = ('; ' + document.cookie).split('; cppwd='); var pwd = ('; ' + document.cookie).split('; cppwd=');
if (pwd.length < 2) if (pwd.length < 2)
return null; return null;
return pwd[1].split(';')[0]; return pwd[1].split(';')[0];
} }

View File

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

View File

@@ -121,6 +121,7 @@ class VHttpConn(object):
self.log_src = "a" self.log_src = "a"
self.lf_url = None self.lf_url = None
self.hsrv = VHttpSrv() self.hsrv = VHttpSrv()
self.nreq = 0
self.nbyte = 0 self.nbyte = 0
self.workload = 0 self.workload = 0
self.ico = None self.ico = None