mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-06-22 08:53:41 +02:00
Remove old Author object & fix issue deleting empty authors
This commit is contained in:
parent
acc4bdbc5e
commit
ba742563c2
13 changed files with 227 additions and 314 deletions
|
@ -21,6 +21,11 @@ const naturalSort = createNewSortInstance({
|
|||
* @property {import('../models/User')} user
|
||||
*
|
||||
* @typedef {Request & RequestUserObject} RequestWithUser
|
||||
*
|
||||
* @typedef RequestEntityObject
|
||||
* @property {import('../models/Author')} author
|
||||
*
|
||||
* @typedef {RequestWithUser & RequestEntityObject} AuthorControllerRequest
|
||||
*/
|
||||
|
||||
class AuthorController {
|
||||
|
@ -29,13 +34,13 @@ class AuthorController {
|
|||
/**
|
||||
* GET: /api/authors/:id
|
||||
*
|
||||
* @param {RequestWithUser} req
|
||||
* @param {AuthorControllerRequest} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
async findOne(req, res) {
|
||||
const include = (req.query.include || '').split(',')
|
||||
|
||||
const authorJson = req.author.toJSON()
|
||||
const authorJson = req.author.toOldJSON()
|
||||
|
||||
// Used on author landing page to include library items and items grouped in series
|
||||
if (include.includes('items')) {
|
||||
|
@ -80,25 +85,30 @@ class AuthorController {
|
|||
/**
|
||||
* PATCH: /api/authors/:id
|
||||
*
|
||||
* @param {RequestWithUser} req
|
||||
* @param {AuthorControllerRequest} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
async update(req, res) {
|
||||
const payload = req.body
|
||||
let hasUpdated = false
|
||||
|
||||
// author imagePath must be set through other endpoints as of v2.4.5
|
||||
if (payload.imagePath !== undefined) {
|
||||
Logger.warn(`[AuthorController] Updating local author imagePath is not supported`)
|
||||
delete payload.imagePath
|
||||
const keysToUpdate = ['name', 'description', 'asin']
|
||||
const payload = {}
|
||||
for (const key in req.body) {
|
||||
if (keysToUpdate.includes(key) && (typeof req.body[key] === 'string' || req.body[key] === null)) {
|
||||
payload[key] = req.body[key]
|
||||
}
|
||||
}
|
||||
if (!Object.keys(payload).length) {
|
||||
Logger.error(`[AuthorController] Invalid request payload. No valid keys found`, req.body)
|
||||
return res.status(400).send('Invalid request payload. No valid keys found')
|
||||
}
|
||||
|
||||
let hasUpdated = false
|
||||
|
||||
const authorNameUpdate = payload.name !== undefined && payload.name !== req.author.name
|
||||
|
||||
// Check if author name matches another author and merge the authors
|
||||
let existingAuthor = null
|
||||
if (authorNameUpdate) {
|
||||
const author = await Database.authorModel.findOne({
|
||||
existingAuthor = await Database.authorModel.findOne({
|
||||
where: {
|
||||
id: {
|
||||
[sequelize.Op.not]: req.author.id
|
||||
|
@ -106,7 +116,6 @@ class AuthorController {
|
|||
name: payload.name
|
||||
}
|
||||
})
|
||||
existingAuthor = author?.getOldAuthor()
|
||||
}
|
||||
if (existingAuthor) {
|
||||
Logger.info(`[AuthorController] Merging author "${req.author.name}" with "${existingAuthor.name}"`)
|
||||
|
@ -143,86 +152,87 @@ class AuthorController {
|
|||
}
|
||||
|
||||
// Remove old author
|
||||
await Database.removeAuthor(req.author.id)
|
||||
SocketAuthority.emitter('author_removed', req.author.toJSON())
|
||||
const oldAuthorJSON = req.author.toOldJSON()
|
||||
await req.author.destroy()
|
||||
SocketAuthority.emitter('author_removed', oldAuthorJSON)
|
||||
// Update filter data
|
||||
Database.removeAuthorFromFilterData(req.author.libraryId, req.author.id)
|
||||
Database.removeAuthorFromFilterData(oldAuthorJSON.libraryId, oldAuthorJSON.id)
|
||||
|
||||
// Send updated num books for merged author
|
||||
const numBooks = await Database.bookAuthorModel.getCountForAuthor(existingAuthor.id)
|
||||
SocketAuthority.emitter('author_updated', existingAuthor.toJSONExpanded(numBooks))
|
||||
SocketAuthority.emitter('author_updated', existingAuthor.toOldJSONExpanded(numBooks))
|
||||
|
||||
res.json({
|
||||
author: existingAuthor.toJSON(),
|
||||
author: existingAuthor.toOldJSON(),
|
||||
merged: true
|
||||
})
|
||||
} else {
|
||||
// Regular author update
|
||||
if (req.author.update(payload)) {
|
||||
hasUpdated = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (hasUpdated) {
|
||||
req.author.updatedAt = Date.now()
|
||||
// Regular author update
|
||||
req.author.set(payload)
|
||||
if (req.author.changed()) {
|
||||
await req.author.save()
|
||||
hasUpdated = true
|
||||
}
|
||||
|
||||
let numBooksForAuthor = 0
|
||||
if (authorNameUpdate) {
|
||||
const allItemsWithAuthor = await Database.authorModel.getAllLibraryItemsForAuthor(req.author.id)
|
||||
if (hasUpdated) {
|
||||
let numBooksForAuthor = 0
|
||||
if (authorNameUpdate) {
|
||||
const allItemsWithAuthor = await Database.authorModel.getAllLibraryItemsForAuthor(req.author.id)
|
||||
|
||||
numBooksForAuthor = allItemsWithAuthor.length
|
||||
const oldLibraryItems = []
|
||||
// Update author name on all books
|
||||
for (const libraryItem of allItemsWithAuthor) {
|
||||
libraryItem.media.authors = libraryItem.media.authors.map((au) => {
|
||||
if (au.id === req.author.id) {
|
||||
au.name = req.author.name
|
||||
}
|
||||
return au
|
||||
})
|
||||
const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
|
||||
oldLibraryItems.push(oldLibraryItem)
|
||||
numBooksForAuthor = allItemsWithAuthor.length
|
||||
const oldLibraryItems = []
|
||||
// Update author name on all books
|
||||
for (const libraryItem of allItemsWithAuthor) {
|
||||
libraryItem.media.authors = libraryItem.media.authors.map((au) => {
|
||||
if (au.id === req.author.id) {
|
||||
au.name = req.author.name
|
||||
}
|
||||
return au
|
||||
})
|
||||
const oldLibraryItem = Database.libraryItemModel.getOldLibraryItem(libraryItem)
|
||||
oldLibraryItems.push(oldLibraryItem)
|
||||
|
||||
await libraryItem.saveMetadataFile()
|
||||
}
|
||||
|
||||
if (oldLibraryItems.length) {
|
||||
SocketAuthority.emitter(
|
||||
'items_updated',
|
||||
oldLibraryItems.map((li) => li.toJSONExpanded())
|
||||
)
|
||||
}
|
||||
} else {
|
||||
numBooksForAuthor = await Database.bookAuthorModel.getCountForAuthor(req.author.id)
|
||||
await libraryItem.saveMetadataFile()
|
||||
}
|
||||
|
||||
await Database.updateAuthor(req.author)
|
||||
SocketAuthority.emitter('author_updated', req.author.toJSONExpanded(numBooksForAuthor))
|
||||
if (oldLibraryItems.length) {
|
||||
SocketAuthority.emitter(
|
||||
'items_updated',
|
||||
oldLibraryItems.map((li) => li.toJSONExpanded())
|
||||
)
|
||||
}
|
||||
} else {
|
||||
numBooksForAuthor = await Database.bookAuthorModel.getCountForAuthor(req.author.id)
|
||||
}
|
||||
|
||||
res.json({
|
||||
author: req.author.toJSON(),
|
||||
updated: hasUpdated
|
||||
})
|
||||
SocketAuthority.emitter('author_updated', req.author.toOldJSONExpanded(numBooksForAuthor))
|
||||
}
|
||||
|
||||
res.json({
|
||||
author: req.author.toOldJSON(),
|
||||
updated: hasUpdated
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE: /api/authors/:id
|
||||
* Remove author from all books and delete
|
||||
*
|
||||
* @param {RequestWithUser} req
|
||||
* @param {AuthorControllerRequest} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
async delete(req, res) {
|
||||
Logger.info(`[AuthorController] Removing author "${req.author.name}"`)
|
||||
|
||||
await Database.authorModel.removeById(req.author.id)
|
||||
|
||||
if (req.author.imagePath) {
|
||||
await CacheManager.purgeImageCache(req.author.id) // Purge cache
|
||||
}
|
||||
|
||||
SocketAuthority.emitter('author_removed', req.author.toJSON())
|
||||
await req.author.destroy()
|
||||
|
||||
SocketAuthority.emitter('author_removed', req.author.toOldJSON())
|
||||
|
||||
// Update filter data
|
||||
Database.removeAuthorFromFilterData(req.author.libraryId, req.author.id)
|
||||
|
@ -234,7 +244,7 @@ class AuthorController {
|
|||
* POST: /api/authors/:id/image
|
||||
* Upload author image from web URL
|
||||
*
|
||||
* @param {RequestWithUser} req
|
||||
* @param {AuthorControllerRequest} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
async uploadImage(req, res) {
|
||||
|
@ -265,13 +275,14 @@ class AuthorController {
|
|||
}
|
||||
|
||||
req.author.imagePath = result.path
|
||||
req.author.updatedAt = Date.now()
|
||||
await Database.authorModel.updateFromOld(req.author)
|
||||
// imagePath may not have changed, but we still want to update the updatedAt field to bust image cache
|
||||
req.author.changed('imagePath', true)
|
||||
await req.author.save()
|
||||
|
||||
const numBooks = await Database.bookAuthorModel.getCountForAuthor(req.author.id)
|
||||
SocketAuthority.emitter('author_updated', req.author.toJSONExpanded(numBooks))
|
||||
SocketAuthority.emitter('author_updated', req.author.toOldJSONExpanded(numBooks))
|
||||
res.json({
|
||||
author: req.author.toJSON()
|
||||
author: req.author.toOldJSON()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -279,7 +290,7 @@ class AuthorController {
|
|||
* DELETE: /api/authors/:id/image
|
||||
* Remove author image & delete image file
|
||||
*
|
||||
* @param {RequestWithUser} req
|
||||
* @param {AuthorControllerRequest} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
async deleteImage(req, res) {
|
||||
|
@ -291,19 +302,19 @@ class AuthorController {
|
|||
await CacheManager.purgeImageCache(req.author.id) // Purge cache
|
||||
await CoverManager.removeFile(req.author.imagePath)
|
||||
req.author.imagePath = null
|
||||
await Database.authorModel.updateFromOld(req.author)
|
||||
await req.author.save()
|
||||
|
||||
const numBooks = await Database.bookAuthorModel.getCountForAuthor(req.author.id)
|
||||
SocketAuthority.emitter('author_updated', req.author.toJSONExpanded(numBooks))
|
||||
SocketAuthority.emitter('author_updated', req.author.toOldJSONExpanded(numBooks))
|
||||
res.json({
|
||||
author: req.author.toJSON()
|
||||
author: req.author.toOldJSON()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* POST: /api/authors/:id/match
|
||||
*
|
||||
* @param {RequestWithUser} req
|
||||
* @param {AuthorControllerRequest} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
async match(req, res) {
|
||||
|
@ -342,24 +353,22 @@ class AuthorController {
|
|||
}
|
||||
|
||||
if (hasUpdates) {
|
||||
req.author.updatedAt = Date.now()
|
||||
|
||||
await Database.updateAuthor(req.author)
|
||||
await req.author.save()
|
||||
|
||||
const numBooks = await Database.bookAuthorModel.getCountForAuthor(req.author.id)
|
||||
SocketAuthority.emitter('author_updated', req.author.toJSONExpanded(numBooks))
|
||||
SocketAuthority.emitter('author_updated', req.author.toOldJSONExpanded(numBooks))
|
||||
}
|
||||
|
||||
res.json({
|
||||
updated: hasUpdates,
|
||||
author: req.author
|
||||
author: req.author.toOldJSON()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* GET: /api/authors/:id/image
|
||||
*
|
||||
* @param {RequestWithUser} req
|
||||
* @param {AuthorControllerRequest} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
async getImage(req, res) {
|
||||
|
@ -392,7 +401,7 @@ class AuthorController {
|
|||
* @param {NextFunction} next
|
||||
*/
|
||||
async middleware(req, res, next) {
|
||||
const author = await Database.authorModel.getOldById(req.params.id)
|
||||
const author = await Database.authorModel.findByPk(req.params.id)
|
||||
if (!author) return res.sendStatus(404)
|
||||
|
||||
if (req.method == 'DELETE' && !req.user.canDelete) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue