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
|
- 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)~~
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
288
src/index.js
288
src/index.js
@ -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() {
|
||||||
@ -82,35 +98,17 @@ function prepareImages() {
|
|||||||
.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));
|
|
||||||
}
|
|
||||||
|
|||||||
15
src/utils.js
15
src/utils.js
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user