mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-03 00:06:46 +01:00
Add:Cover image cache, resize & use webp image #223
This commit is contained in:
parent
d04f3450ec
commit
ddf0fa72e8
@ -10,7 +10,7 @@
|
||||
<p :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }" class="font-book text-gray-300 text-center">{{ title }}</p>
|
||||
</div>
|
||||
|
||||
<img v-show="audiobook" ref="cover" :src="bookCoverSrc" class="w-full h-full object-contain transition-opacity duration-300" @load="imageLoaded" :style="{ opacity: imageReady ? 1 : 0 }" />
|
||||
<img v-show="audiobook" ref="cover" :src="bookCoverSrc" class="w-full h-full object-contain transition-opacity duration-300" :class="showCoverBg ? 'object-contain' : 'object-fill'" @load="imageLoaded" :style="{ opacity: imageReady ? 1 : 0 }" />
|
||||
|
||||
<!-- Placeholder Cover Title & Author -->
|
||||
<div v-if="!hasCover" class="absolute top-0 left-0 right-0 bottom-0 w-full h-full flex items-center justify-center" :style="{ padding: placeholderCoverPadding + 'rem' }">
|
||||
|
@ -53,7 +53,8 @@
|
||||
<div class="h-0.5 bg-primary bg-opacity-30 w-full" />
|
||||
|
||||
<div class="flex items-center py-4">
|
||||
<ui-btn color="bg" small :padding-x="4" class="hidden lg:block" :loading="isResettingAudiobooks" @click="resetAudiobooks">Reset All Audiobooks</ui-btn>
|
||||
<ui-btn color="bg" small :padding-x="4" class="hidden lg:block mr-2" :loading="isPurgingCache" @click="purgeCache">Purge Cache</ui-btn>
|
||||
<ui-btn color="bg" small :padding-x="4" class="hidden lg:block" :loading="isResettingAudiobooks" @click="resetAudiobooks">Remove All Audiobooks</ui-btn>
|
||||
<div class="flex-grow" />
|
||||
<p class="pr-2 text-sm font-book text-yellow-400">Report bugs, request features, provide feedback, and contribute on <a class="underline" href="https://github.com/advplyr/audiobookshelf" target="_blank">github</a>.</p>
|
||||
<a href="https://github.com/advplyr/audiobookshelf" target="_blank" class="text-white hover:text-gray-200 hover:scale-150 hover:rotate-6 transform duration-500">
|
||||
@ -93,6 +94,7 @@ export default {
|
||||
storeCoversInAudiobookDir: false,
|
||||
updatingServerSettings: false,
|
||||
useSquareBookCovers: false,
|
||||
isPurgingCache: false,
|
||||
newServerSettings: {}
|
||||
}
|
||||
},
|
||||
@ -209,6 +211,19 @@ export default {
|
||||
this.$toast.error('Failed to reset audiobooks - manually remove the /config/audiobooks folder')
|
||||
})
|
||||
}
|
||||
},
|
||||
async purgeCache() {
|
||||
this.isPurgingCache = true
|
||||
await this.$axios
|
||||
.$post('/api/purgecache')
|
||||
.then(() => {
|
||||
this.$toast.success('Cache Purged!')
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to purge cache', error)
|
||||
this.$toast.error('Failed to purge cache')
|
||||
})
|
||||
this.isPurgingCache = false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@ -6,8 +6,6 @@ Vue.directive('click-outside', vClickOutside.directive)
|
||||
|
||||
Vue.prototype.$eventBus = new Vue()
|
||||
|
||||
Vue.prototype.$isDev = process.env.NODE_ENV !== 'production'
|
||||
|
||||
Vue.prototype.$dateDistanceFromNow = (unixms) => {
|
||||
if (!unixms) return ''
|
||||
return formatDistance(unixms, Date.now(), { addSuffix: true })
|
||||
@ -145,4 +143,5 @@ export {
|
||||
export default ({ app }, inject) => {
|
||||
app.$decode = decode
|
||||
app.$encode = encode
|
||||
app.$isDev = process.env.NODE_ENV !== 'production'
|
||||
}
|
@ -16,36 +16,17 @@ export const getters = {
|
||||
if (!bookItem) return placeholder
|
||||
var book = bookItem.book
|
||||
if (!book || !book.cover || book.cover === placeholder) return placeholder
|
||||
var cover = book.cover
|
||||
|
||||
// Absolute URL covers (should no longer be used)
|
||||
if (cover.startsWith('http:') || cover.startsWith('https:')) return cover
|
||||
if (book.cover.startsWith('http:') || book.cover.startsWith('https:')) return book.cover
|
||||
|
||||
// Server hosted covers
|
||||
try {
|
||||
// Ensure cover is refreshed if cached
|
||||
var bookLastUpdate = book.lastUpdate || Date.now()
|
||||
var userToken = rootGetters['user/getToken']
|
||||
var userToken = rootGetters['user/getToken']
|
||||
var bookLastUpdate = book.lastUpdate || Date.now()
|
||||
|
||||
cover = cover.replace(/\\/g, '/')
|
||||
|
||||
// Map old covers to new format /s/book/{bookid}/*
|
||||
if (cover.startsWith('/local')) {
|
||||
cover = cover.replace('local', `s/book/${bookItem.id}`)
|
||||
if (cover.includes(bookItem.path + '/')) { // Remove book path
|
||||
cover = cover.replace(bookItem.path + '/', '')
|
||||
}
|
||||
}
|
||||
|
||||
// Easier to replace these special characters then to encodeUriComponent of the filename
|
||||
var encodedCover = cover.replace(/%/g, '%25').replace(/#/g, '%23')
|
||||
|
||||
var url = new URL(encodedCover, document.baseURI)
|
||||
return url.href + `?token=${userToken}&ts=${bookLastUpdate}`
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return placeholder
|
||||
if (process.env.NODE_ENV !== 'production') { // Testing
|
||||
return `http://localhost:3333/api/books/${bookItem.id}/cover?token=${userToken}&ts=${bookLastUpdate}`
|
||||
}
|
||||
return `/api/books/${bookItem.id}/cover?token=${userToken}`
|
||||
}
|
||||
}
|
||||
|
||||
|
234
package-lock.json
generated
234
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "audiobookshelf",
|
||||
"version": "1.6.30",
|
||||
"version": "1.6.39",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -105,14 +105,12 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"optional": true
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
|
||||
"optional": true
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
|
||||
},
|
||||
"archiver": {
|
||||
"version": "5.3.0",
|
||||
@ -173,7 +171,6 @@
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
|
||||
"integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"delegates": "^1.0.0",
|
||||
"readable-stream": "^2.0.6"
|
||||
@ -183,7 +180,6 @@
|
||||
"version": "2.3.7",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
@ -346,8 +342,7 @@
|
||||
"chownr": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
||||
"optional": true
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||
},
|
||||
"clone-response": {
|
||||
"version": "1.0.2",
|
||||
@ -360,8 +355,38 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||
"optional": true
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
|
||||
},
|
||||
"color": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-4.1.0.tgz",
|
||||
"integrity": "sha512-o2rkkxyLGgYoeUy1OodXpbPAQNmlNBrirQ8ODO8QutzDiDMNdezSOZLNnusQ6pUpCQJUsaJIo9DZJKqa2HgH7A==",
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1",
|
||||
"color-string": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"color-string": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz",
|
||||
"integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==",
|
||||
"requires": {
|
||||
"color-name": "^1.0.0",
|
||||
"simple-swizzle": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"command-line-args": {
|
||||
"version": "5.2.0",
|
||||
@ -398,8 +423,7 @@
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||
"optional": true
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
|
||||
},
|
||||
"content-disposition": {
|
||||
"version": "0.5.3",
|
||||
@ -492,8 +516,7 @@
|
||||
"deep-extend": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
|
||||
"optional": true
|
||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
|
||||
},
|
||||
"defer-to-connect": {
|
||||
"version": "2.0.1",
|
||||
@ -503,8 +526,7 @@
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
|
||||
"optional": true
|
||||
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
|
||||
},
|
||||
"depd": {
|
||||
"version": "1.1.2",
|
||||
@ -519,8 +541,7 @@
|
||||
"detect-libc": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
|
||||
"optional": true
|
||||
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
|
||||
},
|
||||
"dicer": {
|
||||
"version": "0.3.0",
|
||||
@ -623,6 +644,11 @@
|
||||
"resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz",
|
||||
"integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw=="
|
||||
},
|
||||
"expand-template": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
||||
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
|
||||
},
|
||||
"express": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
||||
@ -762,7 +788,6 @@
|
||||
"version": "2.7.4",
|
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
|
||||
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"aproba": "^1.0.3",
|
||||
"console-control-strings": "^1.0.0",
|
||||
@ -782,6 +807,11 @@
|
||||
"pump": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"github-from-package": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
||||
"integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.7",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
|
||||
@ -822,8 +852,7 @@
|
||||
"has-unicode": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
|
||||
"optional": true
|
||||
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
|
||||
},
|
||||
"html-entities": {
|
||||
"version": "2.3.2",
|
||||
@ -903,8 +932,7 @@
|
||||
"ini": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||
"optional": true
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
|
||||
},
|
||||
"ip": {
|
||||
"version": "1.1.5",
|
||||
@ -916,11 +944,15 @@
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
|
||||
},
|
||||
"is-arrayish": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -1117,6 +1149,21 @@
|
||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
|
||||
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@ -1166,8 +1213,7 @@
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||
"optional": true
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.9.0",
|
||||
@ -1197,6 +1243,11 @@
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"mkdirp-classic": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.29.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||
@ -1221,6 +1272,11 @@
|
||||
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
|
||||
"optional": true
|
||||
},
|
||||
"napi-build-utils": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
|
||||
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
|
||||
},
|
||||
"needle": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz",
|
||||
@ -1262,6 +1318,29 @@
|
||||
"proper-lockfile": "^4.1.2"
|
||||
}
|
||||
},
|
||||
"node-abi": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.5.0.tgz",
|
||||
"integrity": "sha512-LtHvNIBgOy5mO8mPEUtkCW/YCRWYEKshIvqhe1GHHyXEHEB5mgICyYnAcl4qan3uFeRROErKGzatFHPf6kDxWw==",
|
||||
"requires": {
|
||||
"semver": "^7.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "7.3.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"node-addon-api": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.2.0.tgz",
|
||||
"integrity": "sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q=="
|
||||
},
|
||||
"node-cron": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.0.tgz",
|
||||
@ -1343,7 +1422,6 @@
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"are-we-there-yet": "~1.1.2",
|
||||
"console-control-strings": "~1.1.0",
|
||||
@ -1354,8 +1432,7 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"optional": true
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@ -1443,6 +1520,26 @@
|
||||
"rss": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"prebuild-install": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.0.0.tgz",
|
||||
"integrity": "sha512-IvSenf33K7JcgddNz2D5w521EgO+4aMMjFt73Uk9FRzQ7P+QZPKrp7qPsDydsSwjGt3T5xRNnM1bj1zMTD5fTA==",
|
||||
"requires": {
|
||||
"detect-libc": "^1.0.3",
|
||||
"expand-template": "^2.0.3",
|
||||
"github-from-package": "0.0.0",
|
||||
"minimist": "^1.2.3",
|
||||
"mkdirp-classic": "^0.5.3",
|
||||
"napi-build-utils": "^1.0.1",
|
||||
"node-abi": "^3.3.0",
|
||||
"npmlog": "^4.0.1",
|
||||
"pump": "^3.0.0",
|
||||
"rc": "^1.2.7",
|
||||
"simple-get": "^4.0.0",
|
||||
"tar-fs": "^2.0.0",
|
||||
"tunnel-agent": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"printj": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
|
||||
@ -1554,7 +1651,6 @@
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"deep-extend": "^0.6.0",
|
||||
"ini": "~1.3.0",
|
||||
@ -1719,19 +1815,66 @@
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
|
||||
"optional": true
|
||||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
|
||||
},
|
||||
"setprototypeof": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
|
||||
},
|
||||
"sharp": {
|
||||
"version": "0.29.3",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.29.3.tgz",
|
||||
"integrity": "sha512-fKWUuOw77E4nhpyzCCJR1ayrttHoFHBT2U/kR/qEMRhvPEcluG4BKj324+SCO1e84+knXHwhJ1HHJGnUt4ElGA==",
|
||||
"requires": {
|
||||
"color": "^4.0.1",
|
||||
"detect-libc": "^1.0.3",
|
||||
"node-addon-api": "^4.2.0",
|
||||
"prebuild-install": "^7.0.0",
|
||||
"semver": "^7.3.5",
|
||||
"simple-get": "^4.0.0",
|
||||
"tar-fs": "^2.1.1",
|
||||
"tunnel-agent": "^0.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "7.3.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
|
||||
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
|
||||
},
|
||||
"simple-concat": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
||||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
|
||||
},
|
||||
"simple-get": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.0.tgz",
|
||||
"integrity": "sha512-ZalZGexYr3TA0SwySsr5HlgOOinS4Jsa8YB2GJ6lUNAazyAu4KG/VmzMTwAt2YVXzzVj8QmefmAonZIK2BSGcQ==",
|
||||
"requires": {
|
||||
"decompress-response": "^6.0.0",
|
||||
"once": "^1.3.1",
|
||||
"simple-concat": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"simple-swizzle": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||
"integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
|
||||
"requires": {
|
||||
"is-arrayish": "^0.3.1"
|
||||
}
|
||||
},
|
||||
"socket.io": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.1.3.tgz",
|
||||
@ -1853,7 +1996,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@ -1872,7 +2014,6 @@
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@ -1880,8 +2021,7 @@
|
||||
"strip-json-comments": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
|
||||
"optional": true
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
||||
},
|
||||
"tar": {
|
||||
"version": "4.4.19",
|
||||
@ -1906,6 +2046,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tar-fs": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
|
||||
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
|
||||
"requires": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^2.1.4"
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||
@ -1931,6 +2082,14 @@
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
|
||||
"requires": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
@ -1995,7 +2154,6 @@
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"string-width": "^1.0.2 || 2"
|
||||
}
|
||||
|
@ -52,4 +52,4 @@
|
||||
"xml2js": "^0.4.23"
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ const date = require('date-and-time')
|
||||
|
||||
const Logger = require('./Logger')
|
||||
const { isObject } = require('./utils/index')
|
||||
const resize = require('./utils/resizeImage')
|
||||
const audioFileScanner = require('./utils/audioFileScanner')
|
||||
|
||||
const BookController = require('./controllers/BookController')
|
||||
@ -19,7 +18,7 @@ const BookFinder = require('./BookFinder')
|
||||
const AuthorFinder = require('./AuthorFinder')
|
||||
|
||||
class ApiController {
|
||||
constructor(MetadataPath, db, scanner, auth, streamManager, rssFeeds, downloadManager, coverController, backupManager, watcher, emitter, clientEmitter) {
|
||||
constructor(MetadataPath, db, scanner, auth, streamManager, rssFeeds, downloadManager, coverController, backupManager, watcher, cacheManager, emitter, clientEmitter) {
|
||||
this.db = db
|
||||
this.scanner = scanner
|
||||
this.auth = auth
|
||||
@ -29,6 +28,7 @@ class ApiController {
|
||||
this.backupManager = backupManager
|
||||
this.coverController = coverController
|
||||
this.watcher = watcher
|
||||
this.cacheManager = cacheManager
|
||||
this.emitter = emitter
|
||||
this.clientEmitter = clientEmitter
|
||||
this.MetadataPath = MetadataPath
|
||||
@ -62,12 +62,10 @@ class ApiController {
|
||||
this.router.get('/libraries/:id/authors', LibraryController.middleware.bind(this), LibraryController.getAuthors.bind(this))
|
||||
this.router.post('/libraries/order', LibraryController.reorder.bind(this))
|
||||
|
||||
|
||||
// TEMP: Support old syntax for mobile app
|
||||
this.router.get('/library/:id/audiobooks', LibraryController.middleware.bind(this), LibraryController.getBooksForLibrary.bind(this))
|
||||
this.router.get('/library/:id/search', LibraryController.middleware.bind(this), LibraryController.search.bind(this))
|
||||
|
||||
|
||||
//
|
||||
// Book Routes
|
||||
//
|
||||
@ -83,7 +81,7 @@ class ApiController {
|
||||
this.router.patch('/books/:id/tracks', BookController.updateTracks.bind(this))
|
||||
this.router.get('/books/:id/stream', BookController.openStream.bind(this))
|
||||
this.router.post('/books/:id/cover', BookController.uploadCover.bind(this))
|
||||
this.router.get('/books/:id/cover', this.resizeCover.bind(this))
|
||||
this.router.get('/books/:id/cover', BookController.getCover.bind(this))
|
||||
this.router.patch('/books/:id/coverfile', BookController.updateCoverFromFile.bind(this))
|
||||
|
||||
// TEMP: Support old syntax for mobile app
|
||||
@ -91,7 +89,6 @@ class ApiController {
|
||||
this.router.get('/audiobook/:id', BookController.findOne.bind(this))
|
||||
this.router.get('/audiobook/:id/stream', BookController.openStream.bind(this))
|
||||
|
||||
|
||||
//
|
||||
// User Routes
|
||||
//
|
||||
@ -104,7 +101,6 @@ class ApiController {
|
||||
this.router.get('/users/:id/listening-sessions', UserController.getListeningStats.bind(this))
|
||||
this.router.get('/users/:id/listening-stats', UserController.getListeningStats.bind(this))
|
||||
|
||||
|
||||
//
|
||||
// Collection Routes
|
||||
//
|
||||
@ -123,7 +119,6 @@ class ApiController {
|
||||
this.router.get('/collection/:id', CollectionController.findOne.bind(this))
|
||||
this.router.delete('/collection/:id/book/:bookId', CollectionController.removeBook.bind(this))
|
||||
|
||||
|
||||
//
|
||||
// Current User Routes (Me)
|
||||
//
|
||||
@ -140,7 +135,6 @@ class ApiController {
|
||||
this.router.patch('/user/audiobook/:id', MeController.updateAudiobookData.bind(this))
|
||||
this.router.patch('/user/settings', MeController.updateSettings.bind(this))
|
||||
|
||||
|
||||
//
|
||||
// Backup Routes
|
||||
//
|
||||
@ -176,31 +170,10 @@ class ApiController {
|
||||
this.router.get('/scantracks/:id', this.scanAudioTrackNums.bind(this))
|
||||
|
||||
this.router.post('/syncUserAudiobookData', this.syncUserAudiobookData.bind(this))
|
||||
|
||||
this.router.post('/purgecache', this.purgeCache.bind(this))
|
||||
}
|
||||
|
||||
async resizeCover(req, res) {
|
||||
let { query: { width, height }, params: { id }, user } = req;
|
||||
if (!user) {
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
var audiobook = this.db.audiobooks.find(a => a.id === id)
|
||||
if (!audiobook) return res.sendStatus(404)
|
||||
|
||||
// Check user can access this audiobooks library
|
||||
if (!req.user.checkCanAccessLibrary(audiobook.libraryId)) {
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
|
||||
|
||||
res.type('image/jpeg');
|
||||
|
||||
if (width) width = parseInt(width)
|
||||
if (height) height = parseInt(height)
|
||||
|
||||
return resize(audiobook.book.coverFullPath, width, height).pipe(res)
|
||||
}
|
||||
|
||||
|
||||
async findBooks(req, res) {
|
||||
var provider = req.query.provider || 'google'
|
||||
var title = req.query.title || ''
|
||||
@ -485,6 +458,11 @@ class ApiController {
|
||||
this.clientEmitter(collection.userId, 'collection_updated', collection.toJSONExpanded(this.db.audiobooks))
|
||||
}
|
||||
|
||||
// purge cover cache
|
||||
if (audiobook.cover) {
|
||||
await this.cacheManager.purgeCoverCache(audiobook.id)
|
||||
}
|
||||
|
||||
var audiobookJSON = audiobook.toJSONMinified()
|
||||
await this.db.removeEntity('audiobook', audiobook.id)
|
||||
this.emitter('audiobook_removed', audiobookJSON)
|
||||
@ -527,5 +505,14 @@ class ApiController {
|
||||
})
|
||||
return listeningStats
|
||||
}
|
||||
|
||||
async purgeCache(req, res) {
|
||||
if (!req.user.isRoot) {
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
Logger.info(`[ApiController] Purging all cache`)
|
||||
await this.cacheManager.purgeAll()
|
||||
res.sendStatus(200)
|
||||
}
|
||||
}
|
||||
module.exports = ApiController
|
75
server/CacheManager.js
Normal file
75
server/CacheManager.js
Normal file
@ -0,0 +1,75 @@
|
||||
const Path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
const stream = require('stream')
|
||||
const resize = require('./utils/resizeImage')
|
||||
const Logger = require('./Logger')
|
||||
|
||||
class CacheManager {
|
||||
constructor(MetadataPath) {
|
||||
this.MetadataPath = MetadataPath
|
||||
this.CachePath = Path.join(this.MetadataPath, 'cache')
|
||||
this.CoverCachePath = Path.join(this.CachePath, 'covers')
|
||||
}
|
||||
|
||||
async handleCoverCache(res, audiobook, options = {}) {
|
||||
const format = options.format || 'webp'
|
||||
const width = options.width || 400
|
||||
const height = options.height || null
|
||||
|
||||
res.type(`image/${format}`)
|
||||
|
||||
var path = Path.join(this.CoverCachePath, audiobook.id) + '.' + format
|
||||
|
||||
// Cache exists
|
||||
if (await fs.pathExists(path)) {
|
||||
const r = fs.createReadStream(path)
|
||||
const ps = new stream.PassThrough()
|
||||
stream.pipeline(r, ps, (err) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
return res.sendStatus(400)
|
||||
}
|
||||
})
|
||||
return ps.pipe(res)
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
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)])
|
||||
}
|
||||
|
||||
removeCache(path) {
|
||||
if (!path) return false
|
||||
return fs.pathExists(path).then((exists) => {
|
||||
if (!exists) return false
|
||||
return fs.unlink(path).then(() => true).catch((err) => {
|
||||
Logger.error(`[CacheManager] Failed to remove cache "${path}"`, err)
|
||||
return false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async purgeAll() {
|
||||
if (await fs.pathExists(this.CachePath)) {
|
||||
await fs.remove(this.CachePath).catch((error) => {
|
||||
Logger.error(`[CacheManager] Failed to remove cache dir "${this.CachePath}"`, error)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = CacheManager
|
@ -10,8 +10,10 @@ const { CoverDestination } = require('./utils/constants')
|
||||
const { downloadFile } = require('./utils/fileUtils')
|
||||
|
||||
class CoverController {
|
||||
constructor(db, MetadataPath, AudiobookPath) {
|
||||
constructor(db, cacheManager, MetadataPath, AudiobookPath) {
|
||||
this.db = db
|
||||
this.cacheManager = cacheManager
|
||||
|
||||
this.MetadataPath = MetadataPath.replace(/\\/g, '/')
|
||||
this.BookMetadataPath = Path.posix.join(this.MetadataPath, 'books')
|
||||
this.AudiobookPath = AudiobookPath
|
||||
@ -115,6 +117,7 @@ class CoverController {
|
||||
}
|
||||
|
||||
await this.removeOldCovers(fullPath, extname)
|
||||
await this.cacheManager.purgeCoverCache(audiobook.id)
|
||||
|
||||
Logger.info(`[CoverController] Uploaded audiobook cover "${coverPath}" for "${audiobook.title}"`)
|
||||
|
||||
@ -152,6 +155,7 @@ class CoverController {
|
||||
await fs.rename(temppath, coverFullPath)
|
||||
|
||||
await this.removeOldCovers(fullPath, '.' + imgtype.ext)
|
||||
await this.cacheManager.purgeCoverCache(audiobook.id)
|
||||
|
||||
Logger.info(`[CoverController] Downloaded audiobook cover "${coverPath}" from url "${url}" for "${audiobook.title}"`)
|
||||
|
||||
|
@ -28,6 +28,7 @@ const StreamManager = require('./StreamManager')
|
||||
const RssFeeds = require('./RssFeeds')
|
||||
const DownloadManager = require('./DownloadManager')
|
||||
const CoverController = require('./CoverController')
|
||||
const CacheManager = require('./CacheManager')
|
||||
|
||||
class Server {
|
||||
constructor(PORT, UID, GID, CONFIG_PATH, METADATA_PATH, AUDIOBOOK_PATH) {
|
||||
@ -47,15 +48,16 @@ class Server {
|
||||
this.auth = new Auth(this.db)
|
||||
this.backupManager = new BackupManager(this.MetadataPath, this.Uid, this.Gid, this.db)
|
||||
this.logManager = new LogManager(this.MetadataPath, this.db)
|
||||
this.cacheManager = new CacheManager(this.MetadataPath)
|
||||
this.watcher = new Watcher(this.AudiobookPath)
|
||||
this.coverController = new CoverController(this.db, this.MetadataPath, this.AudiobookPath)
|
||||
this.coverController = new CoverController(this.db, this.cacheManager, this.MetadataPath, this.AudiobookPath)
|
||||
this.scanner = new Scanner(this.AudiobookPath, this.MetadataPath, this.db, this.coverController, this.emitter.bind(this))
|
||||
this.scanner2 = new Scanner2(this.AudiobookPath, this.MetadataPath, this.db, this.coverController, this.emitter.bind(this))
|
||||
|
||||
this.streamManager = new StreamManager(this.db, this.MetadataPath, this.emitter.bind(this), this.clientEmitter.bind(this))
|
||||
this.rssFeeds = new RssFeeds(this.Port, this.db)
|
||||
this.downloadManager = new DownloadManager(this.db, this.MetadataPath, this.AudiobookPath, this.emitter.bind(this))
|
||||
this.apiController = new ApiController(this.MetadataPath, this.db, this.scanner, this.auth, this.streamManager, this.rssFeeds, this.downloadManager, this.coverController, this.backupManager, this.watcher, this.emitter.bind(this), this.clientEmitter.bind(this))
|
||||
this.apiController = new ApiController(this.MetadataPath, this.db, this.scanner, this.auth, this.streamManager, this.rssFeeds, this.downloadManager, this.coverController, this.backupManager, this.watcher, this.cacheManager, this.emitter.bind(this), this.clientEmitter.bind(this))
|
||||
this.hlsController = new HlsController(this.db, this.scanner, this.auth, this.streamManager, this.emitter.bind(this), this.streamManager.StreamsPath)
|
||||
|
||||
Logger.logManager = this.logManager
|
||||
|
@ -72,7 +72,7 @@ class StreamManager {
|
||||
if (!dirs || !dirs.length) return true
|
||||
|
||||
await Promise.all(dirs.map(async (dirname) => {
|
||||
if (dirname !== 'streams' && dirname !== 'books' && dirname !== 'downloads' && dirname !== 'backups' && dirname !== 'logs') {
|
||||
if (dirname !== 'streams' && dirname !== 'books' && dirname !== 'downloads' && dirname !== 'backups' && dirname !== 'logs' && dirname !== 'cache') {
|
||||
var fullPath = Path.join(this.MetadataPath, dirname)
|
||||
Logger.warn(`Removing OLD Orphan Stream ${dirname}`)
|
||||
return fs.remove(fullPath)
|
||||
|
@ -1,4 +1,5 @@
|
||||
const Logger = require('../Logger')
|
||||
const { reqSupportsWebp } = require('../utils/index')
|
||||
|
||||
class BookController {
|
||||
constructor() { }
|
||||
@ -38,6 +39,12 @@ class BookController {
|
||||
}
|
||||
var audiobook = this.db.audiobooks.find(a => a.id === req.params.id)
|
||||
if (!audiobook) return res.sendStatus(404)
|
||||
|
||||
// Book has cover and update is removing cover then purge cache
|
||||
if (audiobook.cover && req.body.book && (req.body.book.cover === '' || req.body.book.cover === null)) {
|
||||
await this.cacheManager.purgeCoverCache(audiobook.id)
|
||||
}
|
||||
|
||||
var hasUpdates = audiobook.update(req.body)
|
||||
if (hasUpdates) {
|
||||
await this.db.updateAudiobook(audiobook)
|
||||
@ -222,5 +229,24 @@ class BookController {
|
||||
if (updated) res.status(200).send('Cover updated successfully')
|
||||
else res.status(200).send('No update was made to cover')
|
||||
}
|
||||
|
||||
// GET api/books/:id/cover
|
||||
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)
|
||||
|
||||
// Check user can access this audiobooks library
|
||||
if (!req.user.checkCanAccessLibrary(audiobook.libraryId)) {
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
|
||||
const options = {
|
||||
format: format || (reqSupportsWebp(req) ? 'webp' : 'jpg'),
|
||||
height: height ? parseInt(height) : null,
|
||||
width: width ? parseInt(width) : null
|
||||
}
|
||||
return this.cacheManager.handleCoverCache(res, audiobook, options)
|
||||
}
|
||||
}
|
||||
module.exports = new BookController()
|
@ -101,4 +101,9 @@ function secondsToTimestamp(seconds, includeMs = false) {
|
||||
}
|
||||
module.exports.secondsToTimestamp = secondsToTimestamp
|
||||
|
||||
module.exports.msToTimestamp = (ms, includeMs) => secondsToTimestamp(ms / 1000, includeMs)
|
||||
module.exports.msToTimestamp = (ms, includeMs) => secondsToTimestamp(ms / 1000, includeMs)
|
||||
|
||||
module.exports.reqSupportsWebp = (req) => {
|
||||
if (!req || !req.headers || !req.headers.accept) return false
|
||||
return req.headers.accept.includes('image/webp') || req.headers.accept === '*/*'
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
const sharp = require('sharp')
|
||||
const fs = require('fs')
|
||||
|
||||
function resize(filePath, width, height) {
|
||||
function resize(filePath, width, height, format = 'webp') {
|
||||
const readStream = fs.createReadStream(filePath);
|
||||
let sharpie = sharp()
|
||||
sharpie.toFormat('jpeg')
|
||||
sharpie.toFormat(format)
|
||||
|
||||
if (width || height) {
|
||||
sharpie.resize(width, height)
|
||||
sharpie.resize(width, height, { withoutEnlargement: true })
|
||||
}
|
||||
|
||||
return readStream.pipe(sharpie)
|
||||
|
Loading…
Reference in New Issue
Block a user