diff --git a/client/components/app/BookShelfToolbar.vue b/client/components/app/BookShelfToolbar.vue
index ed575723..ad1fe226 100644
--- a/client/components/app/BookShelfToolbar.vue
+++ b/client/components/app/BookShelfToolbar.vue
@@ -45,7 +45,7 @@
-
+
Remove All {{ numShowing }} {{ entityName }}
@@ -85,19 +85,17 @@ export default {
processingSeries: false,
processingIssues: false,
processingAuthors: false,
- seriesSort: 'name',
- seriesSortDesc: false,
seriesSortItems: [
{
text: 'Name',
value: 'name'
},
{
- text: '# of Books',
+ text: 'Number of Books',
value: 'numBooks'
},
{
- text: 'Added At',
+ text: 'Date Added',
value: 'addedAt'
},
{
@@ -169,6 +167,22 @@ export default {
},
isIssuesFilter() {
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: {
@@ -249,7 +263,6 @@ export default {
this.saveSettings()
},
updateSeriesSort() {
- this.$store.commit('libraries/setSeriesSort', { sort: this.seriesSort, desc: this.seriesSortDesc })
this.$eventBus.$emit('series-sort-updated')
},
updateCollapseSeries() {
diff --git a/client/components/app/LazyBookshelf.vue b/client/components/app/LazyBookshelf.vue
index 262b5fba..b863f5e1 100644
--- a/client/components/app/LazyBookshelf.vue
+++ b/client/components/app/LazyBookshelf.vue
@@ -99,7 +99,7 @@ export default {
return this.page
},
seriesSortBy() {
- return this.$store.state.libraries.seriesSort
+ return this.$store.state.libraries.seriesSortBy
},
seriesSortDesc() {
return this.$store.state.libraries.seriesSortDesc
diff --git a/client/components/cards/LazySeriesCard.vue b/client/components/cards/LazySeriesCard.vue
index 3932646b..4b12325a 100644
--- a/client/components/cards/LazySeriesCard.vue
+++ b/client/components/cards/LazySeriesCard.vue
@@ -20,6 +20,7 @@
{{ displayTitle }}
+
{{ displaySortLine }}
@@ -40,7 +41,8 @@ export default {
type: Object,
default: () => null
},
- sortingIgnorePrefix: Boolean
+ sortingIgnorePrefix: Boolean,
+ orderBy: String
},
data() {
return {
@@ -52,6 +54,9 @@ export default {
}
},
computed: {
+ dateFormat() {
+ return this.store.state.serverSettings.dateFormat
+ },
labelFontSize() {
if (this.width < 160) return 0.75
return 0.875
@@ -73,12 +78,24 @@ export default {
if (this.sortingIgnorePrefix) return this.nameIgnorePrefix || 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() {
return this.series ? this.series.books || [] : []
},
addedAt() {
return this.series ? this.series.addedAt : 0
},
+ totalDuration() {
+ return this.series ? this.series.totalDuration : 0
+ },
seriesBookProgress() {
return this.books
.map((libraryItem) => {
diff --git a/client/mixins/bookshelfCardsHelpers.js b/client/mixins/bookshelfCardsHelpers.js
index 574dee59..4eec2eea 100644
--- a/client/mixins/bookshelfCardsHelpers.js
+++ b/client/mixins/bookshelfCardsHelpers.js
@@ -59,6 +59,8 @@ export default {
if (this.entityName === 'books') {
props.filterBy = this.filterBy
props.orderBy = this.orderBy
+ } else if (this.entityName === 'series') {
+ props.orderBy = this.seriesSortBy
}
var _this = this
diff --git a/client/pages/library/_library/bookshelf/_id.vue b/client/pages/library/_library/bookshelf/_id.vue
index 134246e8..5ed86eae 100644
--- a/client/pages/library/_library/bookshelf/_id.vue
+++ b/client/pages/library/_library/bookshelf/_id.vue
@@ -14,8 +14,13 @@ export default {
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
- if (query.filter) {
+ if (query.filter && params.id !== 'series') {
store.dispatch('user/updateUserSettings', { filterBy: query.filter })
}
diff --git a/client/store/libraries.js b/client/store/libraries.js
index 519ca4a0..fcc986b6 100644
--- a/client/store/libraries.js
+++ b/client/store/libraries.js
@@ -9,7 +9,7 @@ export const state = () => ({
issues: 0,
folderLastUpdate: 0,
filterData: null,
- seriesSort: 'name',
+ seriesSortBy: 'name',
seriesSortDesc: false
})
@@ -292,8 +292,10 @@ export const mutations = {
}
}
},
- setSeriesSort(state, { sort, desc }) {
- state.seriesSort = sort
- state.seriesSortDesc = desc
+ setSeriesSortBy(state, sortBy) {
+ state.seriesSortBy = sortBy
+ },
+ setSeriesSortDesc(state, sortDesc) {
+ state.seriesSortDesc = sortDesc
}
}
\ No newline at end of file
diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js
index bb75bf24..199a532b 100644
--- a/server/controllers/LibraryController.js
+++ b/server/controllers/LibraryController.js
@@ -222,7 +222,7 @@ class LibraryController {
}
if (payload.collapseseries) {
- libraryItems = libraryHelpers.collapseBookSeries(libraryItems)
+ libraryItems = libraryHelpers.collapseBookSeries(libraryItems, this.db.series)
payload.total = libraryItems.length
} else if (filterSeries) {
// Book media when filtering series will include series object on media metadata
@@ -275,10 +275,24 @@ class LibraryController {
minified: req.query.minified === '1'
}
- var series = libraryHelpers.getSeriesFromBooks(libraryItems, payload.minified)
- series = sort(series).asc(s => {
- return this.db.serverSettings.sortingIgnorePrefix ? s.nameIgnorePrefix : s.name
- })
+ var series = libraryHelpers.getSeriesFromBooks(libraryItems, this.db.series, payload.minified)
+ const direction = payload.sortDesc ? 'desc' : 'asc'
+ 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
if (payload.limit) {
diff --git a/server/utils/libraryHelpers.js b/server/utils/libraryHelpers.js
index f4017ae4..70b22537 100644
--- a/server/utils/libraryHelpers.js
+++ b/server/utils/libraryHelpers.js
@@ -1,5 +1,5 @@
const { sort, createNewSortInstance } = require('../libs/fastSort')
-const { getTitleIgnorePrefix } = require('../utils/index')
+const { getTitleIgnorePrefix, isNullOrNaN } = require('../utils/index')
const naturalSort = createNewSortInstance({
comparer: new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare
})
@@ -114,23 +114,29 @@ module.exports = {
return data
},
- getSeriesFromBooks(books, minified = false) {
- var _series = {}
+ getSeriesFromBooks(books, allSeries, minified = false) {
+ const _series = {}
books.forEach((libraryItem) => {
- var bookSeries = libraryItem.media.metadata.series || []
- bookSeries.forEach((series) => {
- var abJson = minified ? libraryItem.toJSONMinified() : libraryItem.toJSONExpanded()
- abJson.sequence = series.sequence
- if (!_series[series.id]) {
- _series[series.id] = {
- id: series.id,
- name: series.name,
- nameIgnorePrefix: getTitleIgnorePrefix(series.name),
+ const bookSeries = libraryItem.media.metadata.series || []
+ bookSeries.forEach((bookSeriesObj) => {
+ const series = allSeries.find(se => se.id === bookSeriesObj.id)
+
+ const abJson = minified ? libraryItem.toJSONMinified() : libraryItem.toJSONExpanded()
+ abJson.sequence = bookSeriesObj.sequence
+ if (!_series[bookSeriesObj.id]) {
+ _series[bookSeriesObj.id] = {
+ id: bookSeriesObj.id,
+ name: bookSeriesObj.name,
+ nameIgnorePrefix: getTitleIgnorePrefix(bookSeriesObj.name),
type: 'series',
- books: [abJson]
+ books: [abJson],
+ addedAt: series ? series.addedAt : 0,
+ totalDuration: isNullOrNaN(abJson.media.duration) ? 0 : Number(abJson.media.duration)
}
+
} 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
},
- collapseBookSeries(libraryItems) {
- var seriesObjects = this.getSeriesFromBooks(libraryItems, true)
+ collapseBookSeries(libraryItems, series) {
+ var seriesObjects = this.getSeriesFromBooks(libraryItems, series, true)
var seriesToUse = {}
var libraryItemIdsToHide = []
seriesObjects.forEach((series) => {