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)
|
to install a separate binary called graphicsmagick (http://www.graphicsmagick.org)
|
||||||
for images and ffmpeg (https://ffmpeg.org/) for video files
|
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
|
// Folder where to store logs
|
||||||
|
|
|
@ -3,7 +3,8 @@ const db = require('knex')(config.database);
|
||||||
const randomstring = require('randomstring');
|
const randomstring = require('randomstring');
|
||||||
const utils = require('./utilsController.js');
|
const utils = require('./utilsController.js');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const Zip = require('jszip');
|
||||||
const albumsController = {};
|
const albumsController = {};
|
||||||
|
|
||||||
albumsController.list = async (req, res, next) => {
|
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;
|
module.exports = albumsController;
|
||||||
|
|
|
@ -42,7 +42,7 @@ uploadsController.upload = async (req, res, next) => {
|
||||||
const albumid = req.headers.albumid || req.params.albumid;
|
const albumid = req.headers.albumid || req.params.albumid;
|
||||||
|
|
||||||
if (albumid && user) {
|
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) {
|
if (!album) {
|
||||||
return res.json({
|
return res.json({
|
||||||
success: false,
|
success: false,
|
||||||
|
@ -150,6 +150,11 @@ uploadsController.processFilesForDisplay = async (req, res, files, existingFiles
|
||||||
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`;
|
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`;
|
||||||
utils.generateThumbs(file);
|
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 {
|
try {
|
||||||
await uploadsController.deleteFile(file.name);
|
await uploadsController.deleteFile(file.name);
|
||||||
await db.table('files').where('id', id).del();
|
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) {
|
} catch (err) {
|
||||||
console.log(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.logsFolder) || fs.mkdirSync('./' + config.logsFolder);
|
||||||
fs.existsSync('./' + config.uploads.folder) || fs.mkdirSync('./' + config.uploads.folder);
|
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 + '/thumbs') || fs.mkdirSync('./' + config.uploads.folder + '/thumbs');
|
||||||
|
fs.existsSync('./' + config.uploads.folder + '/zips') || fs.mkdirSync('./' + config.uploads.folder + '/zips')
|
||||||
|
|
||||||
safe.use(helmet());
|
safe.use(helmet());
|
||||||
safe.set('trust proxy', 1);
|
safe.set('trust proxy', 1);
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
"fluent-ffmpeg": "^2.1.0",
|
"fluent-ffmpeg": "^2.1.0",
|
||||||
"gm": "^1.23.0",
|
"gm": "^1.23.0",
|
||||||
"helmet": "^3.5.0",
|
"helmet": "^3.5.0",
|
||||||
|
"jszip": "^3.1.4",
|
||||||
"knex": "^0.12.6",
|
"knex": "^0.12.6",
|
||||||
"multer": "^1.2.1",
|
"multer": "^1.2.1",
|
||||||
"randomstring": "^1.1.5",
|
"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', {
|
return res.render('album', {
|
||||||
layout: false,
|
layout: false,
|
||||||
title: album.name,
|
title: album.name,
|
||||||
count: files.length,
|
count: files.length,
|
||||||
thumb,
|
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/delete', (req, res, next) => uploadController.delete(req, res, next));
|
||||||
routes.post('/upload/:albumid', (req, res, next) => uploadController.upload(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/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', (req, res, next) => uploadController.list(req, res, next));
|
||||||
routes.get('/album/:id/:page', (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));
|
routes.get('/albums', (req, res, next) => albumsController.list(req, res, next));
|
||||||
|
|
|
@ -43,8 +43,17 @@
|
||||||
<section class="hero is-fullheight">
|
<section class="hero is-fullheight">
|
||||||
<div class="hero-head">
|
<div class="hero-head">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="title" id='title' style='margin-top: 1.5rem;'>{{ title }}</h1>
|
<div class="columns">
|
||||||
<h1 class="subtitle" id='count'> {{ count }} files</h1>
|
<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>
|
<hr>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue