general-purpose file parsing

This commit is contained in:
ed 2021-05-16 07:04:18 +02:00
parent c0e6df4b63
commit 0fcfe79994
4 changed files with 52 additions and 16 deletions

View file

@ -299,6 +299,10 @@ copyparty can invoke external programs to collect additional metadata for files
* `-mtp key=f,t5,~/bin/audio-key.py` uses `~/bin/audio-key.py` to get the `key` tag, replacing any existing metadata tag (`f,`), aborting if it takes longer than 5sec (`t5,`) * `-mtp key=f,t5,~/bin/audio-key.py` uses `~/bin/audio-key.py` to get the `key` tag, replacing any existing metadata tag (`f,`), aborting if it takes longer than 5sec (`t5,`)
* `-v ~/music::r:cmtp=.bpm=~/bin/audio-bpm.py:cmtp=key=f,t5,~/bin/audio-key.py` both as a per-volume config wow this is getting ugly * `-v ~/music::r:cmtp=.bpm=~/bin/audio-bpm.py:cmtp=key=f,t5,~/bin/audio-key.py` both as a per-volume config wow this is getting ugly
*but wait, there's more!* `-mtp` can be used for non-audio files as well using the `a` flag: `ay` only do audio files, `an` audio files are skipped, or `ad` always do it (d as in dontcare)
* `-mtp ext=an,~/bin/file-ext.py` runs `~/bin/file-ext.py` to get the `ext` tag only if file is not audio (`an`)
## complete examples ## complete examples

9
bin/mtag/file-ext.py Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env python
import sys
"""
example that just prints the file extension
"""
print(sys.argv[1].split(".")[-1])

View file

@ -674,7 +674,7 @@ class HttpCli(object):
self.log("failed to utime ({}, {})".format(path, times)) self.log("failed to utime ({}, {})".format(path, times))
spd = self._spd(post_sz) spd = self._spd(post_sz)
self.log("{} thank".format(spd)) self.log("binpost {} thank".format(spd))
self.reply(b"thank") self.reply(b"thank")
return True return True

View file

@ -511,6 +511,7 @@ class Up2k(object):
def _run_all_mtp(self): def _run_all_mtp(self):
t0 = time.time() t0 = time.time()
self.mtp_audio = {}
self.mtp_force = {} self.mtp_force = {}
self.mtp_parsers = {} self.mtp_parsers = {}
for ptop, flags in self.flags.items(): for ptop, flags in self.flags.items():
@ -527,8 +528,9 @@ class Up2k(object):
entags = self.entags[ptop] entags = self.entags[ptop]
force = {} audio = {} # [r]equire [n]ot [d]ontcare
timeout = {} force = {} # bool
timeout = {} # int
parsers = {} parsers = {}
for parser in self.flags[ptop]["mtp"]: for parser in self.flags[ptop]["mtp"]:
orig = parser orig = parser
@ -536,6 +538,8 @@ class Up2k(object):
if tag not in entags: if tag not in entags:
continue continue
audio[tag] = "y"
while True: while True:
try: try:
bp = os.path.expanduser(parser) bp = os.path.expanduser(parser)
@ -549,6 +553,10 @@ class Up2k(object):
arg, parser = parser.split(",", 1) arg, parser = parser.split(",", 1)
arg = arg.lower() arg = arg.lower()
if arg.startswith("a"):
audio[tag] = arg[1:]
continue
if arg == "f": if arg == "f":
force[tag] = True force[tag] = True
continue continue
@ -563,6 +571,8 @@ class Up2k(object):
self.log("invalid argument: " + orig, 1) self.log("invalid argument: " + orig, 1)
return return
# todo audio/force => parser attributes
self.mtp_audio[ptop] = audio
self.mtp_force[ptop] = force self.mtp_force[ptop] = force
self.mtp_parsers[ptop] = parsers self.mtp_parsers[ptop] = parsers
@ -596,8 +606,8 @@ class Up2k(object):
have = cur.execute(q, (w,)).fetchall() have = cur.execute(q, (w,)).fetchall()
have = [x[0] for x in have] have = [x[0] for x in have]
if ".dur" not in have and ".dur" in entags: parsers = self._get_parsers(ptop, have)
# skip non-audio if not parsers:
to_delete[w] = True to_delete[w] = True
n_left -= 1 n_left -= 1
continue continue
@ -605,10 +615,7 @@ class Up2k(object):
if w in in_progress: if w in in_progress:
continue continue
task_parsers = { jobs.append([parsers, None, w, abspath])
k: v for k, v in parsers.items() if k in force or k not in have
}
jobs.append([task_parsers, None, w, abspath])
in_progress[w] = True in_progress[w] = True
done = self._flush_mpool(wcur) done = self._flush_mpool(wcur)
@ -667,6 +674,26 @@ class Up2k(object):
wcur.close() wcur.close()
cur.close() cur.close()
def _get_parsers(self, ptop, have):
audio = self.mtp_audio[ptop]
force = self.mtp_force[ptop]
entags = self.entags[ptop]
parsers = {}
for k, v in self.mtp_parsers[ptop].items():
if ".dur" in entags:
if ".dur" in have:
# is audio, require non-audio?
if audio[k] == "n":
continue
# is not audio, require audio?
elif audio[k] == "y":
continue
parsers[k] = v
parsers = {k: v for k, v in parsers.items() if k in force or k not in have}
return parsers
def _start_mpool(self): def _start_mpool(self):
# mp.pool.ThreadPool and concurrent.futures.ThreadPoolExecutor # mp.pool.ThreadPool and concurrent.futures.ThreadPoolExecutor
# both do crazy runahead so lets reinvent another wheel # both do crazy runahead so lets reinvent another wheel
@ -1308,13 +1335,9 @@ class Up2k(object):
abspath = os.path.join(ptop, rd, fn) abspath = os.path.join(ptop, rd, fn)
tags = self.mtag.get(abspath) tags = self.mtag.get(abspath)
ntags1 = len(tags) ntags1 = len(tags)
if self.mtp_parsers.get(ptop, {}): parsers = self._get_parsers(ptop, tags)
parser = { if parsers:
k: v tags.update(self.mtag.get_bin(parsers, abspath))
for k, v in self.mtp_parsers[ptop].items()
if k in self.mtp_force[ptop] or k not in tags
}
tags.update(self.mtag.get_bin(parser, abspath))
with self.mutex: with self.mutex:
cur = self.cur[ptop] cur = self.cur[ptop]