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 @@
@@ -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))