From f38b6636e3915dcce2741d816835d7c341942199 Mon Sep 17 00:00:00 2001 From: Greg Lorenzen <31518305+glorenzen@users.noreply.github.com> Date: Tue, 8 Oct 2024 15:20:42 -0700 Subject: [PATCH] Add published decade filter option (#3489) * Add strings for PublishedDecade and PublishedDecades * Add publishedDecades filter options to LibraryFilterSelect * Add publishedDecades to libraries store * Add publishedDecades to getFilterData * Add database method to add published decades to filter data * Add published decade in BookScanner * Add 'publishedDecades' to invalidFilters in user.js * Add publishedDecades filter group to MediaGroupQuery * Update client/strings/en-us.json * Auto formatting --------- Co-authored-by: advplyr Co-authored-by: advplyr --- client/components/controls/LibraryFilterSelect.vue | 9 +++++++++ client/store/libraries.js | 13 ++++++++++++- client/store/user.js | 2 +- client/strings/en-us.json | 2 ++ server/Database.js | 5 +++++ server/scanner/BookScanner.js | 4 ++++ server/utils/queries/libraryFilters.js | 11 +++++++++-- server/utils/queries/libraryItemsBookFilters.js | 5 +++++ 8 files changed, 47 insertions(+), 4 deletions(-) diff --git a/client/components/controls/LibraryFilterSelect.vue b/client/components/controls/LibraryFilterSelect.vue index 2bc6109a..ba96f103 100644 --- a/client/components/controls/LibraryFilterSelect.vue +++ b/client/components/controls/LibraryFilterSelect.vue @@ -189,6 +189,12 @@ export default { value: 'publishers', sublist: true }, + { + text: this.$strings.LabelPublishedDecade, + textPlural: this.$strings.LabelPublishedDecades, + value: 'publishedDecades', + sublist: true + }, { text: this.$strings.LabelLanguage, textPlural: this.$strings.LabelLanguages, @@ -338,6 +344,9 @@ export default { publishers() { return this.filterData.publishers || [] }, + publishedDecades() { + return this.filterData.publishedDecades || [] + }, progress() { return [ { diff --git a/client/store/libraries.js b/client/store/libraries.js index b92800ba..81d32577 100644 --- a/client/store/libraries.js +++ b/client/store/libraries.js @@ -240,7 +240,8 @@ export const mutations = { series: [], narrators: [], languages: [], - publishers: [] + publishers: [], + publishedDecades: [] } */ const mediaMetadata = libraryItem.media.metadata @@ -307,6 +308,16 @@ export const mutations = { state.filterData.publishers.sort((a, b) => a.localeCompare(b)) } + // Add publishedDecades + if (mediaMetadata.publishedYear) { + const publishedYear = parseInt(mediaMetadata.publishedYear, 10) + const decade = Math.floor(publishedYear / 10) * 10 + if (!state.filterData.publishedDecades.includes(decade)) { + state.filterData.publishedDecades.push(decade) + state.filterData.publishedDecades.sort((a, b) => a - b) + } + } + // Add language if (mediaMetadata.language && !state.filterData.languages.includes(mediaMetadata.language)) { state.filterData.languages.push(mediaMetadata.language) diff --git a/client/store/user.js b/client/store/user.js index 10dc8ef6..0e4cc0cc 100644 --- a/client/store/user.js +++ b/client/store/user.js @@ -90,7 +90,7 @@ export const actions = { if (state.settings.orderBy == 'media.metadata.publishedYear') { settingsUpdate.orderBy = 'media.metadata.title' } - const invalidFilters = ['series', 'authors', 'narrators', 'publishers', 'languages', 'progress', 'issues', 'ebooks', 'abridged'] + const invalidFilters = ['series', 'authors', 'narrators', 'publishers', 'publishedDecades', 'languages', 'progress', 'issues', 'ebooks', 'abridged'] const filterByFirstPart = (state.settings.filterBy || '').split('.').shift() if (invalidFilters.includes(filterByFirstPart)) { settingsUpdate.filterBy = 'all' diff --git a/client/strings/en-us.json b/client/strings/en-us.json index 862ed117..34b014dc 100644 --- a/client/strings/en-us.json +++ b/client/strings/en-us.json @@ -465,6 +465,8 @@ "LabelPubDate": "Pub Date", "LabelPublishYear": "Publish Year", "LabelPublishedDate": "Published {0}", + "LabelPublishedDecade": "Published Decade", + "LabelPublishedDecades": "Published Decades", "LabelPublisher": "Publisher", "LabelPublishers": "Publishers", "LabelRSSFeedCustomOwnerEmail": "Custom owner Email", diff --git a/server/Database.js b/server/Database.js index a936327d..9bce2605 100644 --- a/server/Database.js +++ b/server/Database.js @@ -607,6 +607,11 @@ class Database { this.libraryFilterData[libraryId].publishers.push(publisher) } + addPublishedDecadeToFilterData(libraryId, decade) { + if (!this.libraryFilterData[libraryId] || !decade || this.libraryFilterData[libraryId].publishedDecades.includes(decade)) return + this.libraryFilterData[libraryId].publishedDecades.push(decade) + } + addLanguageToFilterData(libraryId, language) { if (!this.libraryFilterData[libraryId] || !language || this.libraryFilterData[libraryId].languages.includes(language)) return this.libraryFilterData[libraryId].languages.push(language) diff --git a/server/scanner/BookScanner.js b/server/scanner/BookScanner.js index 279fcf64..f0737dac 100644 --- a/server/scanner/BookScanner.js +++ b/server/scanner/BookScanner.js @@ -590,6 +590,10 @@ class BookScanner { Database.addPublisherToFilterData(libraryItemData.libraryId, libraryItem.book.publisher) Database.addLanguageToFilterData(libraryItemData.libraryId, libraryItem.book.language) + const publishedYear = libraryItem.book.publishedYear + const decade = publishedYear ? `${Math.floor(publishedYear / 10) * 10}` : null + Database.addPublishedDecadeToFilterData(libraryItemData.libraryId, decade) + // Load for emitting to client libraryItem.media = await libraryItem.getMedia({ include: [ diff --git a/server/utils/queries/libraryFilters.js b/server/utils/queries/libraryFilters.js index 5c7b7dc0..fc04fefc 100644 --- a/server/utils/queries/libraryFilters.js +++ b/server/utils/queries/libraryFilters.js @@ -26,7 +26,7 @@ module.exports = { let filterValue = null let filterGroup = null if (filterBy) { - const searchGroups = ['genres', 'tags', 'series', 'authors', 'progress', 'narrators', 'publishers', 'missing', 'languages', 'tracks', 'ebooks'] + const searchGroups = ['genres', 'tags', 'series', 'authors', 'progress', 'narrators', 'publishers', 'publishedDecades', 'missing', 'languages', 'tracks', 'ebooks'] const group = searchGroups.find((_group) => filterBy.startsWith(_group + '.')) filterGroup = group || filterBy filterValue = group ? this.decode(filterBy.replace(`${group}.`, '')) : null @@ -458,6 +458,7 @@ module.exports = { narrators: new Set(), languages: new Set(), publishers: new Set(), + publishedDecades: new Set(), numIssues: 0 } @@ -492,7 +493,7 @@ module.exports = { libraryId: libraryId } }, - attributes: ['tags', 'genres', 'publisher', 'narrators', 'language'] + attributes: ['tags', 'genres', 'publisher', 'publishedYear', 'narrators', 'language'] }) for (const book of books) { if (book.libraryItem.isMissing || book.libraryItem.isInvalid) data.numIssues++ @@ -506,6 +507,11 @@ module.exports = { book.narrators.forEach((narrator) => data.narrators.add(narrator)) } if (book.publisher) data.publishers.add(book.publisher) + // Check if published year exists and is valid + if (book.publishedYear && !isNaN(book.publishedYear) && book.publishedYear > 0 && book.publishedYear < 3000 && book.publishedYear.toString().length === 4) { + const decade = Math.floor(book.publishedYear / 10) * 10 + data.publishedDecades.add(decade.toString()) + } if (book.language) data.languages.add(book.language) } @@ -532,6 +538,7 @@ module.exports = { data.series = naturalSort(data.series).asc((se) => se.name) data.narrators = naturalSort([...data.narrators]).asc() data.publishers = naturalSort([...data.publishers]).asc() + data.publishedDecades = naturalSort([...data.publishedDecades]).asc() data.languages = naturalSort([...data.languages]).asc() data.loadedAt = Date.now() Database.libraryFilterData[libraryId] = data diff --git a/server/utils/queries/libraryItemsBookFilters.js b/server/utils/queries/libraryItemsBookFilters.js index 6bafa225..43ffafef 100644 --- a/server/utils/queries/libraryItemsBookFilters.js +++ b/server/utils/queries/libraryItemsBookFilters.js @@ -228,6 +228,11 @@ module.exports = { } else if (value === 'series') { mediaWhere['$series.id$'] = null } + } else if (group === 'publishedDecades') { + const year = parseInt(value, 10) + mediaWhere['publishedYear'] = { + [Sequelize.Op.between]: year >= 1000 ? [year, year + 9] : [year * 10, (year + 1) * 10 - 1] + } } return { mediaWhere, replacements }