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:
parent
58c7b149dd
commit
7c8b5188da
@ -39,4 +39,4 @@ Image resizing done by [sharp](http://sharp.dimens.io/) ([github](https://github
|
||||
- use some lightbox on the prepared directory
|
||||
- further development
|
||||
- integration with other projects (iframe? ajax? web component? Polymer?)
|
||||
- config.json support (for specifying conversion settings)
|
||||
- ~~config.json support (for specifying conversion settings)~~
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gallerymaker",
|
||||
"version": "0.0.0",
|
||||
"version": "0.0.1",
|
||||
"description": "A static html gallery maker",
|
||||
"keywords": [
|
||||
"gallery",
|
||||
|
||||
294
src/index.js
294
src/index.js
@ -2,7 +2,7 @@
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var util = require('util');
|
||||
//var util = require('util');
|
||||
var sharp = require('sharp');
|
||||
|
||||
var utils = require('./utils.js');
|
||||
@ -13,15 +13,27 @@ var odir = sanitize(process.argv[4] || idir + '.web');
|
||||
|
||||
var clientDir = path.join(__dirname, 'client');
|
||||
|
||||
var conf = {
|
||||
width: 700,
|
||||
quality: 80,
|
||||
debug: true,
|
||||
template: 'index.html'
|
||||
};
|
||||
var EOL = "\n";
|
||||
var DEPTH = 2;
|
||||
var SORT_DESC = true;
|
||||
var conf;
|
||||
try { conf = require(path.join(__dirname, '..', 'config.json')); }
|
||||
catch (e) { conf = {}; }
|
||||
if (!conf.width) conf.width = 700;
|
||||
if (!conf.quality) conf.quality = 80;
|
||||
if (!conf.fnames) conf.fnames = {}; // odir
|
||||
if (!conf.fnames.list) conf.fnames.list = 'list.html';
|
||||
if (!conf.fnames.gallery) conf.fnames.gallery = 'gallery.html';
|
||||
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 logi = function () {
|
||||
@ -31,17 +43,17 @@ var logi = function () {
|
||||
var ind = depth => utils.indent(depth, DEPTH);
|
||||
|
||||
var cmds = {
|
||||
prepare: prepareImages,
|
||||
prepare: () => prepareImages().then(copyAssets('common')),
|
||||
list: generateList,
|
||||
gallery: generateGallery,
|
||||
all: () => {
|
||||
prepareImages()
|
||||
all: () => prepareImages()
|
||||
.then(cont => Promise.all([
|
||||
copyAssets('common'),
|
||||
['content.json'].concat(cont.cont.map(c => c.dir)),
|
||||
generateList(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) {
|
||||
@ -68,7 +80,11 @@ else if (!cmds[cmd]) {
|
||||
process.exit();
|
||||
}
|
||||
else {
|
||||
cmds[cmd]();
|
||||
cmds[cmd]()
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
|
||||
function prepareImages() {
|
||||
@ -79,38 +95,20 @@ function prepareImages() {
|
||||
var name = file.replace(/[.][^.]*$/, '');
|
||||
|
||||
return sharp(path.join(srcDir, file))
|
||||
.resize(conf.width)
|
||||
.quality(conf.quality)
|
||||
.toFile(path.join(destDir, fname))
|
||||
.then(() => {
|
||||
return { file: fname, name: name };
|
||||
}, err => {
|
||||
console.log('Resizing "' + file + '" ' + err);
|
||||
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 });
|
||||
});
|
||||
});
|
||||
.resize(conf.width)
|
||||
.quality(conf.quality)
|
||||
.toFile(path.join(destDir, fname))
|
||||
.then(() => ({ file: fname, name: name }))
|
||||
.catch(sharpErr => utils.copyFile(srcDir, file, destDir, fname)
|
||||
.then(() => ({ file: fname, name: name, error: '' + sharpErr }))
|
||||
.catch(writeErr => ({ file: '', name: name, error: writeErr }))
|
||||
);
|
||||
})
|
||||
.then(cont => utils.writeFile(cont, 'content.json', odir))
|
||||
.then(cont => {
|
||||
log('Image preparation done!');
|
||||
return utils.writeFile(JSON.stringify(cont), 'content.json', odir);
|
||||
})
|
||||
.then(cont => {
|
||||
log('Content:', util.inspect(cont, { showHidden: false, depth: null }));
|
||||
//log('Preparing images finished. Content:', util.inspect(cont, { showHidden: false, depth: null }));
|
||||
log('Preparing images finished:', ['content.json'].concat(cont.cont.map(c => c.dir)).join(', '));
|
||||
return cont;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
|
||||
@ -118,25 +116,113 @@ function generateList(cont) {
|
||||
log('Generating file list');
|
||||
|
||||
return getContent(cont)
|
||||
.then(cont => {
|
||||
//log('content:', util.inspect(cont, { showHidden: false, depth: null }));
|
||||
return cont
|
||||
})
|
||||
.then(cont => getListEntryHtml(cont.cont))
|
||||
.then(html => getPageHtml(html))
|
||||
.then(html => getPageHtml(html, conf.templates.index))
|
||||
.then(html => Promise.all([
|
||||
utils.writeFile(html, 'list.html', odir),
|
||||
utils.copyFile(clientDir, 'index.css', odir),
|
||||
utils.copyFile(clientDir, 'index.js', odir)
|
||||
utils.writeFile(html, conf.fnames.list, odir, true)
|
||||
]))
|
||||
.then(res => {
|
||||
log('Generated files:', res);
|
||||
return res[0];
|
||||
log('Generating file list finished:', res.join(', '));
|
||||
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 => {
|
||||
console.log(err);
|
||||
process.exit();
|
||||
});
|
||||
console.log('Content cache reading failed: ' + err + '; Re-reading directory tree');
|
||||
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) {
|
||||
@ -156,34 +242,6 @@ function getListEntryHtml(cont, dir, depth) {
|
||||
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) {
|
||||
|
||||
var html = '';
|
||||
@ -192,67 +250,3 @@ function getGalleryEntryHtml(cont) {
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
15
src/utils.js
15
src/utils.js
@ -121,20 +121,21 @@ function diaStrip(str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
function writeFile(data, fname, dir) {
|
||||
return new Promise((resolve, reject) => fs.writeFile(path.join(dir, fname), data, err => {
|
||||
function writeFile(data, fname, dir, returnFileName) {
|
||||
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)
|
||||
resolve(fname);
|
||||
resolve(returnFileName ? fname : data);
|
||||
}));
|
||||
}
|
||||
|
||||
function copyFile(srcDir, fname, destDir) {
|
||||
function copyFile(sdir, sfname, ddir, dfname) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var rs = fs.createReadStream(path.join(srcDir, fname));
|
||||
var ws = fs.createWriteStream(path.join(destDir, fname));
|
||||
var rs = fs.createReadStream(path.join(sdir, sfname));
|
||||
var ws = fs.createWriteStream(path.join(ddir, dfname || sfname));
|
||||
rs.on('error', err => reject('File reading 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);
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user