diff --git a/client/components/app/BookShelfCategorized.vue b/client/components/app/BookShelfCategorized.vue index 202f4992..df97957c 100644 --- a/client/components/app/BookShelfCategorized.vue +++ b/client/components/app/BookShelfCategorized.vue @@ -405,8 +405,6 @@ export default { } }, removeListeners() { - this.$store.commit('user/removeSettingsListener', 'bookshelf') - if (this.$root.socket) { this.$root.socket.off('user_updated', this.userUpdated) this.$root.socket.off('author_updated', this.authorUpdated) diff --git a/client/pages/config/item-metadata-utils/genres.vue b/client/pages/config/item-metadata-utils/genres.vue new file mode 100644 index 00000000..4f4347b4 --- /dev/null +++ b/client/pages/config/item-metadata-utils/genres.vue @@ -0,0 +1,169 @@ + + + + + arrow_back + + + Manage Genres + + + No Genres + + + + + {{ genre }} + + + + + + + + {{ $strings.ButtonSave }} + {{ $strings.ButtonCancel }} + + + + + + + + + + + + diff --git a/client/pages/config/item-metadata-utils/index.vue b/client/pages/config/item-metadata-utils/index.vue index f9e73d1e..7c472a39 100644 --- a/client/pages/config/item-metadata-utils/index.vue +++ b/client/pages/config/item-metadata-utils/index.vue @@ -7,12 +7,12 @@ arrow_forward - + diff --git a/client/pages/config/item-metadata-utils/tags.vue b/client/pages/config/item-metadata-utils/tags.vue index 51a5b92c..759a56b7 100644 --- a/client/pages/config/item-metadata-utils/tags.vue +++ b/client/pages/config/item-metadata-utils/tags.vue @@ -76,8 +76,6 @@ export default { const tagNameExists = this.tags.find((t) => t !== this.editingTag && t === this.newTagName) const tagNameExistsOfDifferentCase = !tagNameExists ? this.tags.find((t) => t !== this.editingTag && t.toLowerCase() === this.newTagName.toLowerCase()) : null - console.log('Tag name', this.newTagName, 'ExistS?', tagNameExists, tagNameExistsOfDifferentCase) - console.log('Saving tag', this.editingTag, 'with name', this.newTagName) let message = `Are you sure you want to rename tag "${this.editingTag}" to "${this.newTagName}" for all items?` if (tagNameExists) { diff --git a/server/controllers/MiscController.js b/server/controllers/MiscController.js index 7d35de46..dddc82a8 100644 --- a/server/controllers/MiscController.js +++ b/server/controllers/MiscController.js @@ -212,6 +212,92 @@ class MiscController { }) } + // GET: api/genres + getAllGenres(req, res) { + if (!req.user.isAdminOrUp) { + Logger.error(`[MiscController] Non-admin user attempted to getAllGenres`) + return res.sendStatus(404) + } + const genres = [] + this.db.libraryItems.forEach((li) => { + if (li.media.metadata.genres && li.media.metadata.genres.length) { + li.media.metadata.genres.forEach((genre) => { + if (!genres.includes(genre)) genres.push(genre) + }) + } + }) + res.json({ + genres + }) + } + + // POST: api/genres/rename + async renameGenre(req, res) { + if (!req.user.isAdminOrUp) { + Logger.error(`[MiscController] Non-admin user attempted to renameGenre`) + return res.sendStatus(404) + } + + const genre = req.body.genre + const newGenre = req.body.newGenre + if (!genre || !newGenre) { + Logger.error(`[MiscController] Invalid request body for renameGenre`) + return res.sendStatus(400) + } + + let genreMerged = false + let numItemsUpdated = 0 + + for (const li of this.db.libraryItems) { + if (!li.media.metadata.genres || !li.media.metadata.genres.length) continue + + if (li.media.metadata.genres.includes(newGenre)) genreMerged = true // new genre is an existing genre so this is a merge + + if (li.media.metadata.genres.includes(genre)) { + li.media.metadata.genres = li.media.metadata.genres.filter(g => g !== genre) // Remove old genre + if (!li.media.metadata.genres.includes(newGenre)) { + li.media.metadata.genres.push(newGenre) // Add new genre + } + Logger.debug(`[MiscController] Rename genre "${genre}" to "${newGenre}" for item "${li.media.metadata.title}"`) + await this.db.updateLibraryItem(li) + SocketAuthority.emitter('item_updated', li.toJSONExpanded()) + numItemsUpdated++ + } + } + + res.json({ + genreMerged, + numItemsUpdated + }) + } + + // DELETE: api/genres/:genre + async deleteGenre(req, res) { + if (!req.user.isAdminOrUp) { + Logger.error(`[MiscController] Non-admin user attempted to deleteGenre`) + return res.sendStatus(404) + } + + const genre = Buffer.from(decodeURIComponent(req.params.genre), 'base64').toString() + + let numItemsUpdated = 0 + for (const li of this.db.libraryItems) { + if (!li.media.metadata.genres || !li.media.metadata.genres.length) continue + + if (li.media.metadata.genres.includes(genre)) { + li.media.metadata.genres = li.media.metadata.genres.filter(t => t !== genre) + Logger.debug(`[MiscController] Remove genre "${genre}" from item "${li.media.metadata.title}"`) + await this.db.updateLibraryItem(li) + SocketAuthority.emitter('item_updated', li.toJSONExpanded()) + numItemsUpdated++ + } + } + + res.json({ + numItemsUpdated + }) + } + validateCronExpression(req, res) { const expression = req.body.expression if (!expression) { diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js index f2c54ca4..30e00fa7 100644 --- a/server/routers/ApiRouter.js +++ b/server/routers/ApiRouter.js @@ -274,6 +274,9 @@ class ApiRouter { this.router.get('/tags', MiscController.getAllTags.bind(this)) this.router.post('/tags/rename', MiscController.renameTag.bind(this)) this.router.delete('/tags/:tag', MiscController.deleteTag.bind(this)) + this.router.get('/genres', MiscController.getAllGenres.bind(this)) + this.router.post('/genres/rename', MiscController.renameGenre.bind(this)) + this.router.delete('/genres/:genre', MiscController.deleteGenre.bind(this)) this.router.post('/validate-cron', MiscController.validateCronExpression.bind(this)) }
No Genres
{{ genre }}