Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ab1acf32c | ||
|
|
8c28266418 | ||
|
|
7f8b8dcb92 | ||
|
|
6dd39811d4 | ||
|
|
35e2138e3e | ||
|
|
239b4e9fe6 | ||
|
|
2fcd0e7e72 | ||
|
|
357347ce3a | ||
|
|
36dc1107fb |
@@ -111,7 +111,7 @@ summary: all planned features work! now please enjoy the bloatening
|
||||
* ☑ FUSE client (read-only)
|
||||
* browser
|
||||
* ☑ tree-view
|
||||
* ☑ audio player
|
||||
* ☑ audio player (with OS media controls)
|
||||
* ☑ thumbnails
|
||||
* ☑ images using Pillow
|
||||
* ☑ videos using FFmpeg
|
||||
|
||||
@@ -48,15 +48,16 @@ you could replace winfsp with [dokan](https://github.com/dokan-dev/dokany/releas
|
||||
|
||||
|
||||
# [`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`:
|
||||
|
||||
```
|
||||
~/bin/dbtool.py -ls up2k.db
|
||||
~/bin/dbtool.py -src up2k.db.v0.10.22 up2k.db -cmp
|
||||
~/bin/dbtool.py -src up2k.db.v0.10.22 up2k.db -rm-mtp-flag -copy key
|
||||
~/bin/dbtool.py -src up2k.db.v0.10.22 up2k.db -rm-mtp-flag -copy .bpm -vac
|
||||
cd /mnt/nas/music/.hist
|
||||
~/src/copyparty/bin/dbtool.py -ls up2k.db
|
||||
~/src/copyparty/bin/dbtool.py -src up2k.*.v3 up2k.db -cmp
|
||||
~/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
|
||||
```
|
||||
|
||||
@@ -410,7 +410,7 @@ def main(argv=None):
|
||||
+ " (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
|
||||
|
||||
# signal.signal(signal.SIGINT, sighandler)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# coding: utf-8
|
||||
|
||||
VERSION = (0, 11, 20)
|
||||
VERSION = (0, 11, 22)
|
||||
CODENAME = "the grid"
|
||||
BUILD_DT = (2021, 6, 20)
|
||||
BUILD_DT = (2021, 6, 21)
|
||||
|
||||
S_VERSION = ".".join(map(str, VERSION))
|
||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||
|
||||
@@ -1765,28 +1765,44 @@ class HttpCli(object):
|
||||
fn = f["name"]
|
||||
rd = f["rd"]
|
||||
del f["rd"]
|
||||
if icur:
|
||||
if vn != dbv:
|
||||
_, rd = vn.get_dbv(rd)
|
||||
if not icur:
|
||||
break
|
||||
|
||||
if vn != dbv:
|
||||
_, rd = vn.get_dbv(rd)
|
||||
|
||||
q = "select w from up where rd = ? and fn = ?"
|
||||
r = None
|
||||
try:
|
||||
r = icur.execute(q, (rd, fn)).fetchone()
|
||||
except Exception as ex:
|
||||
if "database is locked" in str(ex):
|
||||
break
|
||||
|
||||
q = "select w from up where rd = ? and fn = ?"
|
||||
try:
|
||||
r = icur.execute(q, (rd, fn)).fetchone()
|
||||
except:
|
||||
args = s3enc(idx.mem_cur, rd, fn)
|
||||
r = icur.execute(q, args).fetchone()
|
||||
except:
|
||||
m = "tag list error, {}/{}\n{}"
|
||||
self.log(m.format(rd, fn, min_ex()))
|
||||
break
|
||||
|
||||
tags = {}
|
||||
f["tags"] = tags
|
||||
tags = {}
|
||||
f["tags"] = tags
|
||||
|
||||
if not r:
|
||||
continue
|
||||
if not r:
|
||||
continue
|
||||
|
||||
w = r[0][:16]
|
||||
q = "select k, v from mt where w = ? and k != 'x'"
|
||||
w = r[0][:16]
|
||||
q = "select k, v from mt where w = ? and k != 'x'"
|
||||
try:
|
||||
for k, v in icur.execute(q, (w,)):
|
||||
taglist[k] = True
|
||||
tags[k] = v
|
||||
except:
|
||||
m = "tag read error, {}/{} [{}]:\n{}"
|
||||
self.log(m.format(rd, fn, w, min_ex()))
|
||||
break
|
||||
|
||||
if icur:
|
||||
taglist = [k for k in vn.flags.get("mte", "").split(",") if k in taglist]
|
||||
|
||||
@@ -26,7 +26,7 @@ class U2idx(object):
|
||||
self.timeout = self.args.srch_time
|
||||
|
||||
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
|
||||
|
||||
self.cur = {}
|
||||
@@ -57,6 +57,9 @@ class U2idx(object):
|
||||
raise Pebkac(500, min_ex())
|
||||
|
||||
def get_cur(self, ptop):
|
||||
if not HAVE_SQLITE3:
|
||||
return None
|
||||
|
||||
cur = self.cur.get(ptop)
|
||||
if cur:
|
||||
return cur
|
||||
@@ -66,7 +69,7 @@ class U2idx(object):
|
||||
if not os.path.exists(db_path):
|
||||
return None
|
||||
|
||||
cur = sqlite3.connect(db_path).cursor()
|
||||
cur = sqlite3.connect(db_path, 2).cursor()
|
||||
self.cur[ptop] = cur
|
||||
return cur
|
||||
|
||||
|
||||
@@ -811,10 +811,12 @@ input.eq_gain {
|
||||
padding: 0;
|
||||
border-bottom: 1px solid #555;
|
||||
}
|
||||
#thumbs {
|
||||
#thumbs,
|
||||
#au_osd_cv {
|
||||
opacity: .3;
|
||||
}
|
||||
#griden.on+#thumbs {
|
||||
#griden.on+#thumbs,
|
||||
#au_os_ctl.on+#au_osd_cv {
|
||||
opacity: 1;
|
||||
}
|
||||
#ghead {
|
||||
|
||||
@@ -222,10 +222,14 @@ var have_webp = null;
|
||||
|
||||
|
||||
var mpl = (function () {
|
||||
var have_mctl = 'mediaSession' in navigator && window.MediaMetadata;
|
||||
|
||||
ebi('op_player').innerHTML = (
|
||||
'<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_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><h3>playback mode</h3><div id="pb_mode">' +
|
||||
@@ -238,7 +242,9 @@ var mpl = (function () {
|
||||
var r = {
|
||||
"pb_mode": sread('pb_mode') || 'loop-folder',
|
||||
"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) {
|
||||
@@ -254,6 +260,20 @@ var mpl = (function () {
|
||||
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() {
|
||||
var btns = QSA('#pb_mode>a');
|
||||
for (var a = 0, aa = btns.length; a < aa; a++) {
|
||||
@@ -270,6 +290,50 @@ var mpl = (function () {
|
||||
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';
|
||||
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;
|
||||
})();
|
||||
|
||||
@@ -365,6 +429,28 @@ var mp = new MPlayer();
|
||||
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
|
||||
var widget = (function () {
|
||||
var ret = {},
|
||||
@@ -411,22 +497,16 @@ var widget = (function () {
|
||||
};
|
||||
npirc.onclick = nptxt.onclick = function (e) {
|
||||
ev(e);
|
||||
var th = ebi('files').tHead.rows[0].cells,
|
||||
tr = QS('#files tr.play').cells,
|
||||
irc = this.getAttribute('id') == 'npirc',
|
||||
var irc = this.getAttribute('id') == 'npirc',
|
||||
ck = irc ? '06' : '',
|
||||
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++) {
|
||||
if (th[a].className.indexOf('min') !== -1)
|
||||
continue;
|
||||
|
||||
var tv = tr[a].textContent,
|
||||
tk = a == 1 ? '' : th[a].getAttribute('name').split('/').slice(-1)[0];
|
||||
|
||||
m += tk + '(' + cv + tv + ck + ') // ';
|
||||
}
|
||||
for (var a = 0; a < npk.length; a++)
|
||||
m += (npk[a] == 'file' ? '' : npk[a]) + '(' + cv + np[npk[a]] + ck + ') // ';
|
||||
|
||||
m += '[' + cv + s2ms(mp.au.currentTime) + ck + '/' + cv + s2ms(mp.au.duration) + ck + ']';
|
||||
|
||||
@@ -635,6 +715,11 @@ function seek_au_mul(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) {
|
||||
if (!mp.au)
|
||||
return;
|
||||
@@ -685,6 +770,9 @@ function playpause(e) {
|
||||
}
|
||||
else
|
||||
play(0);
|
||||
|
||||
if (navigator.mediaSession)
|
||||
navigator.mediaSession.playbackState = mp.au.paused ? "paused" : "playing";
|
||||
};
|
||||
|
||||
|
||||
@@ -1124,6 +1212,7 @@ function play(tid, seek, call_depth) {
|
||||
|
||||
mpui.progress_updater();
|
||||
pbar.drawbuf();
|
||||
mpl.announce();
|
||||
return true;
|
||||
}
|
||||
catch (ex) {
|
||||
@@ -1206,6 +1295,8 @@ function autoplay_blocked(seek) {
|
||||
seek_au_sec(seek);
|
||||
else
|
||||
mpui.progress_updater();
|
||||
|
||||
mpl.announce();
|
||||
};
|
||||
na.onclick = unblocked;
|
||||
}
|
||||
@@ -1515,18 +1606,18 @@ document.onkeydown = function (e) {
|
||||
pos = parseInt(k.slice(-1)) * 0.1;
|
||||
|
||||
if (pos !== -1)
|
||||
return seek_au_mul(pos);
|
||||
return seek_au_mul(pos) || true;
|
||||
|
||||
var n = k == 'KeyJ' ? -1 : k == 'KeyL' ? 1 : 0;
|
||||
if (n !== 0)
|
||||
return song_skip(n);
|
||||
return song_skip(n) || true;
|
||||
|
||||
if (k == 'KeyP')
|
||||
return playpause();
|
||||
return playpause() || true;
|
||||
|
||||
n = k == 'KeyU' ? -10 : k == 'KeyO' ? 10 : 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;
|
||||
if (n !== 0)
|
||||
@@ -1536,7 +1627,6 @@ document.onkeydown = function (e) {
|
||||
return tree_up();
|
||||
|
||||
if (k == 'KeyB')
|
||||
//return treectl.hidden ? treectl.show() : treectl.hide();
|
||||
return treectl.hidden ? treectl.entree() : treectl.detree();
|
||||
|
||||
if (k == 'KeyG')
|
||||
|
||||
@@ -157,7 +157,7 @@ dbg.asyncStore.pendingBreakpoints = {}
|
||||
about:config >> devtools.debugger.prefs-schema-version = -1
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
##
|
||||
|
||||
Reference in New Issue
Block a user