mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-03-01 05:29:41 +00:00
Allow removing authors with no books
This commit is contained in:
parent
771f8c586f
commit
fc97b10f58
4 changed files with 127 additions and 16 deletions
|
|
@ -1412,6 +1412,57 @@ class LibraryController {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE: /api/libraries/:id/authors/cleanup
|
||||
* Remove authors with no books, no description and no image
|
||||
*
|
||||
* @this {import('../routers/ApiRouter')}
|
||||
*
|
||||
* @param {LibraryControllerRequest} req
|
||||
* @param {Response} res
|
||||
*/
|
||||
async cleanupAuthorsWithNoBooks(req, res) {
|
||||
if (!req.user.isAdminOrUp) {
|
||||
Logger.error(`[LibraryController] Non-admin user "${req.user.username}" attempted to cleanup authors`)
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
|
||||
const force = req.query.force === '1'
|
||||
|
||||
const authors = await Database.authorModel.findAll({
|
||||
where: {
|
||||
libraryId: req.library.id
|
||||
},
|
||||
attributes: ['id']
|
||||
})
|
||||
|
||||
if (!authors.length) {
|
||||
return res.json({
|
||||
removed: 0
|
||||
})
|
||||
}
|
||||
|
||||
const authorIds = authors.map((a) => a.id)
|
||||
const initialCount = authorIds.length
|
||||
|
||||
// This method is defined on ApiRouter
|
||||
await this.checkRemoveAuthorsWithNoBooks(authorIds, force)
|
||||
|
||||
// Check how many are left
|
||||
const remainingCount = await Database.authorModel.count({
|
||||
where: {
|
||||
id: authorIds
|
||||
}
|
||||
})
|
||||
|
||||
const removed = initialCount - remainingCount
|
||||
Logger.info(`[LibraryController] Cleaned up ${removed} authors (force=${force}) with no books for library "${req.library.name}"`)
|
||||
|
||||
res.json({
|
||||
removed
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* GET: /api/library/:id/download
|
||||
* Downloads multiple library items
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ class ApiRouter {
|
|||
this.router.get('/libraries/:id/search', LibraryController.middleware.bind(this), LibraryController.search.bind(this))
|
||||
this.router.get('/libraries/:id/stats', LibraryController.middleware.bind(this), LibraryController.stats.bind(this))
|
||||
this.router.get('/libraries/:id/authors', LibraryController.middleware.bind(this), LibraryController.getAuthors.bind(this))
|
||||
this.router.delete('/libraries/:id/authors/cleanup', LibraryController.middleware.bind(this), LibraryController.cleanupAuthorsWithNoBooks.bind(this))
|
||||
this.router.get('/libraries/:id/narrators', LibraryController.middleware.bind(this), LibraryController.getNarrators.bind(this))
|
||||
this.router.patch('/libraries/:id/narrators/:narratorId', LibraryController.middleware.bind(this), LibraryController.updateNarrator.bind(this))
|
||||
this.router.delete('/libraries/:id/narrators/:narratorId', LibraryController.middleware.bind(this), LibraryController.removeNarrator.bind(this))
|
||||
|
|
@ -464,29 +465,38 @@ class ApiRouter {
|
|||
* @param {string[]} authorIds
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async checkRemoveAuthorsWithNoBooks(authorIds) {
|
||||
async checkRemoveAuthorsWithNoBooks(authorIds, force = false) {
|
||||
if (!authorIds?.length) return
|
||||
|
||||
const transaction = await Database.sequelize.transaction()
|
||||
try {
|
||||
const where = [
|
||||
sequelize.where(sequelize.literal('(SELECT count(*) FROM bookAuthors ba WHERE ba.authorId = author.id)'), 0)
|
||||
]
|
||||
|
||||
if (!force) {
|
||||
where.push({
|
||||
id: authorIds,
|
||||
asin: {
|
||||
[sequelize.Op.or]: [null, '']
|
||||
},
|
||||
description: {
|
||||
[sequelize.Op.or]: [null, '']
|
||||
},
|
||||
imagePath: {
|
||||
[sequelize.Op.or]: [null, '']
|
||||
}
|
||||
})
|
||||
} else {
|
||||
where.push({
|
||||
id: authorIds
|
||||
})
|
||||
}
|
||||
|
||||
// Select authors with locking to prevent concurrent updates
|
||||
const bookAuthorsToRemove = (
|
||||
await Database.authorModel.findAll({
|
||||
where: [
|
||||
{
|
||||
id: authorIds,
|
||||
asin: {
|
||||
[sequelize.Op.or]: [null, '']
|
||||
},
|
||||
description: {
|
||||
[sequelize.Op.or]: [null, '']
|
||||
},
|
||||
imagePath: {
|
||||
[sequelize.Op.or]: [null, '']
|
||||
}
|
||||
},
|
||||
sequelize.where(sequelize.literal('(SELECT count(*) FROM bookAuthors ba WHERE ba.authorId = author.id)'), 0)
|
||||
],
|
||||
where,
|
||||
attributes: ['id', 'name', 'libraryId'],
|
||||
raw: true,
|
||||
transaction
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue