« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/data/things/track.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/data/things/track.js')
-rw-r--r--src/data/things/track.js496
1 files changed, 302 insertions, 194 deletions
diff --git a/src/data/things/track.js b/src/data/things/track.js
index 57aaa90d..e652de52 100644
--- a/src/data/things/track.js
+++ b/src/data/things/track.js
@@ -15,6 +15,7 @@ import {
   parseCommentary,
   parseContributors,
   parseCreditingSources,
+  parseReferencingSources,
   parseDate,
   parseDimensions,
   parseDuration,
@@ -38,11 +39,8 @@ import {
 } from '#composite/wiki-data';
 
 import {
-  additionalFiles,
-  additionalNameList,
   commentatorArtists,
   constitutibleArtworkList,
-  contentString,
   contributionList,
   dimensions,
   directory,
@@ -86,17 +84,24 @@ export class Track extends Thing {
   static [Thing.referenceType] = 'track';
 
   static [Thing.getPropertyDescriptors] = ({
+    AdditionalFile,
+    AdditionalName,
     Album,
     ArtTag,
     Artwork,
     CommentaryEntry,
     CreditingSourcesEntry,
-    Flash,
     LyricsEntry,
-    TrackSection,
+    ReferencingSourcesEntry,
     WikiInfo,
   }) => ({
-    // Update & expose
+    // > Update & expose - Internal relationships
+
+    album: thing({
+      class: input.value(Album),
+    }),
+
+    // > Update & expose - Identifying metadata
 
     name: name('Unnamed Track'),
 
@@ -130,19 +135,94 @@ export class Track extends Thing {
       })
     ],
 
-    album: thing({
-      class: input.value(Album),
-    }),
+    alwaysReferenceByDirectory: [
+      withAlwaysReferenceByDirectory(),
+      exposeDependency({dependency: '#alwaysReferenceByDirectory'}),
+    ],
 
-    additionalNames: additionalNameList(),
+    mainReleaseTrack: singleReference({
+      class: input.value(Track),
+      find: soupyFind.input('track'),
+    }),
 
     bandcampTrackIdentifier: simpleString(),
     bandcampArtworkIdentifier: simpleString(),
 
-    duration: duration(),
-    urls: urls(),
+    additionalNames: thingList({
+      class: input.value(AdditionalName),
+    }),
+
     dateFirstReleased: simpleDate(),
 
+    // > Update & expose - Credits and contributors
+
+    artistContribs: [
+      inheritContributionListFromMainRelease(),
+
+      withDate(),
+
+      withResolvedContribs({
+        from: input.updateValue({validate: isContributionList}),
+        thingProperty: input.thisProperty(),
+        artistProperty: input.value('trackArtistContributions'),
+        date: '#date',
+      }).outputs({
+        '#resolvedContribs': '#artistContribs',
+      }),
+
+      exposeDependencyOrContinue({
+        dependency: '#artistContribs',
+        mode: input.value('empty'),
+      }),
+
+      withPropertyFromAlbum({
+        property: input.value('artistContribs'),
+      }),
+
+      withRecontextualizedContributionList({
+        list: '#album.artistContribs',
+        artistProperty: input.value('trackArtistContributions'),
+      }),
+
+      withRedatedContributionList({
+        list: '#album.artistContribs',
+        date: '#date',
+      }),
+
+      exposeDependency({dependency: '#album.artistContribs'}),
+    ],
+
+    contributorContribs: [
+      inheritContributionListFromMainRelease(),
+
+      withDate(),
+
+      contributionList({
+        date: '#date',
+        artistProperty: input.value('trackContributorContributions'),
+      }),
+    ],
+
+    // > Update & expose - General configuration
+
+    countInArtistTotals: [
+      exposeUpdateValueOrContinue({
+        validate: input.value(isBoolean),
+      }),
+
+      withPropertyFromAlbum({
+        property: input.value('countTracksInArtistTotals'),
+      }),
+
+      exposeDependency({dependency: '#album.countTracksInArtistTotals'}),
+    ],
+
+    disableUniqueCoverArt: flag(),
+
+    // > Update & expose - General metadata
+
+    duration: duration(),
+
     color: [
       exposeUpdateValueOrContinue({
         validate: input.value(isColor),
@@ -164,37 +244,27 @@ export class Track extends Thing {
       exposeDependency({dependency: '#album.color'}),
     ],
 
-    alwaysReferenceByDirectory: [
-      withAlwaysReferenceByDirectory(),
-      exposeDependency({dependency: '#alwaysReferenceByDirectory'}),
-    ],
-
-    // 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: 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: [
-      exitWithoutUniqueCoverArt(),
+    urls: urls(),
 
-      exposeUpdateValueOrContinue({
-        validate: input.value(isFileExtension),
-      }),
+    // > Update & expose - Artworks
 
-      withPropertyFromAlbum({
-        property: input.value('trackCoverArtFileExtension'),
+    trackArtworks: [
+      exitWithoutUniqueCoverArt({
+        value: input.value([]),
       }),
 
-      exposeDependencyOrContinue({dependency: '#album.trackCoverArtFileExtension'}),
+      constitutibleArtworkList.fromYAMLFieldSpec
+        .call(this, 'Track Artwork'),
+    ],
 
-      exposeConstant({
-        value: input.value('jpg'),
+    coverArtistContribs: [
+      withCoverArtistContribs({
+        from: input.updateValue({
+          validate: isContributionList,
+        }),
       }),
+
+      exposeDependency({dependency: '#coverArtistContribs'}),
     ],
 
     coverArtDate: [
@@ -207,105 +277,59 @@ export class Track extends Thing {
       exposeDependency({dependency: '#trackArtDate'}),
     ],
 
-    coverArtDimensions: [
+    coverArtFileExtension: [
       exitWithoutUniqueCoverArt(),
 
-      exposeUpdateValueOrContinue(),
+      exposeUpdateValueOrContinue({
+        validate: input.value(isFileExtension),
+      }),
 
       withPropertyFromAlbum({
-        property: input.value('trackDimensions'),
+        property: input.value('trackCoverArtFileExtension'),
       }),
 
-      exposeDependencyOrContinue({dependency: '#album.trackDimensions'}),
-
-      dimensions(),
-    ],
-
-    commentary: thingList({
-      class: input.value(CommentaryEntry),
-    }),
-
-    creditSources: thingList({
-      class: input.value(CreditingSourcesEntry),
-    }),
-
-    lyrics: [
-      // TODO: Inherited lyrics are literally the same objects, so of course
-      // their .thing properties aren't going to point back to this one, and
-      // certainly couldn't be recontextualized...
-      inheritFromMainRelease(),
+      exposeDependencyOrContinue({dependency: '#album.trackCoverArtFileExtension'}),
 
-      thingList({
-        class: input.value(LyricsEntry),
+      exposeConstant({
+        value: input.value('jpg'),
       }),
     ],
 
-    additionalFiles: additionalFiles(),
-    sheetMusicFiles: additionalFiles(),
-    midiProjectFiles: additionalFiles(),
-
-    mainReleaseTrack: singleReference({
-      class: input.value(Track),
-      find: soupyFind.input('track'),
-    }),
-
-    artistContribs: [
-      inheritContributionListFromMainRelease(),
-
-      withDate(),
-
-      withResolvedContribs({
-        from: input.updateValue({validate: isContributionList}),
-        thingProperty: input.thisProperty(),
-        artistProperty: input.value('trackArtistContributions'),
-        date: '#date',
-      }).outputs({
-        '#resolvedContribs': '#artistContribs',
-      }),
+    coverArtDimensions: [
+      exitWithoutUniqueCoverArt(),
 
-      exposeDependencyOrContinue({
-        dependency: '#artistContribs',
-        mode: input.value('empty'),
-      }),
+      exposeUpdateValueOrContinue(),
 
       withPropertyFromAlbum({
-        property: input.value('artistContribs'),
-      }),
-
-      withRecontextualizedContributionList({
-        list: '#album.artistContribs',
-        artistProperty: input.value('trackArtistContributions'),
+        property: input.value('trackDimensions'),
       }),
 
-      withRedatedContributionList({
-        list: '#album.artistContribs',
-        date: '#date',
-      }),
+      exposeDependencyOrContinue({dependency: '#album.trackDimensions'}),
 
-      exposeDependency({dependency: '#album.artistContribs'}),
+      dimensions(),
     ],
 
-    contributorContribs: [
-      inheritContributionListFromMainRelease(),
-
-      withDate(),
+    artTags: [
+      exitWithoutUniqueCoverArt({
+        value: input.value([]),
+      }),
 
-      contributionList({
-        date: '#date',
-        artistProperty: input.value('trackContributorContributions'),
+      referenceList({
+        class: input.value(ArtTag),
+        find: soupyFind.input('artTag'),
       }),
     ],
 
-    coverArtistContribs: [
-      withCoverArtistContribs({
-        from: input.updateValue({
-          validate: isContributionList,
-        }),
+    referencedArtworks: [
+      exitWithoutUniqueCoverArt({
+        value: input.value([]),
       }),
 
-      exposeDependency({dependency: '#coverArtistContribs'}),
+      referencedArtworkList(),
     ],
 
+    // > Update & expose - Referenced tracks
+
     referencedTracks: [
       inheritFromMainRelease({
         notFoundValue: input.value([]),
@@ -328,35 +352,46 @@ export class Track extends Thing {
       }),
     ],
 
-    trackArtworks: [
-      exitWithoutUniqueCoverArt({
-        value: input.value([]),
-      }),
+    // > Update & expose - Additional files
 
-      constitutibleArtworkList.fromYAMLFieldSpec
-        .call(this, 'Track Artwork'),
-    ],
+    additionalFiles: thingList({
+      class: input.value(AdditionalFile),
+    }),
 
-    artTags: [
-      exitWithoutUniqueCoverArt({
-        value: input.value([]),
-      }),
+    sheetMusicFiles: thingList({
+      class: input.value(AdditionalFile),
+    }),
 
-      referenceList({
-        class: input.value(ArtTag),
-        find: soupyFind.input('artTag'),
+    midiProjectFiles: thingList({
+      class: input.value(AdditionalFile),
+    }),
+
+    // > Update & expose - Content entries
+
+    lyrics: [
+      // TODO: Inherited lyrics are literally the same objects, so of course
+      // their .thing properties aren't going to point back to this one, and
+      // certainly couldn't be recontextualized...
+      inheritFromMainRelease(),
+
+      thingList({
+        class: input.value(LyricsEntry),
       }),
     ],
 
-    referencedArtworks: [
-      exitWithoutUniqueCoverArt({
-        value: input.value([]),
-      }),
+    commentary: thingList({
+      class: input.value(CommentaryEntry),
+    }),
 
-      referencedArtworkList(),
-    ],
+    creditingSources: thingList({
+      class: input.value(CreditingSourcesEntry),
+    }),
 
-    // Update only
+    referencingSources: thingList({
+      class: input.value(ReferencingSourcesEntry),
+    }),
+
+    // > Update only
 
     find: soupyFind(),
     reverse: soupyReverse(),
@@ -376,7 +411,7 @@ export class Track extends Thing {
       class: input.value(WikiInfo),
     }),
 
-    // Expose only
+    // > Expose only
 
     commentatorArtists: commentatorArtists(),
 
@@ -428,6 +463,16 @@ export class Track extends Thing {
       exposeDependency({dependency: '#otherReleases'}),
     ],
 
+    groups: [
+      withPropertyFromAlbum({
+        property: input.value('groups'),
+      }),
+
+      exposeDependency({
+        dependency: '#album.groups',
+      }),
+    ],
+
     referencedByTracks: reverseReferenceList({
       reverse: soupyReverse.input('tracksWhichReference'),
     }),
@@ -443,14 +488,13 @@ export class Track extends Thing {
 
   static [Thing.yamlDocumentSpec] = {
     fields: {
+      // Identifying metadata
+
       'Track': {property: 'name'},
       'Directory': {property: 'directory'},
       'Suffix Directory': {property: 'suffixDirectoryFromAlbum'},
-
-      'Additional Names': {
-        property: 'additionalNames',
-        transform: parseAdditionalNames,
-      },
+      'Always Reference By Directory': {property: 'alwaysReferenceByDirectory'},
+      'Main Release': {property: 'mainReleaseTrack'},
 
       'Bandcamp Track ID': {
         property: 'bandcampTrackIdentifier',
@@ -462,17 +506,71 @@ export class Track extends Thing {
         transform: String,
       },
 
+      'Additional Names': {
+        property: 'additionalNames',
+        transform: parseAdditionalNames,
+      },
+
+      'Date First Released': {
+        property: 'dateFirstReleased',
+        transform: parseDate,
+      },
+
+      // Credits and contributors
+
+      'Artists': {
+        property: 'artistContribs',
+        transform: parseContributors,
+      },
+
+      'Contributors': {
+        property: 'contributorContribs',
+        transform: parseContributors,
+      },
+
+      // General configuration
+
+      'Count In Artist Totals': {property: 'countInArtistTotals'},
+
+      'Has Cover Art': {
+        property: 'disableUniqueCoverArt',
+        transform: value =>
+          (typeof value === 'boolean'
+            ? !value
+            : value),
+      },
+
+      // General metadata
+
       'Duration': {
         property: 'duration',
         transform: parseDuration,
       },
 
       'Color': {property: 'color'},
+
       'URLs': {property: 'urls'},
 
-      'Date First Released': {
-        property: 'dateFirstReleased',
-        transform: parseDate,
+      // Artworks
+
+      'Track Artwork': {
+        property: 'trackArtworks',
+        transform:
+          parseArtwork({
+            thingProperty: 'trackArtworks',
+            dimensionsFromThingProperty: 'coverArtDimensions',
+            fileExtensionFromThingProperty: 'coverArtFileExtension',
+            dateFromThingProperty: 'coverArtDate',
+            artTagsFromThingProperty: 'artTags',
+            referencedArtworksFromThingProperty: 'referencedArtworks',
+            artistContribsFromThingProperty: 'coverArtistContribs',
+            artistContribsArtistProperty: 'trackCoverArtistContributions',
+          }),
+      },
+
+      'Cover Artists': {
+        property: 'coverArtistContribs',
+        transform: parseContributors,
       },
 
       'Cover Art Date': {
@@ -487,30 +585,19 @@ export class Track extends Thing {
         transform: parseDimensions,
       },
 
-      'Has Cover Art': {
-        property: 'disableUniqueCoverArt',
-        transform: value =>
-          (typeof value === 'boolean'
-            ? !value
-            : value),
-      },
-
-      'Always Reference By Directory': {property: 'alwaysReferenceByDirectory'},
+      'Art Tags': {property: 'artTags'},
 
-      'Lyrics': {
-        property: 'lyrics',
-        transform: parseLyrics,
+      'Referenced Artworks': {
+        property: 'referencedArtworks',
+        transform: parseAnnotatedReferences,
       },
 
-      'Commentary': {
-        property: 'commentary',
-        transform: parseCommentary,
-      },
+      // Referenced tracks
 
-      'Credit Sources': {
-        property: 'creditSources',
-        transform: parseCreditingSources,
-      },
+      'Referenced Tracks': {property: 'referencedTracks'},
+      'Sampled Tracks': {property: 'sampledTracks'},
+
+      // Additional files
 
       'Additional Files': {
         property: 'additionalFiles',
@@ -527,54 +614,41 @@ export class Track extends Thing {
         transform: parseAdditionalFiles,
       },
 
-      'Main Release': {property: 'mainReleaseTrack'},
-      'Referenced Tracks': {property: 'referencedTracks'},
-      'Sampled Tracks': {property: 'sampledTracks'},
-
-      'Referenced Artworks': {
-        property: 'referencedArtworks',
-        transform: parseAnnotatedReferences,
-      },
+      // Content entries
 
-      'Franchises': {ignore: true},
-      'Inherit Franchises': {ignore: true},
-
-      'Artists': {
-        property: 'artistContribs',
-        transform: parseContributors,
+      'Lyrics': {
+        property: 'lyrics',
+        transform: parseLyrics,
       },
 
-      'Contributors': {
-        property: 'contributorContribs',
-        transform: parseContributors,
+      'Commentary': {
+        property: 'commentary',
+        transform: parseCommentary,
       },
 
-      'Cover Artists': {
-        property: 'coverArtistContribs',
-        transform: parseContributors,
+      'Crediting Sources': {
+        property: 'creditingSources',
+        transform: parseCreditingSources,
       },
 
-      'Track Artwork': {
-        property: 'trackArtworks',
-        transform:
-          parseArtwork({
-            thingProperty: 'trackArtworks',
-            dimensionsFromThingProperty: 'coverArtDimensions',
-            fileExtensionFromThingProperty: 'coverArtFileExtension',
-            dateFromThingProperty: 'coverArtDate',
-            artTagsFromThingProperty: 'artTags',
-            referencedArtworksFromThingProperty: 'referencedArtworks',
-            artistContribsFromThingProperty: 'coverArtistContribs',
-            artistContribsArtistProperty: 'trackCoverArtistContributions',
-          }),
+      'Referencing Sources': {
+        property: 'referencingSources',
+        transform: parseReferencingSources,
       },
 
-      'Art Tags': {property: 'artTags'},
+      // Shenanigans
 
+      'Franchises': {ignore: true},
+      'Inherit Franchises': {ignore: true},
       'Review Points': {ignore: true},
     },
 
     invalidFieldCombinations: [
+      {message: `Secondary releases never count in artist totals`, fields: [
+        'Main Release',
+        'Count In Artist Totals',
+      ]},
+
       {message: `Secondary releases inherit references from the main one`, fields: [
         'Main Release',
         'Referenced Tracks',
@@ -736,6 +810,16 @@ export class Track extends Thing {
   // Track YAML loading is handled in album.js.
   static [Thing.getYamlLoadingSpec] = null;
 
+  getOwnAdditionalFilePath(_file, filename) {
+    if (!this.album) return null;
+
+    return [
+      'media.albumAdditionalFile',
+      this.album.directory,
+      filename,
+    ];
+  }
+
   getOwnArtworkPath(artwork) {
     if (!this.album) return null;
 
@@ -751,6 +835,30 @@ export class Track extends Thing {
     ];
   }
 
+  countOwnContributionInContributionTotals(_contrib) {
+    if (!this.countInArtistTotals) {
+      return false;
+    }
+
+    if (this.isSecondaryRelease) {
+      return false;
+    }
+
+    return true;
+  }
+
+  countOwnContributionInDurationTotals(_contrib) {
+    if (!this.countInArtistTotals) {
+      return false;
+    }
+
+    if (this.isSecondaryRelease) {
+      return false;
+    }
+
+    return true;
+  }
+
   [inspect.custom](depth) {
     const parts = [];