From 54570a3b27ff401538ff5ee75aadbc7119e7fb6c Mon Sep 17 00:00:00 2001 From: advplyr Date: Tue, 9 Nov 2021 20:30:44 -0600 Subject: [PATCH] Add: More menu on book card #173, Fix: Download worker ffmpeg logs --- client/components/cards/BookCard.vue | 158 ++++++++++++++++++++- client/components/modals/EditModal.vue | 2 +- client/components/ui/Dropdown.vue | 9 +- client/components/ui/LibrariesDropdown.vue | 9 +- client/components/widgets/MoreMenu.vue | 50 +++++++ client/package-lock.json | 7 +- client/package.json | 3 +- client/plugins/init.client.js | 68 ++++----- server/DownloadManager.js | 4 + server/utils/downloadWorker.js | 39 +++-- 10 files changed, 295 insertions(+), 54 deletions(-) create mode 100644 client/components/widgets/MoreMenu.vue diff --git a/client/components/cards/BookCard.vue b/client/components/cards/BookCard.vue index 94d36c74..80b56616 100644 --- a/client/components/cards/BookCard.vue +++ b/client/components/cards/BookCard.vue @@ -1,5 +1,5 @@ \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index 0e427328..11863aae 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf-client", - "version": "1.4.10", + "version": "1.6.13", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -13298,6 +13298,11 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, + "v-click-outside": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/v-click-outside/-/v-click-outside-3.1.2.tgz", + "integrity": "sha512-gMdRqfRE6m6XU6SiFi3dyBlFB2MWogiXpof8Aa3LQysrl9pzTndqp/iEaAphLoadaQUFnQ0ec6fLLaxr7LiY6A==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/client/package.json b/client/package.json index 77d13795..a7afa519 100644 --- a/client/package.json +++ b/client/package.json @@ -22,6 +22,7 @@ "libarchive.js": "^1.3.0", "nuxt": "^2.15.7", "nuxt-socket-io": "^1.1.18", + "v-click-outside": "^3.1.2", "vue-pdf": "^4.3.0", "vue-toastification": "^1.7.11", "vuedraggable": "^2.24.3" @@ -30,4 +31,4 @@ "@nuxtjs/tailwindcss": "^4.2.1", "postcss": "^8.3.6" } -} \ No newline at end of file +} diff --git a/client/plugins/init.client.js b/client/plugins/init.client.js index 47481afa..d3932e1e 100644 --- a/client/plugins/init.client.js +++ b/client/plugins/init.client.js @@ -1,6 +1,9 @@ import Vue from 'vue' +import vClickOutside from 'v-click-outside' import { formatDistance, format } from 'date-fns' +Vue.directive('click-outside', vClickOutside.directive) + Vue.prototype.$eventBus = new Vue() Vue.prototype.$isDev = process.env.NODE_ENV !== 'production' @@ -73,42 +76,41 @@ Vue.prototype.$calculateTextSize = (text, styles = {}) => { } } -function isClickedOutsideEl(clickEvent, elToCheckOutside, ignoreSelectors = [], ignoreElems = []) { - const isDOMElement = (element) => { - return element instanceof Element || element instanceof HTMLDocument - } +// function isClickedOutsideEl(clickEvent, elToCheckOutside, ignoreSelectors = [], ignoreElems = []) { +// const isDOMElement = (element) => { +// return element instanceof Element || element instanceof HTMLDocument +// } - const clickedEl = clickEvent.srcElement - const didClickOnIgnoredEl = ignoreElems.filter((el) => el).some((element) => element.contains(clickedEl) || element.isEqualNode(clickedEl)) - const didClickOnIgnoredSelector = ignoreSelectors.length ? ignoreSelectors.map((selector) => clickedEl.closest(selector)).reduce((curr, accumulator) => curr && accumulator, true) : false +// const clickedEl = clickEvent.srcElement +// const didClickOnIgnoredEl = ignoreElems.filter((el) => el).some((element) => element.contains(clickedEl) || element.isEqualNode(clickedEl)) +// const didClickOnIgnoredSelector = ignoreSelectors.length ? ignoreSelectors.map((selector) => clickedEl.closest(selector)).reduce((curr, accumulator) => curr && accumulator, true) : false - if (isDOMElement(elToCheckOutside) && !elToCheckOutside.contains(clickedEl) && !didClickOnIgnoredEl && !didClickOnIgnoredSelector) { - return true - } +// if (isDOMElement(elToCheckOutside) && !elToCheckOutside.contains(clickedEl) && !didClickOnIgnoredEl && !didClickOnIgnoredSelector) { +// return true +// } +// return false +// } - return false -} - -Vue.directive('click-outside', { - bind: function (el, binding, vnode) { - let vm = vnode.context; - let callback = binding.value; - if (typeof callback !== 'function') { - console.error('Invalid callback', binding) - return - } - el['__click_outside__'] = (ev) => { - if (isClickedOutsideEl(ev, el)) { - callback.call(vm, ev) - } - } - document.addEventListener('click', el['__click_outside__'], false) - }, - unbind: function (el, binding, vnode) { - document.removeEventListener('click', el['__click_outside__'], false) - delete el['__click_outside__'] - } -}) +// Vue.directive('click-outside', { +// bind: function (el, binding, vnode) { +// let vm = vnode.context; +// let callback = binding.value; +// if (typeof callback !== 'function') { +// console.error('Invalid callback', binding) +// return +// } +// el['__click_outside__'] = (ev) => { +// if (isClickedOutsideEl(ev, el)) { +// callback.call(vm, ev) +// } +// } +// document.addEventListener('click', el['__click_outside__'], false) +// }, +// unbind: function (el, binding, vnode) { +// document.removeEventListener('click', el['__click_outside__'], false) +// delete el['__click_outside__'] +// } +// }) Vue.prototype.$sanitizeFilename = (input, replacement = '') => { if (typeof input !== 'string') { diff --git a/server/DownloadManager.js b/server/DownloadManager.js index 9f925976..4ab790bb 100644 --- a/server/DownloadManager.js +++ b/server/DownloadManager.js @@ -285,6 +285,10 @@ class DownloadManager { if (!download.isTimedOut) { this.sendResult(download, message) } + } else if (message.type === 'FFMPEG') { + if (Logger[message.level]) { + Logger[message.level](message.log) + } } } else { Logger.error('Invalid worker message', message) diff --git a/server/utils/downloadWorker.js b/server/utils/downloadWorker.js index f59a26a4..531d52c3 100644 --- a/server/utils/downloadWorker.js +++ b/server/utils/downloadWorker.js @@ -5,10 +5,12 @@ if (process.env.FFMPEG_PATH) { } const { parentPort, workerData } = require("worker_threads") -const Logger = require('../Logger') - -Logger.info('[DownloadWorker] Starting Worker...') +parentPort.postMessage({ + type: 'FFMPEG', + level: 'debug', + log: '[DownloadWorker] Starting Worker...' +}) const ffmpegCommand = Ffmpeg() const startTime = Date.now() @@ -27,25 +29,45 @@ var isKilled = false async function runFfmpeg() { var success = await new Promise((resolve) => { ffmpegCommand.on('start', (command) => { - Logger.info('[DownloadWorker] FFMPEG concat started with command: ' + command) + parentPort.postMessage({ + type: 'FFMPEG', + level: 'info', + log: '[DownloadWorker] FFMPEG concat started with command: ' + command + }) }) ffmpegCommand.on('stderr', (stdErrline) => { - Logger.info(stdErrline) + parentPort.postMessage({ + type: 'FFMPEG', + level: 'error', + log: '[DownloadWorker] Ffmpeg Stderr: ' + stdErrline + }) }) ffmpegCommand.on('error', (err, stdout, stderr) => { if (err.message && err.message.includes('SIGKILL')) { // This is an intentional SIGKILL - Logger.info('[DownloadWorker] User Killed singleAudio') + parentPort.postMessage({ + type: 'FFMPEG', + level: 'info', + log: '[DownloadWorker] User Killed worker' + }) } else { - Logger.error('[DownloadWorker] Ffmpeg Err', err.message) + parentPort.postMessage({ + type: 'FFMPEG', + level: 'error', + log: '[DownloadWorker] Ffmpeg Err: ' + err.message + }) } resolve(false) }) ffmpegCommand.on('end', (stdout, stderr) => { - Logger.info('[DownloadWorker] singleAudio ended') + parentPort.postMessage({ + type: 'FFMPEG', + level: 'info', + log: '[DownloadWorker] worker ended' + }) resolve(true) }) ffmpegCommand.run() @@ -62,7 +84,6 @@ async function runFfmpeg() { parentPort.on('message', (message) => { if (message === 'STOP') { - Logger.info('[DownloadWorker] Requested a hard stop') isKilled = true ffmpegCommand.kill() }