aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPitu <[email protected]>2017-10-04 02:05:38 -0300
committerPitu <[email protected]>2017-10-04 02:05:38 -0300
commit992b632d1a0b6dc6e44daf79bbfceb09f0260770 (patch)
treec2c19afd3d22b806c95e71f0c0bb7c33f733e80c
parentMissing semicolon (diff)
downloadhost.fuwn.me-992b632d1a0b6dc6e44daf79bbfceb09f0260770.tar.xz
host.fuwn.me-992b632d1a0b6dc6e44daf79bbfceb09f0260770.zip
Added album downloading through front-end
-rw-r--r--config.sample.js9
-rw-r--r--controllers/albumsController.js43
-rw-r--r--controllers/uploadController.js10
-rw-r--r--database/migration.js13
-rw-r--r--lolisafe.js1
-rw-r--r--package.json1
-rw-r--r--routes/album.js8
-rw-r--r--routes/api.js1
-rw-r--r--views/album.handlebars13
9 files changed, 93 insertions, 6 deletions
diff --git a/config.sample.js b/config.sample.js
index d32cd75..cf8ee4a 100644
--- a/config.sample.js
+++ b/config.sample.js
@@ -62,7 +62,14 @@ module.exports = {
to install a separate binary called graphicsmagick (http://www.graphicsmagick.org)
for images and ffmpeg (https://ffmpeg.org/) for video files
*/
- generateThumbnails: false
+ generateThumbnails: false,
+
+ /*
+ Allows users to download a .zip file of all files in an album.
+ The file is generated when the user clicks the download button in the view
+ and is re-used if the album has not changed between download requests
+ */
+ generateZips: true
},
// Folder where to store logs
diff --git a/controllers/albumsController.js b/controllers/albumsController.js
index 57597ca..c1a36f1 100644
--- a/controllers/albumsController.js
+++ b/controllers/albumsController.js
@@ -3,7 +3,8 @@ const db = require('knex')(config.database);
const randomstring = require('randomstring');
const utils = require('./utilsController.js');
const path = require('path');
-
+const fs = require('fs');
+const Zip = require('jszip');
const albumsController = {};
albumsController.list = async (req, res, next) => {
@@ -129,4 +130,44 @@ albumsController.get = async (req, res, next) => {
});
};
+
+albumsController.generateZip = async (req, res, next) => {
+ const identifier = req.params.identifier;
+ if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' });
+ if (!config.uploads.generateZips) return res.status(401).json({ success: false, description: 'Zip generation disabled' });
+
+ const album = await db.table('albums').where({ identifier, enabled: 1 }).first();
+ if (!album) return res.json({ success: false, description: 'Album not found' });
+
+ if (album.zipGeneratedAt > album.editedAt) {
+ const filePath = path.join(config.uploads.folder, 'zips', `${identifier}.zip`);
+ const fileName = `${album.name}.zip`;
+ return res.download(filePath, fileName);
+ } else {
+ console.log(`Generating zip for album identifier: ${identifier}`);
+ const files = await db.table('files').select('name').where('albumid', album.id);
+ if (files.length === 0) return res.json({ success: false, description: 'There are no files in the album' });
+
+ const zipPath = path.join(__dirname, '..', config.uploads.folder, 'zips', `${album.identifier}.zip`);
+ let archive = new Zip();
+
+ for (let file of files) {
+ archive.file(file.name, fs.readFileSync(path.join(__dirname, '..', config.uploads.folder, file.name)));
+ }
+
+ archive
+ .generateNodeStream({ type: 'nodebuffer', streamFiles: true })
+ .pipe(fs.createWriteStream(zipPath))
+ .on('finish', async () => {
+ await db.table('albums')
+ .where('id', album.id)
+ .update({ zipGeneratedAt: Math.floor(Date.now() / 1000) });
+
+ const filePath = path.join(config.uploads.folder, 'zips', `${identifier}.zip`);
+ const fileName = `${album.name}.zip`;
+ return res.download(filePath, fileName);
+ });
+ }
+};
+
module.exports = albumsController;
diff --git a/controllers/uploadController.js b/controllers/uploadController.js
index f0f75f6..42fccf5 100644
--- a/controllers/uploadController.js
+++ b/controllers/uploadController.js
@@ -42,7 +42,7 @@ uploadsController.upload = async (req, res, next) => {
const albumid = req.headers.albumid || req.params.albumid;
if (albumid && user) {
- const album = await db.table('albums').where({ id: album, userid: user.id }).first();
+ const album = await db.table('albums').where({ id: albumid, userid: user.id }).first();
if (!album) {
return res.json({
success: false,
@@ -150,6 +150,11 @@ uploadsController.processFilesForDisplay = async (req, res, files, existingFiles
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`;
utils.generateThumbs(file);
}
+
+ if (file.albumid) {
+ db.table('albums').where('id', file.albumid).update('editedAt', file.timestamp).then(() => {})
+ .catch(error => { console.log(error); res.json({ success: false, description: 'Error updating album' }); });
+ }
}
};
@@ -172,6 +177,9 @@ uploadsController.delete = async (req, res) => {
try {
await uploadsController.deleteFile(file.name);
await db.table('files').where('id', id).del();
+ if (file.albumid) {
+ await db.table('albums').where('id', file.albumid).update('editedAt', Math.floor(Date.now() / 1000));
+ }
} catch (err) {
console.log(err);
}
diff --git a/database/migration.js b/database/migration.js
new file mode 100644
index 0000000..fb30779
--- /dev/null
+++ b/database/migration.js
@@ -0,0 +1,13 @@
+const config = require('../config.js');
+const db = require('knex')(config.database);
+
+const migration = {};
+migration.start = async () => {
+ await db.schema.table('albums', table => {
+ table.dateTime('editedAt');
+ table.dateTime('zipGeneratedAt');
+ });
+ console.log('Migration finished! Now start lolisafe normally');
+};
+
+migration.start();
diff --git a/lolisafe.js b/lolisafe.js
index cd22a29..cf8aab6 100644
--- a/lolisafe.js
+++ b/lolisafe.js
@@ -16,6 +16,7 @@ fs.existsSync('./pages/custom' ) || fs.mkdirSync('./pages/custom');
fs.existsSync('./' + config.logsFolder) || fs.mkdirSync('./' + config.logsFolder);
fs.existsSync('./' + config.uploads.folder) || fs.mkdirSync('./' + config.uploads.folder);
fs.existsSync('./' + config.uploads.folder + '/thumbs') || fs.mkdirSync('./' + config.uploads.folder + '/thumbs');
+fs.existsSync('./' + config.uploads.folder + '/zips') || fs.mkdirSync('./' + config.uploads.folder + '/zips')
safe.use(helmet());
safe.set('trust proxy', 1);
diff --git a/package.json b/package.json
index ee37a51..04dce12 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"fluent-ffmpeg": "^2.1.0",
"gm": "^1.23.0",
"helmet": "^3.5.0",
+ "jszip": "^3.1.4",
"knex": "^0.12.6",
"multer": "^1.2.1",
"randomstring": "^1.1.5",
diff --git a/routes/album.js b/routes/album.js
index db9dbd0..46f00d3 100644
--- a/routes/album.js
+++ b/routes/album.js
@@ -38,12 +38,18 @@ routes.get('/a/:identifier', async (req, res, next) => {
}
}
+
+ let enableDownload = false;
+ if (config.uploads.generateZips) enableDownload = true;
+
return res.render('album', {
layout: false,
title: album.name,
count: files.length,
thumb,
- files
+ files,
+ identifier,
+ enableDownload
});
});
diff --git a/routes/api.js b/routes/api.js
index 83d3bc1..5a4d355 100644
--- a/routes/api.js
+++ b/routes/api.js
@@ -21,6 +21,7 @@ routes.post('/upload', (req, res, next) => uploadController.upload(req, res, nex
routes.post('/upload/delete', (req, res, next) => uploadController.delete(req, res, next));
routes.post('/upload/:albumid', (req, res, next) => uploadController.upload(req, res, next));
routes.get('/album/get/:identifier', (req, res, next) => albumsController.get(req, res, next));
+routes.get('/album/zip/:identifier', (req, res, next) => albumsController.generateZip(req, res, next));
routes.get('/album/:id', (req, res, next) => uploadController.list(req, res, next));
routes.get('/album/:id/:page', (req, res, next) => uploadController.list(req, res, next));
routes.get('/albums', (req, res, next) => albumsController.list(req, res, next));
diff --git a/views/album.handlebars b/views/album.handlebars
index d2a2131..3cbe1c4 100644
--- a/views/album.handlebars
+++ b/views/album.handlebars
@@ -43,8 +43,17 @@
<section class="hero is-fullheight">
<div class="hero-head">
<div class="container">
- <h1 class="title" id='title' style='margin-top: 1.5rem;'>{{ title }}</h1>
- <h1 class="subtitle" id='count'> {{ count }} files</h1>
+ <div class="columns">
+ <div class="column is-9">
+ <h1 class="title" id='title' style='margin-top: 1.5rem;'>{{ title }}</h1>
+ <h1 class="subtitle" id='count'>{{ count }} files</h1>
+ </div>
+ <div class="column is-3" style="text-align: center; padding-top: 45px;">
+ {{#if enableDownload}}
+ <a class="button is-primary is-outlined" title="Download album" href="/api/album/zip/{{ identifier }}">Download Album</a>
+ {{/if}}
+ </div>
+ </div>
<hr>
</div>
</div>