mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-06-23 01:18:59 +02:00
Merge 8aef2b78cd
into 28404f37b8
This commit is contained in:
commit
345e357e24
68
client/components/modals/EditSeriesModal.vue
Normal file
68
client/components/modals/EditSeriesModal.vue
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<template>
|
||||||
|
<modals-modal v-model="show" name="edit-series" :processing="isProcessing" :width="500" :height="'unset'">
|
||||||
|
<div ref="container" class="w-full rounded-lg bg-bg box-shadow-md overflow-y-auto overflow-x-hidden p-4" style="max-height: 80vh; min-height: 40vh">
|
||||||
|
<h3 class="text-xl font-semibold mb-8">{{ $strings.LabelEditSeries }}</h3>
|
||||||
|
<div class="flex items-center mb-4">
|
||||||
|
<ui-textarea-with-label v-model="descriptionValue" :label="$strings.LabelSeriesDescription" :rows="8" />
|
||||||
|
</div>
|
||||||
|
<div class="absolute bottom-0 left-0 w-full py-2 md:py-4 bg-bg border-t border-white border-opacity-5">
|
||||||
|
<div class="flex items-center px-4">
|
||||||
|
<div class="flex-grow" />
|
||||||
|
|
||||||
|
<!-- desktop -->
|
||||||
|
<ui-btn @click="save" class="mx-2 hidden md:block">{{ $strings.ButtonSave }}</ui-btn>
|
||||||
|
<ui-btn @click="saveAndClose" class="mx-2 hidden md:block">{{ $strings.ButtonSaveAndClose }}</ui-btn>
|
||||||
|
<!-- mobile -->
|
||||||
|
<ui-btn @click="saveAndClose" class="mx-2 md:hidden">{{ $strings.ButtonSave }}</ui-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</modals-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: Boolean,
|
||||||
|
series: Object
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isProcessing: false,
|
||||||
|
descriptionValue: this.series.description || ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
show: {
|
||||||
|
get() {
|
||||||
|
return this.value
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$emit('input', val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async save() {
|
||||||
|
if (this.isProcessing) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
description: this.descriptionValue
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isProcessing = true
|
||||||
|
const updateResult = await this.$axios.$patch(`/api/series/${this.series.id}`, payload).catch((error) => {
|
||||||
|
console.error('Failed to update', error)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
this.isProcessing = false
|
||||||
|
},
|
||||||
|
async saveAndClose() {
|
||||||
|
const wasUpdated = await this.save()
|
||||||
|
if (wasUpdated !== null) this.show = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -1,7 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="page" :class="streamLibraryItem ? 'streaming' : ''">
|
<div id="page-wrapper" class="page" :class="streamLibraryItem ? 'streaming' : ''">
|
||||||
<app-book-shelf-toolbar :selected-series="series" />
|
<app-book-shelf-toolbar id="series-toolbar" :selected-series="series" />
|
||||||
<app-lazy-bookshelf page="series-books" :series-id="seriesId" />
|
<div class="h-full overflow-y-auto pb-[100px]">
|
||||||
|
<div class="max-w-6xl mx-auto">
|
||||||
|
<div class="px-4e sm:px-8e">
|
||||||
|
<div class="flex items-center my-8">
|
||||||
|
<h1 class="text-2xl">{{ series.name }}</h1>
|
||||||
|
|
||||||
|
<button class="w-8 h-8 rounded-full flex items-center justify-center mx-4 cursor-pointer text-gray-300 hover:text-warning transform hover:scale-125 duration-100" @click="showEditSeries">
|
||||||
|
<span class="material-symbols text-base">edit</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="mb-6">
|
||||||
|
<h2 class="font-semibold">
|
||||||
|
{{ $strings.LabelDescription }}
|
||||||
|
</h2>
|
||||||
|
<div>{{ series.description }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-6">
|
||||||
|
<h2 class="font-semibold">
|
||||||
|
{{ authors.length > 1 ? $strings.LabelAuthors : $strings.LabelAuthor }}
|
||||||
|
</h2>
|
||||||
|
<div>{{ authors.join(', ') }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-6">
|
||||||
|
<h2 class="font-semibold">{{ this.$strings.LabelTotalDuration }}</h2>
|
||||||
|
<div>{{ totalDuration.hours }}<span>hrs</span> {{ totalDuration.minutes }}<span>min</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<app-lazy-bookshelf page="series-books" :series-id="seriesId" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<modals-edit-series-modal v-model="showEditSeriesModal" :series="series" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -19,7 +51,7 @@ export default {
|
|||||||
return redirect(`/library/${libraryId}`)
|
return redirect(`/library/${libraryId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const series = await app.$axios.$get(`/api/libraries/${library.id}/series/${params.id}?include=progress,rssfeed`).catch((error) => {
|
const series = await app.$axios.$get(`/api/libraries/${library.id}/series/${params.id}?include=progress,rssfeed&expanded=1`).catch((error) => {
|
||||||
console.error('Failed', error)
|
console.error('Failed', error)
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
@ -33,16 +65,37 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {}
|
return {
|
||||||
|
showEditSeriesModal: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
streamLibraryItem() {
|
streamLibraryItem() {
|
||||||
return this.$store.state.streamLibraryItem
|
return this.$store.state.streamLibraryItem
|
||||||
|
},
|
||||||
|
totalDuration() {
|
||||||
|
const totalSeconds = this.series.books.reduce((acc, book) => acc + book.duration, 0)
|
||||||
|
const hours = Math.floor(totalSeconds / 3600)
|
||||||
|
const minuteRemainder = totalSeconds % 3600
|
||||||
|
const minutes = Math.floor(minuteRemainder / 60)
|
||||||
|
|
||||||
|
return { hours, minutes }
|
||||||
|
},
|
||||||
|
authors() {
|
||||||
|
// Get nested array of authors
|
||||||
|
const nestedAuthors = this.series.books.map((book) => book.authors.map((author) => author.name))
|
||||||
|
// Flatten to one array
|
||||||
|
const authors = nestedAuthors.flat(1)
|
||||||
|
// Remove duplicates
|
||||||
|
return [...new Set(authors)]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
seriesUpdated(series) {
|
seriesUpdated(series) {
|
||||||
this.series = series
|
this.series = series
|
||||||
|
},
|
||||||
|
showEditSeries() {
|
||||||
|
this.showEditSeriesModal = !this.showEditSeriesModal
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -57,3 +110,12 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#bookshelf {
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
#series-toolbar {
|
||||||
|
background-color: rgba(55, 56, 56, 1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -315,6 +315,7 @@
|
|||||||
"LabelEbook": "Ebook",
|
"LabelEbook": "Ebook",
|
||||||
"LabelEbooks": "Ebooks",
|
"LabelEbooks": "Ebooks",
|
||||||
"LabelEdit": "Edit",
|
"LabelEdit": "Edit",
|
||||||
|
"LabelEditSeries": "Edit Series",
|
||||||
"LabelEmail": "Email",
|
"LabelEmail": "Email",
|
||||||
"LabelEmailSettingsFromAddress": "From Address",
|
"LabelEmailSettingsFromAddress": "From Address",
|
||||||
"LabelEmailSettingsRejectUnauthorized": "Reject unauthorized certificates",
|
"LabelEmailSettingsRejectUnauthorized": "Reject unauthorized certificates",
|
||||||
@ -549,6 +550,7 @@
|
|||||||
"LabelSequence": "Sequence",
|
"LabelSequence": "Sequence",
|
||||||
"LabelSerial": "Serial",
|
"LabelSerial": "Serial",
|
||||||
"LabelSeries": "Series",
|
"LabelSeries": "Series",
|
||||||
|
"LabelSeriesDescription": "Series Description",
|
||||||
"LabelSeriesName": "Series Name",
|
"LabelSeriesName": "Series Name",
|
||||||
"LabelSeriesProgress": "Series Progress",
|
"LabelSeriesProgress": "Series Progress",
|
||||||
"LabelServerLogLevel": "Server Log Level",
|
"LabelServerLogLevel": "Server Log Level",
|
||||||
|
@ -783,12 +783,16 @@ class LibraryController {
|
|||||||
.map((v) => v.trim().toLowerCase())
|
.map((v) => v.trim().toLowerCase())
|
||||||
.filter((v) => !!v)
|
.filter((v) => !!v)
|
||||||
|
|
||||||
const series = await Database.seriesModel.findByPk(req.params.seriesId)
|
const series = req.query.expanded ? await Database.seriesModel.getExpandedById(req.params.seriesId) : await Database.seriesModel.findByPk(req.params.seriesId)
|
||||||
if (!series) return res.sendStatus(404)
|
if (!series) return res.sendStatus(404)
|
||||||
|
|
||||||
const libraryItemsInSeries = await libraryItemsBookFilters.getLibraryItemsForSeries(series, req.user)
|
const libraryItemsInSeries = await libraryItemsBookFilters.getLibraryItemsForSeries(series, req.user)
|
||||||
|
|
||||||
const seriesJson = series.toOldJSON()
|
const seriesJson = series.toOldJSON()
|
||||||
|
if (req.query.expanded) {
|
||||||
|
seriesJson.books = series.books
|
||||||
|
}
|
||||||
|
|
||||||
if (include.includes('progress')) {
|
if (include.includes('progress')) {
|
||||||
const libraryItemsFinished = libraryItemsInSeries.filter((li) => !!req.user.getMediaProgress(li.media.id)?.isFinished)
|
const libraryItemsFinished = libraryItemsInSeries.filter((li) => !!req.user.getMediaProgress(li.media.id)?.isFinished)
|
||||||
seriesJson.progress = {
|
seriesJson.progress = {
|
||||||
|
Loading…
Reference in New Issue
Block a user