diff options
Diffstat (limited to 'src/data/things/artist.js')
| -rw-r--r-- | src/data/things/artist.js | 191 |
1 files changed, 113 insertions, 78 deletions
diff --git a/src/data/things/artist.js b/src/data/things/artist.js index 9e329c74..a2ed0b74 100644 --- a/src/data/things/artist.js +++ b/src/data/things/artist.js @@ -4,15 +4,21 @@ import {inspect} from 'node:util'; import CacheableObject from '#cacheable-object'; import {colors} from '#cli'; -import {input} from '#composite'; -import {sortAlphabetically} from '#sort'; -import {stitchArrays} from '#sugar'; +import {input, V} from '#composite'; import Thing from '#thing'; -import {isName, validateArrayItems} from '#validators'; -import {getKebabCase} from '#wiki-data'; -import {parseArtwork} from '#yaml'; +import {parseArtistAliases, parseArtwork} from '#yaml'; -import {exitWithoutDependency} from '#composite/control-flow'; +import { + sortAlbumsTracksChronologically, + sortArtworksChronologically, + sortAlphabetically, + sortContributionsChronologically, +} from '#sort'; + +import {exitWithoutDependency, exposeConstant, exposeDependency} + from '#composite/control-flow'; +import {withFilteredList, withPropertyFromList} from '#composite/data'; +import {withContributionListSums} from '#composite/wiki-data'; import { constitutibleArtwork, @@ -22,53 +28,46 @@ import { flag, name, reverseReferenceList, - singleReference, soupyFind, soupyReverse, + thing, + thingList, urls, - wikiData, } from '#composite/wiki-properties'; -import {artistTotalDuration} from '#composite/things/artist'; - export class Artist extends Thing { static [Thing.referenceType] = 'artist'; - static [Thing.wikiDataArray] = 'artistData'; + static [Thing.wikiData] = 'artistData'; - static [Thing.getPropertyDescriptors] = ({Album, Flash, Group, Track}) => ({ + static [Thing.constitutibleProperties] = [ + 'avatarArtwork', // from inline fields + ]; + + static [Thing.getPropertyDescriptors] = () => ({ // Update & expose - name: name('Unnamed Artist'), + name: name(V('Unnamed Artist')), directory: directory(), urls: urls(), contextNotes: contentString(), - hasAvatar: flag(false), - avatarFileExtension: fileExtension('jpg'), + hasAvatar: flag(V(false)), + avatarFileExtension: fileExtension(V('jpg')), avatarArtwork: [ - exitWithoutDependency({ - dependency: 'hasAvatar', + exitWithoutDependency('hasAvatar', { value: input.value(null), + mode: input.value('falsy'), }), constitutibleArtwork.fromYAMLFieldSpec .call(this, 'Avatar Artwork'), ], - aliasNames: { - flags: {update: true, expose: true}, - update: {validate: validateArrayItems(isName)}, - expose: {transform: (names) => names ?? []}, - }, - - isAlias: flag(), - - aliasedArtist: singleReference({ - class: input.value(Artist), - find: soupyFind.input('artist'), - }), + isAlias: flag(V(false)), + artistAliases: thingList(V(Artist)), + aliasedArtist: thing(V(Artist)), // Update only @@ -77,6 +76,8 @@ export class Artist extends Thing { // Expose only + isArtist: exposeConstant(V(true)), + trackArtistContributions: reverseReferenceList({ reverse: soupyReverse.input('trackArtistContributionsBy'), }), @@ -97,6 +98,10 @@ export class Artist extends Thing { reverse: soupyReverse.input('albumArtistContributionsBy'), }), + albumTrackArtistContributions: reverseReferenceList({ + reverse: soupyReverse.input('albumTrackArtistContributionsBy'), + }), + albumCoverArtistContributions: reverseReferenceList({ reverse: soupyReverse.input('albumCoverArtistContributionsBy'), }), @@ -125,7 +130,76 @@ export class Artist extends Thing { reverse: soupyReverse.input('groupsCloselyLinkedTo'), }), - totalDuration: artistTotalDuration(), + musicContributions: [ + { + dependencies: [ + 'trackArtistContributions', + 'trackContributorContributions', + ], + + compute: (continuation, { + trackArtistContributions, + trackContributorContributions, + }) => continuation({ + ['#contributions']: [ + ...trackArtistContributions, + ...trackContributorContributions, + ], + }), + }, + + { + dependencies: ['#contributions'], + compute: ({'#contributions': contributions}) => + sortContributionsChronologically( + contributions, + sortAlbumsTracksChronologically), + }, + ], + + artworkContributions: [ + { + dependencies: [ + 'trackCoverArtistContributions', + 'albumCoverArtistContributions', + 'albumWallpaperArtistContributions', + 'albumBannerArtistContributions', + ], + + compute: (continuation, { + trackCoverArtistContributions, + albumCoverArtistContributions, + albumWallpaperArtistContributions, + albumBannerArtistContributions, + }) => continuation({ + ['#contributions']: [ + ...trackCoverArtistContributions, + ...albumCoverArtistContributions, + ...albumWallpaperArtistContributions, + ...albumBannerArtistContributions, + ], + }), + }, + + { + dependencies: ['#contributions'], + compute: ({'#contributions': contributions}) => + sortContributionsChronologically( + contributions, + sortArtworksChronologically), + }, + ], + + totalDuration: [ + withPropertyFromList('musicContributions', V('thing')), + withPropertyFromList('#musicContributions.thing', V('isMainRelease')), + + withFilteredList('musicContributions', '#musicContributions.thing.isMainRelease') + .outputs({'#filteredList': '#mainReleaseContributions'}), + + withContributionListSums('#mainReleaseContributions'), + exposeDependency('#contributionListDuration'), + ], }); static [Thing.getSerializeDescriptors] = ({ @@ -139,8 +213,6 @@ export class Artist extends Thing { hasAvatar: S.id, avatarFileExtension: S.id, - aliasNames: S.id, - tracksAsCommentator: S.toRefs, albumsAsCommentator: S.toRefs, }); @@ -171,17 +243,9 @@ export class Artist extends Thing { // in the original's alias list. This is honestly a bit awkward, but it // avoids artist aliases conflicting with each other when checking for // duplicate directories. - for (const aliasName of originalArtist.aliasNames) { - // These are trouble. We should be accessing aliases' directories - // directly, but artists currently don't expose a reverse reference - // list for aliases. (This is pending a cleanup of "reverse reference" - // behavior in general.) It doesn't actually cause problems *here* - // because alias directories are computed from their names 100% of the - // time, but that *is* an assumption this code makes. - if (aliasName === artist.name) continue; - if (artist.directory === getKebabCase(aliasName)) { - return []; - } + for (const alias of originalArtist.artistAliases) { + if (alias === artist) break; + if (alias.directory === artist.directory) return []; } // And, aliases never return just a blank string. This part is pretty @@ -221,7 +285,10 @@ export class Artist extends Thing { 'Has Avatar': {property: 'hasAvatar'}, 'Avatar File Extension': {property: 'avatarFileExtension'}, - 'Aliases': {property: 'aliasNames'}, + 'Aliases': { + property: 'artistAliases', + transform: parseArtistAliases, + }, 'Dead URLs': {ignore: true}, @@ -239,38 +306,6 @@ export class Artist extends Thing { documentMode: allInOne, documentThing: Artist, - save(results) { - const artists = results; - - const artistRefs = - artists.map(artist => Thing.getReference(artist)); - - const artistAliasNames = - artists.map(artist => artist.aliasNames); - - const artistAliases = - stitchArrays({ - originalArtistRef: artistRefs, - aliasNames: artistAliasNames, - }).flatMap(({originalArtistRef, aliasNames}) => - aliasNames.map(name => { - const alias = new Artist(); - alias.name = name; - alias.isAlias = true; - alias.aliasedArtist = originalArtistRef; - return alias; - })); - - const artistData = [...artists, ...artistAliases]; - - const artworkData = - artistData - .filter(artist => artist.hasAvatar) - .map(artist => artist.avatarArtwork); - - return {artistData, artworkData}; - }, - sort({artistData}) { sortAlphabetically(artistData); }, @@ -287,7 +322,7 @@ export class Artist extends Thing { let aliasedArtist; try { aliasedArtist = this.aliasedArtist.name; - } catch (_error) { + } catch { aliasedArtist = CacheableObject.getUpdateValue(this, 'aliasedArtist'); } |