Add:Tasks widget in appbar for merging m4bs & remove old m4b merge routes

This commit is contained in:
advplyr 2022-10-02 14:16:17 -05:00
parent 441b8c5bb7
commit 39979ff8a3
16 changed files with 285 additions and 457 deletions

View file

@ -7,7 +7,7 @@
</nuxt-link>
<nuxt-link to="/">
<h1 class="text-2xl font-book mr-6 hidden lg:block hover:underline">audiobookshelf</h1>
<h1 class="text-2xl font-book mr-6 hidden lg:block hover:underline">audiobookshelf <span v-if="showExperimentalFeatures" class="material-icons text-lg text-warning pr-1">logo_dev</span></h1>
</nuxt-link>
<ui-libraries-dropdown class="mr-2" />
@ -15,7 +15,7 @@
<controls-global-search v-if="currentLibrary" class="mr-1 sm:mr-0" />
<div class="flex-grow" />
<span v-if="showExperimentalFeatures" class="material-icons text-2xl md:text-4xl text-warning pr-0 sm:pr-2 md:pr-4">logo_dev</span>
<widgets-notification-widget class="hidden md:block" />
<ui-tooltip v-if="isChromecastInitialized && !isHttps" direction="bottom" text="Casting requires a secure connection" class="flex items-center">
<span class="material-icons-outlined text-warning text-opacity-50"> cast </span>

View file

@ -42,7 +42,7 @@
</div>
<div class="flex-grow" />
<div>
<ui-btn :to="`/audiobook/${libraryItemId}/manage`" class="flex items-center"
<ui-btn :to="`/audiobook/${libraryItemId}/manage?tool=embed`" class="flex items-center"
>Open Manager
<span class="material-icons text-lg ml-2">launch</span>
</ui-btn>

View file

@ -0,0 +1,25 @@
<template>
<div v-if="tasksRunning" class="w-4 h-4 mx-3 relative">
<div class="flex h-full items-center justify-center">
<widgets-loading-spinner />
</div>
</div>
</template>
<script>
export default {
data() {
return {}
},
computed: {
tasks() {
return this.$store.state.tasks.tasks
},
tasksRunning() {
return this.tasks.some((t) => !t.isFinished)
}
},
methods: {},
mounted() {}
}
</script>

View file

@ -270,6 +270,14 @@ export default {
this.$store.commit('scanners/addUpdate', data)
},
taskStarted(task) {
console.log('Task started', task)
this.$store.commit('tasks/addUpdateTask', task)
},
taskFinished(task) {
console.log('Task finished', task)
this.$store.commit('tasks/addUpdateTask', task)
},
userUpdated(user) {
if (this.$store.state.user.user.id === user.id) {
this.$store.commit('user/setUser', user)
@ -302,53 +310,6 @@ export default {
}
this.$store.commit('user/removeCollection', collection)
},
abmergeStarted(download) {
download.status = this.$constants.DownloadStatus.PENDING
download.toastId = this.$toast(`Preparing download "${download.filename}"`, { timeout: false, draggable: false, closeOnClick: false })
this.$store.commit('downloads/addUpdateDownload', download)
},
abmergeReady(download) {
download.status = this.$constants.DownloadStatus.READY
var existingDownload = this.$store.getters['downloads/getDownload'](download.id)
if (existingDownload && existingDownload.toastId !== undefined) {
download.toastId = existingDownload.toastId
this.$toast.update(existingDownload.toastId, { content: `Download "${download.filename}" is ready!`, options: { timeout: 5000, type: 'success' } }, true)
} else {
this.$toast.success(`Download "${download.filename}" is ready!`)
}
this.$store.commit('downloads/addUpdateDownload', download)
},
abmergeFailed(download) {
download.status = this.$constants.DownloadStatus.FAILED
var existingDownload = this.$store.getters['downloads/getDownload'](download.id)
var failedMsg = download.isTimedOut ? 'timed out' : 'failed'
if (existingDownload && existingDownload.toastId !== undefined) {
download.toastId = existingDownload.toastId
this.$toast.update(existingDownload.toastId, { content: `Download "${download.filename}" ${failedMsg}`, options: { timeout: 5000, type: 'error' } }, true)
} else {
console.warn('Download failed no existing download', existingDownload)
this.$toast.error(`Download "${download.filename}" ${failedMsg}`)
}
this.$store.commit('downloads/addUpdateDownload', download)
},
abmergeKilled(download) {
var existingDownload = this.$store.getters['downloads/getDownload'](download.id)
if (existingDownload && existingDownload.toastId !== undefined) {
download.toastId = existingDownload.toastId
this.$toast.update(existingDownload.toastId, { content: `Download "${download.filename}" was terminated`, options: { timeout: 5000, type: 'error' } }, true)
} else {
console.warn('Download killed no existing download found', existingDownload)
this.$toast.error(`Download "${download.filename}" was terminated`)
}
this.$store.commit('downloads/removeDownload', download)
},
abmergeExpired(download) {
download.status = this.$constants.DownloadStatus.EXPIRED
this.$store.commit('downloads/addUpdateDownload', download)
},
rssFeedOpen(data) {
this.$store.commit('feeds/addFeed', data)
},
@ -362,7 +323,7 @@ export default {
batchQuickMatchComplete(result) {
var success = result.success || false
var toast = 'Batch quick match complete!\n' + result.updates + ' Updated'
if (result.unmatched && (result.unmatched > 0)) {
if (result.unmatched && result.unmatched > 0) {
toast += '\n' + result.unmatched + ' with no matches'
}
if (success) {
@ -430,19 +391,16 @@ export default {
this.socket.on('scan_complete', this.scanComplete)
this.socket.on('scan_progress', this.scanProgress)
// Download Listeners
this.socket.on('abmerge_started', this.abmergeStarted)
this.socket.on('abmerge_ready', this.abmergeReady)
this.socket.on('abmerge_failed', this.abmergeFailed)
this.socket.on('abmerge_killed', this.abmergeKilled)
this.socket.on('abmerge_expired', this.abmergeExpired)
// Task Listeners
this.socket.on('task_started', this.taskStarted)
this.socket.on('task_finished', this.taskFinished)
// Feed Listeners
this.socket.on('rss_feed_open', this.rssFeedOpen)
this.socket.on('rss_feed_closed', this.rssFeedClosed)
this.socket.on('backup_applied', this.backupApplied)
this.socket.on('batch_quickmatch_complete', this.batchQuickMatchComplete)
},
showUpdateToast(versionData) {
@ -548,6 +506,19 @@ export default {
// Queue auto play
var playerQueueAutoPlay = localStorage.getItem('playerQueueAutoPlay')
this.$store.commit('setPlayerQueueAutoPlay', playerQueueAutoPlay !== '0')
},
loadTasks() {
this.$axios
.$get('/api/tasks')
.then((payload) => {
console.log('Fetched tasks', payload)
if (payload.tasks) {
this.$store.commit('tasks/setTasks', payload.tasks)
}
})
.catch((error) => {
console.error('Failed to load tasks', error)
})
}
},
beforeMount() {
@ -565,6 +536,8 @@ export default {
this.checkVersionUpdate()
this.loadTasks()
if (this.$route.query.error) {
this.$toast.error(this.$route.query.error)
this.$router.replace(this.$route.path)

View file

@ -45,7 +45,7 @@
<div class="w-full max-h-72 overflow-auto">
<p v-if="!metadataChapters.length" class="py-5 text-center text-gray-200">No chapters</p>
<template v-for="(chapter, index) in metadataChapters">
<div :key="index" class="flex py-1 px-4 text-sm" :class="index % 2 === 0 ? 'bg-primary bg-opacity-25' : ''">
<div :key="index" class="flex py-1 px-4 text-sm" :class="index % 2 === 1 ? 'bg-primary bg-opacity-25' : ''">
<div class="flex-grow font-semibold">{{ chapter.title }}</div>
<div class="w-24">
{{ $secondsToTimestamp(chapter.start) }}
@ -67,7 +67,8 @@
<p v-else class="text-success text-lg font-semibold">Embed Finished!</p>
</div>
<div v-else class="w-full flex justify-end items-center mb-4">
<ui-btn v-if="!isFinished" color="primary" :loading="processing" @click.stop="encodeM4bClick">Start M4B Encode</ui-btn>
<ui-btn v-if="!isTaskFinished" color="primary" :loading="processing" @click.stop="encodeM4bClick">Start M4B Encode</ui-btn>
<p v-else-if="taskFailed" class="text-error text-lg font-semibold">M4B Failed! {{ taskError }}</p>
<p v-else class="text-success text-lg font-semibold">M4B Finished!</p>
</div>
@ -160,14 +161,23 @@ export default {
},
data() {
return {
processing: false,
audiofilesEncoding: {},
audiofilesFinished: {},
processing: false,
isFinished: false,
toneObject: null,
selectedTool: 'embed'
}
},
watch: {
task: {
handler(newVal) {
if (newVal) {
this.taskUpdated(newVal)
}
}
}
},
computed: {
libraryItemId() {
return this.libraryItem.id
@ -202,6 +212,21 @@ export default {
{ value: 'm4b', text: 'M4B Encoder' }
]
}
},
taskFailed() {
return this.isTaskFinished && this.task.isFailed
},
taskError() {
return this.taskFailed ? this.task.error || 'Unknown Error' : null
},
isTaskFinished() {
return this.task && this.task.isFinished
},
task() {
return this.$store.getters['tasks/getTaskByLibraryItemId'](this.libraryItemId)
},
taskRunning() {
return this.task && !this.task.isFinished
}
},
methods: {
@ -210,12 +235,12 @@ export default {
this.$axios
.$get(`/api/audiobook-merge/${this.libraryItemId}`)
.then(() => {
this.processing = false
console.log('Ab m4b merge started')
})
.catch((error) => {
var errorMsg = error.response ? error.response.data || 'Unknown Error' : 'Unknown Error'
this.$toast.error(errorMsg)
this.processing = false
this.processing = true
})
},
embedClick() {
@ -246,7 +271,6 @@ export default {
console.log('audio metadata started', data)
if (data.libraryItemId !== this.libraryItemId) return
this.audiofilesFinished = {}
this.processing = true
},
audioMetadataFinished(data) {
console.log('audio metadata finished', data)
@ -278,6 +302,8 @@ export default {
this.selectedToolUpdated()
}
}
if (this.task) this.taskUpdated(this.task)
},
fetchToneObject() {
this.$axios
@ -289,6 +315,9 @@ export default {
.catch((error) => {
console.error('Failed to fetch tone object', error)
})
},
taskUpdated(task) {
this.processing = !task.isFinished
}
},
mounted() {

View file

@ -1,34 +0,0 @@
export const state = () => ({
downloads: []
})
export const getters = {
getDownloads: (state) => (libraryItemId) => {
return state.downloads.filter(d => d.libraryItemId === libraryItemId)
},
getDownload: (state) => (id) => {
return state.downloads.find(d => d.id === id)
}
}
export const actions = {
}
export const mutations = {
setDownloads(state, downloads) {
state.downloads = downloads
},
addUpdateDownload(state, download) {
var index = state.downloads.findIndex(d => d.id === download.id)
if (index >= 0) {
state.downloads.splice(index, 1, download)
} else {
state.downloads.push(download)
}
},
removeDownload(state, download) {
state.downloads = state.downloads.filter(d => d.id !== download.id)
}
}

31
client/store/tasks.js Normal file
View file

@ -0,0 +1,31 @@
export const state = () => ({
tasks: []
})
export const getters = {
getTaskByLibraryItemId: (state) => (libraryItemId) => {
return state.tasks.find(t => t.data && t.data.libraryItemId === libraryItemId)
}
}
export const actions = {
}
export const mutations = {
setTasks(state, tasks) {
state.tasks = tasks
},
addUpdateTask(state, task) {
var index = state.tasks.findIndex(d => d.id === task.id)
if (index >= 0) {
state.tasks.splice(index, 1, task)
} else {
state.tasks.push(task)
}
},
removeTask(state, task) {
state.tasks = state.tasks.filter(d => d.id !== task.id)
}
}