Fixed sorting to be more consistent for multiple series (and generally)

This commit is contained in:
Scott Ruoti 2022-10-30 10:21:12 -04:00
parent d64932dad7
commit b322d0207b
2 changed files with 76 additions and 54 deletions

View File

@ -160,17 +160,30 @@ class LibraryController {
minified: req.query.minified === '1', minified: req.query.minified === '1',
collapseseries: req.query.collapseseries === '1' collapseseries: req.query.collapseseries === '1'
} }
var mediaIsBook = payload.mediaType === 'book'
var filterSeries = null var filterSeries = null
if (payload.filterBy) { if (payload.filterBy) {
// If filtering by series, will include seriesName and seriesSequence on media metadata // If filtering by series, will include seriesName and seriesSequence on media metadata
filterSeries = (payload.mediaType == 'book' && payload.filterBy.startsWith('series.')) ? libraryHelpers.decode(payload.filterBy.replace('series.', '')) : null filterSeries = (mediaIsBook && payload.filterBy.startsWith('series.')) ? libraryHelpers.decode(payload.filterBy.replace('series.', '')) : null
if (filterSeries === 'No Series') filterSeries = null if (filterSeries === 'No Series') filterSeries = null
libraryItems = libraryHelpers.getFilteredLibraryItems(libraryItems, payload.filterBy, req.user, this.rssFeedManager.feedsArray) libraryItems = libraryHelpers.getFilteredLibraryItems(libraryItems, payload.filterBy, req.user, this.rssFeedManager.feedsArray)
payload.total = libraryItems.length payload.total = libraryItems.length
} }
if (payload.collapseseries) {
libraryItems = libraryHelpers.collapseBookSeries(libraryItems, this.db.series, filterSeries)
payload.total = libraryItems.length
}
var sortArray = []
if (filterSeries) {
// Book media when filtering series will sort by the series sequence
sortArray.push({ asc: (li) => li.media.metadata.getSeries(filterSeries).sequence })
}
if (payload.sortBy) { if (payload.sortBy) {
var sortKey = payload.sortBy var sortKey = payload.sortBy
@ -186,29 +199,37 @@ class LibraryController {
sortKey += 'IgnorePrefix' sortKey += 'IgnorePrefix'
} }
// Start sort // If series are collapsed and not sorting by title, sort all collapsed series to the end in alphabetical order
var direction = payload.sortDesc ? 'desc' : 'asc' if (payload.collapseseries && !sortByTitle) {
var sortArray = [ sortArray.push({
{ asc: (li) => {
[direction]: (li) => { if (li.collapsedSeries) {
// When collapsing by series and sorting by title use the series name instead of the book title return this.db.serverSettings.sortingIgnorePrefix ?
if (payload.mediaType === 'book' && payload.collapseseries && li.media.metadata.seriesName) { li.collapsedSeries.nameIgnorePrefix :
if (sortByTitle) { li.collapsedSeries.name
return this.db.serverSettings.sortingIgnorePrefix ? li.media.metadata.seriesNameIgnorePrefix : li.media.metadata.seriesName } else {
} else { return ''
// When not sorting by title always show the collapsed series at the end
return direction === 'desc' ? -1 : 'zzzz'
}
} }
}
})
}
var direction = payload.sortDesc ? 'desc' : 'asc'
sortArray.push({
[direction]: (li) => {
if (mediaIsBook && sortByTitle && li.collapsedSeries) {
return this.db.serverSettings.sortingIgnorePrefix ?
li.collapsedSeries.nameIgnorePrefix :
li.collapsedSeries.name
} else {
// Supports dot notation strings i.e. "media.metadata.title" // Supports dot notation strings i.e. "media.metadata.title"
return sortKey.split('.').reduce((a, b) => a[b], li) return sortKey.split('.').reduce((a, b) => a[b], li)
} }
} }
] })
// Secondary sort when sorting by book author use series sort title // Secondary sort when sorting by book author use series sort title
if (payload.mediaType === 'book' && payload.sortBy.includes('author')) { if (mediaIsBook && payload.sortBy.includes('author')) {
sortArray.push({ sortArray.push({
asc: (li) => { asc: (li) => {
if (li.media.metadata.series && li.media.metadata.series.length) { if (li.media.metadata.series && li.media.metadata.series.length) {
@ -218,30 +239,31 @@ class LibraryController {
} }
}) })
} }
}
// Sort the items
if (sortArray.length) {
libraryItems = naturalSort(libraryItems).by(sortArray) libraryItems = naturalSort(libraryItems).by(sortArray)
} }
if (payload.collapseseries) { payload.results = libraryItems.map(li => {
libraryItems = libraryHelpers.collapseBookSeries(libraryItems, this.db.series) let json = payload.minified ? li.toJSONMinified() : li.toJSON()
payload.total = libraryItems.length
} else if (filterSeries) { if (li.collapsedSeries) {
// Book media when filtering series will include series object on media metadata json.collapsedSeries = {
libraryItems = libraryItems.map(li => { id: li.collapsedSeries.id,
var series = li.media.metadata.getSeries(filterSeries) name: li.collapsedSeries.name,
var liJson = payload.minified ? li.toJSONMinified() : li.toJSON() nameIgnorePrefix: li.collapsedSeries.nameIgnorePrefix,
liJson.media.metadata.series = series libraryItemIds: li.collapsedSeries.books.map(b => b.id),
return liJson numBooks: li.collapsedSeries.books.length
}) }
libraryItems = naturalSort(libraryItems).asc(li => li.media.metadata.series.sequence) } else if (filterSeries) {
} else { json.media.metadata.series = li.media.metadata.getSeries(filterSeries)
libraryItems = libraryItems.map(li => payload.minified ? li.toJSONMinified() : li.toJSON()) }
}
return json
})
if (payload.limit) {
var startIndex = payload.page * payload.limit
libraryItems = libraryItems.slice(startIndex, startIndex + payload.limit)
}
payload.results = libraryItems
res.json(payload) res.json(payload)
} }

View File

@ -280,34 +280,34 @@ module.exports = {
return totalSize return totalSize
}, },
collapseBookSeries(libraryItems, series) {
var seriesObjects = this.getSeriesFromBooks(libraryItems, series, null, null, true) collapseBookSeries(libraryItems, series, filterSeries) {
var seriesToUse = {} // Get series from the library items. If this list is being collapsed after filtering for a series,
var collapsedLibraryItems = [] // don't collapse that series, only books that are in other series.
var seriesObjects = this
.getSeriesFromBooks(libraryItems, series, null, null, true)
.filter(s => s.id != filterSeries)
var filteredLibraryItems = []
libraryItems.forEach((li) => { libraryItems.forEach((li) => {
if (li.mediaType != 'book') return if (li.mediaType != 'book') return
// Handle when this is the first book in a series // Handle when this is the first book in a series
seriesObjects.filter(s => s.books[0].id == li.id).forEach(series => { seriesObjects.filter(s => s.books[0].id == li.id).forEach(series => {
let libraryItemJson = li.toJSONMinified() // Clone the library item as we need to attach data to it, but don't
libraryItemJson.collapsedSeries = { // want to change the global copy of the library item
id: series.id, filteredLibraryItems.push(Object.assign(
name: series.name, Object.create(Object.getPrototypeOf(li)),
nameIgnorePrefix: series.nameIgnorePrefix, li, { collapsedSeries: series }))
libraryItemIds: series.books.map(b => b.id),
numBooks: series.books.length
}
collapsedLibraryItems.push(libraryItemJson);
}); });
// Ignore books contained in series // Only included books not contained in series
if (li.media.metadata.series.length) return if (!seriesObjects.some(s => s.books.some(b => b.id == li.id)))
filteredLibraryItems.push(li)
collapsedLibraryItems.push(li.toJSONMinified())
}); });
return collapsedLibraryItems return filteredLibraryItems
}, },
buildPersonalizedShelves(user, libraryItems, mediaType, allSeries, allAuthors, maxEntitiesPerShelf = 10) { buildPersonalizedShelves(user, libraryItems, mediaType, allSeries, allAuthors, maxEntitiesPerShelf = 10) {