diff --git a/client/pages/config/index.vue b/client/pages/config/index.vue index af84317d..b021c9c7 100644 --- a/client/pages/config/index.vue +++ b/client/pages/config/index.vue @@ -36,7 +36,10 @@
- + +
+ Save +
@@ -260,8 +263,10 @@ export default { homepageUseBookshelfView: false, useBookshelfView: false, isPurgingCache: false, + hasPrefixesChanged: false, newServerSettings: {}, showConfirmPurgeCache: false, + savingPrefixes: false, metadataFileFormats: [ { text: '.json', @@ -304,15 +309,36 @@ export default { } }, methods: { - updateSortingPrefixes(val) { - if (!val || !val.length) { + sortingPrefixesUpdated(val) { + const prefixes = [...new Set(val?.map((prefix) => prefix.trim().toLowerCase()) || [])] + this.newServerSettings.sortingPrefixes = prefixes + const serverPrefixes = this.serverSettings.sortingPrefixes || [] + this.hasPrefixesChanged = prefixes.some((p) => !serverPrefixes.includes(p)) || serverPrefixes.some((p) => !prefixes.includes(p)) + }, + updateSortingPrefixes() { + const prefixes = [...new Set(this.newServerSettings.sortingPrefixes.map((prefix) => prefix.trim().toLowerCase()) || [])] + if (!prefixes.length) { this.$toast.error('Must have at least 1 prefix') return } - var prefixes = val.map((prefix) => prefix.trim().toLowerCase()) - this.updateServerSettings({ - sortingPrefixes: prefixes - }) + + this.savingPrefixes = true + this.$axios + .$patch(`/api/sorting-prefixes`, { sortingPrefixes: prefixes }) + .then((data) => { + this.$toast.success(`Sorting prefixes updated. ${data.rowsUpdated} rows`) + if (data.serverSettings) { + this.$store.commit('setServerSettings', data.serverSettings) + } + this.hasPrefixesChanged = false + }) + .catch((error) => { + console.error('Failed to update prefixes', error) + this.$toast.error('Failed to update sorting prefixes') + }) + .finally(() => { + this.savingPrefixes = false + }) }, updateScannerCoverProvider(val) { this.updateServerSettings({ diff --git a/server/controllers/MiscController.js b/server/controllers/MiscController.js index 3220eddf..0fa1c62f 100644 --- a/server/controllers/MiscController.js +++ b/server/controllers/MiscController.js @@ -7,7 +7,7 @@ const Database = require('../Database') const libraryItemFilters = require('../utils/queries/libraryItemFilters') const patternValidation = require('../libs/nodeCron/pattern-validation') -const { isObject } = require('../utils/index') +const { isObject, getTitleIgnorePrefix } = require('../utils/index') // // This is a controller for routes that don't have a home yet :( @@ -127,7 +127,7 @@ class MiscController { } const settingsUpdate = req.body if (!settingsUpdate || !isObject(settingsUpdate)) { - return res.status(500).send('Invalid settings update object') + return res.status(400).send('Invalid settings update object') } const madeUpdates = Database.serverSettings.update(settingsUpdate) @@ -145,6 +145,103 @@ class MiscController { }) } + /** + * PATCH: /api/sorting-prefixes + * + * @param {import('express').Request} req + * @param {import('express').Response} res + */ + async updateSortingPrefixes(req, res) { + if (!req.user.isAdminOrUp) { + Logger.error('User other than admin attempting to update server sorting prefixes', req.user) + return res.sendStatus(403) + } + let sortingPrefixes = req.body.sortingPrefixes + if (!sortingPrefixes?.length || !Array.isArray(sortingPrefixes)) { + return res.status(400).send('Invalid request body') + } + sortingPrefixes = [...new Set(sortingPrefixes.map(p => p?.trim?.().toLowerCase()).filter(p => p))] + if (!sortingPrefixes.length) { + return res.status(400).send('Invalid sortingPrefixes in request body') + } + + Logger.debug(`[MiscController] Updating sorting prefixes ${sortingPrefixes.join(', ')}`) + Database.serverSettings.sortingPrefixes = sortingPrefixes + await Database.updateServerSettings() + + let rowsUpdated = 0 + // Update titleIgnorePrefix column on books + const books = await Database.bookModel.findAll({ + attributes: ['id', 'title', 'titleIgnorePrefix'] + }) + const bulkUpdateBooks = [] + books.forEach((book) => { + const titleIgnorePrefix = getTitleIgnorePrefix(book.title) + if (titleIgnorePrefix !== book.titleIgnorePrefix) { + bulkUpdateBooks.push({ + id: book.id, + titleIgnorePrefix + }) + } + }) + if (bulkUpdateBooks.length) { + Logger.info(`[MiscController] Updating titleIgnorePrefix on ${bulkUpdateBooks.length} books`) + rowsUpdated += bulkUpdateBooks.length + await Database.bookModel.bulkCreate(bulkUpdateBooks, { + updateOnDuplicate: ['titleIgnorePrefix'] + }) + } + + // Update titleIgnorePrefix column on podcasts + const podcasts = await Database.podcastModel.findAll({ + attributes: ['id', 'title', 'titleIgnorePrefix'] + }) + const bulkUpdatePodcasts = [] + podcasts.forEach((podcast) => { + const titleIgnorePrefix = getTitleIgnorePrefix(podcast.title) + if (titleIgnorePrefix !== podcast.titleIgnorePrefix) { + bulkUpdatePodcasts.push({ + id: podcast.id, + titleIgnorePrefix + }) + } + }) + if (bulkUpdatePodcasts.length) { + Logger.info(`[MiscController] Updating titleIgnorePrefix on ${bulkUpdatePodcasts.length} podcasts`) + rowsUpdated += bulkUpdatePodcasts.length + await Database.podcastModel.bulkCreate(bulkUpdatePodcasts, { + updateOnDuplicate: ['titleIgnorePrefix'] + }) + } + + // Update nameIgnorePrefix column on series + const allSeries = await Database.seriesModel.findAll({ + attributes: ['id', 'name', 'nameIgnorePrefix'] + }) + const bulkUpdateSeries = [] + allSeries.forEach((series) => { + const nameIgnorePrefix = getTitleIgnorePrefix(series.name) + if (nameIgnorePrefix !== series.nameIgnorePrefix) { + bulkUpdateSeries.push({ + id: series.id, + nameIgnorePrefix + }) + } + }) + if (bulkUpdateSeries.length) { + Logger.info(`[MiscController] Updating nameIgnorePrefix on ${bulkUpdateSeries.length} series`) + rowsUpdated += bulkUpdateSeries.length + await Database.seriesModel.bulkCreate(bulkUpdateSeries, { + updateOnDuplicate: ['nameIgnorePrefix'] + }) + } + + res.json({ + rowsUpdated, + serverSettings: Database.serverSettings.toJSONForBrowser() + }) + } + /** * POST: /api/authorize * Used to authorize an API token diff --git a/server/objects/settings/ServerSettings.js b/server/objects/settings/ServerSettings.js index e0416e66..8daca57a 100644 --- a/server/objects/settings/ServerSettings.js +++ b/server/objects/settings/ServerSettings.js @@ -43,7 +43,7 @@ class ServerSettings { // Sorting this.sortingIgnorePrefix = false - this.sortingPrefixes = ['the'] + this.sortingPrefixes = ['the', 'a'] // Misc Flags this.chromecastEnabled = false diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js index d01a7322..a0bb2544 100644 --- a/server/routers/ApiRouter.js +++ b/server/routers/ApiRouter.js @@ -297,6 +297,7 @@ class ApiRouter { this.router.post('/upload', MiscController.handleUpload.bind(this)) this.router.get('/tasks', MiscController.getTasks.bind(this)) this.router.patch('/settings', MiscController.updateServerSettings.bind(this)) + this.router.patch('/sorting-prefixes', MiscController.updateSortingPrefixes.bind(this)) this.router.post('/authorize', MiscController.authorize.bind(this)) this.router.get('/tags', MiscController.getAllTags.bind(this)) this.router.post('/tags/rename', MiscController.renameTag.bind(this))