Compare commits

...

18 Commits

Author SHA1 Message Date
ed
fb81de3b36 v0.11.24 2021-06-22 17:28:09 +02:00
ed
aa4f352301 prefer audio tags in audio files 2021-06-22 17:21:24 +02:00
ed
f1a1c2ea45 recover from opening a corrupt database 2021-06-22 17:19:56 +02:00
ed
6249bd4163 add pebkac hints 2021-06-22 17:18:34 +02:00
ed
2579dc64ce update notes 2021-06-21 22:49:28 +00:00
ed
356512270a file extensions dont contain whitespace 2021-06-21 23:50:35 +02:00
ed
bed27f2b43 mention fix for the OSD popup on windows 2021-06-21 23:43:07 +02:00
ed
54013d861b v0.11.23 2021-06-21 21:15:56 +02:00
ed
ec100210dc support showing album-cover on windows lockscreen 2021-06-21 19:15:22 +00:00
ed
3ab1acf32c v0.11.22 2021-06-21 20:30:29 +02:00
ed
8c28266418 subscribe to media-keys globally as a media player 2021-06-21 20:26:11 +02:00
ed
7f8b8dcb92 scandir is not withable before py3.6 2021-06-21 20:23:35 +02:00
ed
6dd39811d4 disable u2idx if sqlite3 is unavailable 2021-06-21 20:22:54 +02:00
ed
35e2138e3e doc: macos support 2021-06-21 18:42:15 +02:00
ed
239b4e9fe6 v0.11.21 2021-06-20 21:25:18 +02:00
ed
2fcd0e7e72 abandon listing tags in browser when db busy 2021-06-20 21:19:47 +02:00
ed
357347ce3a lower timeout on db reads 2021-06-20 21:03:35 +02:00
ed
36dc1107fb update dbtool desc 2021-06-20 20:05:43 +02:00
13 changed files with 209 additions and 59 deletions

View File

@@ -111,7 +111,7 @@ summary: all planned features work! now please enjoy the bloatening
* ☑ FUSE client (read-only) * ☑ FUSE client (read-only)
* browser * browser
* ☑ tree-view * ☑ tree-view
* ☑ audio player * ☑ audio player (with OS media controls)
* ☑ thumbnails * ☑ thumbnails
* ☑ images using Pillow * ☑ images using Pillow
* ☑ videos using FFmpeg * ☑ videos using FFmpeg
@@ -168,15 +168,16 @@ summary: all planned features work! now please enjoy the bloatening
## hotkeys ## hotkeys
the browser has the following hotkeys the browser has the following hotkeys
* `B` toggle breadcrumbs / directory tree
* `I/K` prev/next folder * `I/K` prev/next folder
* `P` parent folder * `M` parent folder
* `G` toggle list / grid view * `G` toggle list / grid view
* `T` toggle thumbnails / icons * `T` toggle thumbnails / icons
* when playing audio: * when playing audio:
* `0..9` jump to 10%..90% * `0..9` jump to 10%..90%
* `U/O` skip 10sec back/forward * `U/O` skip 10sec back/forward
* `J/L` prev/next song * `J/L` prev/next song
* `M` play/pause (also starts playing the folder) * `P` play/pause (also starts playing the folder)
* in the grid view: * in the grid view:
* `S` toggle multiselect * `S` toggle multiselect
* `A/D` zoom * `A/D` zoom
@@ -184,9 +185,9 @@ the browser has the following hotkeys
## tree-mode ## tree-mode
by default there's a breadcrumbs path; you can replace this with a tree-browser sidebar thing by clicking the 🌲 by default there's a breadcrumbs path; you can replace this with a tree-browser sidebar thing by clicking the `🌲` or pressing the `B` hotkey
click `[-]` and `[+]` to adjust the size, and the `[a]` toggles if the tree should widen dynamically as you go deeper or stay fixed-size click `[-]` and `[+]` (or hotkeys `A`/`D`) to adjust the size, and the `[a]` toggles if the tree should widen dynamically as you go deeper or stay fixed-size
## thumbnails ## thumbnails
@@ -280,6 +281,8 @@ up2k has saved a few uploads from becoming corrupted in-transfer already; caught
* you can link a particular timestamp in an audio file by adding it to the URL, such as `&20` / `&20s` / `&1m20` / `&t=1:20` after the `.../#af-c8960dab` * you can link a particular timestamp in an audio file by adding it to the URL, such as `&20` / `&20s` / `&1m20` / `&t=1:20` after the `.../#af-c8960dab`
* if you are using media hotkeys to switch songs and are getting tired of seeing the OSD popup which Windows doesn't let you disable, consider https://ocv.me/dev/?media-osd-bgone.ps1
# searching # searching

View File

@@ -48,15 +48,16 @@ you could replace winfsp with [dokan](https://github.com/dokan-dev/dokany/releas
# [`dbtool.py`](dbtool.py) # [`dbtool.py`](dbtool.py)
upgrade utility which can show db info and help transfer data between databases, for example when a new version of copyparty recommends to wipe the DB and reindex because it now collects additional metadata during analysis, but you have some really expensive `-mtp` parsers and want to copy over the tags from the old db upgrade utility which can show db info and help transfer data between databases, for example when a new version of copyparty is incompatible with the old DB and automatically rebuilds the DB from scratch, but you have some really expensive `-mtp` parsers and want to copy over the tags from the old db
for that example (upgrading to v0.11.0), first move the old db aside, launch copyparty, let it rebuild the db until the point where it starts running mtp (colored messages as it adds the mtp tags), then CTRL-C and patch in the old mtp tags from the old db instead for that example (upgrading to v0.11.20), first launch the new version of copyparty like usual, let it make a backup of the old db and rebuild the new db until the point where it starts running mtp (colored messages as it adds the mtp tags), that's when you hit CTRL-C and patch in the old mtp tags from the old db instead
so assuming you have `-mtp` parsers to provide the tags `key` and `.bpm`: so assuming you have `-mtp` parsers to provide the tags `key` and `.bpm`:
``` ```
~/bin/dbtool.py -ls up2k.db cd /mnt/nas/music/.hist
~/bin/dbtool.py -src up2k.db.v0.10.22 up2k.db -cmp ~/src/copyparty/bin/dbtool.py -ls up2k.db
~/bin/dbtool.py -src up2k.db.v0.10.22 up2k.db -rm-mtp-flag -copy key ~/src/copyparty/bin/dbtool.py -src up2k.*.v3 up2k.db -cmp
~/bin/dbtool.py -src up2k.db.v0.10.22 up2k.db -rm-mtp-flag -copy .bpm -vac ~/src/copyparty/bin/dbtool.py -src up2k.*.v3 up2k.db -rm-mtp-flag -copy key
~/src/copyparty/bin/dbtool.py -src up2k.*.v3 up2k.db -rm-mtp-flag -copy .bpm -vac
``` ```

View File

@@ -410,7 +410,7 @@ def main(argv=None):
+ " (if you crash with codec errors then that is why)" + " (if you crash with codec errors then that is why)"
) )
if WINDOWS and sys.version_info < (3, 6): if sys.version_info < (3, 6):
al.no_scandir = True al.no_scandir = True
# signal.signal(signal.SIGINT, sighandler) # signal.signal(signal.SIGINT, sighandler)

View File

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

@@ -10,7 +10,6 @@ import json
import string import string
import socket import socket
import ctypes import ctypes
import traceback
from datetime import datetime from datetime import datetime
import calendar import calendar
@@ -50,6 +49,7 @@ class HttpCli(object):
self.tls = hasattr(self.s, "cipher") self.tls = hasattr(self.s, "cipher")
self.bufsz = 1024 * 32 self.bufsz = 1024 * 32
self.hint = None
self.absolute_urls = False self.absolute_urls = False
self.out_headers = {"Access-Control-Allow-Origin": "*"} self.out_headers = {"Access-Control-Allow-Origin": "*"}
@@ -72,6 +72,7 @@ class HttpCli(object):
"""returns true if connection can be reused""" """returns true if connection can be reused"""
self.keepalive = False self.keepalive = False
self.headers = {} self.headers = {}
self.hint = None
try: try:
headerlines = read_header(self.sr) headerlines = read_header(self.sr)
if not headerlines: if not headerlines:
@@ -130,6 +131,9 @@ class HttpCli(object):
if v is not None: if v is not None:
self.log("[H] {}: \033[33m[{}]".format(k, v), 6) self.log("[H] {}: \033[33m[{}]".format(k, v), 6)
if "&" in self.req and "?" not in self.req:
self.hint = "did you mean '?' instead of '&'"
# split req into vpath + uparam # split req into vpath + uparam
uparam = {} uparam = {}
if "?" not in self.req: if "?" not in self.req:
@@ -199,6 +203,9 @@ class HttpCli(object):
self.log("{}\033[0m, {}".format(str(ex), self.vpath), 3) self.log("{}\033[0m, {}".format(str(ex), self.vpath), 3)
msg = "<pre>{}\r\nURL: {}\r\n".format(str(ex), self.vpath) msg = "<pre>{}\r\nURL: {}\r\n".format(str(ex), self.vpath)
if self.hint:
msg += "hint: {}\r\n".format(self.hint)
self.reply(msg.encode("utf-8", "replace"), status=ex.code) self.reply(msg.encode("utf-8", "replace"), status=ex.code)
return self.keepalive return self.keepalive
except Pebkac: except Pebkac:
@@ -1765,16 +1772,27 @@ class HttpCli(object):
fn = f["name"] fn = f["name"]
rd = f["rd"] rd = f["rd"]
del f["rd"] del f["rd"]
if icur: if not icur:
break
if vn != dbv: if vn != dbv:
_, rd = vn.get_dbv(rd) _, rd = vn.get_dbv(rd)
q = "select w from up where rd = ? and fn = ?" q = "select w from up where rd = ? and fn = ?"
r = None
try: try:
r = icur.execute(q, (rd, fn)).fetchone() r = icur.execute(q, (rd, fn)).fetchone()
except: except Exception as ex:
if "database is locked" in str(ex):
break
try:
args = s3enc(idx.mem_cur, rd, fn) args = s3enc(idx.mem_cur, rd, fn)
r = icur.execute(q, args).fetchone() r = icur.execute(q, args).fetchone()
except:
m = "tag list error, {}/{}\n{}"
self.log(m.format(rd, fn, min_ex()))
break
tags = {} tags = {}
f["tags"] = tags f["tags"] = tags
@@ -1784,9 +1802,14 @@ class HttpCli(object):
w = r[0][:16] w = r[0][:16]
q = "select k, v from mt where w = ? and k != 'x'" q = "select k, v from mt where w = ? and k != 'x'"
try:
for k, v in icur.execute(q, (w,)): for k, v in icur.execute(q, (w,)):
taglist[k] = True taglist[k] = True
tags[k] = v tags[k] = v
except:
m = "tag read error, {}/{} [{}]:\n{}"
self.log(m.format(rd, fn, w, min_ex()))
break
if icur: if icur:
taglist = [k for k in vn.flags.get("mte", "").split(",") if k in taglist] taglist = [k for k in vn.flags.get("mte", "").split(",") if k in taglist]

View File

@@ -3,7 +3,6 @@ from __future__ import print_function, unicode_literals
import re import re
import os import os
import sys
import time import time
import socket import socket

View File

@@ -115,6 +115,19 @@ def parse_ffprobe(txt):
ret = {} # processed ret = {} # processed
md = {} # raw tags md = {} # raw tags
is_audio = fmt.get("format_name") in ["mp3", "ogg", "flac", "wav"]
if fmt.get("filename", "").split(".")[-1].lower() in ["m4a", "aac"]:
is_audio = True
# if audio file, ensure audio stream appears first
if (
is_audio
and len(streams) > 2
and streams[1].get("codec_type") != "audio"
and streams[2].get("codec_type") == "audio"
):
streams = [fmt, streams[2], streams[1]] + streams[3:]
have = {} have = {}
for strm in streams: for strm in streams:
typ = strm.get("codec_type") typ = strm.get("codec_type")
@@ -134,9 +147,7 @@ def parse_ffprobe(txt):
] ]
if typ == "video": if typ == "video":
if strm.get("DISPOSITION:attached_pic") == "1" or fmt.get( if strm.get("DISPOSITION:attached_pic") == "1" or is_audio:
"format_name"
) in ["mp3", "ogg", "flac"]:
continue continue
kvm = [ kvm = [
@@ -180,7 +191,7 @@ def parse_ffprobe(txt):
k = k[4:].strip() k = k[4:].strip()
v = v.strip() v = v.strip()
if k and v: if k and v and k not in md:
md[k] = [v] md[k] = [v]
for k in [".q", ".vq", ".aq"]: for k in [".q", ".vq", ".aq"]:

View File

@@ -26,7 +26,7 @@ class U2idx(object):
self.timeout = self.args.srch_time self.timeout = self.args.srch_time
if not HAVE_SQLITE3: if not HAVE_SQLITE3:
self.log("could not load sqlite3; searchign wqill be disabled") self.log("your python does not have sqlite3; searching will be disabled")
return return
self.cur = {} self.cur = {}
@@ -57,6 +57,9 @@ class U2idx(object):
raise Pebkac(500, min_ex()) raise Pebkac(500, min_ex())
def get_cur(self, ptop): def get_cur(self, ptop):
if not HAVE_SQLITE3:
return None
cur = self.cur.get(ptop) cur = self.cur.get(ptop)
if cur: if cur:
return cur return cur
@@ -66,7 +69,7 @@ class U2idx(object):
if not os.path.exists(db_path): if not os.path.exists(db_path):
return None return None
cur = sqlite3.connect(db_path).cursor() cur = sqlite3.connect(db_path, 2).cursor()
self.cur[ptop] = cur self.cur[ptop] = cur
return cur return cur

View File

@@ -653,7 +653,7 @@ class Up2k(object):
try: try:
parser = MParser(parser) parser = MParser(parser)
except: except:
self.log("invalid argument: " + parser, 1) self.log("invalid argument (could not find program): " + parser, 1)
return return
for tag in entags: for tag in entags:
@@ -901,7 +901,7 @@ class Up2k(object):
except: except:
self.log("WARN: could not list files; DB corrupt?\n" + min_ex()) self.log("WARN: could not list files; DB corrupt?\n" + min_ex())
elif ver > DB_VER: if (ver or 0) > DB_VER:
m = "database is version {}, this copyparty only supports versions <= {}" m = "database is version {}, this copyparty only supports versions <= {}"
raise Exception(m.format(ver, DB_VER)) raise Exception(m.format(ver, DB_VER))

View File

@@ -811,10 +811,12 @@ input.eq_gain {
padding: 0; padding: 0;
border-bottom: 1px solid #555; border-bottom: 1px solid #555;
} }
#thumbs { #thumbs,
#au_osd_cv {
opacity: .3; opacity: .3;
} }
#griden.on+#thumbs { #griden.on+#thumbs,
#au_os_ctl.on+#au_osd_cv {
opacity: 1; opacity: 1;
} }
#ghead { #ghead {

View File

@@ -222,10 +222,14 @@ var have_webp = null;
var mpl = (function () { var mpl = (function () {
var have_mctl = 'mediaSession' in navigator && window.MediaMetadata;
ebi('op_player').innerHTML = ( ebi('op_player').innerHTML = (
'<div><h3>switches</h3><div>' + '<div><h3>switches</h3><div>' +
'<a href="#" class="tgl btn" id="au_preload" tt="start loading the next song near the end for gapless playback">preload</a>' + '<a href="#" class="tgl btn" id="au_preload" tt="start loading the next song near the end for gapless playback">preload</a>' +
'<a href="#" class="tgl btn" id="au_npclip" tt="show buttons for clipboarding the currently playing song">/np clip</a>' + '<a href="#" class="tgl btn" id="au_npclip" tt="show buttons for clipboarding the currently playing song">/np clip</a>' +
'<a href="#" class="tgl btn" id="au_os_ctl" tt="os integration (media hotkeys / osd)">os-ctl</a>' +
'<a href="#" class="tgl btn" id="au_osd_cv" tt="show album cover in osd">osd-cv</a>' +
'</div></div>' + '</div></div>' +
'<div><h3>playback mode</h3><div id="pb_mode">' + '<div><h3>playback mode</h3><div id="pb_mode">' +
@@ -238,7 +242,9 @@ var mpl = (function () {
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', false) && have_mctl,
"osd_cv": bcfg_get('au_osd_cv', true),
}; };
ebi('au_preload').onclick = function (e) { ebi('au_preload').onclick = function (e) {
@@ -254,6 +260,20 @@ var mpl = (function () {
clmod(ebi('wtoggle'), 'np', r.clip && mp.au); clmod(ebi('wtoggle'), 'np', r.clip && mp.au);
}; };
ebi('au_os_ctl').onclick = function (e) {
ev(e);
r.os_ctl = !r.os_ctl && have_mctl;
bcfg_set('au_os_ctl', r.os_ctl);
if (!have_mctl)
alert('need firefox 82+ or chrome 73+');
};
ebi('au_osd_cv').onclick = function (e) {
ev(e);
r.osd_cv = !r.osd_cv;
bcfg_set('au_osd_cv', r.osd_cv);
};
function draw_pb_mode() { function draw_pb_mode() {
var btns = QSA('#pb_mode>a'); var btns = QSA('#pb_mode>a');
for (var a = 0, aa = btns.length; a < aa; a++) { for (var a = 0, aa = btns.length; a < aa; a++) {
@@ -270,6 +290,55 @@ var mpl = (function () {
draw_pb_mode(); draw_pb_mode();
} }
r.announce = function () {
if (!r.os_ctl)
return;
var np = get_np()[0],
fns = np.file.split(' - '),
artist = (np.circle ? np.circle + ' // ' : '') + (np.artist || (fns.length > 1 ? fns[0] : '')),
tags = {
title: np.title || fns.slice(-1)[0]
};
if (artist)
tags.artist = artist;
if (np.album)
tags.album = np.album;
if (r.osd_cv) {
var files = QSA("#files tr>td:nth-child(2)>a[id]"),
cover = null;
for (var a = 0, aa = files.length; a < aa; a++) {
if (/^(cover|folder)\.(jpe?g|png|gif)$/.test(files[a].textContent)) {
cover = files[a].getAttribute('href');
break;
}
}
if (cover) {
cover += (cover.indexOf('?') === -1 ? '?' : '&') + 'th=j';
var pwd = get_pwd();
if (pwd)
cover += '&pw=' + uricom_enc(pwd);
tags.artwork = [{ "src": cover, type: "image/jpeg" }];
}
}
navigator.mediaSession.metadata = new MediaMetadata(tags);
navigator.mediaSession.playbackState = mp.au.paused ? "paused" : "playing";
navigator.mediaSession.setActionHandler('play', playpause);
navigator.mediaSession.setActionHandler('pause', playpause);
navigator.mediaSession.setActionHandler('seekbackward', function () { seek_au_rel(-10); });
navigator.mediaSession.setActionHandler('seekforward', function () { seek_au_rel(10); });
navigator.mediaSession.setActionHandler('previoustrack', prev_song);
navigator.mediaSession.setActionHandler('nexttrack', next_song);
};
return r; return r;
})(); })();
@@ -365,6 +434,28 @@ var mp = new MPlayer();
makeSortable(ebi('files'), mp.read_order.bind(mp)); makeSortable(ebi('files'), mp.read_order.bind(mp));
function get_np() {
var th = ebi('files').tHead.rows[0].cells,
tr = QS('#files tr.play').cells,
rv = [],
ra = [],
rt = {};
for (var a = 1, aa = th.length; a < aa; a++) {
var tv = tr[a].textContent,
tk = a == 1 ? 'file' : th[a].getAttribute('name').split('/').slice(-1)[0],
vis = th[a].className.indexOf('min') === -1;
if (!tv)
continue;
(vis ? rv : ra).push(tk);
rt[tk] = tv;
}
return [rt, rv, ra];
};
// toggle player widget // toggle player widget
var widget = (function () { var widget = (function () {
var ret = {}, var ret = {},
@@ -411,22 +502,16 @@ var widget = (function () {
}; };
npirc.onclick = nptxt.onclick = function (e) { npirc.onclick = nptxt.onclick = function (e) {
ev(e); ev(e);
var th = ebi('files').tHead.rows[0].cells, var irc = this.getAttribute('id') == 'npirc',
tr = QS('#files tr.play').cells,
irc = this.getAttribute('id') == 'npirc',
ck = irc ? '06' : '', ck = irc ? '06' : '',
cv = irc ? '07' : '', cv = irc ? '07' : '',
m = ck + 'np: '; m = ck + 'np: ',
npr = get_np(),
npk = npr[1],
np = npr[0];
for (var a = 1, aa = th.length; a < aa; a++) { for (var a = 0; a < npk.length; a++)
if (th[a].className.indexOf('min') !== -1) m += (npk[a] == 'file' ? '' : npk[a]) + '(' + cv + np[npk[a]] + ck + ') // ';
continue;
var tv = tr[a].textContent,
tk = a == 1 ? '' : th[a].getAttribute('name').split('/').slice(-1)[0];
m += tk + '(' + cv + tv + ck + ') // ';
}
m += '[' + cv + s2ms(mp.au.currentTime) + ck + '/' + cv + s2ms(mp.au.duration) + ck + ']'; m += '[' + cv + s2ms(mp.au.currentTime) + ck + '/' + cv + s2ms(mp.au.duration) + ck + ']';
@@ -635,6 +720,11 @@ function seek_au_mul(mul) {
seek_au_sec(mp.au.duration * mul); seek_au_sec(mp.au.duration * mul);
} }
function seek_au_rel(sec) {
if (mp.au)
seek_au_sec(mp.au.currentTime + sec);
}
function seek_au_sec(seek) { function seek_au_sec(seek) {
if (!mp.au) if (!mp.au)
return; return;
@@ -685,6 +775,9 @@ function playpause(e) {
} }
else else
play(0); play(0);
if (navigator.mediaSession)
navigator.mediaSession.playbackState = mp.au.paused ? "paused" : "playing";
}; };
@@ -1124,6 +1217,7 @@ function play(tid, seek, call_depth) {
mpui.progress_updater(); mpui.progress_updater();
pbar.drawbuf(); pbar.drawbuf();
mpl.announce();
return true; return true;
} }
catch (ex) { catch (ex) {
@@ -1206,6 +1300,8 @@ function autoplay_blocked(seek) {
seek_au_sec(seek); seek_au_sec(seek);
else else
mpui.progress_updater(); mpui.progress_updater();
mpl.announce();
}; };
na.onclick = unblocked; na.onclick = unblocked;
} }
@@ -1515,18 +1611,18 @@ document.onkeydown = function (e) {
pos = parseInt(k.slice(-1)) * 0.1; pos = parseInt(k.slice(-1)) * 0.1;
if (pos !== -1) if (pos !== -1)
return seek_au_mul(pos); return seek_au_mul(pos) || true;
var n = k == 'KeyJ' ? -1 : k == 'KeyL' ? 1 : 0; var n = k == 'KeyJ' ? -1 : k == 'KeyL' ? 1 : 0;
if (n !== 0) if (n !== 0)
return song_skip(n); return song_skip(n) || true;
if (k == 'KeyP') if (k == 'KeyP')
return playpause(); return playpause() || true;
n = k == 'KeyU' ? -10 : k == 'KeyO' ? 10 : 0; n = k == 'KeyU' ? -10 : k == 'KeyO' ? 10 : 0;
if (n !== 0) if (n !== 0)
return mp.au ? seek_au_sec(mp.au.currentTime + n) : true; return seek_au_rel(n) || true;
n = k == 'KeyI' ? -1 : k == 'KeyK' ? 1 : 0; n = k == 'KeyI' ? -1 : k == 'KeyK' ? 1 : 0;
if (n !== 0) if (n !== 0)
@@ -1536,7 +1632,6 @@ document.onkeydown = function (e) {
return tree_up(); return tree_up();
if (k == 'KeyB') if (k == 'KeyB')
//return treectl.hidden ? treectl.show() : treectl.hide();
return treectl.hidden ? treectl.entree() : treectl.detree(); return treectl.hidden ? treectl.entree() : treectl.detree();
if (k == 'KeyG') if (k == 'KeyG')

View File

@@ -359,6 +359,15 @@ function get_vpath() {
} }
function get_pwd() {
var pwd = ('; ' + document.cookie).split('; cppwd=');
if (pwd.length < 2)
return null;
return pwd[1].split(';')[0];
}
function unix2iso(ts) { function unix2iso(ts) {
return new Date(ts * 1000).toISOString().replace("T", " ").slice(0, -5); return new Date(ts * 1000).toISOString().replace("T", " ").slice(0, -5);
} }

View File

@@ -103,6 +103,9 @@ cat warks | while IFS= read -r x; do sqlite3 up2k.db "delete from mt where w = '
# dump all dbs # dump all dbs
find -iname up2k.db | while IFS= read -r x; do sqlite3 "$x" 'select substr(w,1,12), rd, fn from up' | sed -r 's/\|/ \| /g' | while IFS= read -r y; do printf '%s | %s\n' "$x" "$y"; done; done find -iname up2k.db | while IFS= read -r x; do sqlite3 "$x" 'select substr(w,1,12), rd, fn from up' | sed -r 's/\|/ \| /g' | while IFS= read -r y; do printf '%s | %s\n' "$x" "$y"; done; done
# unschedule mtp scan for all files somewhere under "enc/"
sqlite3 -readonly up2k.db 'select substr(up.w,1,16) from up inner join mt on mt.w = substr(up.w,1,16) where rd like "enc/%" and +mt.k = "t:mtp"' > keys; awk '{printf "delete from mt where w = \"%s\" and +k = \"t:mtp\";\n", $0}' <keys | tee /dev/stderr | sqlite3 up2k.db
## ##
## media ## media
@@ -157,7 +160,7 @@ dbg.asyncStore.pendingBreakpoints = {}
about:config >> devtools.debugger.prefs-schema-version = -1 about:config >> devtools.debugger.prefs-schema-version = -1
# determine server version # determine server version
git reset --hard origin/HEAD && git log --format=format:"%H %ai %d" --decorate=full > /dev/shm/revs && cat /dev/shm/revs | while read -r rev extra; do (git reset --hard $rev >/dev/null 2>/dev/null && dsz=$(cat copyparty/web/{util,browser,up2k}.js 2>/dev/null | diff -wNarU0 - <(cat /mnt/Users/ed/Downloads/ref/{util,browser,up2k}.js) | wc -c) && printf '%s %6s %s\n' "$rev" $dsz "$extra") </dev/null; done git pull; git reset --hard origin/HEAD && git log --format=format:"%H %ai %d" --decorate=full > ../revs && cat ../{util,browser}.js >../vr && cat ../revs | while read -r rev extra; do (git reset --hard $rev >/dev/null 2>/dev/null && dsz=$(cat copyparty/web/{util,browser}.js >../vg 2>/dev/null && diff -wNarU0 ../{vg,vr} | wc -c) && printf '%s %6s %s\n' "$rev" $dsz "$extra") </dev/null; done
## ##
@@ -200,3 +203,4 @@ mk() { rm -rf /tmp/foo; sudo -u ed bash -c 'mkdir /tmp/foo; echo hi > /tmp/foo/b
mk && t0="$(date)" && while true; do date -s "$(date '+ 1 hour')"; systemd-tmpfiles --clean; ls -1 /tmp | grep foo || break; done; echo "$t0" mk && t0="$(date)" && while true; do date -s "$(date '+ 1 hour')"; systemd-tmpfiles --clean; ls -1 /tmp | grep foo || break; done; echo "$t0"
mk && sudo -u ed flock /tmp/foo sleep 40 & sleep 1; ps aux | grep -E 'sleep 40$' && t0="$(date)" && for n in {1..40}; do date -s "$(date '+ 1 day')"; systemd-tmpfiles --clean; ls -1 /tmp | grep foo || break; done; echo "$t0" mk && sudo -u ed flock /tmp/foo sleep 40 & sleep 1; ps aux | grep -E 'sleep 40$' && t0="$(date)" && for n in {1..40}; do date -s "$(date '+ 1 day')"; systemd-tmpfiles --clean; ls -1 /tmp | grep foo || break; done; echo "$t0"
mk && t0="$(date)" && for n in {1..40}; do date -s "$(date '+ 1 day')"; systemd-tmpfiles --clean; ls -1 /tmp | grep foo || break; tar -cf/dev/null /tmp/foo; done; echo "$t0" mk && t0="$(date)" && for n in {1..40}; do date -s "$(date '+ 1 day')"; systemd-tmpfiles --clean; ls -1 /tmp | grep foo || break; tar -cf/dev/null /tmp/foo; done; echo "$t0"