diff --git a/client/components/app/Appbar.vue b/client/components/app/Appbar.vue index 4bb130ee..e8b441a6 100644 --- a/client/components/app/Appbar.vue +++ b/client/components/app/Appbar.vue @@ -11,6 +11,10 @@
+ + upload + + settings diff --git a/client/components/ui/LoadingIndicator.vue b/client/components/ui/LoadingIndicator.vue index e5970628..9762fde7 100644 --- a/client/components/ui/LoadingIndicator.vue +++ b/client/components/ui/LoadingIndicator.vue @@ -1,13 +1,13 @@ diff --git a/client/components/ui/TextInputWithLabel.vue b/client/components/ui/TextInputWithLabel.vue index 21134272..a77c45a6 100644 --- a/client/components/ui/TextInputWithLabel.vue +++ b/client/components/ui/TextInputWithLabel.vue @@ -1,6 +1,8 @@ @@ -10,6 +12,7 @@ export default { props: { value: [String, Number], label: String, + note: String, type: { type: String, default: 'text' diff --git a/client/package.json b/client/package.json index f344fa22..27789fc2 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf-client", - "version": "1.1.6", + "version": "1.1.7", "description": "Audiobook manager and player", "main": "index.js", "scripts": { diff --git a/client/pages/upload/index.vue b/client/pages/upload/index.vue new file mode 100644 index 00000000..012dd129 --- /dev/null +++ b/client/pages/upload/index.vue @@ -0,0 +1,258 @@ + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index cb88dd9d..f85613e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf", - "version": "0.9.76-beta", + "version": "1.1.6", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -165,6 +165,14 @@ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, + "busboy": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", + "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==", + "requires": { + "dicer": "0.3.0" + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -291,6 +299,14 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "dicer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", + "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", + "requires": { + "streamsearch": "0.1.2" + } + }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -406,6 +422,14 @@ "vary": "~1.1.2" } }, + "express-fileupload": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.2.1.tgz", + "integrity": "sha512-fWPNAkBj+Azt9Itmcz/Reqdg3LeBfaXptDEev2JM8bCC0yDptglCnlizhf0YZauyU5X/g6v7v4Xxqhg8tmEfEA==", + "requires": { + "busboy": "^0.3.1" + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -1017,6 +1041,11 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, "string-indexes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/string-indexes/-/string-indexes-1.0.0.tgz", diff --git a/package.json b/package.json index 8a88e279..6b5f70ca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf", - "version": "1.1.6", + "version": "1.1.7", "description": "Self-hosted audiobook server for managing and playing audiobooks.", "main": "index.js", "scripts": { @@ -14,6 +14,7 @@ "bcryptjs": "^2.4.3", "cookie-parser": "^1.4.5", "express": "^4.17.1", + "express-fileupload": "^1.2.1", "fluent-ffmpeg": "^2.1.2", "fs-extra": "^10.0.0", "ip": "^1.1.5", diff --git a/server/Server.js b/server/Server.js index baee4945..1522def9 100644 --- a/server/Server.js +++ b/server/Server.js @@ -3,6 +3,7 @@ const express = require('express') const http = require('http') const SocketIO = require('socket.io') const fs = require('fs-extra') +const fileUpload = require('express-fileupload') const Auth = require('./Auth') const Watcher = require('./Watcher') @@ -132,6 +133,7 @@ class Server { this.server = http.createServer(app) app.use(this.auth.cors) + app.use(fileUpload()) // Static path to generated nuxt const distPath = Path.join(global.appRoot, '/client/dist') @@ -155,6 +157,39 @@ class Server { // app.use('/hls', this.hlsController.router) app.use('/feeds', this.rssFeeds.router) + app.post('/upload', this.authMiddleware.bind(this), async (req, res) => { + var files = Object.values(req.files) + var title = req.body.title + var author = req.body.author + var series = req.body.series + + if (!files.length || !title || !author) { + return res.json({ + error: 'Invalid post data received' + }) + } + + var outputDirectory = '' + if (series && series.length && series !== 'null') { + outputDirectory = Path.join(this.AudiobookPath, author, series, title) + } else { + outputDirectory = Path.join(this.AudiobookPath, author, title) + } + + await fs.ensureDir(outputDirectory) + Logger.info(`Uploading ${files.length} files to`, outputDirectory) + + for (let i = 0; i < files.length; i++) { + var file = files[i] + + var path = Path.join(outputDirectory, file.name) + await file.mv(path).catch((error) => { + Logger.error('Failed to move file', path, error) + }) + } + res.sendStatus(200) + }) + app.post('/login', (req, res) => this.auth.login(req, res)) app.post('/logout', this.logout.bind(this)) app.get('/ping', (req, res) => {