mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-22 00:07:52 +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",
|
"podcast": "^1.3.0",
|
||||||
"read-chunk": "^3.1.0",
|
"read-chunk": "^3.1.0",
|
||||||
"recursive-readdir-async": "^1.1.8",
|
"recursive-readdir-async": "^1.1.8",
|
||||||
"sharp": "^0.29.3",
|
|
||||||
"socket.io": "^4.1.3",
|
"socket.io": "^4.1.3",
|
||||||
"string-strip-html": "^8.3.0",
|
"string-strip-html": "^8.3.0",
|
||||||
"watcher": "^1.2.0",
|
"watcher": "^1.2.0",
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
const Path = require('path')
|
const Path = require('path')
|
||||||
const fs = require('fs-extra')
|
const fs = require('fs-extra')
|
||||||
const stream = require('stream')
|
const stream = require('stream')
|
||||||
const resize = require('./utils/resizeImage')
|
|
||||||
const Logger = require('./Logger')
|
const Logger = require('./Logger')
|
||||||
|
const { resizeImage } = require('./utils/ffmpegHelpers')
|
||||||
|
|
||||||
class CacheManager {
|
class CacheManager {
|
||||||
constructor(MetadataPath) {
|
constructor(MetadataPath) {
|
||||||
@ -18,7 +18,7 @@ class CacheManager {
|
|||||||
|
|
||||||
res.type(`image/${format}`)
|
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
|
// Cache exists
|
||||||
if (await fs.pathExists(path)) {
|
if (await fs.pathExists(path)) {
|
||||||
@ -35,22 +35,22 @@ class CacheManager {
|
|||||||
|
|
||||||
// Write cache
|
// Write cache
|
||||||
await fs.ensureDir(this.CoverCachePath)
|
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)
|
readStream.pipe(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
purgeCoverCache(audiobookId) {
|
async purgeCoverCache(audiobookId) {
|
||||||
var basepath = Path.join(this.CoverCachePath, audiobookId)
|
// If purgeAll has been called... The cover cache directory no longer exists
|
||||||
// Remove both webp and jpg caches if exist
|
await fs.ensureDir(this.CoverCachePath)
|
||||||
var webpPath = basepath + '.webp'
|
return Promise.all((await fs.readdir(this.CoverCachePath)).reduce((promises, file) => {
|
||||||
var jpgPath = basepath + '.jpg'
|
if (file.startsWith(audiobookId)) {
|
||||||
return Promise.all([this.removeCache(webpPath), this.removeCache(jpgPath)])
|
Logger.debug(`[CacheManager] Going to purge ${file}`);
|
||||||
|
promises.push(this.removeCache(Path.join(this.CoverCachePath, file)))
|
||||||
|
}
|
||||||
|
return promises
|
||||||
|
}, []))
|
||||||
}
|
}
|
||||||
|
|
||||||
removeCache(path) {
|
removeCache(path) {
|
||||||
|
@ -234,15 +234,25 @@ class BookController {
|
|||||||
async getCover(req, res) {
|
async getCover(req, res) {
|
||||||
let { query: { width, height, format }, params: { id } } = req
|
let { query: { width, height, format }, params: { id } } = req
|
||||||
var audiobook = this.db.audiobooks.find(a => a.id === id)
|
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
|
// Check user can access this audiobooks library
|
||||||
if (!req.user.checkCanAccessLibrary(audiobook.libraryId)) {
|
if (!req.user.checkCanAccessLibrary(audiobook.libraryId)) {
|
||||||
return res.sendStatus(403)
|
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 = {
|
const options = {
|
||||||
format: format || (reqSupportsWebp(req) ? 'webp' : 'jpg'),
|
format: format || (reqSupportsWebp(req) ? 'webp' : 'jpeg'),
|
||||||
height: height ? parseInt(height) : null,
|
height: height ? parseInt(height) : null,
|
||||||
width: width ? parseInt(width) : null
|
width: width ? parseInt(width) : null
|
||||||
}
|
}
|
||||||
|
@ -801,7 +801,7 @@ class Audiobook {
|
|||||||
var success = await extractCoverArt(audioFileWithCover.fullPath, coverFilePath)
|
var success = await extractCoverArt(audioFileWithCover.fullPath, coverFilePath)
|
||||||
if (success) {
|
if (success) {
|
||||||
var coverRelPath = Path.join(coverDirRelPath, coverFilename).replace(/\\/g, '/').replace(/\/\//g, '/')
|
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 coverRelPath
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@ -1022,5 +1022,23 @@ class Audiobook {
|
|||||||
existingOtherFileData
|
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
|
module.exports = Audiobook
|
@ -93,3 +93,28 @@ async function extractCoverArt(filepath, outputpath) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
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