1
0
mirror of https://github.com/tomasvarg/gallerymaker.git synced 2026-03-01 08:28:48 +00:00

Added config file support; optimized & polished execution chains

This commit is contained in:
Tomas Varga 2016-10-30 23:11:13 +01:00
parent 58c7b149dd
commit 7c8b5188da
4 changed files with 154 additions and 159 deletions

View File

@ -39,4 +39,4 @@ Image resizing done by [sharp](http://sharp.dimens.io/) ([github](https://github
- use some lightbox on the prepared directory - use some lightbox on the prepared directory
- further development - further development
- integration with other projects (iframe? ajax? web component? Polymer?) - integration with other projects (iframe? ajax? web component? Polymer?)
- config.json support (for specifying conversion settings) - ~~config.json support (for specifying conversion settings)~~

View File

@ -1,6 +1,6 @@
{ {
"name": "gallerymaker", "name": "gallerymaker",
"version": "0.0.0", "version": "0.0.1",
"description": "A static html gallery maker", "description": "A static html gallery maker",
"keywords": [ "keywords": [
"gallery", "gallery",

View File

@ -2,7 +2,7 @@
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var util = require('util'); //var util = require('util');
var sharp = require('sharp'); var sharp = require('sharp');
var utils = require('./utils.js'); var utils = require('./utils.js');
@ -13,15 +13,27 @@ var odir = sanitize(process.argv[4] || idir + '.web');
var clientDir = path.join(__dirname, 'client'); var clientDir = path.join(__dirname, 'client');
var conf = { var conf;
width: 700, try { conf = require(path.join(__dirname, '..', 'config.json')); }
quality: 80, catch (e) { conf = {}; }
debug: true, if (!conf.width) conf.width = 700;
template: 'index.html' if (!conf.quality) conf.quality = 80;
}; if (!conf.fnames) conf.fnames = {}; // odir
var EOL = "\n"; if (!conf.fnames.list) conf.fnames.list = 'list.html';
var DEPTH = 2; if (!conf.fnames.gallery) conf.fnames.gallery = 'gallery.html';
var SORT_DESC = true; if (!conf.templates) conf.templates = {}; // clientDir
if (!conf.templates.index) conf.templates.index = 'index.html';
if (!conf.assets) conf.assets = {}; // clientDir
if (!conf.assets.common) conf.assets.common = ['index.js', 'index.css'];
if (!conf.ignored_files || !conf.ignored_files.push) conf.ignored_files = [];
if (conf.debug === undefined) conf.debug = true;
for (var ak in conf.assets) conf.ignored_files.push.apply(conf.ignored_files, conf.assets[ak]);
for (var fk in conf.fnames) conf.ignored_files.push(conf.fnames[fk]);
var EOL = conf.eol ? conf.eol : "\n";
var DEPTH = conf.html_init_depth ? conf.html_init_depth : 2;
var SORT_DESC = conf.sort_desc ? conf.sort_desc : true;
var log = conf.debug ? console.log.bind(console) : function () {}; var log = conf.debug ? console.log.bind(console) : function () {};
var logi = function () { var logi = function () {
@ -31,17 +43,17 @@ var logi = function () {
var ind = depth => utils.indent(depth, DEPTH); var ind = depth => utils.indent(depth, DEPTH);
var cmds = { var cmds = {
prepare: prepareImages, prepare: () => prepareImages().then(copyAssets('common')),
list: generateList, list: generateList,
gallery: generateGallery, gallery: generateGallery,
all: () => { all: () => prepareImages()
prepareImages()
.then(cont => Promise.all([ .then(cont => Promise.all([
copyAssets('common'),
['content.json'].concat(cont.cont.map(c => c.dir)),
generateList(cont), generateList(cont),
generateGallery(cont) generateGallery(cont)
])) ]))
.then(res => log('All tasks finished!', res.map(r => { return { type: typeof r, lenght: r.length }; }))); .then(res => log('All tasks finished! Files:', res.reduce((c, n) => c.concat(n), []).join(', ')))
}
}; };
if (!cmd || !idir) { if (!cmd || !idir) {
@ -68,7 +80,11 @@ else if (!cmds[cmd]) {
process.exit(); process.exit();
} }
else { else {
cmds[cmd](); cmds[cmd]()
.catch(err => {
console.log(err);
process.exit();
});
} }
function prepareImages() { function prepareImages() {
@ -79,38 +95,20 @@ function prepareImages() {
var name = file.replace(/[.][^.]*$/, ''); var name = file.replace(/[.][^.]*$/, '');
return sharp(path.join(srcDir, file)) return sharp(path.join(srcDir, file))
.resize(conf.width) .resize(conf.width)
.quality(conf.quality) .quality(conf.quality)
.toFile(path.join(destDir, fname)) .toFile(path.join(destDir, fname))
.then(() => { .then(() => ({ file: fname, name: name }))
return { file: fname, name: name }; .catch(sharpErr => utils.copyFile(srcDir, file, destDir, fname)
}, err => { .then(() => ({ file: fname, name: name, error: '' + sharpErr }))
console.log('Resizing "' + file + '" ' + err); .catch(writeErr => ({ file: '', name: name, error: writeErr }))
return new Promise((resolve) => { );
var rs = fs.createReadStream(path.join(srcDir, file));
var ws = fs.createWriteStream(path.join(destDir, fname));
ws.on('error', e => {
resolve({ file: '', name: name, error: 'Writing file failed: ' + e + '; ' + err });
});
rs.on('end', () => {
resolve({ file: fname, name: name, error: '' + err });
ws.end();
});
rs.pipe(ws, { end: false });
});
});
}) })
.then(cont => utils.writeFile(cont, 'content.json', odir))
.then(cont => { .then(cont => {
log('Image preparation done!'); //log('Preparing images finished. Content:', util.inspect(cont, { showHidden: false, depth: null }));
return utils.writeFile(JSON.stringify(cont), 'content.json', odir); log('Preparing images finished:', ['content.json'].concat(cont.cont.map(c => c.dir)).join(', '));
})
.then(cont => {
log('Content:', util.inspect(cont, { showHidden: false, depth: null }));
return cont; return cont;
})
.catch(err => {
console.log(err);
process.exit();
}); });
} }
@ -118,25 +116,113 @@ function generateList(cont) {
log('Generating file list'); log('Generating file list');
return getContent(cont) return getContent(cont)
.then(cont => {
//log('content:', util.inspect(cont, { showHidden: false, depth: null }));
return cont
})
.then(cont => getListEntryHtml(cont.cont)) .then(cont => getListEntryHtml(cont.cont))
.then(html => getPageHtml(html)) .then(html => getPageHtml(html, conf.templates.index))
.then(html => Promise.all([ .then(html => Promise.all([
utils.writeFile(html, 'list.html', odir), utils.writeFile(html, conf.fnames.list, odir, true)
utils.copyFile(clientDir, 'index.css', odir),
utils.copyFile(clientDir, 'index.js', odir)
])) ]))
.then(res => { .then(res => {
log('Generated files:', res); log('Generating file list finished:', res.join(', '));
return res[0]; return res;
});
}
function generateGallery(cont) {
log('Generating gallery');
return getContent(cont)
.then(cont => getGalleryEntryHtml(cont.cont))
.then(html => getPageHtml(html, conf.templates.index))
.then(html => Promise.all([
utils.writeFile(html, conf.fnames.gallery, odir, true)
]))
.then(files => {
log('Generating gallery finished:', files.join(', '));
return files;
});
}
function copyAssets(asskey) {
log('Copying', asskey, 'assets');
var fnames = conf.assets[asskey] ? conf.assets[asskey] : asskey && asskey.map ? asskey : [];
return Promise.all(fnames.map(fname => utils.copyFile(clientDir, fname, odir)))
.then(files => {
log('Copying', asskey, 'assets finished:', files.join(', '));
return files;
});
}
/**
* Creates sanitized mirror of a directory tree and grants access to its files
*
* @param {string} dir Path of the directory to be processed
* @param {function({string}, {string}, {string})} cb(file, srcDir, destDir) File transformator
* @param {boolean} nomkdir Disable (sanitized) mkdir of the directory being processed
* @param {int} [depth=0] Indents log messages according to an actual depth
* @returns {void}
*/
function mirrorDirTree(dir, cb, nomkdir, depth) {
depth = depth === undefined ? 0 : depth += 1;
var destDir = dir === idir ? odir : path.join(odir, sanitize(dir.replace(idir, '')));
return new Promise((resolve, reject) => fs.readdir(dir, (err, files) => {
if (err) reject('Reading directory "' + dir + '" failed: ' + err);
if (files === undefined) reject('Directory "' + dir +'" does not exist!');
if (files.length && !fs.existsSync(destDir) && !nomkdir) fs.mkdirSync(destDir);
if (files.filter) files = files.filter(file => conf.ignored_files.indexOf(file) === -1);
if (SORT_DESC) files.reverse();
resolve(files);
}))
.then(files => Promise.all(files.map(file =>
new Promise((resolve, reject) => fs.stat(path.join(dir, file), (err, stat) => {
if (stat.isDirectory())
resolve(mirrorDirTree(path.join(dir, file), cb, nomkdir, depth));
else if (typeof cb === 'function')
resolve(cb(file, dir, destDir, depth + 1));
else
reject('No file processing callback provided.');
}))
)))
.then(items => {
logi(depth, '[dir]', dir);
items.forEach(item => logi(depth + 1, item.error ? '[error] ' + item.file + ' ' + item.error
: item.file ? '[file] ' + item.file : item.dir ? '[dir] ' + item.dir : item));
return { dir: destDir.replace(/^.*\//, ''), name: dir.replace(/^.*\//, ''), cont: items };
});
}
function getContent(cont) {
return new Promise((resolve, reject) => {
if (cont) resolve(cont);
fs.readFile(path.join(odir, 'content.json'), (err, data) => {
if (err) reject(err);
else if (!data) reject('File empty: ' + path.join(odir, 'content.json'));
try { resolve(JSON.parse(data)); }
catch (e) { reject(path.join(odir, 'content.json') + ' parsing failed: ' + e); }
});
}) })
.catch(err => { .catch(err => {
console.log(err); console.log('Content cache reading failed: ' + err + '; Re-reading directory tree');
process.exit(); return mirrorDirTree(odir, file => ({ file: file }), true);
}); })
}
function sanitize(path) {
return utils.diaStrip(path).replace(/\ /g, '_')
.replace(/([.][^.]*)$/, match => String.prototype.toLowerCase.call(match));
}
function getPageHtml(htmlPart, pageFname) {
var pageFile = path.join(clientDir, pageFname);
return new Promise((resolve, reject) => fs.readFile(pageFile, 'utf-8', (err, data) => {
if (err) reject(err);
else if (!data) reject('File empty: ' + pageFile);
resolve(data);
}))
.then((pageHtml) => pageHtml.substring(0, pageHtml.search('[ ]*</body>'))
+ htmlPart + pageHtml.substr(pageHtml.search('[ ]*</body>')));
} }
function getListEntryHtml(cont, dir, depth) { function getListEntryHtml(cont, dir, depth) {
@ -156,34 +242,6 @@ function getListEntryHtml(cont, dir, depth) {
return html; return html;
} }
function getPageHtml(htmlPart, pageFname) {
pageFname = pageFname || conf.template;
return new Promise((resolve, reject) => {
var pageFile = path.join(clientDir, pageFname);
fs.readFile(pageFile, 'utf-8', (err, data) => {
if (err) reject(err);
else if (!data) reject('File empty: ' + pageFile);
resolve(data);
});
})
.then((pageHtml) => {
return pageHtml.substring(0, pageHtml.search('[ ]*</body>')) + htmlPart
+ pageHtml.substr(pageHtml.search('[ ]*</body>'));
});
}
function generateGallery(cont) {
log('Generating gallery');
return getContent(cont)
.then(cont => getGalleryEntryHtml(cont.cont))
.then(cont => {
log('Gallery prepared!');
log('content:', util.inspect(cont, { showHidden: false, depth: null }));
return cont;
});
}
function getGalleryEntryHtml(cont) { function getGalleryEntryHtml(cont) {
var html = ''; var html = '';
@ -192,67 +250,3 @@ function getGalleryEntryHtml(cont) {
return html; return html;
} }
/**
* Creates sanitized mirror of a directory tree and grants access to its files
*
* @param {string} dir Path of the directory to be processed
* @param {function({string}, {string}, {string})} cb(file, srcDir, destDir) File transformator
* @param {boolean} nomkdir Disable (sanitized) mkdir of the directory being processed
* @param {int} [depth=0] Indents log messages according to an actual depth
* @returns {void}
*/
function mirrorDirTree(dir, cb, nomkdir, depth) {
depth = depth === undefined ? 0 : depth += 1;
return new Promise((resolve, reject) => fs.readdir(dir, (err, files) => {
if (err) reject('Reading directory "' + dir + '" failed: ' + err);
//logi(depth, '[dir]', dir.replace(/^.*\//, ''));
var destDir = dir === idir ? odir : path.join(odir, sanitize(dir.replace(idir, '')));
if (!nomkdir && files.length && !fs.existsSync(destDir)) {
logi(depth, '[mkdir]', destDir);
fs.mkdirSync(destDir);
}
if (SORT_DESC)
files.reverse();
return Promise.all(files.map(file => {
return new Promise((resolve) => fs.stat(path.join(dir, file), (err, stat) => {
if (stat.isDirectory())
resolve(mirrorDirTree(path.join(dir, file), cb, nomkdir, depth));
else if (typeof cb === 'function')
resolve(cb(file, dir, destDir, depth + 1));
}));
}))
.then(items => {
logi(depth, 'finished:');
items.forEach(item => logi(depth + 1, item.error ? '[error] ' + item.file + ' ' + item.error
: item.file ? '[file] ' + item.file : item.dir ? '[dir] ' + item.dir : item));
resolve({ dir: destDir.replace(/^.*\//, ''), name: dir.replace(/^.*\//, ''), cont: items });
});
}));
}
function getContent(cont) {
return new Promise((resolve, reject) => {
if (cont) resolve(cont);
else
fs.readFile(path.join(odir, 'content.json'), (err, data) => {
if (err) reject(err);
else if (!data) reject('File empty: ' + path.join(odir, 'content.json'));
try { resolve(JSON.parse(data)); }
catch (e) { reject(path.join(odir, 'content.json') + ' parsing failed: ' + e); }
});
})
.catch(err => {
console.log('Content reading failed: ' + err + '; Re-reading directory tree');
return mirrorDirTree(odir, file => { return { file: file }; }, true);
})
}
function sanitize(path) {
return utils.diaStrip(path).replace(/\ /g, '_')
.replace(/([.][^.]*)$/, match => String.prototype.toLowerCase.call(match));
}

View File

@ -121,20 +121,21 @@ function diaStrip(str) {
return str; return str;
} }
function writeFile(data, fname, dir) { function writeFile(data, fname, dir, returnFileName) {
return new Promise((resolve, reject) => fs.writeFile(path.join(dir, fname), data, err => { var serialized = typeof data === 'object' ? JSON.stringify(data) : data;
return new Promise((resolve, reject) => fs.writeFile(path.join(dir, fname), serialized, err => {
if (err) reject('Writing content file failed: ' + err) if (err) reject('Writing content file failed: ' + err)
resolve(fname); resolve(returnFileName ? fname : data);
})); }));
} }
function copyFile(srcDir, fname, destDir) { function copyFile(sdir, sfname, ddir, dfname) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
var rs = fs.createReadStream(path.join(srcDir, fname)); var rs = fs.createReadStream(path.join(sdir, sfname));
var ws = fs.createWriteStream(path.join(destDir, fname)); var ws = fs.createWriteStream(path.join(ddir, dfname || sfname));
rs.on('error', err => reject('File reading failed: ' + err)); rs.on('error', err => reject('File reading failed: ' + err));
ws.on('error', err => reject('File writing failed: ' + err)); ws.on('error', err => reject('File writing failed: ' + err));
ws.on('finish', () => resolve(fname)); ws.on('finish', () => resolve(dfname || sfname));
rs.pipe(ws); rs.pipe(ws);
}); });
} }