diff options
Diffstat (limited to 'src/data/things/track.js')
-rw-r--r-- | src/data/things/track.js | 796 |
1 files changed, 462 insertions, 334 deletions
diff --git a/src/data/things/track.js b/src/data/things/track.js index e176acb4..bf56a6dd 100644 --- a/src/data/things/track.js +++ b/src/data/things/track.js @@ -44,60 +44,72 @@ export class Track extends Thing { sampledTracksByRef: Thing.common.referenceList(Track), artTagsByRef: Thing.common.referenceList(ArtTag), - hasCoverArt: { - flags: {update: true, expose: true}, - - update: { - validate(value) { - if (value !== false) { - throw new TypeError(`Expected false or null`); - } - - return true; - }, - }, - - expose: { - dependencies: ['albumData', 'coverArtistContribsByRef'], - transform: (hasCoverArt, { - albumData, - coverArtistContribsByRef, - [Track.instance]: track, - }) => - Track.hasCoverArt( - track, - albumData, - coverArtistContribsByRef, - hasCoverArt - ), + color: Thing.composite.from(`Track.color`, [ + Thing.composite.exposeUpdateValueOrContinue(), + Track.composite.withContainingTrackSection({earlyExitIfNotFound: false}), + + { + dependencies: ['#trackSection'], + compute: ({'#trackSection': trackSection}, continuation) => + // Album.trackSections guarantees the track section will have a + // color property (inheriting from the album's own color), but only + // if it's actually present! Color will be inherited directly from + // album otherwise. + (trackSection + ? trackSection.color + : continuation()), }, - }, - coverArtFileExtension: { - flags: {update: true, expose: true}, - - update: {validate: isFileExtension}, - - expose: { - dependencies: ['albumData', 'coverArtistContribsByRef'], - transform: (coverArtFileExtension, { - albumData, - coverArtistContribsByRef, - hasCoverArt, - [Track.instance]: track, - }) => - coverArtFileExtension ?? - (Track.hasCoverArt( - track, - albumData, - coverArtistContribsByRef, - hasCoverArt - ) - ? Track.findAlbum(track, albumData)?.trackCoverArtFileExtension - : Track.findAlbum(track, albumData)?.coverArtFileExtension) ?? - 'jpg', - }, - }, + Track.composite.withAlbumProperty('color'), + Thing.composite.exposeDependency('#album.color', { + update: {validate: isColor}, + }), + ]), + + // Disables presenting the track as though it has its own unique artwork. + // This flag should only be used in select circumstances, i.e. to override + // an album's trackCoverArtists. This flag supercedes that property, as well + // as the track's own coverArtists. + disableUniqueCoverArt: Thing.common.flag(), + + // File extension for track's corresponding media file. This represents the + // track's unique cover artwork, if any, and does not inherit the extension + // of the album's main artwork. It does inherit trackCoverArtFileExtension, + // if present on the album. + coverArtFileExtension: Thing.composite.from(`Track.coverArtFileExtension`, [ + // No cover art file extension if the track doesn't have unique artwork + // in the first place. + Track.composite.withHasUniqueCoverArt(), + Thing.composite.earlyExitWithoutDependency('#hasUniqueCoverArt', {mode: 'falsy'}), + + // Expose custom coverArtFileExtension update value first. + Thing.composite.exposeUpdateValueOrContinue(), + + // Expose album's trackCoverArtFileExtension if no update value set. + Track.composite.withAlbumProperty('trackCoverArtFileExtension'), + Thing.composite.exposeDependencyOrContinue('#album.trackCoverArtFileExtension'), + + // Fallback to 'jpg'. + Thing.composite.exposeConstant('jpg', { + update: {validate: isFileExtension}, + }), + ]), + + // Date of cover art release. Like coverArtFileExtension, this represents + // only the track's own unique cover artwork, if any. This exposes only as + // the track's own coverArtDate or its album's trackArtDate, so if neither + // is specified, this value is null. + coverArtDate: Thing.composite.from(`Track.coverArtDate`, [ + Track.composite.withHasUniqueCoverArt(), + Thing.composite.earlyExitWithoutDependency('#hasUniqueCoverArt', {mode: 'falsy'}), + + Thing.composite.exposeUpdateValueOrContinue(), + + Track.composite.withAlbumProperty('trackArtDate'), + Thing.composite.exposeDependency('#album.trackArtDate', { + update: {validate: isDate}, + }), + ]), originalReleaseTrackByRef: Thing.common.singleReference(Track), @@ -121,15 +133,10 @@ export class Track extends Thing { commentatorArtists: Thing.common.commentatorArtists(), - album: { - flags: {expose: true}, - - expose: { - dependencies: ['albumData'], - compute: ({[Track.instance]: track, albumData}) => - albumData?.find((album) => album.tracks.includes(track)) ?? null, - }, - }, + album: Thing.composite.from(`Track.album`, [ + Track.composite.withAlbum(), + Thing.composite.exposeDependency('#album'), + ]), // Note - this is an internal property used only to help identify a track. // It should not be assumed in general that the album and dataSourceAlbum match @@ -138,158 +145,120 @@ export class Track extends Thing { // not generally relevant information). It's also not guaranteed that // dataSourceAlbum is available (depending on the Track creator to optionally // provide dataSourceAlbumByRef). - dataSourceAlbum: Thing.common.dynamicThingFromSingleReference( - 'dataSourceAlbumByRef', - 'albumData', - find.album - ), - - date: { - flags: {expose: true}, - - expose: { - dependencies: ['albumData', 'dateFirstReleased'], - compute: ({albumData, dateFirstReleased, [Track.instance]: track}) => - dateFirstReleased ?? Track.findAlbum(track, albumData)?.date ?? null, + dataSourceAlbum: Thing.common.dynamicThingFromSingleReference('dataSourceAlbumByRef', 'albumData', find.album), + + date: Thing.composite.from(`Track.date`, [ + Thing.composite.exposeDependencyOrContinue('dateFirstReleased'), + Track.composite.withAlbumProperty('date'), + Thing.composite.exposeDependency('#album.date'), + ]), + + // Whether or not the track has "unique" cover artwork - a cover which is + // specifically associated with this track in particular, rather than with + // the track's album as a whole. This is typically used to select between + // displaying the track artwork and a fallback, such as the album artwork + // or a placeholder. (This property is named hasUniqueCoverArt instead of + // the usual hasCoverArt to emphasize that it does not inherit from the + // album.) + hasUniqueCoverArt: Thing.composite.from(`Track.hasUniqueCoverArt`, [ + Track.composite.withHasUniqueCoverArt(), + Thing.composite.exposeDependency('#hasUniqueCoverArt'), + ]), + + originalReleaseTrack: Thing.composite.from(`Track.originalReleaseTrack`, [ + Track.composite.withOriginalRelease(), + Thing.composite.exposeDependency('#originalRelease'), + ]), + + otherReleases: Thing.composite.from(`Track.otherReleases`, [ + Thing.composite.earlyExitWithoutDependency('trackData', {mode: 'empty'}), + Track.composite.withOriginalRelease({selfIfOriginal: true}), + + { + flags: {expose: true}, + expose: { + dependencies: ['this', 'trackData', '#originalRelease'], + compute: ({ + this: thisTrack, + trackData, + '#originalRelease': originalRelease, + }) => + (originalRelease === thisTrack + ? [] + : [originalRelease]) + .concat(trackData.filter(track => + track !== originalRelease && + track !== thisTrack && + track.originalReleaseTrack === originalRelease)), + }, }, - }, - - color: { - flags: {update: true, expose: true}, - - update: {validate: isColor}, - - expose: { - dependencies: ['albumData'], - - transform: (color, {albumData, [Track.instance]: track}) => - color ?? - Track.findAlbum(track, albumData) - ?.trackSections.find(({tracks}) => tracks.includes(track)) - ?.color ?? null, + ]), + + artistContribs: Thing.composite.from(`Track.artistContribs`, [ + Track.composite.inheritFromOriginalRelease({property: 'artistContribs'}), + + Thing.composite.withResolvedContribs({ + from: 'artistContribsByRef', + to: '#artistContribs', + }), + + { + dependencies: ['#artistContribs'], + compute: ({'#artistContribs': contribsFromTrack}, continuation) => + (empty(contribsFromTrack) + ? continuation() + : contribsFromTrack), }, - }, - coverArtDate: { - flags: {update: true, expose: true}, + Track.composite.withAlbumProperty('artistContribs'), + Thing.composite.exposeDependency('#album.artistContribs'), + ]), - update: {validate: isDate}, - - expose: { - dependencies: [ - 'albumData', - 'coverArtistContribsByRef', - 'dateFirstReleased', - 'hasCoverArt', - ], - transform: (coverArtDate, { - albumData, - coverArtistContribsByRef, - dateFirstReleased, - hasCoverArt, - [Track.instance]: track, - }) => - (Track.hasCoverArt(track, albumData, coverArtistContribsByRef, hasCoverArt) - ? coverArtDate ?? - dateFirstReleased ?? - Track.findAlbum(track, albumData)?.trackArtDate ?? - Track.findAlbum(track, albumData)?.date ?? - null - : null), - }, - }, + contributorContribs: Thing.composite.from(`Track.contributorContribs`, [ + Track.composite.inheritFromOriginalRelease({property: 'contributorContribs'}), + Thing.common.dynamicContribs('contributorContribsByRef'), + ]), - hasUniqueCoverArt: { - flags: {expose: true}, - - expose: { - dependencies: ['albumData', 'coverArtistContribsByRef', 'hasCoverArt'], - compute: ({ - albumData, - coverArtistContribsByRef, - hasCoverArt, - [Track.instance]: track, - }) => - Track.hasUniqueCoverArt( - track, - albumData, - coverArtistContribsByRef, - hasCoverArt - ), + // Cover artists aren't inherited from the original release, since it + // typically varies by release and isn't defined by the musical qualities + // of the track. + coverArtistContribs: Thing.composite.from(`Track.coverArtistContribs`, [ + { + dependencies: ['disableUniqueCoverArt'], + compute: ({disableUniqueCoverArt}, continuation) => + (disableUniqueCoverArt + ? null + : continuation()), }, - }, - originalReleaseTrack: Thing.common.dynamicThingFromSingleReference( - 'originalReleaseTrackByRef', - 'trackData', - find.track - ), - - otherReleases: { - flags: {expose: true}, - - expose: { - dependencies: ['originalReleaseTrackByRef', 'trackData'], - - compute: ({ - originalReleaseTrackByRef: t1origRef, - trackData, - [Track.instance]: t1, - }) => { - if (!trackData) { - return []; - } - - const t1orig = find.track(t1origRef, trackData); - - return [ - t1orig, - ...trackData.filter((t2) => { - const {originalReleaseTrack: t2orig} = t2; - return t2 !== t1 && t2orig && (t2orig === t1orig || t2orig === t1); - }), - ].filter(Boolean); - }, + Thing.composite.withResolvedContribs({ + from: 'coverArtistContribsByRef', + to: '#coverArtistContribs', + }), + + { + dependencies: ['#coverArtistContribs'], + compute: ({'#coverArtistContribs': contribsFromTrack}, continuation) => + (empty(contribsFromTrack) + ? continuation() + : contribsFromTrack), }, - }, - artistContribs: - Track.inheritFromOriginalRelease('artistContribs', [], - Thing.common.dynamicInheritContribs( - null, - 'artistContribsByRef', - 'artistContribsByRef', - 'albumData', - Track.findAlbum)), + Track.composite.withAlbumProperty('trackCoverArtistContribs'), + Thing.composite.exposeDependency('#album.trackCoverArtistContribs'), + ]), - contributorContribs: - Track.inheritFromOriginalRelease('contributorContribs', [], - Thing.common.dynamicContribs('contributorContribsByRef')), + referencedTracks: Thing.composite.from(`Track.referencedTracks`, [ + Track.composite.inheritFromOriginalRelease({property: 'referencedTracks'}), + Thing.common.dynamicThingsFromReferenceList('referencedTracksByRef', 'trackData', find.track), + ]), - // Cover artists aren't inherited from the original release, since it - // typically varies by release and isn't defined by the musical qualities - // of the track. - coverArtistContribs: - Thing.common.dynamicInheritContribs( - 'hasCoverArt', - 'coverArtistContribsByRef', - 'trackCoverArtistContribsByRef', - 'albumData', - Track.findAlbum), - - referencedTracks: - Track.inheritFromOriginalRelease('referencedTracks', [], - Thing.common.dynamicThingsFromReferenceList( - 'referencedTracksByRef', - 'trackData', - find.track)), - - sampledTracks: - Track.inheritFromOriginalRelease('sampledTracks', [], - Thing.common.dynamicThingsFromReferenceList( - 'sampledTracksByRef', - 'trackData', - find.track)), + sampledTracks: Thing.composite.from(`Track.sampledTracks`, [ + Track.composite.inheritFromOriginalRelease({property: 'sampledTracks'}), + Thing.common.dynamicThingsFromReferenceList('sampledTracksByRef', 'trackData', find.track), + ]), + + artTags: Thing.common.dynamicThingsFromReferenceList('artTagsByRef', 'artTagData', find.artTag), // Specifically exclude re-releases from this list - while it's useful to // get from a re-release to the tracks it references, re-releases aren't @@ -299,162 +268,321 @@ export class Track extends Thing { // counting the number of times a track has been referenced, for use in // the "Tracks - by Times Referenced" listing page (or other data // processing). - referencedByTracks: { - flags: {expose: true}, - - expose: { - dependencies: ['trackData'], - - compute: ({trackData, [Track.instance]: track}) => - trackData - ? trackData - .filter((t) => !t.originalReleaseTrack) - .filter((t) => t.referencedTracks?.includes(track)) - : [], - }, - }, + referencedByTracks: Track.composite.trackReverseReferenceList('referencedTracks'), // For the same reasoning, exclude re-releases from sampled tracks too. - sampledByTracks: { - flags: {expose: true}, - - expose: { - dependencies: ['trackData'], - - compute: ({trackData, [Track.instance]: track}) => - trackData - ? trackData - .filter((t) => !t.originalReleaseTrack) - .filter((t) => t.sampledTracks?.includes(track)) - : [], - }, - }, - - featuredInFlashes: Thing.common.reverseReferenceList( - 'flashData', - 'featuredTracks' - ), + sampledByTracks: Track.composite.trackReverseReferenceList('sampledTracks'), - artTags: Thing.common.dynamicThingsFromReferenceList( - 'artTagsByRef', - 'artTagData', - find.artTag - ), + featuredInFlashes: Thing.common.reverseReferenceList({ + data: 'flashData', + refList: 'featuredTracks', + }), }); - // This is a quick utility function for now, since the same code is reused in - // several places. Ideally it wouldn't be - we'd just reuse the `album` - // property - but support for that hasn't been coded yet :P - static findAlbum = (track, albumData) => - albumData?.find((album) => album.tracks.includes(track)); - - // Another reused utility function. This one's logic is a bit more complicated. - static hasCoverArt( - track, - albumData, - coverArtistContribsByRef, - hasCoverArt - ) { - if (!empty(coverArtistContribsByRef)) { - return true; - } + static composite = { + // Early exits with a value inherited from the original release, if + // this track is a rerelease, and otherwise continues with no further + // dependencies provided. If allowOverride is true, then the continuation + // will also be called if the original release exposed the requested + // property as null. + inheritFromOriginalRelease({ + property: originalProperty, + allowOverride = false, + }) { + return Thing.composite.from(`Track.composite.inheritFromOriginalRelease`, [ + Track.composite.withOriginalRelease(), + + { + dependencies: ['#originalRelease'], + compute({'#originalRelease': originalRelease}, continuation) { + if (!originalRelease) return continuation.raise(); + + const value = originalRelease[originalProperty]; + if (allowOverride && value === null) return continuation.raise(); + + return continuation.exit(value); + }, + }, + ]); + }, - const album = Track.findAlbum(track, albumData); - if (album && !empty(album.trackCoverArtistContribsByRef)) { - return true; - } + // Gets the track's album. Unless earlyExitIfNotFound is overridden false, + // this will early exit with null in two cases - albumData being missing, + // or not including an album whose .tracks array includes this track. + withAlbum({to = '#album', earlyExitIfNotFound = true} = {}) { + return Thing.composite.from(`Track.composite.withAlbum`, [ + Thing.composite.withResultOfAvailabilityCheck({ + fromDependency: 'albumData', + mode: 'empty', + to: '#albumDataAvailability', + }), + + { + dependencies: ['#albumDataAvailability'], + options: {earlyExitIfNotFound}, + mapContinuation: {to}, + + compute: ({ + '#albumDataAvailability': albumDataAvailability, + '#options': {earlyExitIfNotFound}, + }, continuation) => + (albumDataAvailability + ? continuation() + : (earlyExitIfNotFound + ? continuation.exit(null) + : continuation.raise({to: null}))), + }, - return false; - } + { + dependencies: ['this', 'albumData'], + compute: ({this: track, albumData}, continuation) => + continuation({ + '#album': + albumData.find(album => album.tracks.includes(track)), + }), + }, - static hasUniqueCoverArt( - track, - albumData, - coverArtistContribsByRef, - hasCoverArt - ) { - if (!empty(coverArtistContribsByRef)) { - return true; - } + { + dependencies: ['#album'], + options: {earlyExitIfNotFound}, + mapContinuation: {to}, + compute: ({ + '#album': album, + '#options': {earlyExitIfNotFound}, + }, continuation) => + (album + ? continuation.raise({to: album}) + : (earlyExitIfNotFound + ? continuation.exit(null) + : continuation.raise({to: album}))), + }, + ]); + }, - if (hasCoverArt === false) { - return false; - } + // Gets a single property from this track's album, providing it as the same + // property name prefixed with '#album.' (by default). If the track's album + // isn't available, and earlyExitIfNotFound hasn't been set, the property + // will be provided as null. + withAlbumProperty(property, { + to = '#album.' + property, + earlyExitIfNotFound = false, + } = {}) { + return Thing.composite.from(`Track.composite.withAlbumProperty`, [ + Track.composite.withAlbum({earlyExitIfNotFound}), + + { + dependencies: ['#album'], + options: {property}, + mapContinuation: {to}, + + compute: ({ + '#album': album, + '#options': {property}, + }, continuation) => + (album + ? continuation.raise({to: album[property]}) + : continuation.raise({to: null})), + }, + ]); + }, - const album = Track.findAlbum(track, albumData); - if (album && !empty(album.trackCoverArtistContribsByRef)) { - return true; - } + // Gets the listed properties from this track's album, providing them as + // dependencies (by default) with '#album.' prefixed before each property + // name. If the track's album isn't available, and earlyExitIfNotFound + // hasn't been set, the same dependency names will be provided as null. + withAlbumProperties({ + properties, + prefix = '#album', + earlyExitIfNotFound = false, + }) { + return Thing.composite.from(`Track.composite.withAlbumProperties`, [ + Track.composite.withAlbum({earlyExitIfNotFound}), + + { + dependencies: ['#album'], + options: {properties, prefix}, + + compute({ + '#album': album, + '#options': {properties, prefix}, + }, continuation) { + const raise = {}; + + if (album) { + for (const property of properties) { + raise[prefix + '.' + property] = album[property]; + } + } else { + for (const property of properties) { + raise[prefix + '.' + property] = null; + } + } + + return continuation.raise(raise); + }, + }, + ]); + }, - return false; - } + // Gets the track section containing this track from its album's track list. + // Unless earlyExitIfNotFound is overridden false, this will early exit if + // the album can't be found or if none of its trackSections includes the + // track for some reason. + withContainingTrackSection({ + to = '#trackSection', + earlyExitIfNotFound = true, + } = {}) { + return Thing.composite.from(`Track.composite.withContainingTrackSection`, [ + Track.composite.withAlbumProperty('trackSections', {earlyExitIfNotFound}), + + { + dependencies: ['this', '#album.trackSections'], + mapContinuation: {to}, + + compute({ + this: track, + '#album.trackSections': trackSections, + }, continuation) { + if (!trackSections) { + return continuation.raise({to: null}); + } + + const trackSection = + trackSections.find(({tracks}) => tracks.includes(track)); + + if (trackSection) { + return continuation.raise({to: trackSection}); + } else if (earlyExitIfNotFound) { + return continuation.exit(null); + } else { + return continuation.raise({to: null}); + } + }, + }, + ]); + }, - static inheritFromOriginalRelease( - originalProperty, - originalMissingValue, - ownPropertyDescriptor - ) { - return { - flags: {expose: true}, - - expose: { - dependencies: [ - ...ownPropertyDescriptor.expose.dependencies, - 'originalReleaseTrackByRef', - 'trackData', - ], - - compute(dependencies) { - const { - originalReleaseTrackByRef, - trackData, - } = dependencies; + // Just includes the original release of this track as a dependency. + // If this track isn't a rerelease, then it'll provide null, unless the + // {selfIfOriginal} option is set, in which case it'll provide this track + // itself. Note that this will early exit if the original release is + // specified by reference and that reference doesn't resolve to anything. + // Outputs to '#originalRelease' by default. + withOriginalRelease({ + to = '#originalRelease', + selfIfOriginal = false, + } = {}) { + return Thing.composite.from(`Track.composite.withOriginalRelease`, [ + Thing.composite.withResolvedReference({ + ref: 'originalReleaseTrackByRef', + data: 'trackData', + to: '#originalRelease', + find: find.track, + earlyExitIfNotFound: true, + }), + + { + dependencies: ['this', '#originalRelease'], + options: {selfIfOriginal}, + mapContinuation: {to}, + compute: ({ + this: track, + '#originalRelease': originalRelease, + '#options': {selfIfOriginal}, + }, continuation) => + continuation.raise({ + to: + (originalRelease ?? + (selfIfOriginal + ? track + : null)), + }), + }, + ]); + }, - if (originalReleaseTrackByRef) { - if (!trackData) return originalMissingValue; - const original = find.track(originalReleaseTrackByRef, trackData, {mode: 'quiet'}); - if (!original) return originalMissingValue; - return original[originalProperty]; - } + // The algorithm for checking if a track has unique cover art is used in a + // couple places, so it's defined in full as a compositional step. + withHasUniqueCoverArt({ + to = '#hasUniqueCoverArt', + } = {}) { + return Thing.composite.from(`Track.composite.withHasUniqueCoverArt`, [ + { + dependencies: ['disableUniqueCoverArt'], + mapContinuation: {to}, + compute: ({disableUniqueCoverArt}, continuation) => + (disableUniqueCoverArt + ? continuation.raise({to: false}) + : continuation()), + }, - return ownPropertyDescriptor.expose.compute(dependencies); + Thing.composite.withResolvedContribs({ + from: 'coverArtistContribsByRef', + to: '#coverArtistContribs', + }), + + { + dependencies: ['#coverArtistContribs'], + mapContinuation: {to}, + compute: ({'#coverArtistContribs': contribsFromTrack}, continuation) => + (empty(contribsFromTrack) + ? continuation() + : continuation.raise({to: true})), }, - }, - }; - } - [inspect.custom]() { - const base = Thing.prototype[inspect.custom].apply(this); + Track.composite.withAlbumProperty('trackCoverArtistContribs'), - const rereleasePart = - (this.originalReleaseTrackByRef - ? `${color.yellow('[rerelease]')} ` - : ``); + { + dependencies: ['#album.trackCoverArtistContribs'], + mapContinuation: {to}, + compute: ({'#album.trackCoverArtistContribs': contribsFromAlbum}, continuation) => + (empty(contribsFromAlbum) + ? continuation.raise({to: false}) + : continuation.raise({to: true})), + }, + ]); + }, - const {album, dataSourceAlbum} = this; + trackReverseReferenceList(refListProperty) { + return Thing.composite.from(`Track.composite.trackReverseReferenceList`, [ + Thing.composite.withReverseReferenceList({ + data: 'trackData', + refList: refListProperty, + originalTracksOnly: true, + }), + + { + flags: {expose: true}, + expose: { + dependencies: ['#reverseReferenceList'], + compute: ({'#reverseReferenceList': reverseReferenceList}) => + reverseReferenceList.filter(track => !track.originalReleaseTrack), + }, + }, + ]); + }, + }; - const albumName = - (album - ? album.name - : dataSourceAlbum?.name); + [inspect.custom](depth) { + const parts = []; - const albumIndex = - albumName && - (album - ? album.tracks.indexOf(this) - : dataSourceAlbum.tracks.indexOf(this)); + parts.push(Thing.prototype[inspect.custom].apply(this)); - const trackNum = - albumName && + if (this.originalReleaseTrackByRef) { + parts.unshift(`${color.yellow('[rerelease]')} `); + } + + let album; + if (depth >= 0 && (album = this.album ?? this.dataSourceAlbum)) { + const albumName = album.name; + const albumIndex = album.tracks.indexOf(this); + const trackNum = (albumIndex === -1 ? '#?' : `#${albumIndex + 1}`); + parts.push(` (${color.yellow(trackNum)} in ${color.green(albumName)})`); + } - const albumPart = - albumName - ? ` (${color.yellow(trackNum)} in ${color.green(albumName)})` - : ``; - - return rereleasePart + base + albumPart; + return parts.join(''); } } |