mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-13 17:38:40 +01:00
Add: Sortable collection books, play from collections, collection book table row updates #151
This commit is contained in:
parent
0980b6d5d5
commit
5e5792c0f8
@ -155,13 +155,13 @@ export default {
|
||||
methods: {
|
||||
audioPlayed() {
|
||||
if (!this.$refs.audio) return
|
||||
console.log('Audio Played', this.$refs.audio.paused, this.$refs.audio.currentTime)
|
||||
// console.log('Audio Played', this.$refs.audio.paused, this.$refs.audio.currentTime)
|
||||
this.playStartTime = Date.now()
|
||||
this.isPaused = this.$refs.audio.paused
|
||||
},
|
||||
audioPaused() {
|
||||
if (!this.$refs.audio) return
|
||||
console.log('Audio Paused', this.$refs.audio.paused, this.$refs.audio.currentTime)
|
||||
// console.log('Audio Paused', this.$refs.audio.paused, this.$refs.audio.currentTime)
|
||||
this.isPaused = this.$refs.audio.paused
|
||||
},
|
||||
audioError(err) {
|
||||
|
@ -128,6 +128,7 @@ export default {
|
||||
methods: {
|
||||
async back() {
|
||||
var popped = await this.$store.dispatch('popRoute')
|
||||
if (popped) this.$store.commit('setIsRoutingBack', true)
|
||||
var backTo = popped || '/'
|
||||
this.$router.push(backTo)
|
||||
},
|
||||
|
@ -298,12 +298,12 @@ export default {
|
||||
this.$nextTick(this.setBookshelfEntities)
|
||||
},
|
||||
audiobooksUpdated() {
|
||||
console.log('[AudioBookshelf] Audiobooks Updated')
|
||||
console.log('[Bookshelf] Audiobooks Updated')
|
||||
this.setBookshelfEntities()
|
||||
},
|
||||
collectionsUpdated() {
|
||||
if (!this.isCollections) return
|
||||
console.log('[AudioBookshelf] Collections Updated')
|
||||
console.log('[Bookshelf] Collections Updated')
|
||||
this.setBookshelfEntities()
|
||||
},
|
||||
buildSearchParams() {
|
||||
|
@ -31,10 +31,10 @@
|
||||
<div class="bookshelfDividerCategorized h-6 w-full absolute bottom-0 left-0 right-0 z-20"></div>
|
||||
|
||||
<div v-show="canScrollLeft && !isScrolling" class="absolute top-0 left-0 w-32 pr-8 bg-black book-shelf-arrow-left flex items-center justify-center cursor-pointer opacity-0 hover:opacity-100 z-30" @click="scrollLeft">
|
||||
<span class="material-icons text-8xl text-white">chevron_left</span>
|
||||
<span class="material-icons text-6xl text-white">chevron_left</span>
|
||||
</div>
|
||||
<div v-show="canScrollRight && !isScrolling" class="absolute top-0 right-0 w-32 pl-8 bg-black book-shelf-arrow-right flex items-center justify-center cursor-pointer opacity-0 hover:opacity-100 z-30" @click="scrollRight">
|
||||
<span class="material-icons text-8xl text-white">chevron_right</span>
|
||||
<span class="material-icons text-6xl text-white">chevron_right</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -9,10 +9,10 @@
|
||||
<p class="font-book" :style="{ fontSize: sizeMultiplier + 'rem' }">{{ groupName }}</p>
|
||||
</div>
|
||||
|
||||
<div class="absolute top-2 right-2 w-7 h-7 rounded-lg bg-black bg-opacity-90 text-gray-300 box-shadow-book flex items-center justify-center border border-white border-opacity-25 pointer-events-none z-10">
|
||||
<div class="absolute top-2 right-2 w-7 h-7 rounded-lg bg-black bg-opacity-90 text-gray-300 box-shadow-book flex items-center justify-center border border-white border-opacity-25 pointer-events-none z-40">
|
||||
<p class="font-book text-xl">{{ bookItems.length }}</p>
|
||||
</div>
|
||||
<div class="absolute bottom-0 left-0 w-full h-1 flex flex-nowrap">
|
||||
<div class="absolute bottom-0 left-0 w-full h-1 flex flex-nowrap z-40">
|
||||
<div v-for="userProgress in userProgressItems" :key="userProgress.audiobookId" class="h-full w-full" :class="userProgress.isRead ? 'bg-success' : userProgress.progress > 0 ? 'bg-yellow-400' : ''" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,7 +8,10 @@
|
||||
<div class="p-4 w-full text-sm py-6 rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-hidden" style="min-height: 400px; max-height: 80vh">
|
||||
<form @submit.prevent="submitForm">
|
||||
<div class="flex">
|
||||
<covers-collection-cover :book-items="books" :width="200" :height="100 * 1.6" />
|
||||
<div>
|
||||
<covers-collection-cover :book-items="books" :width="200" :height="100 * 1.6" />
|
||||
<!-- <ui-btn type="button" @click="showUploadImageModal = true">Upload</ui-btn> -->
|
||||
</div>
|
||||
<div class="flex-grow px-4">
|
||||
<ui-text-input-with-label v-model="newCollectionName" label="Name" class="mb-2" />
|
||||
|
||||
@ -21,6 +24,8 @@
|
||||
<ui-btn color="success" type="submit">Save</ui-btn>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<modals-upload-image-modal v-model="showUploadImageModal" entity="collection" :entity-id="collection.id" />
|
||||
</div>
|
||||
</modals-modal>
|
||||
</template>
|
||||
@ -31,7 +36,8 @@ export default {
|
||||
return {
|
||||
processing: false,
|
||||
newCollectionName: null,
|
||||
newCollectionDescription: null
|
||||
newCollectionDescription: null,
|
||||
showUploadImageModal: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -175,7 +175,6 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
goPrevBook() {
|
||||
console.log('GO PREV', this.currentBookshelfIndex)
|
||||
if (this.currentBookshelfIndex - 1 < 0) return
|
||||
var prevBookId = this.bookshelfBookIds[this.currentBookshelfIndex - 1]
|
||||
var prevBook = this.$store.getters['audiobooks/getAudiobook'](prevBookId)
|
||||
@ -187,7 +186,6 @@ export default {
|
||||
}
|
||||
},
|
||||
goNextBook() {
|
||||
console.log('GO NEXT', this.currentBookshelfIndex)
|
||||
if (this.currentBookshelfIndex >= this.bookshelfBookIds.length - 1) return
|
||||
|
||||
var nextBookId = this.bookshelfBookIds[this.currentBookshelfIndex + 1]
|
||||
@ -226,7 +224,6 @@ export default {
|
||||
}
|
||||
},
|
||||
hotkey(action) {
|
||||
console.log('HOTKEY', action)
|
||||
if (action === this.$hotkeys.Modal.NEXT_PAGE) {
|
||||
this.goNextBook()
|
||||
} else if (action === this.$hotkeys.Modal.PREV_PAGE) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div ref="wrapper" class="modal modal-bg w-full h-full fixed top-0 left-0 bg-primary bg-opacity-75 flex items-center justify-center z-40 opacity-0">
|
||||
<div ref="wrapper" class="modal modal-bg w-full h-full fixed top-0 left-0 bg-primary bg-opacity-75 items-center justify-center z-40 opacity-0 hidden">
|
||||
<div class="absolute top-0 left-0 right-0 w-full h-36 bg-gradient-to-t from-transparent via-black-500 to-black-700 opacity-90 pointer-events-none" />
|
||||
|
||||
<div class="absolute top-5 right-5 h-12 w-12 flex items-center justify-center cursor-pointer text-white hover:text-gray-300" @click="clickClose">
|
||||
@ -47,7 +47,7 @@ export default {
|
||||
watch: {
|
||||
show(newVal) {
|
||||
if (newVal) {
|
||||
this.setShow()
|
||||
this.$nextTick(this.setShow)
|
||||
} else {
|
||||
this.setHide()
|
||||
}
|
||||
@ -77,9 +77,9 @@ export default {
|
||||
clickClose() {
|
||||
this.show = false
|
||||
},
|
||||
clickBg(vm, ev) {
|
||||
clickBg(ev) {
|
||||
if (this.processing && this.persistent) return
|
||||
if (vm.srcElement.classList.contains('modal-bg')) {
|
||||
if (ev.srcElement.classList.contains('modal-bg')) {
|
||||
this.show = false
|
||||
}
|
||||
},
|
||||
@ -89,6 +89,13 @@ export default {
|
||||
}
|
||||
},
|
||||
setShow() {
|
||||
if (!this.el || !this.content) {
|
||||
this.init()
|
||||
}
|
||||
if (!this.el || !this.content) {
|
||||
return
|
||||
}
|
||||
|
||||
document.body.appendChild(this.el)
|
||||
setTimeout(() => {
|
||||
this.content.style.transform = 'scale(1)'
|
||||
@ -105,15 +112,22 @@ export default {
|
||||
|
||||
this.$eventBus.$off('modal-hotkey', this.hotkey)
|
||||
this.$store.commit('setOpenModal', null)
|
||||
},
|
||||
init() {
|
||||
this.el = this.$refs.wrapper
|
||||
this.content = this.$refs.content
|
||||
if (this.content && this.el) {
|
||||
this.el.classList.remove('hidden')
|
||||
this.el.classList.add('flex')
|
||||
this.content.style.transform = 'scale(0)'
|
||||
this.content.style.transition = 'transform 0.25s cubic-bezier(0.16, 1, 0.3, 1)'
|
||||
this.el.style.opacity = 1
|
||||
this.el.remove()
|
||||
} else {
|
||||
console.warn('Invalid modal init', this.name)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.el = this.$refs.wrapper
|
||||
this.content = this.$refs.content
|
||||
this.content.style.transform = 'scale(0)'
|
||||
this.content.style.transition = 'transform 0.25s cubic-bezier(0.16, 1, 0.3, 1)'
|
||||
this.el.style.opacity = 1
|
||||
this.el.remove()
|
||||
}
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
107
client/components/modals/UploadImageModal.vue
Normal file
107
client/components/modals/UploadImageModal.vue
Normal file
@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<modals-modal v-model="show" name="upload-image" :width="500" :height="'unset'">
|
||||
<div ref="container" class="w-full rounded-lg bg-primary box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh">
|
||||
<div class="flex items-center">
|
||||
<div class="w-40 pr-2 pt-4" style="min-width: 160px">
|
||||
<ui-file-input ref="fileInput" @change="fileUploadSelected">Upload Cover</ui-file-input>
|
||||
</div>
|
||||
<form @submit.prevent="submitForm" class="flex flex-grow">
|
||||
<ui-text-input-with-label v-model="imageUrl" label="Cover Image URL" />
|
||||
<ui-btn color="success" type="submit" :padding-x="4" class="mt-5 ml-3 w-24">Update</ui-btn>
|
||||
</form>
|
||||
</div>
|
||||
<div v-if="previewUpload" class="absolute top-0 left-0 w-full h-full z-10 bg-bg p-8">
|
||||
<p class="text-lg">Preview Cover</p>
|
||||
<span class="absolute top-4 right-4 material-icons text-2xl cursor-pointer" @click="resetCoverPreview">close</span>
|
||||
<div class="flex justify-center py-4">
|
||||
<covers-preview-cover :src="previewUpload" :width="240" />
|
||||
</div>
|
||||
<div class="absolute bottom-0 right-0 flex py-4 px-5">
|
||||
<ui-btn :disabled="processingUpload" class="mx-2" @click="resetCoverPreview">Clear</ui-btn>
|
||||
<ui-btn :loading="processingUpload" color="success" @click="submitCoverUpload">Upload</ui-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</modals-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: Boolean,
|
||||
entity: String,
|
||||
entityId: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imageUrl: null,
|
||||
previewUpload: null,
|
||||
processingUpload: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
if (newVal) this.init()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
show: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init() {},
|
||||
fileUploadSelected() {
|
||||
this.previewUpload = URL.createObjectURL(file)
|
||||
this.selectedFile = file
|
||||
},
|
||||
resetCoverPreview() {
|
||||
if (this.$refs.fileInput) {
|
||||
this.$refs.fileInput.reset()
|
||||
}
|
||||
this.previewUpload = null
|
||||
this.selectedFile = null
|
||||
},
|
||||
submitCoverUpload() {
|
||||
this.processingUpload = true
|
||||
var form = new FormData()
|
||||
form.set('cover', this.selectedFile)
|
||||
|
||||
this.$axios
|
||||
.$post(`/api/${this.entity}/${this.entityId}/cover`, form)
|
||||
.then((data) => {
|
||||
if (data.error) {
|
||||
this.$toast.error(data.error)
|
||||
} else {
|
||||
this.$toast.success('Cover Uploaded')
|
||||
this.resetCoverPreview()
|
||||
}
|
||||
this.processingUpload = false
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed', error)
|
||||
var errorMsg = error.response && error.response.data ? error.response.data : 'Unknown Error'
|
||||
this.$toast.error(errorMsg)
|
||||
this.processingUpload = false
|
||||
})
|
||||
},
|
||||
async submitForm() {
|
||||
this.processingUpload = true
|
||||
|
||||
var success = await this.$axios.$post(`/api/${this.entity}/${this.entityId}/cover`, { url: this.imageUrl }).catch((error) => {
|
||||
console.error('Failed to download cover from url', error)
|
||||
var errorMsg = error.response && error.response.data ? error.response.data : 'Unknown Error'
|
||||
this.$toast.error(errorMsg)
|
||||
return false
|
||||
})
|
||||
|
||||
this.processingUpload = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -2,16 +2,6 @@
|
||||
<div class="w-full h-full relative">
|
||||
<form class="w-full h-full" @submit.prevent="submitForm">
|
||||
<div ref="formWrapper" class="px-4 py-6 details-form-wrapper w-full overflow-hidden overflow-y-auto">
|
||||
<!-- <div v-if="userProgress" class="bg-success bg-opacity-40 rounded-md w-full px-4 py-1 mb-4 border border-success border-opacity-50">
|
||||
<div class="w-full flex items-center">
|
||||
<p>
|
||||
Your progress: <span class="font-mono text-lg">{{ (userProgress * 100).toFixed(0) }}%</span>
|
||||
</p>
|
||||
<div class="flex-grow" />
|
||||
<ui-btn v-if="!resettingProgress" small :padding-x="2" class="-mr-3" @click="resetProgress">Reset</ui-btn>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<ui-text-input-with-label v-model="details.title" label="Title" />
|
||||
|
||||
<ui-text-input-with-label v-model="details.subtitle" label="Subtitle" class="mt-2" />
|
||||
@ -278,7 +268,6 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// this.init()
|
||||
this.setResizeObserver()
|
||||
}
|
||||
}
|
||||
|
@ -60,9 +60,6 @@ export default {
|
||||
}
|
||||
var newfolderpaths = this.folderPaths.join(',')
|
||||
var origfolderpaths = this.library.folders.map((f) => f.fullPath).join(',')
|
||||
console.log(newfolderpaths)
|
||||
console.log(origfolderpaths)
|
||||
console.log(newfolderpaths === origfolderpaths)
|
||||
|
||||
return newfolderpaths === origfolderpaths && this.name === this.library.name
|
||||
}
|
||||
@ -158,7 +155,6 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.log('Mounted edit library')
|
||||
this.init()
|
||||
}
|
||||
}
|
||||
|
@ -148,7 +148,6 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.log('folder chooser mounted')
|
||||
this.init()
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,10 @@
|
||||
<div class="flex-grow" />
|
||||
<p v-if="totalDuration">{{ totalDurationPretty }}</p>
|
||||
</div>
|
||||
<draggable v-model="books" v-bind="dragOptions" class="list-group" handle=".drag-handle" draggable=".item" tag="div" @start="drag = true" @end="drag = false" @update="draggableUpdate">
|
||||
<transition-group type="transition" :name="!drag ? 'list-complete' : null">
|
||||
<template v-for="book in books">
|
||||
<tables-collection-book-table-row :key="book.id" :book="book" :collection-id="collectionId" class="item list-complete-item" @edit="editBook" />
|
||||
<draggable v-model="booksCopy" v-bind="dragOptions" class="list-group" handle=".drag-handle" draggable=".item" tag="div" @start="drag = true" @end="drag = false" @update="draggableUpdate">
|
||||
<transition-group type="transition" :name="!drag ? 'collection-book' : null">
|
||||
<template v-for="book in booksCopy">
|
||||
<tables-collection-book-table-row :key="book.id" :is-dragging="drag" :book="book" :collection-id="collectionId" class="item" :class="drag ? '' : 'collection-book-item'" @edit="editBook" />
|
||||
</template>
|
||||
</transition-group>
|
||||
</draggable>
|
||||
@ -39,6 +39,14 @@ export default {
|
||||
animation: 200,
|
||||
group: 'description',
|
||||
ghostClass: 'ghost'
|
||||
},
|
||||
booksCopy: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
books: {
|
||||
handler(newVal) {
|
||||
this.init()
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -55,29 +63,47 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
draggableUpdate() {},
|
||||
draggableUpdate() {
|
||||
var collectionUpdate = {
|
||||
books: this.booksCopy.map((b) => b.id)
|
||||
}
|
||||
this.$axios
|
||||
.$patch(`/api/collection/${this.collectionId}`, collectionUpdate)
|
||||
.then((collection) => {
|
||||
console.log('Collection updated', collection)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to update collection', error)
|
||||
this.$toast.error('Failed to save collection books order')
|
||||
})
|
||||
},
|
||||
editBook(book) {
|
||||
var bookIds = this.books.map((b) => b.id)
|
||||
this.$store.commit('setBookshelfBookIds', bookIds)
|
||||
this.$store.commit('showEditModal', book)
|
||||
},
|
||||
init() {
|
||||
this.booksCopy = this.books.map((b) => ({ ...b }))
|
||||
}
|
||||
},
|
||||
mounted() {}
|
||||
mounted() {
|
||||
this.init()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.list-complete-item {
|
||||
transition: all 0.8s ease;
|
||||
.collection-book-item {
|
||||
transition: all 0.4s ease;
|
||||
}
|
||||
|
||||
.list-complete-enter-from,
|
||||
.list-complete-leave-to {
|
||||
.collection-book-enter-from,
|
||||
.collection-book-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
transform: translateX(30px);
|
||||
}
|
||||
|
||||
.list-complete-leave-active {
|
||||
.collection-book-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
@ -6,11 +6,18 @@
|
||||
<span class="material-icons drag-handle text-xl">menu</span>
|
||||
</div>
|
||||
</div>
|
||||
<covers-book-cover :audiobook="book" :width="50" />
|
||||
<div class="h-full relative" :style="{ width: '50px' }">
|
||||
<covers-book-cover :audiobook="book" :width="50" />
|
||||
<div class="absolute top-0 left-0 bg-black bg-opacity-50 flex items-center justify-center h-full w-full" v-show="isHovering && showPlayBtn">
|
||||
<div class="w-8 h-8 bg-white bg-opacity-20 rounded-full flex items-center justify-center hover:bg-opacity-40 cursor-pointer" @click="playClick">
|
||||
<span class="material-icons">play_arrow</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-80 h-full px-2 flex items-center">
|
||||
<div>
|
||||
<p class="truncate">{{ bookTitle }}</p>
|
||||
<p class="truncate text-gray-400 text-sm">{{ bookAuthor }}</p>
|
||||
<nuxt-link :to="`/audiobook/${book.id}`" class="truncate hover:underline">{{ bookTitle }}</nuxt-link>
|
||||
<nuxt-link :to="`/library/${book.libraryId}/bookshelf?filter=authors.${$encode(bookAuthor)}`" class="truncate block text-gray-400 text-sm hover:underline">{{ bookAuthor }}</nuxt-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-grow flex items-center">
|
||||
@ -26,12 +33,12 @@
|
||||
<span class="material-icons">edit</span>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="w-40 absolute top-0 -right-40 h-full transform transition-transform" :class="!isHovering ? 'translate-x-0' : '-translate-x-40'">
|
||||
<div class="w-40 absolute top-0 -right-24 h-full transform transition-transform" :class="!isHovering ? 'translate-x-0' : '-translate-x-24'">
|
||||
<div class="flex h-full items-center">
|
||||
<ui-tooltip :text="isRead ? 'Mark as Not Read' : 'Mark as Read'" direction="top">
|
||||
<ui-read-icon-btn :disabled="isProcessingReadUpdate" :is-read="isRead" borderless class="mx-1 mt-0.5" @click="toggleRead" />
|
||||
</ui-tooltip>
|
||||
<div class="mx-1">
|
||||
<div class="mx-1" :class="isHovering ? '' : 'ml-6'">
|
||||
<ui-icon-btn icon="edit" borderless @click="clickEdit" />
|
||||
</div>
|
||||
<div class="mx-1">
|
||||
@ -49,7 +56,8 @@ export default {
|
||||
book: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
isDragging: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -64,6 +72,13 @@ export default {
|
||||
handler(newVal) {
|
||||
this.isRead = newVal
|
||||
}
|
||||
},
|
||||
isDragging: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.isHovering = false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -79,6 +94,21 @@ export default {
|
||||
bookDuration() {
|
||||
return this.$secondsToTimestamp(this.book.duration)
|
||||
},
|
||||
isMissing() {
|
||||
return this.book.isMissing
|
||||
},
|
||||
isIncomplete() {
|
||||
return this.book.isIncomplete
|
||||
},
|
||||
numTracks() {
|
||||
return this.book.numTracks
|
||||
},
|
||||
isStreaming() {
|
||||
return this.$store.getters['getAudiobookIdStreaming'] === this.book.id
|
||||
},
|
||||
showPlayBtn() {
|
||||
return !this.isMissing && !this.isIncomplete && !this.isStreaming && this.numTracks
|
||||
},
|
||||
userAudiobooks() {
|
||||
return this.$store.state.user.user ? this.$store.state.user.user.audiobooks || {} : {}
|
||||
},
|
||||
@ -91,12 +121,16 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
mouseover() {
|
||||
if (this.isDragging) return
|
||||
this.isHovering = true
|
||||
},
|
||||
mouseleave() {
|
||||
this.isHovering = false
|
||||
},
|
||||
clickRemove() {},
|
||||
playClick() {
|
||||
this.$store.commit('setStreamAudiobook', this.book)
|
||||
this.$root.socket.emit('open_stream', this.book.id)
|
||||
},
|
||||
clickEdit() {
|
||||
this.$emit('edit', this.book)
|
||||
},
|
||||
|
@ -11,8 +11,14 @@ export default function (context) {
|
||||
return
|
||||
}
|
||||
|
||||
if (store.state.isRoutingBack) {
|
||||
// pressing back button in appbar do not add to route history
|
||||
store.commit('setIsRoutingBack', false)
|
||||
return
|
||||
}
|
||||
|
||||
if (route.name.startsWith('config') || route.name === 'upload' || route.name === 'account' || route.name.startsWith('audiobook-id') || route.name.startsWith('collection-id')) {
|
||||
if (from.name !== route.name && from.name !== 'audiobook-id-edit' && from.name !== 'collection-id' && !from.name.startsWith('config') && from.name !== 'upload' && from.name !== 'account') {
|
||||
if (from.name !== route.name && from.name !== 'audiobook-id-edit' && !from.name.startsWith('config') && from.name !== 'upload' && from.name !== 'account') {
|
||||
var _history = [...store.state.routeHistory]
|
||||
if (!_history.length || _history[_history.length - 1] !== from.fullPath) {
|
||||
_history.push(from.fullPath)
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "audiobookshelf-client",
|
||||
"version": "1.6.10",
|
||||
"version": "1.6.11",
|
||||
"description": "Audiobook manager and player",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
@ -14,6 +14,11 @@
|
||||
</h1>
|
||||
<div class="flex-grow" />
|
||||
|
||||
<ui-btn v-if="showPlayButton" :disabled="streaming" color="success" :padding-x="4" small class="flex items-center h-9 mr-2" @click="clickPlay">
|
||||
<span v-show="!streaming" class="material-icons -ml-2 pr-1 text-white">play_arrow</span>
|
||||
{{ streaming ? 'Streaming' : 'Play' }}
|
||||
</ui-btn>
|
||||
|
||||
<ui-icon-btn icon="edit" class="mx-0.5" @click="editClick" />
|
||||
|
||||
<ui-icon-btn icon="delete" class="mx-0.5" @click="removeClick" />
|
||||
@ -75,6 +80,20 @@ export default {
|
||||
},
|
||||
collection() {
|
||||
return this.$store.getters['user/getCollection'](this.collectionId)
|
||||
},
|
||||
playableBooks() {
|
||||
return this.bookItems.filter((book) => {
|
||||
return !book.isMissing && !book.isIncomplete && book.numTracks
|
||||
})
|
||||
},
|
||||
streaming() {
|
||||
return !!this.playableBooks.find((b) => b.id === this.$store.getters['getAudiobookIdStreaming'])
|
||||
},
|
||||
showPlayButton() {
|
||||
return this.playableBooks.length
|
||||
},
|
||||
userAudiobooks() {
|
||||
return this.$store.state.user.user ? this.$store.state.user.user.audiobooks || {} : {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -98,13 +117,15 @@ export default {
|
||||
})
|
||||
}
|
||||
},
|
||||
collectionsUpdated() {
|
||||
// this.collectionCopy = { ...this.collection }
|
||||
clickPlay() {
|
||||
var nextBookNotRead = this.playableBooks.find((pb) => !this.userAudiobooks[pb.id] || !this.userAudiobooks[pb.id].isRead)
|
||||
if (nextBookNotRead) {
|
||||
this.$store.commit('setStreamAudiobook', nextBookNotRead)
|
||||
this.$root.socket.emit('open_stream', nextBookNotRead.id)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// this.$store.commit('user/addCollectionsListener', { meth: this.collectionsUpdated, key: 'collection-page' })
|
||||
},
|
||||
mounted() {},
|
||||
beforeDestroy() {}
|
||||
}
|
||||
</script>
|
@ -98,7 +98,6 @@ export default {
|
||||
})
|
||||
this.searchQuery = query
|
||||
}
|
||||
},
|
||||
mounted() {}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -16,6 +16,7 @@ export const state = () => ({
|
||||
processingBatch: false,
|
||||
previousPath: '/',
|
||||
routeHistory: [],
|
||||
isRoutingBack: false,
|
||||
showExperimentalFeatures: false,
|
||||
backups: [],
|
||||
bookshelfBookIds: [],
|
||||
@ -78,6 +79,9 @@ export const mutations = {
|
||||
setRouteHistory(state, val) {
|
||||
state.routeHistory = val
|
||||
},
|
||||
setIsRoutingBack(state, val) {
|
||||
state.isRoutingBack = val
|
||||
},
|
||||
setPreviousPath(state, val) {
|
||||
state.previousPath = val
|
||||
},
|
||||
|
@ -136,6 +136,7 @@ export const mutations = {
|
||||
setCollections(state, collections) {
|
||||
state.collectionsLoaded = true
|
||||
state.collections = collections
|
||||
state.collectionsListeners.forEach((listener) => listener.meth())
|
||||
},
|
||||
addUpdateCollection(state, collection) {
|
||||
var index = state.collections.findIndex(c => c.id === collection.id)
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "audiobookshelf",
|
||||
"version": "1.6.10",
|
||||
"version": "1.6.11",
|
||||
"description": "Self-hosted audiobook server for managing and playing audiobooks",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
Loading…
Reference in New Issue
Block a user