mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-02-24 00:21:12 +01:00
Add:Series sort #712
This commit is contained in:
parent
dc4c30d791
commit
ce133cd6f2
@ -45,7 +45,7 @@
|
|||||||
<ui-checkbox v-if="isLibraryPage && !isPodcastLibrary" v-model="settings.collapseSeries" label="Collapse Series" checkbox-bg="bg" check-color="white" small class="mr-2" @input="updateCollapseSeries" />
|
<ui-checkbox v-if="isLibraryPage && !isPodcastLibrary" v-model="settings.collapseSeries" label="Collapse Series" checkbox-bg="bg" check-color="white" small class="mr-2" @input="updateCollapseSeries" />
|
||||||
<controls-filter-select v-if="isLibraryPage" v-model="settings.filterBy" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateFilter" />
|
<controls-filter-select v-if="isLibraryPage" v-model="settings.filterBy" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateFilter" />
|
||||||
<controls-order-select v-if="isLibraryPage" v-model="settings.orderBy" :descending.sync="settings.orderDesc" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateOrder" />
|
<controls-order-select v-if="isLibraryPage" v-model="settings.orderBy" :descending.sync="settings.orderDesc" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateOrder" />
|
||||||
<controls-sort-select v-if="isSeriesPage" v-model="seriesSort" :descending.sync="seriesSortDesc" :items="seriesSortItems" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateSeriesSort" />
|
<controls-sort-select v-if="isSeriesPage" v-model="seriesSortBy" :descending.sync="seriesSortDesc" :items="seriesSortItems" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateSeriesSort" />
|
||||||
|
|
||||||
<ui-btn v-if="isIssuesFilter && userCanDelete" :loading="processingIssues" color="error" small class="ml-4" @click="removeAllIssues">Remove All {{ numShowing }} {{ entityName }}</ui-btn>
|
<ui-btn v-if="isIssuesFilter && userCanDelete" :loading="processingIssues" color="error" small class="ml-4" @click="removeAllIssues">Remove All {{ numShowing }} {{ entityName }}</ui-btn>
|
||||||
</template>
|
</template>
|
||||||
@ -85,19 +85,17 @@ export default {
|
|||||||
processingSeries: false,
|
processingSeries: false,
|
||||||
processingIssues: false,
|
processingIssues: false,
|
||||||
processingAuthors: false,
|
processingAuthors: false,
|
||||||
seriesSort: 'name',
|
|
||||||
seriesSortDesc: false,
|
|
||||||
seriesSortItems: [
|
seriesSortItems: [
|
||||||
{
|
{
|
||||||
text: 'Name',
|
text: 'Name',
|
||||||
value: 'name'
|
value: 'name'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '# of Books',
|
text: 'Number of Books',
|
||||||
value: 'numBooks'
|
value: 'numBooks'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Added At',
|
text: 'Date Added',
|
||||||
value: 'addedAt'
|
value: 'addedAt'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -169,6 +167,22 @@ export default {
|
|||||||
},
|
},
|
||||||
isIssuesFilter() {
|
isIssuesFilter() {
|
||||||
return this.filterBy === 'issues' && this.$route.query.filter === 'issues'
|
return this.filterBy === 'issues' && this.$route.query.filter === 'issues'
|
||||||
|
},
|
||||||
|
seriesSortBy: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.libraries.seriesSortBy
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$store.commit('libraries/setSeriesSortBy', val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
seriesSortDesc: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.libraries.seriesSortDesc
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$store.commit('libraries/setSeriesSortDesc', val)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -249,7 +263,6 @@ export default {
|
|||||||
this.saveSettings()
|
this.saveSettings()
|
||||||
},
|
},
|
||||||
updateSeriesSort() {
|
updateSeriesSort() {
|
||||||
this.$store.commit('libraries/setSeriesSort', { sort: this.seriesSort, desc: this.seriesSortDesc })
|
|
||||||
this.$eventBus.$emit('series-sort-updated')
|
this.$eventBus.$emit('series-sort-updated')
|
||||||
},
|
},
|
||||||
updateCollapseSeries() {
|
updateCollapseSeries() {
|
||||||
|
@ -99,7 +99,7 @@ export default {
|
|||||||
return this.page
|
return this.page
|
||||||
},
|
},
|
||||||
seriesSortBy() {
|
seriesSortBy() {
|
||||||
return this.$store.state.libraries.seriesSort
|
return this.$store.state.libraries.seriesSortBy
|
||||||
},
|
},
|
||||||
seriesSortDesc() {
|
seriesSortDesc() {
|
||||||
return this.$store.state.libraries.seriesSortDesc
|
return this.$store.state.libraries.seriesSortDesc
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else class="absolute z-30 left-0 right-0 mx-auto -bottom-8 h-8 py-1 rounded-md text-center">
|
<div v-else class="absolute z-30 left-0 right-0 mx-auto -bottom-8 h-8 py-1 rounded-md text-center">
|
||||||
<p class="truncate" :style="{ fontSize: labelFontSize + 'rem' }">{{ displayTitle }}</p>
|
<p class="truncate" :style="{ fontSize: labelFontSize + 'rem' }">{{ displayTitle }}</p>
|
||||||
|
<p v-if="displaySortLine" class="truncate text-gray-400" :style="{ fontSize: 0.8 * sizeMultiplier + 'rem' }">{{ displaySortLine }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -40,7 +41,8 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: () => null
|
default: () => null
|
||||||
},
|
},
|
||||||
sortingIgnorePrefix: Boolean
|
sortingIgnorePrefix: Boolean,
|
||||||
|
orderBy: String
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -52,6 +54,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
dateFormat() {
|
||||||
|
return this.store.state.serverSettings.dateFormat
|
||||||
|
},
|
||||||
labelFontSize() {
|
labelFontSize() {
|
||||||
if (this.width < 160) return 0.75
|
if (this.width < 160) return 0.75
|
||||||
return 0.875
|
return 0.875
|
||||||
@ -73,12 +78,24 @@ export default {
|
|||||||
if (this.sortingIgnorePrefix) return this.nameIgnorePrefix || this.title
|
if (this.sortingIgnorePrefix) return this.nameIgnorePrefix || this.title
|
||||||
return this.title
|
return this.title
|
||||||
},
|
},
|
||||||
|
displaySortLine() {
|
||||||
|
if (this.orderBy === 'addedAt') {
|
||||||
|
// return this.addedAt
|
||||||
|
return 'Added ' + this.$formatDate(this.addedAt, this.dateFormat)
|
||||||
|
} else if (this.orderBy === 'totalDuration') {
|
||||||
|
return 'Duration: ' + this.$elapsedPrettyExtended(this.totalDuration, false)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
books() {
|
books() {
|
||||||
return this.series ? this.series.books || [] : []
|
return this.series ? this.series.books || [] : []
|
||||||
},
|
},
|
||||||
addedAt() {
|
addedAt() {
|
||||||
return this.series ? this.series.addedAt : 0
|
return this.series ? this.series.addedAt : 0
|
||||||
},
|
},
|
||||||
|
totalDuration() {
|
||||||
|
return this.series ? this.series.totalDuration : 0
|
||||||
|
},
|
||||||
seriesBookProgress() {
|
seriesBookProgress() {
|
||||||
return this.books
|
return this.books
|
||||||
.map((libraryItem) => {
|
.map((libraryItem) => {
|
||||||
|
@ -59,6 +59,8 @@ export default {
|
|||||||
if (this.entityName === 'books') {
|
if (this.entityName === 'books') {
|
||||||
props.filterBy = this.filterBy
|
props.filterBy = this.filterBy
|
||||||
props.orderBy = this.orderBy
|
props.orderBy = this.orderBy
|
||||||
|
} else if (this.entityName === 'series') {
|
||||||
|
props.orderBy = this.seriesSortBy
|
||||||
}
|
}
|
||||||
|
|
||||||
var _this = this
|
var _this = this
|
||||||
|
@ -14,8 +14,13 @@ export default {
|
|||||||
return redirect('/oops?message=Library not found')
|
return redirect('/oops?message=Library not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set series sort by
|
||||||
|
if (query.sort && params.id === 'series') {
|
||||||
|
store.commit('libraries/setSeriesSortBy', query.sort)
|
||||||
|
store.commit('libraries/setSeriesSortDesc', !!query.desc)
|
||||||
|
}
|
||||||
// Set filter by
|
// Set filter by
|
||||||
if (query.filter) {
|
if (query.filter && params.id !== 'series') {
|
||||||
store.dispatch('user/updateUserSettings', { filterBy: query.filter })
|
store.dispatch('user/updateUserSettings', { filterBy: query.filter })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ export const state = () => ({
|
|||||||
issues: 0,
|
issues: 0,
|
||||||
folderLastUpdate: 0,
|
folderLastUpdate: 0,
|
||||||
filterData: null,
|
filterData: null,
|
||||||
seriesSort: 'name',
|
seriesSortBy: 'name',
|
||||||
seriesSortDesc: false
|
seriesSortDesc: false
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -292,8 +292,10 @@ export const mutations = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setSeriesSort(state, { sort, desc }) {
|
setSeriesSortBy(state, sortBy) {
|
||||||
state.seriesSort = sort
|
state.seriesSortBy = sortBy
|
||||||
state.seriesSortDesc = desc
|
},
|
||||||
|
setSeriesSortDesc(state, sortDesc) {
|
||||||
|
state.seriesSortDesc = sortDesc
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -222,7 +222,7 @@ class LibraryController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (payload.collapseseries) {
|
if (payload.collapseseries) {
|
||||||
libraryItems = libraryHelpers.collapseBookSeries(libraryItems)
|
libraryItems = libraryHelpers.collapseBookSeries(libraryItems, this.db.series)
|
||||||
payload.total = libraryItems.length
|
payload.total = libraryItems.length
|
||||||
} else if (filterSeries) {
|
} else if (filterSeries) {
|
||||||
// Book media when filtering series will include series object on media metadata
|
// Book media when filtering series will include series object on media metadata
|
||||||
@ -275,10 +275,24 @@ class LibraryController {
|
|||||||
minified: req.query.minified === '1'
|
minified: req.query.minified === '1'
|
||||||
}
|
}
|
||||||
|
|
||||||
var series = libraryHelpers.getSeriesFromBooks(libraryItems, payload.minified)
|
var series = libraryHelpers.getSeriesFromBooks(libraryItems, this.db.series, payload.minified)
|
||||||
series = sort(series).asc(s => {
|
const direction = payload.sortDesc ? 'desc' : 'asc'
|
||||||
return this.db.serverSettings.sortingIgnorePrefix ? s.nameIgnorePrefix : s.name
|
series = naturalSort(series).by([
|
||||||
})
|
{
|
||||||
|
[direction]: (se) => {
|
||||||
|
if (payload.sortBy === 'numBooks') {
|
||||||
|
return se.books.length
|
||||||
|
} else if (payload.sortBy === 'totalDuration') {
|
||||||
|
return se.totalDuration
|
||||||
|
} else if (payload.sortBy === 'addedAt') {
|
||||||
|
return se.addedAt
|
||||||
|
} else { // sort by name
|
||||||
|
return this.db.serverSettings.sortingIgnorePrefix ? se.nameIgnorePrefix : se.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
payload.total = series.length
|
payload.total = series.length
|
||||||
|
|
||||||
if (payload.limit) {
|
if (payload.limit) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const { sort, createNewSortInstance } = require('../libs/fastSort')
|
const { sort, createNewSortInstance } = require('../libs/fastSort')
|
||||||
const { getTitleIgnorePrefix } = require('../utils/index')
|
const { getTitleIgnorePrefix, isNullOrNaN } = require('../utils/index')
|
||||||
const naturalSort = createNewSortInstance({
|
const naturalSort = createNewSortInstance({
|
||||||
comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare
|
comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare
|
||||||
})
|
})
|
||||||
@ -114,23 +114,29 @@ module.exports = {
|
|||||||
return data
|
return data
|
||||||
},
|
},
|
||||||
|
|
||||||
getSeriesFromBooks(books, minified = false) {
|
getSeriesFromBooks(books, allSeries, minified = false) {
|
||||||
var _series = {}
|
const _series = {}
|
||||||
books.forEach((libraryItem) => {
|
books.forEach((libraryItem) => {
|
||||||
var bookSeries = libraryItem.media.metadata.series || []
|
const bookSeries = libraryItem.media.metadata.series || []
|
||||||
bookSeries.forEach((series) => {
|
bookSeries.forEach((bookSeriesObj) => {
|
||||||
var abJson = minified ? libraryItem.toJSONMinified() : libraryItem.toJSONExpanded()
|
const series = allSeries.find(se => se.id === bookSeriesObj.id)
|
||||||
abJson.sequence = series.sequence
|
|
||||||
if (!_series[series.id]) {
|
const abJson = minified ? libraryItem.toJSONMinified() : libraryItem.toJSONExpanded()
|
||||||
_series[series.id] = {
|
abJson.sequence = bookSeriesObj.sequence
|
||||||
id: series.id,
|
if (!_series[bookSeriesObj.id]) {
|
||||||
name: series.name,
|
_series[bookSeriesObj.id] = {
|
||||||
nameIgnorePrefix: getTitleIgnorePrefix(series.name),
|
id: bookSeriesObj.id,
|
||||||
|
name: bookSeriesObj.name,
|
||||||
|
nameIgnorePrefix: getTitleIgnorePrefix(bookSeriesObj.name),
|
||||||
type: 'series',
|
type: 'series',
|
||||||
books: [abJson]
|
books: [abJson],
|
||||||
|
addedAt: series ? series.addedAt : 0,
|
||||||
|
totalDuration: isNullOrNaN(abJson.media.duration) ? 0 : Number(abJson.media.duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
_series[series.id].books.push(abJson)
|
_series[bookSeriesObj.id].books.push(abJson)
|
||||||
|
_series[bookSeriesObj.id].totalDuration += isNullOrNaN(abJson.media.duration) ? 0 : Number(abJson.media.duration)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -209,8 +215,8 @@ module.exports = {
|
|||||||
return totalSize
|
return totalSize
|
||||||
},
|
},
|
||||||
|
|
||||||
collapseBookSeries(libraryItems) {
|
collapseBookSeries(libraryItems, series) {
|
||||||
var seriesObjects = this.getSeriesFromBooks(libraryItems, true)
|
var seriesObjects = this.getSeriesFromBooks(libraryItems, series, true)
|
||||||
var seriesToUse = {}
|
var seriesToUse = {}
|
||||||
var libraryItemIdsToHide = []
|
var libraryItemIdsToHide = []
|
||||||
seriesObjects.forEach((series) => {
|
seriesObjects.forEach((series) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user