Refactor album mapping code to be much simpler based on (media) => string

pull/64/merge
Romain 7 years ago
parent 07ec7702cd
commit 7e73e6a10b

@ -6,6 +6,7 @@ const database = require('./input/database')
const progress = require('./utils/progress')
const File = require('./input/file')
const hierarchy = require('./model/hierarchy.js')
const mapper = require('./model/mapper')
const Media = require('./model/media')
const resize = require('./output-media/resize')
const tasks = require('./output-media/tasks')
@ -19,33 +20,34 @@ exports.build = function (opts) {
const databaseFile = path.join(opts.output, 'metadata.json')
var album = null // root album with nested albums
var collection = null // all files in the database
var fileCollection = null // all files in the database
async.series([
function updateDatabase (callback) {
database.update(opts.input, databaseFile, (err, dbFiles) => {
collection = dbFiles.map(f => new File(f, opts))
fileCollection = dbFiles.map(f => new File(f, opts))
callback(err)
})
},
function processPhotos (callback) {
const photos = tasks.create(opts, collection, 'image')
const photos = tasks.create(opts, fileCollection, 'image')
const bar = progress.create('Processing photos', photos.length)
parallel(photos, bar, callback)
},
function processVideos (callback) {
const videos = tasks.create(opts, collection, 'video')
const videos = tasks.create(opts, fileCollection, 'video')
const bar = progress.create('Processing videos', videos.length)
parallel(videos, bar, callback)
},
function createAlbums (callback) {
const bar = progress.create('Creating albums')
const mediaCollection = collection.map(f => new Media(f))
album = hierarchy.createAlbums(mediaCollection, opts)
const albumMapper = mapper.create(opts)
const mediaCollection = fileCollection.map(f => new Media(f))
album = hierarchy.createAlbums(mediaCollection, albumMapper, opts)
bar.tick(1)
callback()
},

@ -42,8 +42,8 @@ Album.prototype.finalize = function (options, parent) {
var albumsOutputFolder = options.albumsOutputFolder || '.'
// calculate final file paths and URLs
if (parent == null) {
this.path = options.index
this.url = options.index
this.path = options.index || 'index.html'
this.url = options.index || 'index.html'
this.depth = 0
} else {
if (parent.depth > 0) {

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

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

@ -1,22 +1,45 @@
var Album = require('./album')
var byFolder = require('./by-folder')
var byDate = require('./by-date')
const _ = require('lodash')
const path = require('path')
const Album = require('./album')
exports.createAlbums = function (collection, opts) {
// top-level album for the home page
exports.createAlbums = function (collection, mapper, opts) {
// returns a top-level album for the home page
// under which all files are grouped into sub-albums
// and finalised recursively (calculate stats, etc...)
var home = new Album('Home')
home.filename = opts.index || 'index'
// create albums
if (opts.albumsFrom === 'folders') {
home.albums = byFolder.albums(collection, opts)
} else if (opts.albumsFrom === 'date') {
home.albums = byDate.albums(collection, opts)
} else {
throw new Error('Invalid <albumsFrom> option')
}
// finalize all albums recursively (calculate stats, etc...)
home.albums = group(collection, mapper)
home.finalize(opts)
return home
}
function group (collection, mapper) {
var groups = {}
// put all files in the right albums
collection.forEach(function (media) {
var groupName = mapper(media)
createAlbumHierarchy(groups, groupName)
groups[groupName].files.push(media)
})
// only return top-level albums
var topLevel = _.keys(groups).filter(function (dir) {
return path.dirname(dir) === '.'
})
return _.values(_.pick(groups, topLevel))
}
function createAlbumHierarchy (allGroupNames, segment) {
if (!allGroupNames.hasOwnProperty(segment)) {
// create parent albums first
var parent = path.dirname(segment)
if (parent !== '.') {
createAlbumHierarchy(allGroupNames, parent)
}
// then create album if it doesn't exist
var lastSegment = path.basename(segment)
allGroupNames[segment] = new Album({title: lastSegment})
// then attach to parent
if (parent !== '.') {
allGroupNames[parent].albums.push(allGroupNames[segment])
}
}
}

@ -0,0 +1,15 @@
const moment = require('moment')
const path = require('path')
exports.create = function (opts) {
var mapper = null
if (opts.albumsFrom === 'folders') {
mapper = (media) => path.dirname(media.file.path)
} else if (opts.albumsFrom === 'date') {
var dateFormat = opts.albumsDateFormat || 'YYYY MMMM'
mapper = (media) => moment(media.date).format(dateFormat)
} else {
throw new Error('Invalid <albumsFrom> option')
}
return mapper
}

@ -1,83 +0,0 @@
/* eslint-disable camelcase */
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')
describe('ByDate', function () {
beforeEach(function () {
Album.resetIds()
})
it('creates top-level albums grouped by month', function () {
// create files from different dates
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 c_2016_07 = fixtures.photo({date: fixtures.date('2016-07-23')})
var d_2016_07 = fixtures.video({date: fixtures.date('2016-07-18')})
// group them per month
var collection = [a_2016_06, b_2016_06, c_2016_07, d_2016_07]
var albums = bydate.albums(collection, {
grouping: 'YYYY-MM'
})
// assert on the result
should(albums).eql([
new Album({
id: 1,
'title': '2016-06',
files: [a_2016_06, b_2016_06]
}),
new Album({
id: 2,
title: '2016-07',
files: [c_2016_07, d_2016_07]
})
])
})
it('creates albums using a date hierarchy', function () {
// create files from different dates
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 c_2016_07 = fixtures.photo({date: fixtures.date('2016-07-23')})
var d_2016_08 = fixtures.video({date: fixtures.date('2016-08-18')})
// group them per year, and nested month
var collection = [a_2015_06, b_2015_06, c_2016_07, d_2016_08]
var albums = bydate.albums(collection, {
grouping: 'YYYY/MM'
})
// assert on the result
should(albums).eql([
new Album({
id: 1,
'title': '2015',
files: [],
albums: [
new Album({
id: 2,
title: '06',
files: [a_2015_06, b_2015_06]
})
]
}),
new Album({
id: 3,
title: '2016',
files: [],
albums: [
new Album({
id: 4,
title: '07',
files: [c_2016_07]
}),
new Album({
id: 5,
title: '08',
files: [d_2016_08]
})
]
})
])
})
})

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

@ -0,0 +1,90 @@
const path = require('path')
const should = require('should/as-function')
const hierarchy = require('../../src/model/hierarchy.js')
const Album = require('../../src/model/album.js')
const fixtures = require('../fixtures')
describe('hierarchy', function () {
beforeEach(function () {
Album.resetIds()
})
describe('root album', function () {
it('creates a root album (homepage) to put all sub-albums', function () {
const mapper = (media) => 'all'
const home = hierarchy.createAlbums([], mapper, {})
should(home.title).eql('Home')
})
it('defaults the homepage to index.html', function () {
const mapper = (media) => 'all'
const home = hierarchy.createAlbums([], mapper, {})
should(home.path).eql('index.html')
should(home.url).eql('index.html')
})
it('can configure the homepage path', function () {
const mapper = (media) => 'all'
const home = hierarchy.createAlbums([], mapper, {index: 'default.html'})
should(home.path).eql('default.html')
should(home.url).eql('default.html')
})
})
describe('nested albums', function () {
it('can group media into a single folder', function () {
const media = [
fixtures.photo({path: 'IMG_000001.jpg'}),
fixtures.photo({path: 'IMG_000002.jpg'})
]
const mapper = (media) => 'all'
const home = hierarchy.createAlbums(media, mapper)
should(home.albums.length).eql(1)
should(home.albums[0].title).eql('all')
should(home.albums[0].files).eql([media[0], media[1]])
})
it('can group media into several folders', function () {
const media = [
fixtures.photo({path: 'one/IMG_000001.jpg'}),
fixtures.photo({path: 'two/IMG_000002.jpg'})
]
const mapper = (media) => path.dirname(media.file.path)
const home = hierarchy.createAlbums(media, mapper)
should(home.albums.length).eql(2)
should(home.albums[0].title).eql('one')
should(home.albums[0].files).eql([media[0]])
should(home.albums[1].title).eql('two')
should(home.albums[1].files).eql([media[1]])
})
it('can group media into one nested folder', function () {
const media = [
fixtures.photo({path: 'IMG_000001.jpg'}),
fixtures.photo({path: 'IMG_000002.jpg'})
]
const mapper = (media) => 'one/two'
const home = hierarchy.createAlbums(media, mapper)
should(home.albums.length).eql(1)
should(home.albums[0].title).eql('one')
should(home.albums[0].albums.length).eql(1)
should(home.albums[0].albums[0].title).eql('two')
should(home.albums[0].albums[0].files).eql([media[0], media[1]])
})
it('can group media at different levels', function () {
const media = [
fixtures.photo({path: 'one/IMG_000001.jpg'}),
fixtures.photo({path: 'one/two/IMG_000002.jpg'})
]
const mapper = (media) => path.dirname(media.file.path)
const home = hierarchy.createAlbums(media, mapper)
should(home.albums.length).eql(1)
should(home.albums[0].title).eql('one')
should(home.albums[0].files).eql([media[0]])
should(home.albums[0].albums.length).eql(1)
should(home.albums[0].albums[0].title).eql('two')
should(home.albums[0].albums[0].files).eql([media[1]])
})
})
})

@ -0,0 +1,32 @@
var should = require('should/as-function')
var mapper = require('../../src/model/mapper.js')
var fixtures = require('../fixtures.js')
describe('mapper', function () {
it('can create a path mapper', function () {
const map = mapper.create({albumsFrom: 'folders'})
const media = fixtures.photo({
path: 'holidays/canada/IMG_0001.jpg'
})
should(map(media)).eql('holidays/canada')
})
it('can create a default date mapper', function () {
const map = mapper.create({albumsFrom: 'date'})
const media = fixtures.photo({
path: 'holidays/canada/IMG_0001.jpg',
date: '2016-07-14 12:07:41'
})
should(map(media)).eql('2016 July')
})
it('can create a custom date mapper', function () {
const map = mapper.create({
albumsFrom: 'date',
albumsDateFormat: 'YYYY/MM'
})
const media = fixtures.photo({
path: 'holidays/canada/IMG_0001.jpg',
date: '2016-07-14 12:07:41'
})
should(map(media)).eql('2016/07')
})
})
Loading…
Cancel
Save