add audio transcoder

This commit is contained in:
ed 2021-11-02 02:59:37 +01:00
parent 26c8589399
commit f6f9fc5a45
6 changed files with 89 additions and 12 deletions

View file

@ -416,6 +416,9 @@ def run_argparse(argv, formatter):
ap2.add_argument("--th-maxage", metavar="SEC", type=int, default=604800, help="max folder age")
ap2.add_argument("--th-covers", metavar="N,N", type=u, default="folder.png,folder.jpg,cover.png,cover.jpg", help="folder thumbnails to stat for")
ap2 = ap.add_argument_group('transcoding options')
ap2.add_argument("--no-acode", action="store_true", help="disable audio transcoding")
ap2 = ap.add_argument_group('general db options')
ap2.add_argument("-e2d", action="store_true", help="enable up2k database")
ap2.add_argument("-e2ds", action="store_true", help="enable up2k db-scanner, sets -e2d")

View file

@ -2072,6 +2072,7 @@ class HttpCli(object):
"def_hcols": [],
"have_up2k_idx": ("e2d" in vn.flags),
"have_tags_idx": ("e2t" in vn.flags),
"have_acode": (not self.args.no_acode),
"have_mv": (not self.args.no_mv),
"have_del": (not self.args.no_del),
"have_zip": (not self.args.no_zip),

View file

@ -26,8 +26,16 @@ class ThumbCli(object):
if is_vid and self.args.no_vthumb:
return None
want_opus = fmt == "opus"
is_au = ext in FMT_FFA
if is_au and self.args.no_athumb:
if is_au:
if want_opus:
if self.args.no_acode:
return None
else:
if self.args.no_athumb:
return None
elif want_opus:
return None
if rem.startswith(".hist/th/") and rem.split(".")[-1] in ["webp", "jpg"]:

View file

@ -51,7 +51,7 @@ except:
# ffmpeg -formats
FMT_PIL = "bmp dib gif icns ico jpg jpeg jp2 jpx pcx png pbm pgm ppm pnm sgi tga tif tiff webp xbm dds xpm"
FMT_FFV = "av1 asf avi flv m4v mkv mjpeg mjpg mpg mpeg mpg2 mpeg2 h264 avc mts h265 hevc mov 3gp mp4 ts mpegts nut ogv ogm rm vob webm wmv"
FMT_FFA = "aac m4a ogg opus flac alac mp3 mp2 ac3 dts wma wav aif aiff au amr gsm ape tak tta wv"
FMT_FFA = "aac m4a ogg opus flac alac mp3 mp2 ac3 dts wma ra wav aif aiff au amr gsm ape tak tta wv"
if HAVE_HEIF:
FMT_PIL += " heif heifs heic heics"
@ -90,9 +90,10 @@ def thumb_path(histpath, rem, mtime, fmt):
h = hashlib.sha512(fsenc(fn)).digest()
fn = base64.urlsafe_b64encode(h).decode("ascii")[:24]
return "{}/th/{}/{}.{:x}.{}".format(
histpath, rd, fn, int(mtime), "webp" if fmt == "w" else "jpg"
)
if fmt != "opus":
fmt = "webp" if fmt == "w" else "jpg"
return "{}/th/{}/{}.{:x}.{}".format(histpath, rd, fn, int(mtime), fmt)
class ThumbSrv(object):
@ -207,7 +208,10 @@ class ThumbSrv(object):
elif ext in FMT_FFV:
fun = self.conv_ffmpeg
elif ext in FMT_FFA:
fun = self.conv_spec
if tpath.endswith(".opus"):
fun = self.conv_opus
else:
fun = self.conv_spec
if fun:
try:
@ -346,7 +350,6 @@ class ThumbSrv(object):
def conv_spec(self, abspath, tpath):
ret, _ = ffprobe(abspath)
if "ac" not in ret:
raise Exception("not audio")
@ -381,6 +384,30 @@ class ThumbSrv(object):
cmd += [fsenc(tpath)]
self._run_ff(cmd)
def conv_opus(self, abspath, tpath):
if self.args.no_acode:
raise Exception("disabled in server config")
ret, _ = ffprobe(abspath)
if "ac" not in ret:
raise Exception("not audio")
# fmt: off
cmd = [
b"ffmpeg",
b"-nostdin",
b"-v", b"error",
b"-hide_banner",
b"-i", fsenc(abspath),
b"-map", b"0:a:0",
b"-c:a", b"libopus",
b"-b:a", b"128k",
fsenc(tpath)
]
# fmt: on
self._run_ff(cmd)
def poke(self, tdir):
if not self.poke_cd.poke(tdir):
return

View file

@ -130,6 +130,7 @@
def_hcols = {{ def_hcols|tojson }},
have_up2k_idx = {{ have_up2k_idx|tojson }},
have_tags_idx = {{ have_tags_idx|tojson }},
have_acode = {{ have_acode|tojson }},
have_mv = {{ have_mv|tojson }},
have_del = {{ have_del|tojson }},
have_unpost = {{ have_unpost|tojson }},

View file

@ -320,6 +320,14 @@ var mpl = (function () {
'<a href="#" class="tgl btn" tt="load the next folder and continue">📂 next-folder</a>' +
'</div></div>' +
(have_acode ? (
'<div><h3>transcode</h3><div>' +
'<a href="#" id="ac_flac" class="tgl btn" tt="convert flac to opus">flac</a>' +
'<a href="#" id="ac_aac" class="tgl btn" tt="convert aac/m4a to opus">aac</a>' +
'<a href="#" id="ac_oth" class="tgl btn" tt="convert all others (not mp3) to opus">oth</a>' +
'</div></div>'
) : '') +
'<div><h3>tint</h3><div>' +
'<input type="text" id="pb_tint" size="3" value="0" tt="background level (0-100) on the seekbar$Nto make buffering less distracting" />' +
'</div></div>' +
@ -335,6 +343,9 @@ var mpl = (function () {
bcfg_bind(r, 'clip', 'au_npclip', false, function (v) {
clmod(ebi('wtoggle'), 'np', v && mp.au);
});
bcfg_bind(r, 'ac_flac', 'ac_flac', true);
bcfg_bind(r, 'ac_aac', 'ac_aac', false);
bcfg_bind(r, 'ac_oth', 'ac_oth', true, reload_mp);
ebi('au_os_ctl').onclick = function (e) {
ev(e);
@ -373,6 +384,23 @@ var mpl = (function () {
};
set_tint();
r.acode = function (url) {
var c = true;
if (!have_acode)
c = false;
else if (/\.flac$/i.exec(url))
c = r.ac_flac;
else if (/\.(aac|m4a)$/i.exec(url))
c = r.ac_aac;
else if (re_au_native.exec(url))
c = false;
if (!c)
return url;
return url + (url.indexOf('?') < 0 ? '?' : '&') + 'th=opus';
};
r.pp = function () {
if (!r.os_ctl)
return;
@ -441,6 +469,10 @@ var mpl = (function () {
})();
var re_au_native = /\.(opus|ogg|m4a|aac|mp3|wav|flac)$/i,
re_au_all = /\.(aac|m4a|ogg|opus|flac|alac|mp3|mp2|ac3|dts|wma|ra|wav|aif|aiff|au|amr|gsm|ape|tak|tta|wv)$/i;
// extract songs + add play column
function MPlayer() {
var r = this;
@ -454,7 +486,7 @@ function MPlayer() {
r.tracks = {};
r.order = [];
var re_audio = /\.(opus|ogg|m4a|aac|mp3|wav|flac)$/i,
var re_audio = mpl.ac_oth ? re_au_all : re_au_native,
trs = QSA('#files tbody tr');
for (var a = 0, aa = trs.length; a < aa; a++) {
@ -552,6 +584,7 @@ function MPlayer() {
r.preload = function (url) {
var au = null;
url = mpl.acode(url);
if (need_ogv_for(url)) {
au = mp.au_ogvjs2;
if (!au && window['OGVPlayer']) {
@ -1033,7 +1066,7 @@ var mpui = (function () {
if (pos > 0 && pos > len - 10) {
preloaded = mp.au.src;
try {
mp.preload(ebi(mp.order[mp.order.indexOf(mp.au.tid) + 1]).href);
mp.preload(mp.tracks[mp.order[mp.order.indexOf(mp.au.tid) + 1]]);
}
catch (ex) {
console.log("preload failed", ex);
@ -1078,7 +1111,7 @@ catch (ex) { }
function need_ogv_for(url) {
return need_ogv && /\.(ogg|opus)$/i.test(url);
return need_ogv && /\.(ogg|opus)|\?th=opus/i.test(url);
}
@ -1378,7 +1411,7 @@ function play(tid, is_ev, seek, call_depth) {
// ogv.js breaks on .play() unless directly user-triggered
var attempt_play = true;
var url = mp.tracks[tid];
var url = mpl.acode(mp.tracks[tid]);
if (need_ogv_for(url)) {
var m = /.* Version\/([0-9]+)\.[0-9\.]+ Mobile\/[^ ]+ Safari\/[0-9\.]+$/.exec(navigator.userAgent),
safari = m ? parseInt(m[1]) : 99;
@ -1435,7 +1468,7 @@ function play(tid, is_ev, seek, call_depth) {
audio_eq.apply();
url += (url.indexOf('?') < 0 ? '?cache' : '&cache');
url += (url.indexOf('?') < 0 ? '?' : '&') + 'cache';
if (mp.au.src == url)
mp.au.currentTime = 0;
else {
@ -4450,6 +4483,10 @@ function reload_mp() {
}
mpl.stop();
widget.close();
var plays = QSA('tr>td:first-child>a.play');
for (var a = plays.length - 1; a >= 0; a--)
plays[a].parentNode.innerHTML = '-';
mp = new MPlayer();
setTimeout(pbar.onresize, 1);
}