mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +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" /> | ||||
|         <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-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> | ||||
|       </template> | ||||
| @ -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() { | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -20,6 +20,7 @@ | ||||
|     </div> | ||||
|     <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 v-if="displaySortLine" class="truncate text-gray-400" :style="{ fontSize: 0.8 * sizeMultiplier + 'rem' }">{{ displaySortLine }}</p> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| @ -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) => { | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 }) | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
|   } | ||||
| } | ||||
| @ -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) { | ||||
|  | ||||
| @ -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) => { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user