mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-03 00:06:46 +01:00
Add:Download button to download full library item #580
This commit is contained in:
parent
418adcf891
commit
718890cfad
@ -222,6 +222,13 @@
|
|||||||
<ui-tooltip v-if="showRssFeedBtn" :text="$strings.LabelOpenRSSFeed" direction="top">
|
<ui-tooltip v-if="showRssFeedBtn" :text="$strings.LabelOpenRSSFeed" direction="top">
|
||||||
<ui-icon-btn icon="rss_feed" class="mx-0.5" :bg-color="rssFeed ? 'success' : 'primary'" outlined @click="clickRSSFeed" />
|
<ui-icon-btn icon="rss_feed" class="mx-0.5" :bg-color="rssFeed ? 'success' : 'primary'" outlined @click="clickRSSFeed" />
|
||||||
</ui-tooltip>
|
</ui-tooltip>
|
||||||
|
|
||||||
|
<!-- Download -->
|
||||||
|
<ui-tooltip v-if="userCanDownload" :text="$strings.LabelDownload" direction="top">
|
||||||
|
<a :href="downloadPath" class="h-9 w-9 mx-0.5 rounded-md flex items-center justify-center relative bg-primary border border-gray-600">
|
||||||
|
<span class="material-icons-outlined" :style="{ fontSize: '1.4rem' }">download</span>
|
||||||
|
</a>
|
||||||
|
</ui-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-4 max-w-2xl">
|
<div class="my-4 max-w-2xl">
|
||||||
@ -284,6 +291,12 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
userToken() {
|
||||||
|
return this.$store.getters['user/getToken']
|
||||||
|
},
|
||||||
|
downloadPath() {
|
||||||
|
return `${process.env.serverUrl}/api/items/${this.libraryItemId}/download?token=${this.userToken}`
|
||||||
|
},
|
||||||
dateFormat() {
|
dateFormat() {
|
||||||
return this.$store.state.serverSettings.dateFormat
|
return this.$store.state.serverSettings.dateFormat
|
||||||
},
|
},
|
||||||
|
@ -2,6 +2,7 @@ const fs = require('../libs/fsExtra')
|
|||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const SocketAuthority = require('../SocketAuthority')
|
const SocketAuthority = require('../SocketAuthority')
|
||||||
|
|
||||||
|
const zipHelpers = require('../utils/zipHelpers')
|
||||||
const { reqSupportsWebp, isNullOrNaN } = require('../utils/index')
|
const { reqSupportsWebp, isNullOrNaN } = require('../utils/index')
|
||||||
const { ScanResult } = require('../utils/constants')
|
const { ScanResult } = require('../utils/constants')
|
||||||
|
|
||||||
@ -69,6 +70,17 @@ class LibraryItemController {
|
|||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
download(req, res) {
|
||||||
|
if (!req.user.canDownload) {
|
||||||
|
Logger.warn('User attempted to download without permission', req.user)
|
||||||
|
return res.sendStatus(403)
|
||||||
|
}
|
||||||
|
|
||||||
|
const libraryItemPath = req.libraryItem.path
|
||||||
|
const filename = `${req.libraryItem.media.metadata.title}.zip`
|
||||||
|
zipHelpers.zipDirectoryPipe(libraryItemPath, filename, res)
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// PATCH: will create new authors & series if in payload
|
// PATCH: will create new authors & series if in payload
|
||||||
//
|
//
|
||||||
|
@ -98,6 +98,7 @@ class ApiRouter {
|
|||||||
this.router.get('/items/:id', LibraryItemController.middleware.bind(this), LibraryItemController.findOne.bind(this))
|
this.router.get('/items/:id', LibraryItemController.middleware.bind(this), LibraryItemController.findOne.bind(this))
|
||||||
this.router.patch('/items/:id', LibraryItemController.middleware.bind(this), LibraryItemController.update.bind(this))
|
this.router.patch('/items/:id', LibraryItemController.middleware.bind(this), LibraryItemController.update.bind(this))
|
||||||
this.router.delete('/items/:id', LibraryItemController.middleware.bind(this), LibraryItemController.delete.bind(this))
|
this.router.delete('/items/:id', LibraryItemController.middleware.bind(this), LibraryItemController.delete.bind(this))
|
||||||
|
this.router.get('/items/:id/download', LibraryItemController.middleware.bind(this), LibraryItemController.download.bind(this))
|
||||||
this.router.patch('/items/:id/media', LibraryItemController.middleware.bind(this), LibraryItemController.updateMedia.bind(this))
|
this.router.patch('/items/:id/media', LibraryItemController.middleware.bind(this), LibraryItemController.updateMedia.bind(this))
|
||||||
this.router.get('/items/:id/cover', LibraryItemController.middleware.bind(this), LibraryItemController.getCover.bind(this))
|
this.router.get('/items/:id/cover', LibraryItemController.middleware.bind(this), LibraryItemController.getCover.bind(this))
|
||||||
this.router.post('/items/:id/cover', LibraryItemController.middleware.bind(this), LibraryItemController.uploadCover.bind(this))
|
this.router.post('/items/:id/cover', LibraryItemController.middleware.bind(this), LibraryItemController.uploadCover.bind(this))
|
||||||
|
52
server/utils/zipHelpers.js
Normal file
52
server/utils/zipHelpers.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
const Logger = require('../Logger')
|
||||||
|
const archiver = require('../libs/archiver')
|
||||||
|
|
||||||
|
module.exports.zipDirectoryPipe = (path, filename, res) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// create a file to stream archive data to
|
||||||
|
res.attachment(filename)
|
||||||
|
|
||||||
|
const archive = archiver('zip', {
|
||||||
|
zlib: { level: 9 } // Sets the compression level.
|
||||||
|
})
|
||||||
|
|
||||||
|
// listen for all archive data to be written
|
||||||
|
// 'close' event is fired only when a file descriptor is involved
|
||||||
|
res.on('close', () => {
|
||||||
|
Logger.info(archive.pointer() + ' total bytes')
|
||||||
|
Logger.debug('archiver has been finalized and the output file descriptor has closed.')
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
|
||||||
|
// This event is fired when the data source is drained no matter what was the data source.
|
||||||
|
// It is not part of this library but rather from the NodeJS Stream API.
|
||||||
|
// @see: https://nodejs.org/api/stream.html#stream_event_end
|
||||||
|
res.on('end', () => {
|
||||||
|
Logger.debug('Data has been drained')
|
||||||
|
})
|
||||||
|
|
||||||
|
// good practice to catch warnings (ie stat failures and other non-blocking errors)
|
||||||
|
archive.on('warning', function (err) {
|
||||||
|
if (err.code === 'ENOENT') {
|
||||||
|
// log warning
|
||||||
|
Logger.warn(`[DownloadManager] Archiver warning: ${err.message}`)
|
||||||
|
} else {
|
||||||
|
// throw error
|
||||||
|
Logger.error(`[DownloadManager] Archiver error: ${err.message}`)
|
||||||
|
// throw err
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
archive.on('error', function (err) {
|
||||||
|
Logger.error(`[DownloadManager] Archiver error: ${err.message}`)
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
// pipe archive data to the file
|
||||||
|
archive.pipe(res)
|
||||||
|
|
||||||
|
archive.directory(path, false)
|
||||||
|
|
||||||
|
archive.finalize()
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user