mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
allow setting lifetimes from up2k ui
This commit is contained in:
parent
1882afb8b6
commit
0b87a4a810
13
README.md
13
README.md
|
@ -49,6 +49,7 @@ try the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running fro
|
|||
* [uploading](#uploading) - drag files/folders into the web-browser to upload
|
||||
* [file-search](#file-search) - dropping files into the browser also lets you see if they exist on the server
|
||||
* [unpost](#unpost) - undo/delete accidental uploads
|
||||
* [self-destruct](#self-destruct) - uploads can be given a lifetime
|
||||
* [file manager](#file-manager) - cut/paste, rename, and delete files/folders (if you have permission)
|
||||
* [batch rename](#batch-rename) - select some files and press `F2` to bring up the rename UI
|
||||
* [markdown viewer](#markdown-viewer) - and there are *two* editors
|
||||
|
@ -168,6 +169,7 @@ feature summary
|
|||
* ☑ [up2k](#uploading): js, resumable, multithreaded
|
||||
* ☑ stash: simple PUT filedropper
|
||||
* ☑ [unpost](#unpost): undo/delete accidental uploads
|
||||
* ☑ [self-destruct](#self-destruct) (specified server-side or client-side)
|
||||
* ☑ symlink/discard existing files (content-matching)
|
||||
* download
|
||||
* ☑ single files in browser
|
||||
|
@ -543,6 +545,17 @@ undo/delete accidental uploads
|
|||
you can unpost even if you don't have regular move/delete access, however only for files uploaded within the past `--unpost` seconds (default 12 hours) and the server must be running with `-e2d`
|
||||
|
||||
|
||||
### self-destruct
|
||||
|
||||
uploads can be given a lifetime, afer which they expire / self-destruct
|
||||
|
||||
the feature must be enabled per-volume with the `lifetime` [upload rule](#upload-rules) which sets the upper limit for how long a file gets to stay on the server
|
||||
|
||||
clients can specify a shorter expiration time using the [up2k ui](#uploading) -- the relevant options become visible upon navigating into a folder with `lifetimes` enabled -- or by using the `life` [upload modifier](#write)
|
||||
|
||||
specifying a custom expiration time client-side will affect the timespan in which unposts are permitted, so keep an eye on the estimates in the up2k ui
|
||||
|
||||
|
||||
## file manager
|
||||
|
||||
cut/paste, rename, and delete files/folders (if you have permission)
|
||||
|
|
|
@ -1121,6 +1121,16 @@ class AuthSrv(object):
|
|||
|
||||
vol.flags = {k: v for k, v in vol.flags.items() if not k.startswith(rm)}
|
||||
|
||||
ints = ["lifetime"]
|
||||
for k in list(vol.flags):
|
||||
if k in ints:
|
||||
vol.flags[k] = int(vol.flags[k])
|
||||
|
||||
if "lifetime" in vol.flags and "e2d" not in vol.flags:
|
||||
t = 'removing lifetime config from volume "/{}" because e2d is disabled'
|
||||
self.log(t.format(vol.vpath), 1)
|
||||
del vol.flags["lifetime"]
|
||||
|
||||
# verify tags mentioned by -mt[mp] are used by -mte
|
||||
local_mtp = {}
|
||||
local_only_mtp = {}
|
||||
|
|
|
@ -1313,7 +1313,7 @@ class HttpCli(object):
|
|||
want_url = ac == "url"
|
||||
zs = self.uparam.get("life", self.headers.get("life", ""))
|
||||
if zs:
|
||||
vlife = int(vfs.flags.get("lifetime") or 0)
|
||||
vlife = vfs.flags.get("lifetime") or 0
|
||||
lifetime = max(0, int(vlife - int(zs)))
|
||||
else:
|
||||
lifetime = 0
|
||||
|
@ -2495,6 +2495,7 @@ class HttpCli(object):
|
|||
"srvinf": srv_infot,
|
||||
"acct": self.uname,
|
||||
"idx": ("e2d" in vn.flags),
|
||||
"lifetime": vn.flags.get("lifetime") or 0,
|
||||
"perms": perms,
|
||||
"logues": logues,
|
||||
"readme": readme,
|
||||
|
@ -2506,6 +2507,7 @@ class HttpCli(object):
|
|||
"ls0": None,
|
||||
"acct": self.uname,
|
||||
"perms": json.dumps(perms),
|
||||
"lifetime": ls_ret["lifetime"],
|
||||
"taglist": [],
|
||||
"def_hcols": [],
|
||||
"have_emp": self.args.emp,
|
||||
|
@ -2515,7 +2517,7 @@ class HttpCli(object):
|
|||
"have_mv": (not self.args.no_mv),
|
||||
"have_del": (not self.args.no_del),
|
||||
"have_zip": (not self.args.no_zip),
|
||||
"have_unpost": (self.args.unpost > 0),
|
||||
"have_unpost": int(self.args.unpost),
|
||||
"have_b_u": (self.can_write and self.uparam.get("b") == "u"),
|
||||
"url_suf": url_suf,
|
||||
"logues": logues,
|
||||
|
|
|
@ -351,11 +351,9 @@ class Up2k(object):
|
|||
if not cur:
|
||||
continue
|
||||
|
||||
lifetime = int(lifetime)
|
||||
timeout = min(timeout, now + lifetime)
|
||||
|
||||
nrm = 0
|
||||
deadline = time.time() - lifetime
|
||||
timeout = min(timeout, now + lifetime)
|
||||
q = "select rd, fn from up where at > 0 and at < ? limit 100"
|
||||
while True:
|
||||
with self.mutex:
|
||||
|
@ -1960,6 +1958,7 @@ class Up2k(object):
|
|||
"sprs": sprs, # dontcare; finished anyways
|
||||
"size": dsize,
|
||||
"lmod": dtime,
|
||||
"life": cj.get("life"),
|
||||
"addr": ip,
|
||||
"at": at,
|
||||
"hash": [],
|
||||
|
@ -2072,6 +2071,7 @@ class Up2k(object):
|
|||
"name",
|
||||
"size",
|
||||
"lmod",
|
||||
"life",
|
||||
"poke",
|
||||
]:
|
||||
job[k] = cj[k]
|
||||
|
@ -2293,12 +2293,30 @@ class Up2k(object):
|
|||
pass
|
||||
|
||||
z2 = [job[x] for x in "ptop wark prel name lmod size addr".split()]
|
||||
z2 += [job.get("at") or time.time()]
|
||||
upt = job.get("at") or time.time()
|
||||
wake_sr = False
|
||||
try:
|
||||
flt = job["life"]
|
||||
vfs = self.asrv.vfs.all_vols[job["vtop"]]
|
||||
vlt = vfs.flags["lifetime"]
|
||||
if vlt and flt < vlt:
|
||||
upt -= vlt - flt
|
||||
wake_sr = True
|
||||
t = "using client lifetime; at={:.0f} ({}-{})"
|
||||
self.log(t.format(upt, vlt, flt))
|
||||
except:
|
||||
pass
|
||||
|
||||
z2 += [upt]
|
||||
if self.idx_wark(*z2):
|
||||
del self.registry[ptop][wark]
|
||||
else:
|
||||
self.regdrop(ptop, wark)
|
||||
|
||||
if wake_sr:
|
||||
with self.rescan_cond:
|
||||
self.rescan_cond.notify_all()
|
||||
|
||||
dupes = self.dupesched.pop(dst, [])
|
||||
if not dupes:
|
||||
return
|
||||
|
|
|
@ -2498,13 +2498,34 @@ html.b #u2conf a.b:hover {
|
|||
text-align: center;
|
||||
border: .2em dashed rgba(128, 128, 128, 0.3);
|
||||
}
|
||||
#u2foot {
|
||||
#u2foot,
|
||||
#u2life {
|
||||
color: var(--fg-max);
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
font-size: 1.2em;
|
||||
margin: .8em 0;
|
||||
}
|
||||
#u2life {
|
||||
margin: 2.5em 0;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
#u2life div {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
margin: 0 2em;
|
||||
}
|
||||
#u2life div:first-child {
|
||||
margin-bottom: .2em;
|
||||
}
|
||||
#u2life small {
|
||||
opacity: .6;
|
||||
}
|
||||
#lifew {
|
||||
border-bottom: 1px dotted var(--fg-max);
|
||||
}
|
||||
#u2foot {
|
||||
font-size: 1.2em;
|
||||
font-style: italic;
|
||||
}
|
||||
#u2foot .warn {
|
||||
font-size: 1.2em;
|
||||
padding: .5em .8em;
|
||||
|
@ -2523,6 +2544,10 @@ html.b #u2conf a.b:hover {
|
|||
#u2foot>*+* {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
#u2life input {
|
||||
width: 4em;
|
||||
text-align: right;
|
||||
}
|
||||
.prog {
|
||||
font-family: 'scp', monospace, monospace;
|
||||
}
|
||||
|
|
|
@ -146,8 +146,9 @@
|
|||
have_acode = {{ have_acode|tojson }},
|
||||
have_mv = {{ have_mv|tojson }},
|
||||
have_del = {{ have_del|tojson }},
|
||||
have_unpost = {{ have_unpost|tojson }},
|
||||
have_unpost = {{ have_unpost }},
|
||||
have_zip = {{ have_zip|tojson }},
|
||||
lifetime = {{ lifetime }},
|
||||
turbolvl = {{ turbolvl }},
|
||||
u2sort = "{{ u2sort }}",
|
||||
have_emp = {{ have_emp|tojson }},
|
||||
|
|
|
@ -25,6 +25,12 @@ var Ls = {
|
|||
"hz": "sample rate"
|
||||
},
|
||||
|
||||
"ht_s": "second!s",
|
||||
"ht_m": "minute!s",
|
||||
"ht_h": "hour!s",
|
||||
"ht_d": "day!s",
|
||||
"ht_and": " and ",
|
||||
|
||||
"goh": "control-panel",
|
||||
"logout": "Logout ",
|
||||
"access": " access",
|
||||
|
@ -311,7 +317,8 @@ var Ls = {
|
|||
"u_badf": 'These {0} files (of {1} total) were skipped, possibly due to filesystem permissions:\n\n',
|
||||
"u_blankf": 'These {0} files (of {1} total) are blank / empty; upload them anyways?\n\n',
|
||||
"u_just1": '\nMaybe it works better if you select just one file',
|
||||
"u_ff_many": "This amount of files <em>may</em> cause Firefox to skip some files, or crash.\nPlease try again with fewer files (or use Chrome) if that happens.\n\n",
|
||||
"u_ff_many": "This amount of files <em>may</em> cause Firefox to skip some files, or crash.\nPlease try again with fewer files (or use Chrome) if that happens.",
|
||||
"u_up_life": "This upload will be deleted from the server\n{0} after it completes",
|
||||
"u_asku": 'upload these {0} files to <code>{1}</code>',
|
||||
"u_unpt": "you can undo / delete this upload using the top-left 🧯",
|
||||
"u_etadone": 'Done ({0}, {1} files)',
|
||||
|
@ -338,6 +345,11 @@ var Ls = {
|
|||
"u_expl": "explain",
|
||||
"u_tu": '<p class="warn">WARNING: turbo enabled, <span> client may not detect and resume incomplete uploads; see turbo-button tooltip</span></p>',
|
||||
"u_ts": '<p class="warn">WARNING: turbo enabled, <span> search results can be incorrect; see turbo-button tooltip</span></p>',
|
||||
"u_life_cfg": 'autodelete after <input id="lifem" p="60" /> min (or <input id="lifeh" p="3600" /> hours)',
|
||||
"u_life_est": 'upload will be deleted <span id="lifew" tt="local time">---</span>',
|
||||
"u_life_max": 'this folder enforces a\nmax lifetime of {0}',
|
||||
"u_unp_ok": 'unpost is allowed for {0}',
|
||||
"u_unp_ng": 'unpost will NOT be allowed',
|
||||
"ue_ro": 'your access to this folder is Read-Only\n\n',
|
||||
"ue_nl": 'you are currently not logged in',
|
||||
"ue_la": 'you are currently logged in as "{0}"',
|
||||
|
@ -379,6 +391,12 @@ var Ls = {
|
|||
"hz": "lyd-oppløsning"
|
||||
},
|
||||
|
||||
"ht_s": "sekund!er",
|
||||
"ht_m": "minutt!er",
|
||||
"ht_h": "time!r",
|
||||
"ht_d": "dag!er",
|
||||
"ht_and": " og ",
|
||||
|
||||
"goh": "kontrollpanel",
|
||||
"logout": "Logg ut ",
|
||||
"access": " tilgang",
|
||||
|
@ -665,7 +683,8 @@ var Ls = {
|
|||
"u_badf": 'Disse {0} filene (av totalt {1}) kan ikke leses, kanskje pga rettighetsproblemer i filsystemet på datamaskinen din:\n\n',
|
||||
"u_blankf": 'Disse {0} filene (av totalt {1}) er blanke / uten innhold; ønsker du å laste dem opp uansett?\n\n',
|
||||
"u_just1": '\nFunker kanskje bedre hvis du bare tar én fil om gangen',
|
||||
"u_ff_many": "Det var mange filer! Mulig at Firefox kommer til å krasje, eller\nhoppe over et par av dem. Smart å ha Chrome på lur i tilfelle.\n\n",
|
||||
"u_ff_many": "Det var mange filer! Mulig at Firefox kommer til å krasje, eller\nhoppe over et par av dem. Smart å ha Chrome på lur i tilfelle.",
|
||||
"u_up_life": "Filene slettes fra serveren {0}\netter at opplastningen er fullført",
|
||||
"u_asku": 'Laste opp disse {0} filene til <code>{1}</code>',
|
||||
"u_unpt": "Du kan angre / slette opplastningen med 🧯 oppe til venstre",
|
||||
"u_etadone": 'Ferdig ({0}, {1} filer)',
|
||||
|
@ -692,6 +711,11 @@ var Ls = {
|
|||
"u_expl": "forklar",
|
||||
"u_tu": '<p class="warn">ADVARSEL: turbo er på, <span> avbrutte opplastninger vil muligens ikke oppdages og gjenopptas; hold musepekeren over turbo-knappen for mer info</span></p>',
|
||||
"u_ts": '<p class="warn">ADVARSEL: turbo er på, <span> søkeresultater kan være feil; hold musepekeren over turbo-knappen for mer info</span></p>',
|
||||
"u_life_cfg": 'slett opplastning etter <input id="lifem" p="60" /> min (eller <input id="lifeh" p="3600" /> timer)',
|
||||
"u_life_est": 'opplastningen slettes <span id="lifew" tt="lokal tid">---</span>',
|
||||
"u_life_max": 'denne mappen tillater ikke å \noppbevare filer i mer enn {0}',
|
||||
"u_unp_ok": 'opplastning kan angres i {0}',
|
||||
"u_unp_ng": 'opplastning kan IKKE angres',
|
||||
"ue_ro": 'du har ikke skrivetilgang i denne mappen\n\n',
|
||||
"ue_nl": 'du er ikke logget inn',
|
||||
"ue_la": 'du er logget inn som "{0}"',
|
||||
|
@ -837,6 +861,7 @@ ebi('op_up2k').innerHTML = (
|
|||
'</table><div id="u2mu"></div></div>\n' +
|
||||
|
||||
'<p id="u2flagblock"><b>' + L.ul_flagblk + '</p>\n' +
|
||||
'<div id="u2life"></div>' +
|
||||
'<div id="u2foot"></div>'
|
||||
);
|
||||
|
||||
|
@ -4646,6 +4671,7 @@ var treectl = (function () {
|
|||
|
||||
r.hide();
|
||||
ebi('path').style.display = '';
|
||||
ebi('treeul').innerHTML = '';
|
||||
}
|
||||
|
||||
r.hide = function () {
|
||||
|
@ -4865,10 +4891,18 @@ var treectl = (function () {
|
|||
}
|
||||
}
|
||||
}
|
||||
QS('#treeul>li>a+a').textContent = '[root]';
|
||||
despin('#tree');
|
||||
reload_tree();
|
||||
|
||||
try {
|
||||
QS('#treeul>li>a+a').textContent = '[root]';
|
||||
}
|
||||
catch (ex) {
|
||||
console.log('got no root yet');
|
||||
r.dir_cb = null;
|
||||
return;
|
||||
}
|
||||
|
||||
reload_tree();
|
||||
var fun = r.dir_cb;
|
||||
if (fun) {
|
||||
r.dir_cb = null;
|
||||
|
@ -5042,7 +5076,7 @@ var treectl = (function () {
|
|||
if (this.hpush && !showfile.active())
|
||||
hist_push(this.top);
|
||||
|
||||
if (!this.back && treectl.entreed) {
|
||||
if (!this.back && !treectl.hidden) {
|
||||
var dirs = [];
|
||||
for (var a = 0; a < res.dirs.length; a++)
|
||||
dirs.push(res.dirs[a].href.split('/')[0].split('?')[0]);
|
||||
|
@ -5159,6 +5193,7 @@ var treectl = (function () {
|
|||
if (res.acct) {
|
||||
acct = res.acct;
|
||||
have_up2k_idx = res.idx;
|
||||
lifetime = res.lifetime;
|
||||
apply_perms(res.perms);
|
||||
fileman.render();
|
||||
}
|
||||
|
@ -6348,7 +6383,7 @@ var unpost = (function () {
|
|||
|
||||
for (var a = n; a < n2; a++)
|
||||
if (QS('#op_unpost a.n' + a))
|
||||
req.push(uricom_dec(r.files[a].vp)[0]);
|
||||
req.push(uricom_dec(r.files[a].vp));
|
||||
|
||||
var links = QSA('#op_unpost a.n' + n);
|
||||
for (var a = 0, aa = links.length; a < aa; a++) {
|
||||
|
|
|
@ -1179,8 +1179,11 @@ function up2k_init(subtle) {
|
|||
|
||||
var msg = [];
|
||||
|
||||
if (lifetime)
|
||||
msg.push('<b>' + L.u_up_life.format(lhumantime(st.lifetime || lifetime)) + '</b>\n\n');
|
||||
|
||||
if (FIREFOX && good_files.length > 3000)
|
||||
msg.push(L.u_ff_many);
|
||||
msg.push(L.u_ff_many + "\n\n");
|
||||
|
||||
msg.push(L.u_asku.format(good_files.length, esc(get_vpath())) + '<ul>');
|
||||
for (var a = 0, aa = Math.min(20, good_files.length); a < aa; a++)
|
||||
|
@ -2226,6 +2229,7 @@ function up2k_init(subtle) {
|
|||
"name": t.name,
|
||||
"size": t.size,
|
||||
"lmod": t.lmod,
|
||||
"life": st.lifetime,
|
||||
"hash": t.hash
|
||||
};
|
||||
if (t.srch)
|
||||
|
@ -2459,6 +2463,69 @@ function up2k_init(subtle) {
|
|||
}
|
||||
draw_turbo();
|
||||
|
||||
function draw_life() {
|
||||
var el = ebi('u2life');
|
||||
if (!lifetime) {
|
||||
el.style.display = 'none';
|
||||
el.innerHTML = '';
|
||||
st.lifetime = 0;
|
||||
return;
|
||||
}
|
||||
el.style.display = uc.fsearch ? 'none' : '';
|
||||
el.innerHTML = '<div>' + L.u_life_cfg + '</div><div>' + L.u_life_est + '</div><div id="undor"></div>';
|
||||
set_life(Math.min(lifetime, icfg_get('lifetime', lifetime)));
|
||||
ebi('lifem').oninput = ebi('lifeh').oninput = mod_life;
|
||||
tt.att(ebi('u2life'));
|
||||
}
|
||||
draw_life();
|
||||
|
||||
function mod_life(e) {
|
||||
var el = e.target,
|
||||
pow = parseInt(el.getAttribute('p')),
|
||||
v = parseInt(el.value);
|
||||
|
||||
if (!isNum(v))
|
||||
return;
|
||||
|
||||
if (toast.tag == mod_life)
|
||||
toast.hide();
|
||||
|
||||
v *= pow;
|
||||
if (v > lifetime) {
|
||||
v = lifetime;
|
||||
toast.warn(20, L.u_life_max.format(lhumantime(lifetime)), mod_life);
|
||||
}
|
||||
|
||||
swrite('lifetime', v);
|
||||
set_life(v);
|
||||
}
|
||||
|
||||
function set_life(v) {
|
||||
//ebi('lifes').value = v;
|
||||
ebi('lifem').value = parseInt(v / 60);
|
||||
ebi('lifeh').value = parseInt(v / 3600);
|
||||
|
||||
var undo = have_unpost - (v || lifetime);
|
||||
ebi('undor').innerHTML = undo <= 0 ?
|
||||
L.u_unp_ng : L.u_unp_ok.format(lhumantime(undo));
|
||||
|
||||
st.lifetime = v;
|
||||
rel_life();
|
||||
}
|
||||
|
||||
function rel_life() {
|
||||
if (!lifetime)
|
||||
return;
|
||||
|
||||
try {
|
||||
ebi('lifew').innerHTML = unix2iso((st.lifetime || lifetime) +
|
||||
Date.now() / 1000 - new Date().getTimezoneOffset() * 60
|
||||
).replace(' ', ', ').slice(0, -3);
|
||||
}
|
||||
catch (ex) { }
|
||||
}
|
||||
setInterval(rel_life, 9000);
|
||||
|
||||
function set_potato() {
|
||||
pvis.potato();
|
||||
set_fsearch();
|
||||
|
@ -2509,6 +2576,7 @@ function up2k_init(subtle) {
|
|||
ebi('u2mu').style.display = potato ? '' : 'none';
|
||||
|
||||
draw_turbo();
|
||||
draw_life();
|
||||
onresize();
|
||||
}
|
||||
|
||||
|
|
|
@ -626,7 +626,7 @@ function uricom_sdec(txt) {
|
|||
function uricom_adec(arr, li) {
|
||||
var ret = [];
|
||||
for (var a = 0; a < arr.length; a++) {
|
||||
var txt = uricom_dec(arr[a])[0];
|
||||
var txt = uricom_dec(arr[a]);
|
||||
ret.push(li ? '<li>' + esc(txt) + '</li>' : txt);
|
||||
}
|
||||
|
||||
|
@ -682,7 +682,7 @@ var isNum = function (v) {
|
|||
var n = parseFloat(v);
|
||||
return !isNaN(v - n) && n === v;
|
||||
};
|
||||
if (window.Number)
|
||||
if (window.Number && Number.isFinite)
|
||||
isNum = Number.isFinite;
|
||||
|
||||
|
||||
|
@ -717,7 +717,7 @@ function humantime(v) {
|
|||
}
|
||||
|
||||
|
||||
function shumantime(v) {
|
||||
function shumantime(v, long) {
|
||||
if (v < 10)
|
||||
return f2f(v, 2) + 's';
|
||||
if (v < 60)
|
||||
|
@ -737,11 +737,27 @@ function shumantime(v) {
|
|||
var v1 = parseInt(v / m1),
|
||||
v2 = ('0' + parseInt((v % m1) / m2)).slice(-2);
|
||||
|
||||
return v1 + ch + (v1 >= 10 ? '' : v2);
|
||||
return v1 + ch + (v1 >= 10 || v2 == '00' ? '' : v2 + (
|
||||
long && a < st.length - 1 ? st[a + 1][2] : ''));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function lhumantime(v) {
|
||||
var t = shumantime(v, 1),
|
||||
tp = t.replace(/([a-z])/g, " $1 ").split(/ /g).slice(0, -1);
|
||||
|
||||
if (!window.L || tp.length < 2 || tp[1].indexOf('$') + 1)
|
||||
return t;
|
||||
|
||||
var ret = '';
|
||||
for (var a = 0; a < tp.length; a += 2)
|
||||
ret += tp[a] + ' ' + L['ht_' + tp[a + 1]].replace(tp[a] == 1 ? /!.*/ : /!/, '') + L.ht_and;
|
||||
|
||||
return ret.slice(0, -L.ht_and.length);
|
||||
}
|
||||
|
||||
|
||||
function clamp(v, a, b) {
|
||||
return Math.min(Math.max(v, a), b);
|
||||
}
|
||||
|
@ -812,7 +828,7 @@ function fcfg_get(name, defval) {
|
|||
var o = ebi(name),
|
||||
val = parseFloat(sread(name));
|
||||
|
||||
if (isNaN(val))
|
||||
if (!isNum(val))
|
||||
return parseFloat(o ? o.value : defval);
|
||||
|
||||
if (o)
|
||||
|
@ -1068,7 +1084,7 @@ var tt = (function () {
|
|||
};
|
||||
|
||||
r.hide = function (e) {
|
||||
ev(e);
|
||||
//ev(e); // eats checkbox-label clicks
|
||||
clearTimeout(tev);
|
||||
window.removeEventListener('scroll', r.hide);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
set -e
|
||||
|
||||
curl http://192.168.123.1:3923/cpp/scripts/pyinstaller/build.sh |
|
||||
tee build2.sh | cmp build.sh || {
|
||||
tee build2.sh | cmp build.sh && rm build2.sh || {
|
||||
echo "new build script; upgrade y/n:"
|
||||
while true; do read -u1 -n1 -r r; [[ $r =~ [yYnN] ]] && break; done
|
||||
[[ $r =~ [yY] ]] && mv build{2,}.sh && exec ./build.sh
|
||||
|
|
|
@ -6,11 +6,11 @@ this is the EXE edition of copyparty, compatible with Windows7-SP1
|
|||
and later. To make this possible, the EXE was compiled with Python
|
||||
3.7.9, which is EOL and does not receive security patches anymore.
|
||||
|
||||
it is strongly recommended to use the python sfx instead:
|
||||
if possible, for performance and security reasons, please use this instead:
|
||||
https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py
|
||||
"""
|
||||
|
||||
print(v.replace("\n", "\n░▌ ")[1:] + "\n")
|
||||
print(v.replace("\n", "\n▒▌ ")[1:] + "\n")
|
||||
|
||||
|
||||
import re
|
||||
|
|
Loading…
Reference in a new issue