Use <standard> for code style, which removed lots of unused variables/requires

pull/64/merge
Romain 7 years ago
parent e71e5d4207
commit 59b9bdbbd2

@ -5,6 +5,7 @@
[![Travis CI](https://travis-ci.org/thumbsup/node-thumbsup.svg?branch=master)](https://travis-ci.org/thumbsup/node-thumbsup) [![Travis CI](https://travis-ci.org/thumbsup/node-thumbsup.svg?branch=master)](https://travis-ci.org/thumbsup/node-thumbsup)
[![Dependencies](http://img.shields.io/david/thumbsup/node-thumbsup.svg?style=flat)](https://david-dm.org/thumbsup/node-thumbsup) [![Dependencies](http://img.shields.io/david/thumbsup/node-thumbsup.svg?style=flat)](https://david-dm.org/thumbsup/node-thumbsup)
[![Dev dependencies](https://david-dm.org/thumbsup/node-thumbsup/dev-status.svg?style=flat)](https://david-dm.org/thumbsup/node-thumbsup?type=dev) [![Dev dependencies](https://david-dm.org/thumbsup/node-thumbsup/dev-status.svg?style=flat)](https://david-dm.org/thumbsup/node-thumbsup?type=dev)
[![Standard - JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](http://standardjs.com/)
![banner](banner.jpg) ![banner](banner.jpg)

@ -1,10 +1,10 @@
#!/usr/bin/env node #!/usr/bin/env node
var yargs = require('yargs'); var yargs = require('yargs')
var path = require('path'); var path = require('path')
var index = require('../src/index'); var index = require('../src/index')
console.log(''); console.log('')
var opts = yargs var opts = yargs
.usage('Usages:\n' + .usage('Usages:\n' +
' thumbsup [required] [options]\n' + ' thumbsup [required] [options]\n' +
@ -126,31 +126,30 @@ var opts = yargs
.epilogue('The optional JSON config should contain a single object with one key ' + .epilogue('The optional JSON config should contain a single object with one key ' +
'per argument, not including the leading "--". For example:\n\n' + 'per argument, not including the leading "--". For example:\n\n' +
'{ "sort-albums-by": "start-date" }') '{ "sort-albums-by": "start-date" }')
.argv; .argv
// Compatibility // Compatibility
if (opts['sort-folders'] == 'name') opts['sort-albums-by'] = 'title'; if (opts['sort-folders'] === 'name') opts['sort-albums-by'] = 'title'
if (opts['sort-folders'] == 'date') opts['sort-albums-by'] = 'start-date'; if (opts['sort-folders'] === 'date') opts['sort-albums-by'] = 'start-date'
index.build({ index.build({
input: path.resolve(opts['input']), input: path.resolve(opts['input']),
output: path.resolve(opts['output']), output: path.resolve(opts['output']),
title: opts['title'], title: opts['title'],
thumbSize: opts['thumb-size'], thumbSize: opts['thumb-size'],
largeSize: opts['large-size'], largeSize: opts['large-size'],
originalPhotos: opts['original-photos'], originalPhotos: opts['original-photos'],
originalVideos: opts['original-videos'], originalVideos: opts['original-videos'],
albumsFrom: opts['albums-from'], albumsFrom: opts['albums-from'],
albumsDateFormat: opts['albums-date-format'], albumsDateFormat: opts['albums-date-format'],
sortAlbumsBy: opts['sort-albums-by'], sortAlbumsBy: opts['sort-albums-by'],
sortAlbumsDirection: opts['sort-albums-direction'], sortAlbumsDirection: opts['sort-albums-direction'],
sortMediaBy: opts['sort-media-by'], sortMediaBy: opts['sort-media-by'],
sortMediaDirection: opts['sort-media-direction'], sortMediaDirection: opts['sort-media-direction'],
theme: opts['theme'], theme: opts['theme'],
css: opts['css'], css: opts['css'],
googleAnalytics: opts['google-analytics'], googleAnalytics: opts['google-analytics'],
index: opts['index'], index: opts['index'],
footer: opts['footer'], footer: opts['footer'],
albumsOutputFolder: opts['albums-output-folder'] albumsOutputFolder: opts['albums-output-folder']
}); })

@ -44,6 +44,19 @@
"markdown-toc": "^0.13.0", "markdown-toc": "^0.13.0",
"mocha": "^3.1.2", "mocha": "^3.1.2",
"require-lint": "^1.1.2", "require-lint": "^1.1.2",
"should": "^11.1.1" "should": "^11.1.1",
"standard": "^9.0.0"
},
"standard": {
"ignore": [
"public/**"
],
"globals": [
"afterEach",
"beforeEach",
"describe",
"it",
"xit"
]
} }
} }

@ -9,6 +9,9 @@ PATH=$(pwd)/node_modules/.bin:$PATH
echo "--- Check all dependencies in package.json" echo "--- Check all dependencies in package.json"
require-lint --src bin/thumbsup.js --ignore-extra lightgallery require-lint --src bin/thumbsup.js --ignore-extra lightgallery
echo "--- Static code analysis"
standard
echo "--- Verify lightgallery dependency is built in" echo "--- Verify lightgallery dependency is built in"
grep \"lightgallery\" package.json 1>/dev/null grep \"lightgallery\" package.json 1>/dev/null

@ -1,6 +1,5 @@
const async = require('async') const async = require('async')
const fs = require('fs-extra') const fs = require('fs-extra')
const pad = require('pad')
const path = require('path') const path = require('path')
const database = require('./input/database') const database = require('./input/database')
const progress = require('./utils/progress') const progress = require('./utils/progress')
@ -11,12 +10,10 @@ const resize = require('./output-media/resize')
const website = require('./output-website/website') const website = require('./output-website/website')
exports.build = function (opts) { exports.build = function (opts) {
resize.sizes.thumb = opts.thumbSize resize.sizes.thumb = opts.thumbSize
resize.sizes.large = opts.largeSize resize.sizes.large = opts.largeSize
fs.mkdirpSync(opts.output) fs.mkdirpSync(opts.output)
const media = path.join(opts.output, 'media')
const databaseFile = path.join(opts.output, 'metadata.json') const databaseFile = path.join(opts.output, 'metadata.json')
var album = null // root album with nested albums var album = null // root album with nested albums
@ -62,7 +59,6 @@ exports.build = function (opts) {
} }
], finish) ], finish)
} }
function asyncProgress (bar) { function asyncProgress (bar) {

@ -1,10 +1,8 @@
const debug = require('debug')('thumbsup') const debug = require('debug')('thumbsup')
const exifdb = require('exiftool-json-db') const exifdb = require('exiftool-json-db')
const pad = require('pad')
const progress = require('../utils/progress') const progress = require('../utils/progress')
exports.update = function(media, databasePath, callback) { exports.update = function (media, databasePath, callback) {
var updateBar = null var updateBar = null
var emitter = null var emitter = null
@ -32,5 +30,4 @@ exports.update = function(media, databasePath, callback) {
}) })
emitter.on('error', callback) emitter.on('error', callback)
} }

@ -1,11 +1,12 @@
const debug = require('debug')('thumbsup') const debug = require('debug')('thumbsup')
exports.paths = function (filepath, mediaType, config) { exports.paths = function (filepath, mediaType, config) {
var originals = false
if (mediaType === 'image') { if (mediaType === 'image') {
var originals = config ? config.originalPhotos : false originals = config ? config.originalPhotos : false
return imageOutput(filepath, originals) return imageOutput(filepath, originals)
} else if (mediaType === 'video') { } else if (mediaType === 'video') {
var originals = config ? config.originalVideos : false originals = config ? config.originalVideos : false
return videoOutput(filepath, originals) return videoOutput(filepath, originals)
} else { } else {
debug(`Unsupported file type: ${mediaType}`) debug(`Unsupported file type: ${mediaType}`)
@ -58,9 +59,9 @@ function videoOutput (filepath, originals) {
} else { } else {
output.download = output.video output.download = output.video
} }
return output; return output
} }
function ext(file, ext) { function ext (file, ext) {
return file.replace(/\.[a-z0-9]+$/i, '.' + ext) return file.replace(/\.[a-z0-9]+$/i, '.' + ext)
} }

@ -1,141 +1,141 @@
var _ = require('lodash'); var _ = require('lodash')
var path = require('path'); var path = require('path')
var url = require('url'); var url = require('url')
var index = 0; var index = 0
// number of images to show in the album preview grid // number of images to show in the album preview grid
var PREVIEW_COUNT = 10; var PREVIEW_COUNT = 10
var SORT_ALBUMS_BY = { var SORT_ALBUMS_BY = {
'title': function(album) { return album.title; }, 'title': function (album) { return album.title },
'start-date': function(album) { return album.stats.fromDate; }, 'start-date': function (album) { return album.stats.fromDate },
'end-date': function(album) { return album.stats.toDate; } 'end-date': function (album) { return album.stats.toDate }
}; }
var SORT_MEDIA_BY = { var SORT_MEDIA_BY = {
'filename': function(file) { return file.filename; }, 'filename': function (file) { return file.filename },
'date': function(file) { return file.date; } 'date': function (file) { return file.date }
}; }
var PREVIEW_MISSING = { var PREVIEW_MISSING = {
urls: { urls: {
thumbnail: 'public/missing.png' thumbnail: 'public/missing.png'
} }
}; }
function Album(opts) { function Album (opts) {
if (typeof opts === 'string') opts = { title: opts }; if (typeof opts === 'string') opts = { title: opts }
this.id = opts.id || ++index; this.id = opts.id || ++index
this.title = opts.title || ('Album ' + this.id); this.title = opts.title || ('Album ' + this.id)
this.basename = sanitise(this.title); this.basename = sanitise(this.title)
this.files = opts.files || []; this.files = opts.files || []
this.albums = opts.albums || []; this.albums = opts.albums || []
this.depth = 0; this.depth = 0
this.home = false; this.home = false
this.stats = null; this.stats = null
this.previews = null; this.previews = null
this.allFiles = []; this.allFiles = []
} }
Album.prototype.finalize = function(options, parent) { Album.prototype.finalize = function (options, parent) {
options = options || {}; options = options || {}
var albumsOutputFolder = options.albumsOutputFolder || '.'; var albumsOutputFolder = options.albumsOutputFolder || '.'
// calculate final file paths and URLs // calculate final file paths and URLs
if (parent == null) { if (parent == null) {
this.path = options.index; this.path = options.index
this.url = options.index; this.url = options.index
this.depth = 0; this.depth = 0
} else { } else {
if (parent.depth > 0) { if (parent.depth > 0) {
this.basename = parent.basename + '-' + this.basename; this.basename = parent.basename + '-' + this.basename
} }
this.path = path.join(albumsOutputFolder, this.basename + '.html'); this.path = path.join(albumsOutputFolder, this.basename + '.html')
this.url = url.resolve(albumsOutputFolder + '/', this.basename + '.html'); this.url = url.resolve(albumsOutputFolder + '/', this.basename + '.html')
this.depth = parent.depth + 1; this.depth = parent.depth + 1
} }
// then finalize all nested albums (which uses the parent basename) // then finalize all nested albums (which uses the parent basename)
for (var i = 0; i < this.albums.length; ++i) { for (var i = 0; i < this.albums.length; ++i) {
this.albums[i].finalize(options, this); this.albums[i].finalize(options, this)
} }
// perform stats & other calculations // perform stats & other calculations
// once the nested albums have been finalized too // once the nested albums have been finalized too
this.home = this.depth === 0; this.home = this.depth === 0
this.calculateStats(); this.calculateStats()
this.calculateSummary(); this.calculateSummary()
this.sort(options); this.sort(options)
this.pickPreviews(); this.pickPreviews()
this.aggregateAllFiles(); this.aggregateAllFiles()
}; }
Album.prototype.calculateStats = function() { Album.prototype.calculateStats = function () {
// nested albums // nested albums
var nestedPhotos = _.map(this.albums, 'stats.photos'); var nestedPhotos = _.map(this.albums, 'stats.photos')
var nestedVideos = _.map(this.albums, 'stats.videos'); var nestedVideos = _.map(this.albums, 'stats.videos')
var nestedFromDates = _.map(this.albums, 'stats.fromDate'); var nestedFromDates = _.map(this.albums, 'stats.fromDate')
var nestedToDates = _.map(this.albums, 'stats.toDate'); var nestedToDates = _.map(this.albums, 'stats.toDate')
// current level // current level
var currentPhotos = _.filter(this.files, {isVideo: false}).length; var currentPhotos = _.filter(this.files, {isVideo: false}).length
var currentVideos = _.filter(this.files, {isVideo: true}).length; var currentVideos = _.filter(this.files, {isVideo: true}).length
var currentFromDate = _.map(this.files, 'date'); var currentFromDate = _.map(this.files, 'date')
var currentToDate = _.map(this.files, 'date'); var currentToDate = _.map(this.files, 'date')
// aggregate all stats // aggregate all stats
this.stats = { this.stats = {
albums: this.albums.length, albums: this.albums.length,
photos: _.sum(_.compact(_.concat(nestedPhotos, currentPhotos))) || 0, photos: _.sum(_.compact(_.concat(nestedPhotos, currentPhotos))) || 0,
videos: _.sum(_.compact(_.concat(nestedVideos, currentVideos))) || 0, videos: _.sum(_.compact(_.concat(nestedVideos, currentVideos))) || 0,
fromDate: _.min(_.compact(_.concat(nestedFromDates, currentFromDate))), fromDate: _.min(_.compact(_.concat(nestedFromDates, currentFromDate))),
toDate: _.max(_.compact(_.concat(nestedToDates, currentToDate))) toDate: _.max(_.compact(_.concat(nestedToDates, currentToDate)))
}; }
this.stats.total = this.stats.photos + this.stats.videos; this.stats.total = this.stats.photos + this.stats.videos
} }
Album.prototype.calculateSummary = function() { Album.prototype.calculateSummary = function () {
var items = [ var items = [
itemCount(this.stats.albums, 'album'), itemCount(this.stats.albums, 'album'),
itemCount(this.stats.photos, 'photo'), itemCount(this.stats.photos, 'photo'),
itemCount(this.stats.videos, 'video') itemCount(this.stats.videos, 'video')
]; ]
this.summary = _.compact(items).join(', '); this.summary = _.compact(items).join(', ')
}; }
Album.prototype.sort = function(options) { Album.prototype.sort = function (options) {
this.files = _.orderBy(this.files, SORT_MEDIA_BY[options.sortMediaBy], options.sortMediaDirection); this.files = _.orderBy(this.files, SORT_MEDIA_BY[options.sortMediaBy], options.sortMediaDirection)
this.albums = _.orderBy(this.albums, SORT_ALBUMS_BY[options.sortAlbumsBy], options.sortAlbumsDirection); this.albums = _.orderBy(this.albums, SORT_ALBUMS_BY[options.sortAlbumsBy], options.sortAlbumsDirection)
}; }
Album.prototype.pickPreviews = function() { Album.prototype.pickPreviews = function () {
// also consider previews from nested albums // also consider previews from nested albums
var nestedPicks = _.flatten(_.map(this.albums, 'previews')).filter(function(file) { var nestedPicks = _.flatten(_.map(this.albums, 'previews')).filter(function (file) {
return file !== PREVIEW_MISSING; return file !== PREVIEW_MISSING
}); })
// then pick the top ones // then pick the top ones
var potentialPicks = _.concat(this.files, nestedPicks); var potentialPicks = _.concat(this.files, nestedPicks)
this.previews = potentialPicks.slice(0, PREVIEW_COUNT); this.previews = potentialPicks.slice(0, PREVIEW_COUNT)
// and fill the gap with a placeholder // and fill the gap with a placeholder
var missing = PREVIEW_COUNT - this.previews.length; var missing = PREVIEW_COUNT - this.previews.length
for (var i = 0; i < missing; ++i) { for (var i = 0; i < missing; ++i) {
this.previews.push(PREVIEW_MISSING); this.previews.push(PREVIEW_MISSING)
} }
}; }
Album.prototype.aggregateAllFiles = function() { Album.prototype.aggregateAllFiles = function () {
var nestedFiles = _.flatten(_.map(this.albums, 'allFiles')) var nestedFiles = _.flatten(_.map(this.albums, 'allFiles'))
this.allFiles = _.concat(nestedFiles, this.files); this.allFiles = _.concat(nestedFiles, this.files)
}; }
function sanitise(filename) { function sanitise (filename) {
return filename.replace(/[^a-z0-9-_]/ig, ''); return filename.replace(/[^a-z0-9-_]/ig, '')
} }
function itemCount(count, type) { function itemCount (count, type) {
if (count === 0) return ''; if (count === 0) return ''
var plural = (count > 1) ? 's' : ''; var plural = (count > 1) ? 's' : ''
return '' + count + ' ' + type + plural; return '' + count + ' ' + type + plural
} }
// for testing purposes // for testing purposes
Album.resetIds = function() { Album.resetIds = function () {
index = 0; index = 0
}; }
module.exports = Album; module.exports = Album

@ -1,42 +1,42 @@
var _ = require('lodash'); var _ = require('lodash')
var path = require('path'); var path = require('path')
var moment = require('moment'); var moment = require('moment')
var Album = require('./album'); var Album = require('./album')
// creates nested albums based on the media date, e.g. "{year}/{month}" // creates nested albums based on the media date, e.g. "{year}/{month}"
// opts = {format}, where format is a valid <moment> format // opts = {format}, where format is a valid <moment> format
// e.g. "YYYY-MM" or "YYYY/MMMM" for nested albums // e.g. "YYYY-MM" or "YYYY/MMMM" for nested albums
exports.albums = function(collection, opts) { exports.albums = function (collection, opts) {
opts = _.defaults(opts, { opts = _.defaults(opts, {
grouping: opts.albumsDateFormat || 'YYYY-MMMM' grouping: opts.albumsDateFormat || 'YYYY-MMMM'
}); })
var groups = {}; var groups = {}
// put all files in the right albums // put all files in the right albums
collection.forEach(function(media) { collection.forEach(function (media) {
var groupName = moment(media.date).format(opts.grouping); var groupName = moment(media.date).format(opts.grouping)
createAlbumHierarchy(groups, groupName); createAlbumHierarchy(groups, groupName)
groups[groupName].files.push(media); groups[groupName].files.push(media)
}); })
// only return top-level albums // only return top-level albums
var topLevel = _.keys(groups).filter(function(dir) { var topLevel = _.keys(groups).filter(function (dir) {
return path.dirname(dir) === '.'; return path.dirname(dir) === '.'
}); })
return _.values(_.pick(groups, topLevel)); return _.values(_.pick(groups, topLevel))
}; }
function createAlbumHierarchy(albumsByFullDate, dateSegment) { function createAlbumHierarchy (albumsByFullDate, dateSegment) {
if (!albumsByFullDate.hasOwnProperty(dateSegment)) { if (!albumsByFullDate.hasOwnProperty(dateSegment)) {
// create parent albums first // create parent albums first
var parentDate = path.dirname(dateSegment); var parentDate = path.dirname(dateSegment)
if (parentDate !== '.') { if (parentDate !== '.') {
createAlbumHierarchy(albumsByFullDate, parentDate); createAlbumHierarchy(albumsByFullDate, parentDate)
} }
// then create album if it doesn't exist // then create album if it doesn't exist
var lastDateSegment = path.basename(dateSegment); var lastDateSegment = path.basename(dateSegment)
albumsByFullDate[dateSegment] = new Album({title: lastDateSegment}); albumsByFullDate[dateSegment] = new Album({title: lastDateSegment})
// then attach to parent // then attach to parent
if (parentDate !== '.') { if (parentDate !== '.') {
albumsByFullDate[parentDate].albums.push(albumsByFullDate[dateSegment]); albumsByFullDate[parentDate].albums.push(albumsByFullDate[dateSegment])
} }
} }
} }

@ -1,38 +1,38 @@
var _ = require('lodash'); var _ = require('lodash')
var path = require('path'); var path = require('path')
var Album = require('./album'); var Album = require('./album')
// for now only 1 level of folders, // for now only 1 level of folders,
// e.g. an album might be called "holidays/newyork" or "holidays/tokyo"n // e.g. an album might be called "holidays/newyork" or "holidays/tokyo"n
// eventually we could return nested albums as an option // eventually we could return nested albums as an option
exports.albums = function(collection, opts) { exports.albums = function (collection, opts) {
var albumsByFullPath = {}; var albumsByFullPath = {}
// put all files in the right album // put all files in the right album
collection.forEach(function(media) { collection.forEach(function (media) {
var fullDir = path.dirname(media.file.path); var fullDir = path.dirname(media.file.path)
createAlbumHierarchy(albumsByFullPath, fullDir); createAlbumHierarchy(albumsByFullPath, fullDir)
albumsByFullPath[fullDir].files.push(media); albumsByFullPath[fullDir].files.push(media)
}); })
// only return top-level albums // only return top-level albums
var topLevel = _.keys(albumsByFullPath).filter(function(dir) { var topLevel = _.keys(albumsByFullPath).filter(function (dir) {
return path.dirname(dir) === '.'; return path.dirname(dir) === '.'
}); })
return _.values(_.pick(albumsByFullPath, topLevel)); return _.values(_.pick(albumsByFullPath, topLevel))
}; }
function createAlbumHierarchy(albumsByFullPath, fullDir) { function createAlbumHierarchy (albumsByFullPath, fullDir) {
if (!albumsByFullPath.hasOwnProperty(fullDir)) { if (!albumsByFullPath.hasOwnProperty(fullDir)) {
// create parent albums first // create parent albums first
var parentDir = path.dirname(fullDir); var parentDir = path.dirname(fullDir)
if (parentDir !== '.') { if (parentDir !== '.') {
createAlbumHierarchy(albumsByFullPath, parentDir); createAlbumHierarchy(albumsByFullPath, parentDir)
} }
// then create album if it doesn't exist // then create album if it doesn't exist
var dirname = path.basename(fullDir); var dirname = path.basename(fullDir)
albumsByFullPath[fullDir] = new Album({title: dirname}); albumsByFullPath[fullDir] = new Album({title: dirname})
// then attach to parent // then attach to parent
if (parentDir !== '.') { if (parentDir !== '.') {
albumsByFullPath[parentDir].albums.push(albumsByFullPath[fullDir]); albumsByFullPath[parentDir].albums.push(albumsByFullPath[fullDir])
} }
} }
} }

@ -1,14 +1,8 @@
var _ = require('lodash')
var gm = require('gm')
var pad = require('pad')
var path = require('path')
var Album = require('./album') var Album = require('./album')
var Media = require('./media')
var byFolder = require('./by-folder') var byFolder = require('./by-folder')
var byDate = require('./by-date') var byDate = require('./by-date')
exports.createAlbums = function (collection, opts) { exports.createAlbums = function (collection, opts) {
// top-level album for the home page // top-level album for the home page
var home = new Album('Home') var home = new Album('Home')
home.filename = opts.index || 'index' home.filename = opts.index || 'index'
@ -19,7 +13,7 @@ exports.createAlbums = function (collection, opts) {
} else if (opts.albumsFrom === 'date') { } else if (opts.albumsFrom === 'date') {
home.albums = byDate.albums(collection, opts) home.albums = byDate.albums(collection, opts)
} else { } else {
throw 'Invalid <albumsFrom> option' throw new Error('Invalid <albumsFrom> option')
} }
// finalize all albums recursively (calculate stats, etc...) // finalize all albums recursively (calculate stats, etc...)

@ -3,7 +3,7 @@ const moment = require('moment')
const path = require('path') const path = require('path')
const EXIF_DATE_FORMAT = 'YYYY:MM:DD HH:mm:ssZ' const EXIF_DATE_FORMAT = 'YYYY:MM:DD HH:mm:ssZ'
var index = 0; var index = 0
/* /*
View model for album entries View model for album entries

@ -6,7 +6,7 @@ const path = require('path')
exports.sizes = { exports.sizes = {
thumb: 120, thumb: 120,
large: 1000, large: 1000
} }
exports.copy = function (task, callback) { exports.copy = function (task, callback) {
@ -36,7 +36,7 @@ exports.photoLarge = function (task, callback) {
// Web-streaming friendly video // Web-streaming friendly video
exports.videoWeb = function (task, callback) { exports.videoWeb = function (task, callback) {
var ffmpeg = 'ffmpeg -i "' + task.src + '" -y "'+ task.dest +'" -f mp4 -vcodec libx264 -ab 96k' var ffmpeg = 'ffmpeg -i "' + task.src + '" -y "' + task.dest + '" -f mp4 -vcodec libx264 -ab 96k'
// AVCHD/MTS videos need a full-frame export to avoid interlacing artefacts // AVCHD/MTS videos need a full-frame export to avoid interlacing artefacts
if (path.extname(task.src).toLowerCase() === '.mts') { if (path.extname(task.src).toLowerCase() === '.mts') {
ffmpeg += ' -vf yadif=1 -qscale:v 4' ffmpeg += ' -vf yadif=1 -qscale:v 4'
@ -49,10 +49,10 @@ exports.videoWeb = function (task, callback) {
// Large video preview (before you click play) // Large video preview (before you click play)
exports.videoLarge = function (task, callback) { exports.videoLarge = function (task, callback) {
async.series([ async.series([
function(next) { function (next) {
extractFrame(task, next) extractFrame(task, next)
}, },
function(next) { function (next) {
exports.photoLarge({ exports.photoLarge({
src: task.dest, src: task.dest,
dest: task.dest dest: task.dest
@ -64,10 +64,10 @@ exports.videoLarge = function (task, callback) {
// Small square video preview // Small square video preview
exports.videoSquare = function (task, callback) { exports.videoSquare = function (task, callback) {
async.series([ async.series([
function(next) { function (next) {
extractFrame(task, next) extractFrame(task, next)
}, },
function(next) { function (next) {
exports.photoSquare({ exports.photoSquare({
src: task.dest, src: task.dest,
dest: task.dest dest: task.dest

@ -1,64 +1,62 @@
var fs = require('fs'); var fs = require('fs')
var path = require('path'); var path = require('path')
var handlebars = require('handlebars'); var handlebars = require('handlebars')
var moment = require('moment'); var moment = require('moment')
exports.create = function(options) { exports.create = function (options) {
var DIR_TEMPLATES = path.join(__dirname, '..', '..', 'templates')
var DIR_THEME = path.join(DIR_TEMPLATES, 'themes', options.theme)
var DIR_TEMPLATES = path.join(__dirname, '..', '..', 'templates'); function isTemplate (filepath) {
var DIR_THEME = path.join(DIR_TEMPLATES, 'themes', options.theme); return path.extname(filepath) === '.hbs'
function isTemplate(filepath) {
return path.extname(filepath) === '.hbs';
} }
function compileTemplate(hbsFile) { function compileTemplate (hbsFile) {
var src = fs.readFileSync(hbsFile); var src = fs.readFileSync(hbsFile)
return handlebars.compile(src.toString()); return handlebars.compile(src.toString())
} }
// main entry points // main entry points
var templates = { var templates = {
'album': compileTemplate(path.join(DIR_TEMPLATES, 'album.hbs')) 'album': compileTemplate(path.join(DIR_TEMPLATES, 'album.hbs'))
}; }
// common partials // common partials
handlebars.registerPartial('analytics', compileTemplate(path.join(DIR_TEMPLATES, 'analytics.hbs'))); handlebars.registerPartial('analytics', compileTemplate(path.join(DIR_TEMPLATES, 'analytics.hbs')))
// theme partials // theme partials
var files = fs.readdirSync(DIR_THEME); var files = fs.readdirSync(DIR_THEME)
files.filter(isTemplate).forEach(function(filename) { files.filter(isTemplate).forEach(function (filename) {
var templateName = path.basename(filename, path.extname(filename)); var templateName = path.basename(filename, path.extname(filename))
handlebars.registerPartial(templateName, compileTemplate(path.join(DIR_THEME, filename))); handlebars.registerPartial(templateName, compileTemplate(path.join(DIR_THEME, filename)))
}) })
// utility helper // utility helper
// render a date in a legible format // render a date in a legible format
handlebars.registerHelper('date', function(date) { handlebars.registerHelper('date', function (date) {
return moment(date).format('DD MMM YYYY'); return moment(date).format('DD MMM YYYY')
}); })
// utility helper // utility helper
// render the first X items in an array // render the first X items in an array
handlebars.registerHelper('slice', function(context, block) { handlebars.registerHelper('slice', function (context, block) {
var ret = ""; var ret = ''
var count = parseInt(block.hash.count) || 1; var count = parseInt(block.hash.count) || 1
var i = 0; var i = 0
var j = (count < context.length) ? count : context.length; var j = (count < context.length) ? count : context.length
for(i,j; i<j; i++) { for (i, j; i < j; i++) {
ret += block.fn(context[i]); ret += block.fn(context[i])
} }
return ret; return ret
}); })
// utility helper // utility helper
// execute the child block N times // execute the child block N times
handlebars.registerHelper('times', function(n, block) { handlebars.registerHelper('times', function (n, block) {
var accum = ''; var accum = ''
for(var i = 0; i < n; ++i) for (var i = 0; i < n; ++i) { accum += block.fn(i) }
accum += block.fn(i); return accum
return accum; })
});
// utility helper // utility helper
// execute a block if a condition matches // execute a block if a condition matches
@ -66,48 +64,47 @@ exports.create = function(options) {
// however this lets theme authors be more creative without changing the core model // however this lets theme authors be more creative without changing the core model
// THANKS TO http://doginthehat.com.au/2012/02/comparison-block-helper-for-handlebars-templates/#comment-44 // THANKS TO http://doginthehat.com.au/2012/02/comparison-block-helper-for-handlebars-templates/#comment-44
handlebars.registerHelper('compare', function (lvalue, operator, rvalue, options) { handlebars.registerHelper('compare', function (lvalue, operator, rvalue, options) {
var operators, result; var operators, result
if (arguments.length < 3) { if (arguments.length < 3) {
throw new Error("Handlerbars Helper 'compare' needs 2 parameters"); throw new Error("Handlerbars Helper 'compare' needs 2 parameters")
} }
if (options === undefined) { if (options === undefined) {
options = rvalue; options = rvalue
rvalue = operator; rvalue = operator
operator = "==="; operator = '==='
} }
operators = { operators = {
'==': function (l, r) { return l == r; }, '==': function (l, r) { return l == r }, // eslint-disable-line eqeqeq
'===': function (l, r) { return l === r; }, '===': function (l, r) { return l === r },
'!=': function (l, r) { return l != r; }, '!=': function (l, r) { return l != r }, // eslint-disable-line eqeqeq
'!==': function (l, r) { return l !== r; }, '!==': function (l, r) { return l !== r },
'<': function (l, r) { return l < r; }, '<': function (l, r) { return l < r },
'>': function (l, r) { return l > r; }, '>': function (l, r) { return l > r },
'<=': function (l, r) { return l <= r; }, '<=': function (l, r) { return l <= r },
'>=': function (l, r) { return l >= r; } '>=': function (l, r) { return l >= r }
}; }
if (!operators[operator]) { if (!operators[operator]) {
throw new Error("Handlerbars Helper 'compare' doesn't know the operator " + operator); throw new Error(`Handlerbars Helper 'compare' doesn't know the operator ${operator}`)
} }
result = operators[operator](lvalue, rvalue); result = operators[operator](lvalue, rvalue)
if (result) { if (result) {
return options.fn(this); return options.fn(this)
} else { } else {
return options.inverse(this); return options.inverse(this)
} }
}); })
// utility helper // utility helper
// return the relative path from the current folder to the argument // return the relative path from the current folder to the argument
var currentFolder = '.'; var currentFolder = '.'
handlebars.registerHelper('relative', function(target, options) { handlebars.registerHelper('relative', function (target, options) {
return path.relative(currentFolder, target); return path.relative(currentFolder, target)
}); })
return { return {
render: function(template, data, folder) { render: function (template, data, folder) {
currentFolder = folder; currentFolder = folder
return templates[template](data); return templates[template](data)
} }
}; }
}
};

@ -1,23 +1,17 @@
var _ = require('lodash'); var fs = require('fs-extra')
var fs = require('fs-extra'); var path = require('path')
var path = require('path'); var async = require('async')
var async = require('async'); var less = require('less')
var pad = require('pad'); var template = require('./template')
var less = require('less');
var Album = require('../model/album');
var byFolder = require('../model//by-folder');
var byDate = require('../model//by-date');
var template = require('./template');
var DIR_PUBLIC = path.join(__dirname, '..', '..', 'public'); var DIR_PUBLIC = path.join(__dirname, '..', '..', 'public')
var DIR_TEMPLATES = path.join(__dirname, '..', '..', 'templates'); var DIR_TEMPLATES = path.join(__dirname, '..', '..', 'templates')
exports.build = function(rootAlbum, opts, callback) {
exports.build = function (rootAlbum, opts, callback) {
// create the right renderer (theme, download path, etc...) // create the right renderer (theme, download path, etc...)
var renderer = template.create(opts); var renderer = template.create(opts)
function website(callback) { function website (callback) {
// create top level gallery // create top level gallery
var gallery = { var gallery = {
home: rootAlbum, home: rootAlbum,
@ -28,71 +22,71 @@ exports.build = function(rootAlbum, opts, callback) {
thumbSize: opts.thumbSize, thumbSize: opts.thumbSize,
largeSize: opts.largeSize, largeSize: opts.largeSize,
googleAnalytics: opts.googleAnalytics googleAnalytics: opts.googleAnalytics
}; }
// render entire album hierarchy // render entire album hierarchy
var tasks = renderAlbum(gallery, [], rootAlbum); var tasks = renderAlbum(gallery, [], rootAlbum)
async.parallel(tasks, callback); async.parallel(tasks, callback)
} }
function renderAlbum(gallery, breadcrumbs, album) { function renderAlbum (gallery, breadcrumbs, album) {
// render this album // render this album
var thisAlbumTask = renderTemplate(album.path, 'album', { var thisAlbumTask = renderTemplate(album.path, 'album', {
gallery: gallery, gallery: gallery,
breadcrumbs: breadcrumbs, breadcrumbs: breadcrumbs,
album: album album: album
}); })
var tasks = [thisAlbumTask]; var tasks = [thisAlbumTask]
// and all nested albums // and all nested albums
album.albums.forEach(function(nested) { album.albums.forEach(function (nested) {
var nestedAlbumsTasks = renderAlbum(gallery, breadcrumbs.concat([album]), nested); var nestedAlbumsTasks = renderAlbum(gallery, breadcrumbs.concat([album]), nested)
Array.prototype.push.apply(tasks, nestedAlbumsTasks); Array.prototype.push.apply(tasks, nestedAlbumsTasks)
}); })
return tasks; return tasks
} }
function renderTemplate(targetPath, templateName, data) { function renderTemplate (targetPath, templateName, data) {
// render a given HBS template // render a given HBS template
var fullPath = path.join(opts.output, targetPath); var fullPath = path.join(opts.output, targetPath)
var targetFolder = path.dirname(targetPath); var targetFolder = path.dirname(targetPath)
var contents = renderer.render(templateName, data, targetFolder); var contents = renderer.render(templateName, data, targetFolder)
return function(next) { return function (next) {
fs.mkdirpSync(path.dirname(fullPath)) fs.mkdirpSync(path.dirname(fullPath))
fs.writeFile(fullPath, contents, next); fs.writeFile(fullPath, contents, next)
}; }
} }
function support(callback) { function support (callback) {
// copy all external JS/CSS // copy all external JS/CSS
var dest = path.join(opts.output, 'public'); var dest = path.join(opts.output, 'public')
fs.copy(DIR_PUBLIC, dest, callback); fs.copy(DIR_PUBLIC, dest, callback)
} }
function lightGallery(callback) { function lightGallery (callback) {
// note: this module might be deduped // note: this module might be deduped
// so we can't assume it's in the local node_modules // so we can't assume it's in the local node_modules
var lgPackage = require.resolve('lightgallery/package.json'); var lgPackage = require.resolve('lightgallery/package.json')
var src = path.join(path.dirname(lgPackage), 'dist'); var src = path.join(path.dirname(lgPackage), 'dist')
var dest = path.join(opts.output, 'public', 'light-gallery'); var dest = path.join(opts.output, 'public', 'light-gallery')
fs.copy(src, dest, callback); fs.copy(src, dest, callback)
} }
function videoJS (callback) { function videoJS (callback) {
// copy VideoJS fonts that need to be one level above in a folder called "f" // copy VideoJS fonts that need to be one level above in a folder called "f"
var dest = path.join(opts.output, 'f'); var dest = path.join(opts.output, 'f')
fs.copy(path.join(DIR_PUBLIC, 'f'), dest, callback); fs.copy(path.join(DIR_PUBLIC, 'f'), dest, callback)
} }
function renderStyles(callback) { function renderStyles (callback) {
var themeFile = path.join(DIR_TEMPLATES, 'themes', opts.theme, 'theme.less'); var themeFile = path.join(DIR_TEMPLATES, 'themes', opts.theme, 'theme.less')
var themeLess = fs.readFileSync(themeFile, 'utf-8'); var themeLess = fs.readFileSync(themeFile, 'utf-8')
if (opts.css) { if (opts.css) {
themeLess += '\n' + fs.readFileSync(opts.css, 'utf-8'); themeLess += '\n' + fs.readFileSync(opts.css, 'utf-8')
} }
less.render(themeLess, function (err, output) { less.render(themeLess, function (err, output) {
if (err) return callback(err); if (err) return callback(err)
var dest = path.join(opts.output, 'public', 'style.css'); var dest = path.join(opts.output, 'public', 'style.css')
fs.writeFile(dest, output.css, callback); fs.writeFile(dest, output.css, callback)
}); })
} }
async.series([ async.series([
@ -101,8 +95,7 @@ exports.build = function(rootAlbum, opts, callback) {
lightGallery, lightGallery,
videoJS, videoJS,
renderStyles renderStyles
], function(err) { ], function (err) {
callback(err); callback(err)
}); })
}
};

@ -2,17 +2,18 @@ const pad = require('pad')
const ProgressBar = require('progress') const ProgressBar = require('progress')
const util = require('util') const util = require('util')
exports.create = function(message, count) { exports.create = function (message, count) {
var format = ''
if (typeof count === 'undefined') { if (typeof count === 'undefined') {
var format = pad(message, 20) + '[:bar] :eta' format = pad(message, 20) + '[:bar] :eta'
return new BetterProgressBar(format, 1) return new BetterProgressBar(format, 1)
} }
if (Array.isArray(count)) count = count.length if (Array.isArray(count)) count = count.length
if (count > 0) { if (count > 0) {
var format = pad(message, 20) + '[:bar] :current/:total :eta' format = pad(message, 20) + '[:bar] :current/:total :eta'
return new BetterProgressBar(format, count) return new BetterProgressBar(format, count)
} else { } else {
var format = pad(message, 20) + '[:bar] up to date' format = pad(message, 20) + '[:bar] up to date'
var bar = new BetterProgressBar(format, 1) var bar = new BetterProgressBar(format, 1)
bar.tick(1) bar.tick(1)
return bar return bar
@ -30,8 +31,8 @@ BetterProgressBar.prototype.eta = function () {
var ratio = this.curr / this.total var ratio = this.curr / this.total
ratio = Math.min(Math.max(ratio, 0), 1) ratio = Math.min(Math.max(ratio, 0), 1)
var percent = ratio * 100 var percent = ratio * 100
var elapsed = new Date - this.start var elapsed = new Date() - this.start
return (percent == 100) ? 0 : elapsed * (this.total / this.curr - 1) return (percent === 100) ? 0 : elapsed * ((this.total / this.curr) - 1)
} }
BetterProgressBar.prototype.render = function (tokens) { BetterProgressBar.prototype.render = function (tokens) {
@ -44,15 +45,17 @@ BetterProgressBar.prototype.render = function (tokens) {
} }
function formatEta (ms) { function formatEta (ms) {
var min = 0
var sec = 0
if (isNaN(ms) || !isFinite(ms)) return '' if (isNaN(ms) || !isFinite(ms)) return ''
if (ms > 60 * 1000) { if (ms > 60 * 1000) {
var min = Math.floor(ms / 60 / 1000) min = Math.floor(ms / 60 / 1000)
return `(${min.toFixed(0)}min left)` return `(${min.toFixed(0)}min left)`
} else if (ms > 10 * 1000) { } else if (ms > 10 * 1000) {
var sec = Math.floor(ms / 10000) * 10 sec = Math.floor(ms / 10000) * 10
return `(${sec.toFixed(0)}s left)` return `(${sec.toFixed(0)}s left)`
} else if (ms > 0) { } else if (ms > 0) {
var sec = ms / 1000 sec = ms / 1000
return `(a few seconds left)` return `(a few seconds left)`
} else { } else {
return 'done' return 'done'

@ -1,4 +1,3 @@
const File = require('../src/input/file')
const Media = require('../src/model/media') const Media = require('../src/model/media')
exports.file = function (opts) { exports.file = function (opts) {

@ -2,7 +2,6 @@ const should = require('should/as-function')
const File = require('../../src/input/file') const File = require('../../src/input/file')
describe('Input file', function () { describe('Input file', function () {
it('reads the relative file path', function () { it('reads the relative file path', function () {
var file = new File(dbFile({ var file = new File(dbFile({
SourceFile: 'holidays/beach.jpg' SourceFile: 'holidays/beach.jpg'
@ -36,13 +35,12 @@ describe('Input file', function () {
})) }))
should(file.type).eql('video') should(file.type).eql('video')
}) })
}) })
function dbFile (data) { function dbFile (data) {
// some required data // some required data
if (!data.SourceFile) data.SourceFile = 'photo.jpg' if (!data.SourceFile) data.SourceFile = 'photo.jpg'
if (!data.File) data.File = {} if (!data.File) data.File = {}
if (!data.File.FileModifyDate) data.File.FileModifyDate = '1999:12:31 23:59:59+00:00' if (!data.File.FileModifyDate) data.File.FileModifyDate = '1999:12:31 23:59:59+00:00'
return data return data
} }

@ -1,58 +1,53 @@
var should = require('should/as-function'); var should = require('should/as-function')
var Album = require('../../src/model/album'); var Album = require('../../src/model/album')
var fixtures = require('../fixtures'); var fixtures = require('../fixtures')
describe('Album', function() { describe('Album', function () {
describe('stats', function () {
describe('stats', function() { describe('single level stats', function () {
it('has no nested albums', function () {
describe('single level stats', function() { var a = new Album({})
a.finalize()
it('has no nested albums', function() { should(a.stats.albums).eql(0)
var a = new Album({});
a.finalize();
should(a.stats.albums).eql(0);
}) })
it('calculates counts for a single level', function() { it('calculates counts for a single level', function () {
var a = new Album({ var a = new Album({
files: [ files: [
fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(),
fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(),
fixtures.video(), fixtures.video(), fixtures.video(), fixtures.video()
] ]
}); })
a.finalize(); a.finalize()
should(a.stats.photos).eql(4); should(a.stats.photos).eql(4)
should(a.stats.videos).eql(2); should(a.stats.videos).eql(2)
}); })
it('calculates from/to dates', function() { it('calculates from/to dates', function () {
var a = new Album({ var a = new Album({
files: [ files: [
fixtures.photo({date: '2016-09-14'}), fixtures.photo({date: '2016-09-14'}),
fixtures.photo({date: '2016-09-02'}), fixtures.photo({date: '2016-09-02'}),
fixtures.photo({date: '2016-10-21'}), fixtures.photo({date: '2016-10-21'})
] ]
}); })
a.finalize(); a.finalize()
should(a.stats.fromDate).eql(fixtures.date('2016-09-02').getTime()); should(a.stats.fromDate).eql(fixtures.date('2016-09-02').getTime())
should(a.stats.toDate).eql(fixtures.date('2016-10-21').getTime()); should(a.stats.toDate).eql(fixtures.date('2016-10-21').getTime())
}); })
})
});
describe('nested albums stats', function() {
it('counts all nested albums', function() { describe('nested albums stats', function () {
it('counts all nested albums', function () {
var root = new Album({ var root = new Album({
albums: [new Album('a'), new Album('b')] albums: [new Album('a'), new Album('b')]
}); })
root.finalize(); root.finalize()
should(root.stats.albums).eql(2); should(root.stats.albums).eql(2)
}); })
it('counts all nested photos', function() { it('counts all nested photos', function () {
var root = new Album({ var root = new Album({
files: [fixtures.photo()], files: [fixtures.photo()],
albums: [ albums: [
@ -60,12 +55,12 @@ describe('Album', function() {
files: [fixtures.photo(), fixtures.photo()] files: [fixtures.photo(), fixtures.photo()]
}) })
] ]
}); })
root.finalize(); root.finalize()
should(root.stats.photos).eql(3); should(root.stats.photos).eql(3)
}); })
it('counts all nested photos', function() { it('counts all nested photos', function () {
var root = new Album({ var root = new Album({
files: [fixtures.video()], files: [fixtures.video()],
albums: [ albums: [
@ -73,12 +68,12 @@ describe('Album', function() {
files: [fixtures.video(), fixtures.video()] files: [fixtures.video(), fixtures.video()]
}) })
] ]
}); })
root.finalize(); root.finalize()
should(root.stats.videos).eql(3); should(root.stats.videos).eql(3)
}); })
it('calculates from/to dates across all albums', function() { it('calculates from/to dates across all albums', function () {
var a = new Album({ var a = new Album({
files: [fixtures.photo({date: '2016-09-14'})], files: [fixtures.photo({date: '2016-09-14'})],
albums: [ albums: [
@ -89,79 +84,74 @@ describe('Album', function() {
})] })]
}) })
] ]
}); })
a.finalize(); a.finalize()
should(a.stats.fromDate).eql(fixtures.date('2016-09-02').getTime()); should(a.stats.fromDate).eql(fixtures.date('2016-09-02').getTime())
should(a.stats.toDate).eql(fixtures.date('2016-10-21').getTime()); should(a.stats.toDate).eql(fixtures.date('2016-10-21').getTime())
}); })
})
}); })
});
describe('summary', function() {
it('creates a summary with a single photo', function() { describe('summary', function () {
var a = new Album('single'); it('creates a summary with a single photo', function () {
var a = new Album('single')
a.files = [ a.files = [
fixtures.photo() fixtures.photo()
]; ]
a.finalize(); a.finalize()
should(a.summary).eql('1 photo') should(a.summary).eql('1 photo')
}); })
it('creates a summary with a single video', function() { it('creates a summary with a single video', function () {
var a = new Album('single'); var a = new Album('single')
a.files = [ a.files = [
fixtures.video() fixtures.video()
]; ]
a.finalize(); a.finalize()
should(a.summary).eql('1 video') should(a.summary).eql('1 video')
}); })
it('creates a summary with a single album', function() { it('creates a summary with a single album', function () {
var a = new Album('single'); var a = new Album('single')
a.albums = [new Album('nested')]; a.albums = [new Album('nested')]
a.finalize(); a.finalize()
should(a.summary).eql('1 album') should(a.summary).eql('1 album')
}); })
it('creates a summary with several photos', function() { it('creates a summary with several photos', function () {
var a = new Album('single'); var a = new Album('single')
a.files = [ a.files = [
fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo()
]; ]
a.finalize(); a.finalize()
should(a.summary).eql('2 photos') should(a.summary).eql('2 photos')
}); })
it('creates a summary with several videos', function() { it('creates a summary with several videos', function () {
var a = new Album('single'); var a = new Album('single')
a.files = [ a.files = [
fixtures.video(), fixtures.video(), fixtures.video(), fixtures.video()
]; ]
a.finalize(); a.finalize()
should(a.summary).eql('2 videos') should(a.summary).eql('2 videos')
}); })
it('creates a summary with several albums', function() { it('creates a summary with several albums', function () {
var a = new Album('single'); var a = new Album('single')
a.albums = [new Album('nested 1'), new Album('nested 2')]; a.albums = [new Album('nested 1'), new Album('nested 2')]
a.finalize(); a.finalize()
should(a.summary).eql('2 albums') should(a.summary).eql('2 albums')
}); })
it('creates a summary with a mix of albums, photos and videos', function() { it('creates a summary with a mix of albums, photos and videos', function () {
var a = new Album('single'); var a = new Album('single')
a.albums = [new Album('nested')]; a.albums = [new Album('nested')]
a.files = [ a.files = [
fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(),
fixtures.video(), fixtures.video(), fixtures.video(), fixtures.video()
]; ]
a.finalize(); a.finalize()
should(a.summary).eql('1 album, 2 photos, 2 videos') should(a.summary).eql('1 album, 2 photos, 2 videos')
}); })
})
}); })
});

@ -1,33 +1,29 @@
var should = require('should/as-function'); var should = require('should/as-function')
var Album = require('../../src/model/album'); var Album = require('../../src/model/album')
var fixtures = require('../fixtures'); var fixtures = require('../fixtures')
var path = require('path'); var path = require('path')
describe('Album', function() { describe('Album', function () {
describe('options', function () {
describe('options', function() { it('can pass the title as a single argument', function () {
var a = new Album('Holidays')
it('can pass the title as a single argument', function() { should(a.title).eql('Holidays')
var a = new Album('Holidays'); })
should(a.title).eql('Holidays');
}); it('can pass a full hash of options', function () {
var a = new Album({id: 12, title: 'Holidays'})
it('can pass a full hash of options', function() { should(a.id).eql(12)
var a = new Album({id: 12, title: 'Holidays'}); should(a.title).eql('Holidays')
should(a.id).eql(12); })
should(a.title).eql('Holidays'); })
});
describe('output paths', function () {
}); it('sanitises album titles for the file name', function () {
var a = new Album('hello & world')
describe('output paths', function() { should(a.basename).eql('helloworld')
})
it('sanitises album titles for the file name', function() {
var a = new Album('hello & world'); it('concatenates nested filenames for uniqueness', function () {
should(a.basename).eql('helloworld');
});
it('concatenates nested filenames for uniqueness', function() {
// to avoid having two nested albums called "October" overwrite each other // to avoid having two nested albums called "October" overwrite each other
// note: doesn't use the root title to avoid "home-" or "index-" // note: doesn't use the root title to avoid "home-" or "index-"
var root = new Album({ var root = new Album({
@ -44,81 +40,79 @@ describe('Album', function() {
albums: [ albums: [
new Album({title: 'October'}) new Album({title: 'October'})
] ]
}), })
] ]
}); })
root.finalize(); root.finalize()
should(root.basename).eql('home'); should(root.basename).eql('home')
should(root.albums[0].basename).eql('2010'); should(root.albums[0].basename).eql('2010')
should(root.albums[1].basename).eql('2011'); should(root.albums[1].basename).eql('2011')
should(root.albums[0].albums[0].basename).eql('2010-October'); should(root.albums[0].albums[0].basename).eql('2010-October')
should(root.albums[1].albums[0].basename).eql('2011-October'); should(root.albums[1].albums[0].basename).eql('2011-October')
}); })
it('calculates the output file path', function() { it('calculates the output file path', function () {
var root = new Album({ var root = new Album({
title: 'home', title: 'home',
albums: [new Album({title: '2010'})] albums: [new Album({title: '2010'})]
}); })
root.finalize({index: 'index.html'}); root.finalize({index: 'index.html'})
should(root.path).eql('index.html'); should(root.path).eql('index.html')
should(root.albums[0].path).eql('2010.html'); should(root.albums[0].path).eql('2010.html')
}); })
it('calculates the URL for the browser', function() { it('calculates the URL for the browser', function () {
var root = new Album({ var root = new Album({
title: 'home', title: 'home',
albums: [new Album({title: '2010'})] albums: [new Album({title: '2010'})]
}); })
root.finalize({index: 'index.html'}); root.finalize({index: 'index.html'})
should(root.url).eql('index.html'); should(root.url).eql('index.html')
should(root.albums[0].url).eql('2010.html'); should(root.albums[0].url).eql('2010.html')
}); })
it('calculates the output path with a target folder (slashes match the OS)', function() { it('calculates the output path with a target folder (slashes match the OS)', function () {
var root = new Album({ var root = new Album({
title: 'home', title: 'home',
albums: [new Album({title: '2010'})] albums: [new Album({title: '2010'})]
}); })
root.finalize({index: 'index.html', albumsOutputFolder: 'albums'}); root.finalize({index: 'index.html', albumsOutputFolder: 'albums'})
should(root.path).eql('index.html'); should(root.path).eql('index.html')
should(root.albums[0].path).eql(path.join('albums', '2010.html')); should(root.albums[0].path).eql(path.join('albums', '2010.html'))
}); })
it('calculates the URL with a target folder (always forward slashes)', function() { it('calculates the URL with a target folder (always forward slashes)', function () {
var root = new Album({ var root = new Album({
title: 'home', title: 'home',
albums: [new Album({title: '2010'})] albums: [new Album({title: '2010'})]
}); })
root.finalize({index: 'index.html', albumsOutputFolder: 'albums'}); root.finalize({index: 'index.html', albumsOutputFolder: 'albums'})
should(root.path).eql('index.html'); should(root.path).eql('index.html')
should(root.albums[0].path).eql('albums/2010.html'); should(root.albums[0].path).eql('albums/2010.html')
}); })
})
});
describe('previews', function () {
describe('previews', function() { it('picks the first 10 files as previews', function () {
it('picks the first 10 files as previews', function() {
var a = new Album({files: [ var a = new Album({files: [
fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(),
fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(),
fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo()
]}); ]})
a.finalize(); a.finalize()
should(a.previews).have.length(10); should(a.previews).have.length(10)
}); })
it('adds <missing> thumbnails to fill', function() { it('adds <missing> thumbnails to fill', function () {
var a = new Album({files: [ var a = new Album({files: [
fixtures.photo(), fixtures.photo(), fixtures.photo(), fixtures.photo()
]}); ]})
a.finalize(); a.finalize()
should(a.previews[2].urls.thumbnail).eql('public/missing.png'); should(a.previews[2].urls.thumbnail).eql('public/missing.png')
should(a.previews[9].urls.thumbnail).eql('public/missing.png'); should(a.previews[9].urls.thumbnail).eql('public/missing.png')
}); })
it('uses files from nested albums too', function() { it('uses files from nested albums too', function () {
var a = new Album({ var a = new Album({
title: 'a', title: 'a',
albums: [ albums: [
@ -131,125 +125,120 @@ describe('Album', function() {
files: [fixtures.photo(), fixtures.photo()] files: [fixtures.photo(), fixtures.photo()]
}) })
] ]
}); })
a.finalize(); a.finalize()
should(a.previews).have.length(10); should(a.previews).have.length(10)
for (var i = 0; i < 4; ++i) { for (var i = 0; i < 4; ++i) {
should(a.previews[i].urls.thumbnail).not.eql('public/missing.png'); should(a.previews[i].urls.thumbnail).not.eql('public/missing.png')
} }
}); })
})
});
describe('sorting', function () {
describe('sorting', function() { it('can sort albums by title', function () {
var a = new Album('A')
it('can sort albums by title', function() { var b = new Album('B')
var a = new Album('A'); var c = new Album('C')
var b = new Album('B'); var root = new Album({albums: [c, a, b]})
var c = new Album('C'); root.finalize({sortAlbumsBy: 'title'})
var root = new Album({albums: [c, a, b]}); should(root.albums).eql([a, b, c])
root.finalize({sortAlbumsBy: 'title'}); })
should(root.albums).eql([a, b, c]);
}); it('can sort albums by start date', function () {
var startJan = albumWithFileDates(['2010-01-01', '2010-05-01'])
it('can sort albums by start date', function() { var startFeb = albumWithFileDates(['2010-02-01', '2010-04-01'])
var startJan = albumWithFileDates(['2010-01-01', '2010-05-01']); var startMar = albumWithFileDates(['2010-03-01', '2010-03-01'])
var startFeb = albumWithFileDates(['2010-02-01', '2010-04-01']); var root = new Album({albums: [startFeb, startMar, startJan]})
var startMar = albumWithFileDates(['2010-03-01', '2010-03-01']); root.finalize({sortAlbumsBy: 'start-date'})
var root = new Album({albums: [startFeb, startMar, startJan]}); should(root.albums).eql([startJan, startFeb, startMar])
root.finalize({sortAlbumsBy: 'start-date'}); })
should(root.albums).eql([startJan, startFeb, startMar]);
}); it('can sort albums by end date', function () {
var endMay = albumWithFileDates(['2010-01-01', '2010-05-01'])
it('can sort albums by end date', function() { var endApr = albumWithFileDates(['2010-02-01', '2010-04-01'])
var endMay = albumWithFileDates(['2010-01-01', '2010-05-01']); var endMar = albumWithFileDates(['2010-03-01', '2010-03-01'])
var endApr = albumWithFileDates(['2010-02-01', '2010-04-01']); var root = new Album({albums: [endMay, endMar, endApr]})
var endMar = albumWithFileDates(['2010-03-01', '2010-03-01']); root.finalize({sortAlbumsBy: 'end-date'})
var root = new Album({albums: [endMay, endMar, endApr]}); should(root.albums).eql([endMar, endApr, endMay])
root.finalize({sortAlbumsBy: 'end-date'}); })
should(root.albums).eql([endMar, endApr, endMay]);
}); it('can sort media by filename', function () {
var a = fixtures.photo({path: 'a'})
it('can sort media by filename', function() { var b = fixtures.photo({path: 'b'})
var a = fixtures.photo({path: 'a'}); var c = fixtures.photo({path: 'c'})
var b = fixtures.photo({path: 'b'}); var album = new Album({files: [c, a, b]})
var c = fixtures.photo({path: 'c'}); album.finalize({sortMediaBy: 'filename'})
var album = new Album({files: [c, a, b]}); should(album.files).eql([a, b, c])
album.finalize({sortMediaBy: 'filename'}); })
should(album.files).eql([a, b, c]);
}); it('can sort media by reverse filename', function () {
var a = fixtures.photo({path: 'a'})
it('can sort media by reverse filename', function() { var b = fixtures.photo({path: 'b'})
var a = fixtures.photo({path: 'a'}); var c = fixtures.photo({path: 'c'})
var b = fixtures.photo({path: 'b'}); var album = new Album({files: [c, a, b]})
var c = fixtures.photo({path: 'c'}); album.finalize({sortMediaBy: 'filename', sortMediaDirection: 'desc'})
var album = new Album({files: [c, a, b]}); should(album.files).eql([c, b, a])
album.finalize({sortMediaBy: 'filename', sortMediaDirection: 'desc'}); })
should(album.files).eql([c, b, a]);
}); it('sorts nested albums too', function () {
var nested = new Album({title: 'nested',
it('sorts nested albums too', function() { files: [
var nested = new Album({title: 'nested', files: [ fixtures.photo({path: 'b'}),
fixtures.photo({path: 'b'}), fixtures.photo({path: 'a'})
fixtures.photo({path: 'a'}), ]})
]}); var root = new Album({title: 'home', albums: [nested]})
var root = new Album({title: 'home', albums: [nested]}); root.finalize({sortMediaBy: 'filename'})
root.finalize({sortMediaBy: 'filename'}); should(nested.files[0].file.path).eql('a')
should(nested.files[0].file.path).eql('a'); should(nested.files[1].file.path).eql('b')
should(nested.files[1].file.path).eql('b'); })
}); })
}); describe('nested albums basic logic', function () {
it('calculates the depth of every album', function () {
describe('nested albums basic logic', function() { var a = new Album('single')
var b = new Album('single')
it('calculates the depth of every album', function() { var c = new Album('single')
var a = new Album('single'); var d = new Album('single')
var b = new Album('single'); a.albums = [b, c]
var c = new Album('single'); c.albums = [d]
var d = new Album('single'); a.finalize()
a.albums = [b, c]; should(a.depth).eql(0)
c.albums = [d]; should(b.depth).eql(1)
a.finalize(); should(c.depth).eql(1)
should(a.depth).eql(0); should(d.depth).eql(2)
should(b.depth).eql(1); })
should(c.depth).eql(1);
should(d.depth).eql(2); it('sets the home flag on the top-level album', function () {
}); var a = new Album('single')
var b = new Album('single')
it('sets the home flag on the top-level album', function() { var c = new Album('single')
var a = new Album('single'); var d = new Album('single')
var b = new Album('single'); a.albums = [b, c]
var c = new Album('single'); c.albums = [d]
var d = new Album('single'); a.finalize()
a.albums = [b, c]; should(a.home).eql(true)
c.albums = [d]; should(b.home).eql(false)
a.finalize(); should(c.home).eql(false)
should(a.home).eql(true); should(d.home).eql(false)
should(b.home).eql(false); })
should(c.home).eql(false);
should(d.home).eql(false); it('passes finalising options to all nested albums (e.g. sorting)', function () {
}); var nested = new Album({title: 'nested',
files: [
it('passes finalising options to all nested albums (e.g. sorting)', function() { fixtures.photo({path: 'b'}),
var nested = new Album({title: 'nested', files: [ fixtures.photo({path: 'a'})
fixtures.photo({path: 'b'}), ]})
fixtures.photo({path: 'a'}), var root = new Album({title: 'home', albums: [nested]})
]}); root.finalize({sortMediaBy: 'filename'})
var root = new Album({title: 'home', albums: [nested]}); should(nested.files[0].file.path).eql('a')
root.finalize({sortMediaBy: 'filename'}); should(nested.files[1].file.path).eql('b')
should(nested.files[0].file.path).eql('a'); })
should(nested.files[1].file.path).eql('b'); })
}); })
}); function albumWithFileDates (dates) {
var files = dates.map(function (d) {
}); return fixtures.photo({date: d})
})
return new Album({files: files})
function albumWithFileDates(dates) {
var files = dates.map(function(d) {
return fixtures.photo({date: d});
});
return new Album({files: files});
} }

@ -1,25 +1,26 @@
var should = require('should/as-function'); /* eslint-disable camelcase */
var Album = require('../../src/model/album.js');
var bydate = require('../../src/model/by-date.js');
var fixtures = require('../fixtures');
describe('ByDate', function() { var should = require('should/as-function')
var Album = require('../../src/model/album.js')
var bydate = require('../../src/model/by-date.js')
var fixtures = require('../fixtures')
beforeEach(function() { describe('ByDate', function () {
Album.resetIds(); beforeEach(function () {
}); Album.resetIds()
})
it('creates top-level albums grouped by month', function () { it('creates top-level albums grouped by month', function () {
// create files from different dates // create files from different dates
var a_2016_06 = fixtures.photo({date: fixtures.date('2016-06-01')}); var a_2016_06 = fixtures.photo({date: fixtures.date('2016-06-01')})
var b_2016_06 = fixtures.photo({date: fixtures.date('2016-06-10')}); var b_2016_06 = fixtures.photo({date: fixtures.date('2016-06-10')})
var c_2016_07 = fixtures.photo({date: fixtures.date('2016-07-23')}); var c_2016_07 = fixtures.photo({date: fixtures.date('2016-07-23')})
var d_2016_07 = fixtures.video({date: fixtures.date('2016-07-18')}); var d_2016_07 = fixtures.video({date: fixtures.date('2016-07-18')})
// group them per month // group them per month
var collection = [a_2016_06, b_2016_06, c_2016_07, d_2016_07] var collection = [a_2016_06, b_2016_06, c_2016_07, d_2016_07]
var albums = bydate.albums(collection, { var albums = bydate.albums(collection, {
grouping: 'YYYY-MM' grouping: 'YYYY-MM'
}); })
// assert on the result // assert on the result
should(albums).eql([ should(albums).eql([
new Album({ new Album({
@ -32,20 +33,20 @@ describe('ByDate', function() {
title: '2016-07', title: '2016-07',
files: [c_2016_07, d_2016_07] files: [c_2016_07, d_2016_07]
}) })
]); ])
}); })
it('creates albums using a date hierarchy', function () { it('creates albums using a date hierarchy', function () {
// create files from different dates // create files from different dates
var a_2015_06 = fixtures.photo({date: fixtures.date('2015-06-01')}); var a_2015_06 = fixtures.photo({date: fixtures.date('2015-06-01')})
var b_2015_06 = fixtures.photo({date: fixtures.date('2015-06-10')}); var b_2015_06 = fixtures.photo({date: fixtures.date('2015-06-10')})
var c_2016_07 = fixtures.photo({date: fixtures.date('2016-07-23')}); var c_2016_07 = fixtures.photo({date: fixtures.date('2016-07-23')})
var d_2016_08 = fixtures.video({date: fixtures.date('2016-08-18')}); var d_2016_08 = fixtures.video({date: fixtures.date('2016-08-18')})
// group them per year, and nested month // group them per year, and nested month
var collection = [a_2015_06, b_2015_06, c_2016_07, d_2016_08] var collection = [a_2015_06, b_2015_06, c_2016_07, d_2016_08]
var albums = bydate.albums(collection, { var albums = bydate.albums(collection, {
grouping: 'YYYY/MM' grouping: 'YYYY/MM'
}); })
// assert on the result // assert on the result
should(albums).eql([ should(albums).eql([
new Album({ new Album({
@ -77,7 +78,6 @@ describe('ByDate', function() {
}) })
] ]
}) })
]); ])
}); })
})
});

@ -1,23 +1,22 @@
var should = require('should/as-function'); var should = require('should/as-function')
var Album = require('../../src/model/album.js'); var Album = require('../../src/model/album.js')
var byfolder = require('../../src/model/by-folder.js'); var byfolder = require('../../src/model/by-folder.js')
var fixtures = require('../fixtures'); var fixtures = require('../fixtures')
describe('ByFolder', function() { describe('ByFolder', function () {
beforeEach(function () {
beforeEach(function() { Album.resetIds()
Album.resetIds(); })
});
it('creates albums by folders', function () { it('creates albums by folders', function () {
// create files in different folders // create files in different folders
var london1 = fixtures.photo({path: 'london/IMG_000001.jpg'}); var london1 = fixtures.photo({path: 'london/IMG_000001.jpg'})
var london2 = fixtures.photo({path: 'london/IMG_000002.jpg'}); var london2 = fixtures.photo({path: 'london/IMG_000002.jpg'})
var newyork1 = fixtures.photo({path: 'newyork/IMG_000003.jpg'}); var newyork1 = fixtures.photo({path: 'newyork/IMG_000003.jpg'})
var newyork2 = fixtures.video({path: 'newyork/IMG_000004.mp4'}); var newyork2 = fixtures.video({path: 'newyork/IMG_000004.mp4'})
// group them per folder // group them per folder
var collection = [london1, london2, newyork1, newyork2] var collection = [london1, london2, newyork1, newyork2]
var albums = byfolder.albums(collection, {}); var albums = byfolder.albums(collection, {})
// assert on the result // assert on the result
should(albums).eql([ should(albums).eql([
new Album({ new Album({
@ -30,16 +29,16 @@ describe('ByFolder', function() {
title: 'newyork', title: 'newyork',
files: [newyork1, newyork2] files: [newyork1, newyork2]
}) })
]); ])
}); })
it('creates nested albums for nested folders', function () { it('creates nested albums for nested folders', function () {
// create files in nested folders // create files in nested folders
var photo1 = fixtures.photo({path: 'a/b/c/IMG_000001.jpg'}); var photo1 = fixtures.photo({path: 'a/b/c/IMG_000001.jpg'})
var photo2 = fixtures.photo({path: 'a/d/IMG_000002.jpg'}); var photo2 = fixtures.photo({path: 'a/d/IMG_000002.jpg'})
// group them per folder // group them per folder
var collection = [photo1, photo2] var collection = [photo1, photo2]
var albums = byfolder.albums(collection, {}); var albums = byfolder.albums(collection, {})
// assert on the result // assert on the result
should(albums).eql([ should(albums).eql([
new Album({ new Album({
@ -66,7 +65,6 @@ describe('ByFolder', function() {
}) })
] ]
}) })
]); ])
}); })
})
});

@ -1,9 +1,8 @@
var should = require('should/as-function'); var should = require('should/as-function')
var Media = require('../../src/model/media'); var Media = require('../../src/model/media')
var fixtures = require('../fixtures'); var fixtures = require('../fixtures')
describe('Media', function () { describe('Media', function () {
describe('date taken', function () { describe('date taken', function () {
it('reads the EXIF date if present', function () { it('reads the EXIF date if present', function () {
const file = fixtures.file() const file = fixtures.file()
@ -20,7 +19,7 @@ describe('Media', function () {
}) })
describe('photos and videos', function () { describe('photos and videos', function () {
it('can tell if a file is a regular photo', function() { it('can tell if a file is a regular photo', function () {
const file = fixtures.file({type: 'image'}) const file = fixtures.file({type: 'image'})
file.meta.File.MIMEType = 'image/jpeg' file.meta.File.MIMEType = 'image/jpeg'
const media = new Media(file) const media = new Media(file)
@ -28,7 +27,7 @@ describe('Media', function () {
should(media.isAnimated).eql(false) should(media.isAnimated).eql(false)
}) })
it('can tell if a file is a non-animated gif', function() { it('can tell if a file is a non-animated gif', function () {
const file = fixtures.file({type: 'image'}) const file = fixtures.file({type: 'image'})
file.meta.File.MIMEType = 'image/gif' file.meta.File.MIMEType = 'image/gif'
const media = new Media(file) const media = new Media(file)
@ -36,7 +35,7 @@ describe('Media', function () {
should(media.isAnimated).eql(false) should(media.isAnimated).eql(false)
}) })
it('can tell if a file is an animated gif', function() { it('can tell if a file is an animated gif', function () {
const file = fixtures.file({type: 'image'}) const file = fixtures.file({type: 'image'})
file.meta.File.MIMEType = 'image/gif' file.meta.File.MIMEType = 'image/gif'
file.meta.GIF = {FrameCount: 10} file.meta.GIF = {FrameCount: 10}
@ -45,7 +44,7 @@ describe('Media', function () {
should(media.isAnimated).eql(true) should(media.isAnimated).eql(true)
}) })
it('can tell if a file is a video', function() { it('can tell if a file is a video', function () {
const file = fixtures.file({type: 'video'}) const file = fixtures.file({type: 'video'})
const media = new Media(file) const media = new Media(file)
should(media.isVideo).eql(true) should(media.isVideo).eql(true)
@ -53,7 +52,7 @@ describe('Media', function () {
}) })
}) })
describe('caption', function() { describe('caption', function () {
it('uses the EXIF caption if present', function () { it('uses the EXIF caption if present', function () {
const file = fixtures.file() const file = fixtures.file()
file.meta.EXIF['ImageDescription'] = 'some caption' file.meta.EXIF['ImageDescription'] = 'some caption'
@ -76,5 +75,4 @@ describe('Media', function () {
should(media.caption).eql('exif caption') should(media.caption).eql('exif caption')
}) })
}) })
}) })

Loading…
Cancel
Save