From 60c3902edc80d6bdbb23737fdfe67a7901ebfa6e Mon Sep 17 00:00:00 2001 From: Romain Date: Mon, 11 May 2020 00:36:46 +0200 Subject: [PATCH] feat(albums): Support custom sorting per nesting levels, fix #179 --- bin/options.js | 9 ++++ src/model/album.js | 15 +++++- test/bin/options.spec.js | 17 +++++++ test/model/album.spec.js | 103 ++++++++++++++++++++++++++++++--------- 4 files changed, 118 insertions(+), 26 deletions(-) diff --git a/bin/options.js b/bin/options.js index faf0101..0296642 100644 --- a/bin/options.js +++ b/bin/options.js @@ -178,24 +178,28 @@ const OPTIONS = { group: 'Album options:', description: 'How to sort albums', choices: ['title', 'start-date', 'end-date'], + coerce: commaSeparated, 'default': 'start-date' }, 'sort-albums-direction': { group: 'Album options:', description: 'Album sorting direction', choices: ['asc', 'desc'], + coerce: commaSeparated, 'default': 'asc' }, 'sort-media-by': { group: 'Album options:', description: 'How to sort photos and videos', choices: ['filename', 'date'], + coerce: commaSeparated, 'default': 'date' }, 'sort-media-direction': { group: 'Album options:', description: 'Media sorting direction', choices: ['asc', 'desc'], + coerce: commaSeparated, 'default': 'asc' }, 'home-album-name': { @@ -424,3 +428,8 @@ function replaceInArray (list, match, replacement) { } } } + +function commaSeparated (value) { + if (value.indexOf(',') === -1) return value + return value.split(',') +} diff --git a/src/model/album.js b/src/model/album.js index 3b39326..1000861 100644 --- a/src/model/album.js +++ b/src/model/album.js @@ -111,8 +111,12 @@ Album.prototype.calculateSummary = function () { } 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) + const sortAlbumsBy = getItemOrLast(options.sortAlbumsBy, this.depth) + const sortAlbumsDirection = getItemOrLast(options.sortAlbumsDirection, this.depth) + const sortMediaBy = getItemOrLast(options.sortMediaBy, this.depth) + const sortMediaDirection = getItemOrLast(options.sortMediaDirection, this.depth) + this.files = _.orderBy(this.files, SORT_MEDIA_BY[sortMediaBy], sortMediaDirection) + this.albums = _.orderBy(this.albums, SORT_ALBUMS_BY[sortAlbumsBy], sortAlbumsDirection) } Album.prototype.pickPreviews = function () { @@ -136,6 +140,13 @@ function itemCount (count, type) { return '' + count + ' ' + type + plural } +function getItemOrLast (array, index) { + if (typeof (array) === 'undefined') return undefined + if (typeof (array) === 'string') return array + if (index > array.length) return array[array.length - 1] + return array[index] +} + // for testing purposes Album.resetIds = function () { index = 0 diff --git a/test/bin/options.spec.js b/test/bin/options.spec.js index 077022d..cf7d781 100644 --- a/test/bin/options.spec.js +++ b/test/bin/options.spec.js @@ -51,6 +51,23 @@ describe('options', function () { should(opts.albumsFrom).eql(['%path', '%keywords']) }) }) + describe('--sort-albums-direction', () => { + it('can be specified with multiple arguments', () => { + const args = BASE_ARGS.concat([ + '--sort-albums-direction', 'asc', + '--sort-albums-direction', 'desc' + ]) + const opts = options.get(args) + should(opts.sortAlbumsDirection).eql(['asc', 'desc']) + }) + it('can be specified multiple times with a comma', () => { + const args = BASE_ARGS.concat([ + '--sort-albums-direction', 'asc,desc' + ]) + const opts = options.get(args) + should(opts.sortAlbumsDirection).eql(['asc', 'desc']) + }) + }) describe('--gm-args', () => { it('is optional', () => { const opts = options.get(BASE_ARGS) diff --git a/test/model/album.spec.js b/test/model/album.spec.js index d554046..24c3498 100644 --- a/test/model/album.spec.js +++ b/test/model/album.spec.js @@ -4,6 +4,13 @@ const should = require('should/as-function') const Album = require('../../src/model/album') const fixtures = require('../fixtures') +// Common fixtures +const fileA = fixtures.photo({ path: 'a' }) +const fileB = fixtures.photo({ path: 'b' }) +const fileC = fixtures.photo({ path: 'c' }) +const file2010 = fixtures.photo({ date: '2010-01-01' }) +const file2011 = fixtures.photo({ path: '2011-01-01' }) + describe('Album', function () { describe('options', function () { it('can pass the title as a single argument', function () { @@ -30,7 +37,7 @@ describe('Album', function () { }) it('doesn\'t use a dash if the words have no space', function () { - // not ideal but that's hoz slugify() works + // not ideal but that's how slugify() works const a = new Album("aujourd'hui") should(a.basename).eql('aujourdhui') }) @@ -175,21 +182,15 @@ describe('Album', function () { }) it('can sort media by filename', function () { - const a = fixtures.photo({ path: 'a' }) - const b = fixtures.photo({ path: 'b' }) - const c = fixtures.photo({ path: 'c' }) - const album = new Album({ files: [c, a, b] }) + const album = new Album({ files: [fileC, fileA, fileB] }) album.finalize({ sortMediaBy: 'filename' }) - should(album.files).eql([a, b, c]) + should(album.files).eql([fileA, fileB, fileC]) }) it('can sort media by reverse filename', function () { - const a = fixtures.photo({ path: 'a' }) - const b = fixtures.photo({ path: 'b' }) - const c = fixtures.photo({ path: 'c' }) - const album = new Album({ files: [c, a, b] }) + const album = new Album({ files: [fileC, fileA, fileB] }) album.finalize({ sortMediaBy: 'filename', sortMediaDirection: 'desc' }) - should(album.files).eql([c, b, a]) + should(album.files).eql([fileC, fileB, fileA]) }) it('can sort media by date', function () { @@ -203,14 +204,11 @@ describe('Album', function () { it('sorts nested albums too', function () { const nested = new Album({ title: 'nested', - files: [ - fixtures.photo({ path: 'b' }), - fixtures.photo({ path: 'a' }) - ] }) + files: [fileB, fileA] + }) const root = new Album({ title: 'home', albums: [nested] }) root.finalize({ sortMediaBy: 'filename' }) - should(nested.files[0].path).eql('a') - should(nested.files[1].path).eql('b') + should(nested.files).eql([fileA, fileB]) }) }) @@ -244,15 +242,72 @@ describe('Album', function () { }) it('passes finalising options to all nested albums (e.g. sorting)', function () { - const nested = new Album({ title: 'nested', - files: [ - fixtures.photo({ path: 'b' }), - fixtures.photo({ path: 'a' }) - ] }) + const nested = new Album({ + title: 'nested', + files: [fileB, fileA] + }) const root = new Album({ title: 'home', albums: [nested] }) root.finalize({ sortMediaBy: 'filename' }) - should(nested.files[0].path).eql('a') - should(nested.files[1].path).eql('b') + should(nested.files).eql([fileA, fileB]) + }) + }) + + describe('nested sorting', function () { + it('can specify nested album sorting method', function () { + const a1 = new Album({ files: [file2011] }) + const a2 = new Album({ files: [file2010] }) + const a = new Album({ title: 'A', albums: [a1, a2] }) + const b = new Album('B') + const root = new Album({ albums: [b, a] }) + root.finalize({ + sortAlbumsBy: ['title', 'start-date'], + sortAlbumsDirection: 'asc' + }) + should(root.albums).eql([a, b]) + should(a.albums).eql([a2, a1]) + }) + it('can specify nested album sorting direction', function () { + const a1 = new Album('A1') + const a2 = new Album('A2') + const a = new Album({ albums: [a1, a2] }) + const b = new Album('B') + const root = new Album({ albums: [a, b] }) + root.finalize({ + sortAlbumsBy: 'title', + sortAlbumsDirection: ['asc', 'desc'] + }) + should(root.albums).eql([a, b]) + should(a.albums).eql([a2, a1]) + }) + it('can specify nested media sorting method', function () { + const nested = new Album({ + files: [fileB, fileA] + }) + const root = new Album({ + albums: [nested], + files: [file2011, file2010] + }) + root.finalize({ + sortMediaBy: ['date', 'filename'], + sortMediaDirection: 'asc' + }) + should(root.files).eql([file2010, file2011]) + should(nested.files).eql([fileA, fileB]) + }) + it('can specify nested media sorting direction', function () { + const nested = new Album({ + files: [fileA, fileB] + }) + const root = new Album({ + albums: [nested], + files: [fileB, fileA] + }) + root.finalize({ + sortMediaBy: 'filename', + sortMediaDirection: ['asc', 'desc'] + }) + should(root.files).eql([fileA, fileB]) + should(nested.files).eql([fileB, fileA]) }) })