mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Merge pull request #3584 from mikiher/author-image-performance
Improve author image performance
This commit is contained in:
		
						commit
						978c2b05f2
					
				@ -56,7 +56,7 @@ export default {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    imgSrc() {
 | 
					    imgSrc() {
 | 
				
			||||||
      if (!this.imagePath) return null
 | 
					      if (!this.imagePath) return null
 | 
				
			||||||
      return `${this.$config.routerBasePath}/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
 | 
					      return `${this.$config.routerBasePath}/api/authors/${this.authorId}/image?ts=${this.updatedAt}`
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,7 @@ class Auth {
 | 
				
			|||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    // Map of openId sessions indexed by oauth2 state-variable
 | 
					    // Map of openId sessions indexed by oauth2 state-variable
 | 
				
			||||||
    this.openIdAuthSession = new Map()
 | 
					    this.openIdAuthSession = new Map()
 | 
				
			||||||
    this.ignorePattern = /\/api\/items\/[^/]+\/cover/
 | 
					    this.ignorePatterns = [/\/api\/items\/[^/]+\/cover/, /\/api\/authors\/[^/]+\/image/]
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@ -28,7 +28,7 @@ class Auth {
 | 
				
			|||||||
   * @private
 | 
					   * @private
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  authNotNeeded(req) {
 | 
					  authNotNeeded(req) {
 | 
				
			||||||
    return req.method === 'GET' && this.ignorePattern.test(req.originalUrl)
 | 
					    return req.method === 'GET' && this.ignorePatterns.some((pattern) => pattern.test(req.originalUrl))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ifAuthNeeded(middleware) {
 | 
					  ifAuthNeeded(middleware) {
 | 
				
			||||||
 | 
				
			|||||||
@ -381,16 +381,23 @@ class AuthorController {
 | 
				
			|||||||
   */
 | 
					   */
 | 
				
			||||||
  async getImage(req, res) {
 | 
					  async getImage(req, res) {
 | 
				
			||||||
    const {
 | 
					    const {
 | 
				
			||||||
      query: { width, height, format, raw },
 | 
					      query: { width, height, format, raw }
 | 
				
			||||||
      author
 | 
					 | 
				
			||||||
    } = req
 | 
					    } = req
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!author.imagePath || !(await fs.pathExists(author.imagePath))) {
 | 
					    const authorId = req.params.id
 | 
				
			||||||
      Logger.warn(`[AuthorController] Author "${author.name}" has invalid imagePath: ${author.imagePath}`)
 | 
					 | 
				
			||||||
      return res.sendStatus(404)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (raw) {
 | 
					    if (raw) {
 | 
				
			||||||
 | 
					      const author = await Database.authorModel.findByPk(authorId)
 | 
				
			||||||
 | 
					      if (!author) {
 | 
				
			||||||
 | 
					        Logger.warn(`[AuthorController] Author "${authorId}" not found`)
 | 
				
			||||||
 | 
					        return res.sendStatus(404)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (!author.imagePath || !(await fs.pathExists(author.imagePath))) {
 | 
				
			||||||
 | 
					        Logger.warn(`[AuthorController] Author "${author.name}" has invalid imagePath: ${author.imagePath}`)
 | 
				
			||||||
 | 
					        return res.sendStatus(404)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return res.sendFile(author.imagePath)
 | 
					      return res.sendFile(author.imagePath)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -399,7 +406,7 @@ class AuthorController {
 | 
				
			|||||||
      height: height ? parseInt(height) : null,
 | 
					      height: height ? parseInt(height) : null,
 | 
				
			||||||
      width: width ? parseInt(width) : null
 | 
					      width: width ? parseInt(width) : null
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return CacheManager.handleAuthorCache(res, author, options)
 | 
					    return CacheManager.handleAuthorCache(res, authorId, options)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
 | 
				
			|||||||
@ -134,22 +134,22 @@ class CacheManager {
 | 
				
			|||||||
  /**
 | 
					  /**
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
   * @param {import('express').Response} res
 | 
					   * @param {import('express').Response} res
 | 
				
			||||||
   * @param {import('../models/Author')} author
 | 
					   * @param {String} authorId
 | 
				
			||||||
   * @param {{ format?: string, width?: number, height?: number }} options
 | 
					   * @param {{ format?: string, width?: number, height?: number }} options
 | 
				
			||||||
   * @returns
 | 
					   * @returns
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  async handleAuthorCache(res, author, options = {}) {
 | 
					  async handleAuthorCache(res, authorId, options = {}) {
 | 
				
			||||||
    const format = options.format || 'webp'
 | 
					    const format = options.format || 'webp'
 | 
				
			||||||
    const width = options.width || 400
 | 
					    const width = options.width || 400
 | 
				
			||||||
    const height = options.height || null
 | 
					    const height = options.height || null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    res.type(`image/${format}`)
 | 
					    res.type(`image/${format}`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var path = Path.join(this.ImageCachePath, `${author.id}_${width}${height ? `x${height}` : ''}`) + '.' + format
 | 
					    var cachePath = Path.join(this.ImageCachePath, `${authorId}_${width}${height ? `x${height}` : ''}`) + '.' + format
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Cache exists
 | 
					    // Cache exists
 | 
				
			||||||
    if (await fs.pathExists(path)) {
 | 
					    if (await fs.pathExists(cachePath)) {
 | 
				
			||||||
      const r = fs.createReadStream(path)
 | 
					      const r = fs.createReadStream(cachePath)
 | 
				
			||||||
      const ps = new stream.PassThrough()
 | 
					      const ps = new stream.PassThrough()
 | 
				
			||||||
      stream.pipeline(r, ps, (err) => {
 | 
					      stream.pipeline(r, ps, (err) => {
 | 
				
			||||||
        if (err) {
 | 
					        if (err) {
 | 
				
			||||||
@ -160,7 +160,12 @@ class CacheManager {
 | 
				
			|||||||
      return ps.pipe(res)
 | 
					      return ps.pipe(res)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let writtenFile = await resizeImage(author.imagePath, path, width, height)
 | 
					    const author = await Database.authorModel.findByPk(authorId)
 | 
				
			||||||
 | 
					    if (!author || !author.imagePath || !(await fs.pathExists(author.imagePath))) {
 | 
				
			||||||
 | 
					      return res.sendStatus(404)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let writtenFile = await resizeImage(author.imagePath, cachePath, width, height)
 | 
				
			||||||
    if (!writtenFile) return res.sendStatus(500)
 | 
					    if (!writtenFile) return res.sendStatus(500)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var readStream = fs.createReadStream(writtenFile)
 | 
					    var readStream = fs.createReadStream(writtenFile)
 | 
				
			||||||
 | 
				
			|||||||
@ -216,7 +216,7 @@ class ApiRouter {
 | 
				
			|||||||
    this.router.patch('/authors/:id', AuthorController.middleware.bind(this), AuthorController.update.bind(this))
 | 
					    this.router.patch('/authors/:id', AuthorController.middleware.bind(this), AuthorController.update.bind(this))
 | 
				
			||||||
    this.router.delete('/authors/:id', AuthorController.middleware.bind(this), AuthorController.delete.bind(this))
 | 
					    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.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.get('/authors/:id/image', AuthorController.getImage.bind(this))
 | 
				
			||||||
    this.router.post('/authors/:id/image', AuthorController.middleware.bind(this), AuthorController.uploadImage.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))
 | 
					    this.router.delete('/authors/:id/image', AuthorController.middleware.bind(this), AuthorController.deleteImage.bind(this))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user