2
0
mirror of https://github.com/thumbsup/thumbsup synced 2024-11-15 18:12:46 +00:00
thumbsup/src/model/album.js

143 lines
4.5 KiB
JavaScript
Raw Normal View History

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