Added album downloading through front-end
This commit is contained in:
parent
8d8dbc7078
commit
992b632d1a
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue