mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Merge pull request #240 from keaganhilliard/master
Remove sharp in favor of ffmpeg, fallback to cover
This commit is contained in:
		
						commit
						87216bbd88
					
				| @ -45,7 +45,6 @@ | ||||
|     "podcast": "^1.3.0", | ||||
|     "read-chunk": "^3.1.0", | ||||
|     "recursive-readdir-async": "^1.1.8", | ||||
|     "sharp": "^0.29.3", | ||||
|     "socket.io": "^4.1.3", | ||||
|     "string-strip-html": "^8.3.0", | ||||
|     "watcher": "^1.2.0", | ||||
|  | ||||
| @ -1,8 +1,8 @@ | ||||
| const Path = require('path') | ||||
| const fs = require('fs-extra') | ||||
| const stream = require('stream') | ||||
| const resize = require('./utils/resizeImage') | ||||
| const Logger = require('./Logger') | ||||
| const { resizeImage } = require('./utils/ffmpegHelpers') | ||||
| 
 | ||||
| class CacheManager { | ||||
|   constructor(MetadataPath) { | ||||
| @ -18,7 +18,7 @@ class CacheManager { | ||||
| 
 | ||||
|     res.type(`image/${format}`) | ||||
| 
 | ||||
|     var path = Path.join(this.CoverCachePath, audiobook.id) + '.' + format | ||||
|     var path = Path.join(this.CoverCachePath, `${audiobook.id}_${width}${height ? `x${height}` : ''}`) + '.' + format | ||||
| 
 | ||||
|     // Cache exists
 | ||||
|     if (await fs.pathExists(path)) { | ||||
| @ -35,22 +35,22 @@ class CacheManager { | ||||
| 
 | ||||
|     // Write cache
 | ||||
|     await fs.ensureDir(this.CoverCachePath) | ||||
|     var readStream = resize(audiobook.book.coverFullPath, width, height, format) | ||||
|     var writeStream = fs.createWriteStream(path) | ||||
|     writeStream.on('error', (e) => { | ||||
|       Logger.error(`[CacheManager] Cache write error ${e.message}`) | ||||
|     }) | ||||
|     readStream.pipe(writeStream) | ||||
| 
 | ||||
|     let writtenFile = await resizeImage(audiobook.book.coverFullPath, path, width, height) | ||||
|     var readStream = fs.createReadStream(writtenFile) | ||||
|     readStream.pipe(res) | ||||
|   } | ||||
| 
 | ||||
|   purgeCoverCache(audiobookId) { | ||||
|     var basepath = Path.join(this.CoverCachePath, audiobookId) | ||||
|     // Remove both webp and jpg caches if exist
 | ||||
|     var webpPath = basepath + '.webp' | ||||
|     var jpgPath = basepath + '.jpg' | ||||
|     return Promise.all([this.removeCache(webpPath), this.removeCache(jpgPath)]) | ||||
|   async purgeCoverCache(audiobookId) { | ||||
|     // If purgeAll has been called... The cover cache directory no longer exists
 | ||||
|     await fs.ensureDir(this.CoverCachePath) | ||||
|     return Promise.all((await fs.readdir(this.CoverCachePath)).reduce((promises, file) => { | ||||
|       if (file.startsWith(audiobookId)) { | ||||
|         Logger.debug(`[CacheManager] Going to purge ${file}`); | ||||
|         promises.push(this.removeCache(Path.join(this.CoverCachePath, file))) | ||||
|       } | ||||
|       return promises | ||||
|     }, [])) | ||||
|   } | ||||
| 
 | ||||
|   removeCache(path) { | ||||
|  | ||||
| @ -234,15 +234,25 @@ class BookController { | ||||
|   async getCover(req, res) { | ||||
|     let { query: { width, height, format }, params: { id } } = req | ||||
|     var audiobook = this.db.audiobooks.find(a => a.id === id) | ||||
|     if (!audiobook || !audiobook.book.coverFullPath) return res.sendStatus(404) | ||||
|     if (!audiobook || !audiobook.book.cover) return res.sendStatus(404) | ||||
| 
 | ||||
|     // Check user can access this audiobooks library
 | ||||
|     if (!req.user.checkCanAccessLibrary(audiobook.libraryId)) { | ||||
|       return res.sendStatus(403) | ||||
|     } | ||||
| 
 | ||||
|     // Temp fix for books without a full cover path
 | ||||
|     if (audiobook.book.cover && !audiobook.book.coverFullPath) { | ||||
|       var isFixed = audiobook.fixFullCoverPath() | ||||
|       if (!isFixed) { | ||||
|         Logger.warn(`[BookController] Failed to fix full cover path "${audiobook.book.cover}" for "${audiobook.book.title}"`) | ||||
|         return res.sendStatus(404) | ||||
|       } | ||||
|       await this.db.updateEntity('audiobook', audiobook) | ||||
|     } | ||||
| 
 | ||||
|     const options = { | ||||
|       format: format || (reqSupportsWebp(req) ? 'webp' : 'jpg'), | ||||
|       format: format || (reqSupportsWebp(req) ? 'webp' : 'jpeg'), | ||||
|       height: height ? parseInt(height) : null, | ||||
|       width: width ? parseInt(width) : null | ||||
|     } | ||||
|  | ||||
| @ -801,7 +801,7 @@ class Audiobook { | ||||
|     var success = await extractCoverArt(audioFileWithCover.fullPath, coverFilePath) | ||||
|     if (success) { | ||||
|       var coverRelPath = Path.join(coverDirRelPath, coverFilename).replace(/\\/g, '/').replace(/\/\//g, '/') | ||||
|       this.update({ book: { cover: coverRelPath } }) | ||||
|       this.update({ book: { cover: coverRelPath, coverFullPath: audioFileWithCover.fullPath } }) | ||||
|       return coverRelPath | ||||
|     } | ||||
|     return false | ||||
| @ -1022,5 +1022,23 @@ class Audiobook { | ||||
|       existingOtherFileData | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Temp fix for cover is set but coverFullPath is not set
 | ||||
|   fixFullCoverPath(metadataPath) { | ||||
|     if (!this.book.cover) return | ||||
|     var bookCoverPath = this.book.cover.replace(/\\/g, '/') | ||||
|     var newFullCoverPath = null | ||||
|     if (bookCoverPath.startsWith('/s/book/')) { | ||||
|       newFullCoverPath = Path.join(this.fullPath, bookCoverPath.substr(`/s/book/${this.id}`.length)).replace(/\/\//g, '/') | ||||
|     } else if (bookCoverPath.startsWith('/metadata/')) { | ||||
|       newFullCoverPath = Path.join(metadataPath, bookCoverPath.substr('/metadata/'.length)).replace(/\/\//g, '/') | ||||
|     } | ||||
|     if (newFullCoverPath) { | ||||
|       Logger.debug(`[Audiobook] "${this.title}" fixing full cover path "${this.book.cover}" => "${newFullCoverPath}"`) | ||||
|       this.update({ book: { fullCoverPath: newFullCoverPath } }) | ||||
|       return true | ||||
|     } | ||||
|     return false | ||||
|   } | ||||
| } | ||||
| module.exports = Audiobook | ||||
| @ -92,4 +92,29 @@ async function extractCoverArt(filepath, outputpath) { | ||||
|     ffmpeg.run() | ||||
|   }) | ||||
| } | ||||
| module.exports.extractCoverArt = extractCoverArt | ||||
| module.exports.extractCoverArt = extractCoverArt | ||||
| 
 | ||||
| //This should convert based on the output file extension as well
 | ||||
| async function resizeImage(filePath, outputPath, width, height) { | ||||
|   var dirname = Path.dirname(outputPath); | ||||
|   await fs.ensureDir(dirname); | ||||
| 
 | ||||
|   return new Promise((resolve) => { | ||||
|     var ffmpeg = Ffmpeg(filePath) | ||||
|     ffmpeg.addOption(['-vf', `scale=${width || -1}:${height || -1}`]) | ||||
|     ffmpeg.addOutput(outputPath) | ||||
|     ffmpeg.on('start', (cmd) => { | ||||
|       Logger.debug(`[FfmpegHelpers] Resize Image Cmd: ${cmd}`) | ||||
|     }) | ||||
|     ffmpeg.on('error', (err, stdout, stderr) => { | ||||
|       Logger.error(`[FfmpegHelpers] Resize Image Error ${err}`) | ||||
|       resolve(false) | ||||
|     }) | ||||
|     ffmpeg.on('end', () => { | ||||
|       Logger.debug(`[FfmpegHelpers] Image resized Successfully`) | ||||
|       resolve(outputPath) | ||||
|     }) | ||||
|     ffmpeg.run() | ||||
|   }) | ||||
| } | ||||
| module.exports.resizeImage = resizeImage | ||||
|  | ||||
| @ -1,16 +0,0 @@ | ||||
| const sharp = require('sharp') | ||||
| const fs = require('fs') | ||||
| 
 | ||||
| function resize(filePath, width, height, format = 'webp') { | ||||
|   const readStream = fs.createReadStream(filePath); | ||||
|   let sharpie = sharp() | ||||
|   sharpie.toFormat(format) | ||||
| 
 | ||||
|   if (width || height) { | ||||
|     sharpie.resize(width, height, { withoutEnlargement: true }) | ||||
|   } | ||||
| 
 | ||||
|   return readStream.pipe(sharpie) | ||||
| } | ||||
| 
 | ||||
| module.exports = resize; | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user