add hook: usb-eject

This commit is contained in:
ed 2025-01-25 17:02:41 +00:00
parent fff38f484d
commit f38c754301
4 changed files with 111 additions and 7 deletions

View file

@ -30,4 +30,5 @@ these are `--xiu` hooks; unlike `xbu` and `xau` (which get executed on every sin
# on message
* [wget.py](wget.py) lets you download files by POSTing URLs to copyparty
* [qbittorrent-magnet.py](qbittorrent-magnet.py) starts downloading a torrent if you post a magnet url
* [usb-eject.py](usb-eject.py) adds web-UI buttons to safe-remove usb flashdrives shared through copyparty
* [msg-log.py](msg-log.py) is a guestbook; logs messages to a doc in the same folder

54
bin/hooks/usb-eject.js Normal file
View file

@ -0,0 +1,54 @@
// see usb-eject.py for usage
function usbclick() {
QS('#treeul a[href="/usb/"]').click();
}
function eject_cb() {
var t = this.responseText;
if (t.indexOf('can be safely unplugged') < 0 && t.indexOf('Device can be removed') < 0)
return toast.err(30, 'usb eject failed:\n\n' + t);
toast.ok(5, esc(t.replace(/ - /g, '\n\n')));
usbclick(); setTimeout(usbclick, 10);
};
function add_eject_2(a) {
var aw = a.getAttribute('href').split(/\//g);
if (aw.length != 4 || aw[3])
return;
var v = aw[2],
k = 'umount_' + v;
qsr('#' + k);
a.appendChild(mknod('span', k, '⏏'), a);
var o = ebi(k);
o.style.cssText = 'position:absolute; right:1em; margin-top:-.2em; font-size:1.3em';
o.onclick = function (e) {
ev(e);
var xhr = new XHR();
xhr.open('POST', get_evpath(), true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8');
xhr.send('msg=' + uricom_enc(':usb-eject:' + v + ':'));
xhr.onload = xhr.onerror = eject_cb;
toast.inf(10, "ejecting " + v + "...");
};
};
function add_eject() {
for (var a of QSA('#treeul a[href^="/usb/"]'))
add_eject_2(a);
};
(function() {
var f0 = treectl.rendertree;
treectl.rendertree = function (res, ts, top0, dst, rst) {
var ret = f0(res, ts, top0, dst, rst);
add_eject();
return ret;
};
})();
setTimeout(add_eject, 50);

49
bin/hooks/usb-eject.py Normal file
View file

@ -0,0 +1,49 @@
#!/usr/bin/env python3
import os
import stat
import subprocess as sp
import sys
"""
if you've found yourself using copyparty to serve flashdrives on a LAN
and your only wish is that the web-UI had a button to unmount / safely
remove those flashdrives, then boy howdy are you in the right place :D
put usb-eject.js in the webroot (or somewhere else http-accessible)
then run copyparty with these args:
-v /run/media/ed:/usb:A:c,hist=/tmp/junk
--xm=c1,bin/hooks/usb-eject.py
--js-browser=/usb-eject.js
which does the following respectively,
* share all of /run/media/ed as /usb with admin for everyone
and put the histpath somewhere it won't cause trouble
* run the usb-eject hook with stdout redirect to the web-ui
* add the complementary usb-eject.js to the browser
"""
def main():
try:
label = sys.argv[1].split(":usb-eject:")[1].split(":")[0]
mp = "/run/media/ed/" + label
# print("ejecting [%s]... " % (mp,), end="")
mp = os.path.abspath(os.path.realpath(mp.encode("utf-8")))
st = os.lstat(mp)
if not stat.S_ISDIR(st.st_mode):
raise Exception("not a regular directory")
cmd = [b"gio", b"mount", b"-e", mp]
print(sp.check_output(cmd).decode("utf-8", "replace").strip())
except Exception as ex:
print("unmount failed: %r" % (ex,))
if __name__ == "__main__":
main()

View file

@ -7460,12 +7460,12 @@ var treectl = (function () {
xhr.rst = rst;
xhr.ts = Date.now();
xhr.open('GET', addq(dst, 'tree=' + top + (r.dots ? '&dots' : '') + k), true);
xhr.onload = xhr.onerror = recvtree;
xhr.onload = xhr.onerror = r.recvtree;
xhr.send();
enspin('#tree');
}
function recvtree() {
r.recvtree = function () {
if (!xhrchk(this, L.tl_xe1, L.tl_xe2))
return;
@ -7475,10 +7475,10 @@ var treectl = (function () {
catch (ex) {
return toast.err(30, "bad <code>?tree</code> reply;\nexpected json, got this:\n\n" + esc(this.responseText + ''));
}
rendertree(res, this.ts, this.top, this.dst, this.rst);
}
r.rendertree(res, this.ts, this.top, this.dst, this.rst);
};
function rendertree(res, ts, top0, dst, rst) {
r.rendertree = function (res, ts, top0, dst, rst) {
var cur = ebi('treeul').getAttribute('ts');
if (cur && parseInt(cur) > ts + 20 && QS('#treeul>li>a+a')) {
console.log("reject tree; " + cur + " / " + (ts - cur));
@ -7532,7 +7532,7 @@ var treectl = (function () {
console.log("dir_cb failed", ex);
}
}
}
};
function reload_tree() {
var cdir = r.nextdir || get_vpath(),
@ -7754,7 +7754,7 @@ var treectl = (function () {
dirs.push(dn);
}
rendertree({ "a": dirs }, this.ts, ".", get_evpath() + (dk ? '?k=' + dk : ''));
r.rendertree({ "a": dirs }, this.ts, ".", get_evpath() + (dk ? '?k=' + dk : ''));
}
r.gentab(this.top, res);