mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-12-20 18:59:37 +00:00
Update:Remove image path input from author modal, add API endpoints for uploading and removing author image
This commit is contained in:
parent
290a377ef9
commit
656c81a1fa
4 changed files with 162 additions and 80 deletions
|
|
@ -67,30 +67,10 @@ class AuthorController {
|
|||
const payload = req.body
|
||||
let hasUpdated = false
|
||||
|
||||
// Updating/removing cover image
|
||||
if (payload.imagePath !== undefined && payload.imagePath !== req.author.imagePath) {
|
||||
if (!payload.imagePath && req.author.imagePath) { // If removing image then remove file
|
||||
await CacheManager.purgeImageCache(req.author.id) // Purge cache
|
||||
await CoverManager.removeFile(req.author.imagePath)
|
||||
} else if (payload.imagePath.startsWith('http')) { // Check if image path is a url
|
||||
const imageData = await AuthorFinder.saveAuthorImage(req.author.id, payload.imagePath)
|
||||
if (imageData) {
|
||||
if (req.author.imagePath) {
|
||||
await CacheManager.purgeImageCache(req.author.id) // Purge cache
|
||||
}
|
||||
payload.imagePath = imageData.path
|
||||
hasUpdated = true
|
||||
}
|
||||
} else if (payload.imagePath && payload.imagePath !== req.author.imagePath) { // Changing image path locally
|
||||
if (!await fs.pathExists(payload.imagePath)) { // Make sure image path exists
|
||||
Logger.error(`[AuthorController] Image path does not exist: "${payload.imagePath}"`)
|
||||
return res.status(400).send('Author image path does not exist')
|
||||
}
|
||||
|
||||
if (req.author.imagePath) {
|
||||
await CacheManager.purgeImageCache(req.author.id) // Purge cache
|
||||
}
|
||||
}
|
||||
// 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 authorNameUpdate = payload.name !== undefined && payload.name !== req.author.name
|
||||
|
|
@ -131,7 +111,7 @@ class AuthorController {
|
|||
Database.removeAuthorFromFilterData(req.author.libraryId, req.author.id)
|
||||
|
||||
// Send updated num books for merged author
|
||||
const numBooks = await Database.libraryItemModel.getForAuthor(existingAuthor).length
|
||||
const numBooks = (await Database.libraryItemModel.getForAuthor(existingAuthor)).length
|
||||
SocketAuthority.emitter('author_updated', existingAuthor.toJSONExpanded(numBooks))
|
||||
|
||||
res.json({
|
||||
|
|
@ -191,6 +171,75 @@ class AuthorController {
|
|||
res.sendStatus(200)
|
||||
}
|
||||
|
||||
/**
|
||||
* POST: /api/authors/:id/image
|
||||
* Upload author image from web URL
|
||||
*
|
||||
* @param {import('express').Request} req
|
||||
* @param {import('express').Response} res
|
||||
*/
|
||||
async uploadImage(req, res) {
|
||||
if (!req.user.canUpload) {
|
||||
Logger.warn('User attempted to upload an image without permission', req.user)
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
if (!req.body.url) {
|
||||
Logger.error(`[AuthorController] Invalid request payload. 'url' not in request body`)
|
||||
return res.status(400).send(`Invalid request payload. 'url' not in request body`)
|
||||
}
|
||||
if (!req.body.url.startsWith?.('http:') && !req.body.url.startsWith?.('https:')) {
|
||||
Logger.error(`[AuthorController] Invalid request payload. Invalid url "${req.body.url}"`)
|
||||
return res.status(400).send(`Invalid request payload. Invalid url "${req.body.url}"`)
|
||||
}
|
||||
|
||||
Logger.debug(`[AuthorController] Requesting download author image from url "${req.body.url}"`)
|
||||
const result = await AuthorFinder.saveAuthorImage(req.author.id, req.body.url)
|
||||
|
||||
if (result?.error) {
|
||||
return res.status(400).send(result.error)
|
||||
} else if (!result?.path) {
|
||||
return res.status(500).send('Unknown error occurred')
|
||||
}
|
||||
|
||||
if (req.author.imagePath) {
|
||||
await CacheManager.purgeImageCache(req.author.id) // Purge cache
|
||||
}
|
||||
|
||||
req.author.imagePath = result.path
|
||||
await Database.authorModel.updateFromOld(req.author)
|
||||
|
||||
const numBooks = (await Database.libraryItemModel.getForAuthor(req.author)).length
|
||||
SocketAuthority.emitter('author_updated', req.author.toJSONExpanded(numBooks))
|
||||
res.json({
|
||||
author: req.author.toJSON()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE: /api/authors/:id/image
|
||||
* Remove author image & delete image file
|
||||
*
|
||||
* @param {import('express').Request} req
|
||||
* @param {import('express').Response} res
|
||||
*/
|
||||
async deleteImage(req, res) {
|
||||
if (!req.author.imagePath) {
|
||||
Logger.error(`[AuthorController] Author "${req.author.imagePath}" has no imagePath set`)
|
||||
return res.status(400).send('Author has no image path set')
|
||||
}
|
||||
Logger.info(`[AuthorController] Removing image for author "${req.author.name}" at "${req.author.imagePath}"`)
|
||||
await CacheManager.purgeImageCache(req.author.id) // Purge cache
|
||||
await CoverManager.removeFile(req.author.imagePath)
|
||||
req.author.imagePath = null
|
||||
await Database.authorModel.updateFromOld(req.author)
|
||||
|
||||
const numBooks = (await Database.libraryItemModel.getForAuthor(req.author)).length
|
||||
SocketAuthority.emitter('author_updated', req.author.toJSONExpanded(numBooks))
|
||||
res.json({
|
||||
author: req.author.toJSON()
|
||||
})
|
||||
}
|
||||
|
||||
async match(req, res) {
|
||||
let authorData = null
|
||||
const region = req.body.region || 'us'
|
||||
|
|
@ -215,7 +264,7 @@ class AuthorController {
|
|||
await CacheManager.purgeImageCache(req.author.id)
|
||||
|
||||
const imageData = await AuthorFinder.saveAuthorImage(req.author.id, authorData.image)
|
||||
if (imageData) {
|
||||
if (imageData?.path) {
|
||||
req.author.imagePath = imageData.path
|
||||
hasUpdates = true
|
||||
}
|
||||
|
|
@ -231,7 +280,7 @@ class AuthorController {
|
|||
|
||||
await Database.updateAuthor(req.author)
|
||||
|
||||
const numBooks = await Database.libraryItemModel.getForAuthor(req.author).length
|
||||
const numBooks = (await Database.libraryItemModel.getForAuthor(req.author)).length
|
||||
SocketAuthority.emitter('author_updated', req.author.toJSONExpanded(numBooks))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,13 +10,6 @@ class AuthorFinder {
|
|||
this.audnexus = new Audnexus()
|
||||
}
|
||||
|
||||
async downloadImage(url, outputPath) {
|
||||
return downloadFile(url, outputPath).then(() => true).catch((error) => {
|
||||
Logger.error('[AuthorFinder] Failed to download author image', error)
|
||||
return null
|
||||
})
|
||||
}
|
||||
|
||||
findAuthorByASIN(asin, region) {
|
||||
if (!asin) return null
|
||||
return this.audnexus.findAuthorByASIN(asin, region)
|
||||
|
|
@ -33,28 +26,36 @@ class AuthorFinder {
|
|||
return author
|
||||
}
|
||||
|
||||
/**
|
||||
* Download author image from url and save in authors folder
|
||||
*
|
||||
* @param {string} authorId
|
||||
* @param {string} url
|
||||
* @returns {Promise<{path:string, error:string}>}
|
||||
*/
|
||||
async saveAuthorImage(authorId, url) {
|
||||
var authorDir = Path.join(global.MetadataPath, 'authors')
|
||||
var relAuthorDir = Path.posix.join('/metadata', 'authors')
|
||||
const authorDir = Path.join(global.MetadataPath, 'authors')
|
||||
|
||||
if (!await fs.pathExists(authorDir)) {
|
||||
await fs.ensureDir(authorDir)
|
||||
}
|
||||
|
||||
var imageExtension = url.toLowerCase().split('.').pop()
|
||||
var ext = imageExtension === 'png' ? 'png' : 'jpg'
|
||||
var filename = authorId + '.' + ext
|
||||
var outputPath = Path.posix.join(authorDir, filename)
|
||||
var relPath = Path.posix.join(relAuthorDir, filename)
|
||||
const imageExtension = url.toLowerCase().split('.').pop()
|
||||
const ext = imageExtension === 'png' ? 'png' : 'jpg'
|
||||
const filename = authorId + '.' + ext
|
||||
const outputPath = Path.posix.join(authorDir, filename)
|
||||
|
||||
var success = await this.downloadImage(url, outputPath)
|
||||
if (!success) {
|
||||
return null
|
||||
}
|
||||
return {
|
||||
path: outputPath,
|
||||
relPath
|
||||
}
|
||||
return downloadFile(url, outputPath).then(() => {
|
||||
return {
|
||||
path: outputPath
|
||||
}
|
||||
}).catch((err) => {
|
||||
let errorMsg = err.message || 'Unknown error'
|
||||
Logger.error(`[AuthorFinder] Download image file failed for "${url}"`, errorMsg)
|
||||
return {
|
||||
error: errorMsg
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
module.exports = new AuthorFinder()
|
||||
|
|
@ -202,6 +202,8 @@ class ApiRouter {
|
|||
this.router.delete('/authors/:id', AuthorController.middleware.bind(this), AuthorController.delete.bind(this))
|
||||
this.router.post('/authors/:id/match', AuthorController.middleware.bind(this), AuthorController.match.bind(this))
|
||||
this.router.get('/authors/:id/image', AuthorController.middleware.bind(this), AuthorController.getImage.bind(this))
|
||||
this.router.post('/authors/:id/image', AuthorController.middleware.bind(this), AuthorController.uploadImage.bind(this))
|
||||
this.router.delete('/authors/:id/image', AuthorController.middleware.bind(this), AuthorController.deleteImage.bind(this))
|
||||
|
||||
//
|
||||
// Series Routes
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue