« 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
diff options
context:
space:
mode:
Diffstat (limited to 'src/data/things')
-rw-r--r--src/data/things/album.js73
-rw-r--r--src/data/things/art-tag.js21
-rw-r--r--src/data/things/artwork.js51
-rw-r--r--src/data/things/content.js122
-rw-r--r--src/data/things/flash.js40
-rw-r--r--src/data/things/index.js2
-rw-r--r--src/data/things/track.js44
7 files changed, 306 insertions, 47 deletions
diff --git a/src/data/things/album.js b/src/data/things/album.js
index 7c85366a..8a25a8ac 100644
--- a/src/data/things/album.js
+++ b/src/data/things/album.js
@@ -3,6 +3,7 @@ export const DATA_ALBUM_DIRECTORY = 'album';
 import * as path from 'node:path';
 import {inspect} from 'node:util';
 
+import CacheableObject from '#cacheable-object';
 import {colors} from '#cli';
 import {input} from '#composite';
 import {traverse} from '#node-utils';
@@ -16,7 +17,9 @@ import {
   parseAdditionalNames,
   parseAnnotatedReferences,
   parseArtwork,
+  parseCommentary,
   parseContributors,
+  parseCreditingSources,
   parseDate,
   parseDimensions,
   parseWallpaperParts,
@@ -32,7 +35,6 @@ import {exitWithoutContribs, withDirectory, withCoverArtDate}
 import {
   additionalFiles,
   additionalNameList,
-  commentary,
   color,
   commentatorArtists,
   constitutibleArtwork,
@@ -59,7 +61,7 @@ import {
   wikiData,
 } from '#composite/wiki-properties';
 
-import {withTracks} from '#composite/things/album';
+import {withHasCoverArt, withTracks} from '#composite/things/album';
 import {withAlbum, withContinueCountingFrom, withStartCountingFrom}
   from '#composite/things/track-section';
 
@@ -69,6 +71,8 @@ export class Album extends Thing {
   static [Thing.getPropertyDescriptors] = ({
     ArtTag,
     Artwork,
+    CommentaryEntry,
+    CreditingSourcesEntry,
     Group,
     Track,
     TrackSection,
@@ -188,9 +192,11 @@ export class Album extends Thing {
     ],
 
     coverArtworks: [
+      withHasCoverArt(),
+
       exitWithoutDependency({
-        dependency: 'coverArtistContribs',
-        mode: input.value('empty'),
+        dependency: '#hasCoverArt',
+        mode: input.value('falsy'),
         value: input.value([]),
       }),
 
@@ -202,8 +208,14 @@ export class Album extends Thing {
     isListedOnHomepage: flag(true),
     isListedInGalleries: flag(true),
 
-    commentary: commentary(),
-    creditSources: commentary(),
+    commentary: thingList({
+      class: input.value(CommentaryEntry),
+    }),
+
+    creditSources: thingList({
+      class: input.value(CreditingSourcesEntry),
+    }),
+
     additionalFiles: additionalFiles(),
 
     trackSections: thingList({
@@ -297,7 +309,11 @@ export class Album extends Thing {
 
     commentatorArtists: commentatorArtists(),
 
-    hasCoverArt: contribsPresent({contribs: 'coverArtistContribs'}),
+    hasCoverArt: [
+      withHasCoverArt(),
+      exposeDependency({dependency: '#hasCoverArt'}),
+    ],
+
     hasWallpaperArt: contribsPresent({contribs: 'wallpaperArtistContribs'}),
     hasBannerArt: contribsPresent({contribs: 'bannerArtistContribs'}),
 
@@ -505,6 +521,8 @@ export class Album extends Thing {
             dateFromThingProperty: 'coverArtDate',
             artistContribsFromThingProperty: 'coverArtistContribs',
             artistContribsArtistProperty: 'albumCoverArtistContributions',
+            artTagsFromThingProperty: 'artTags',
+            referencedArtworksFromThingProperty: 'referencedArtworks',
           }),
       },
 
@@ -588,8 +606,15 @@ export class Album extends Thing {
         transform: parseDimensions,
       },
 
-      'Commentary': {property: 'commentary'},
-      'Credit Sources': {property: 'creditSources'},
+      'Commentary': {
+        property: 'commentary',
+        transform: parseCommentary,
+      },
+
+      'Credit Sources': {
+        property: 'creditSources',
+        transform: parseCreditingSources,
+      },
 
       'Additional Files': {
         property: 'additionalFiles',
@@ -660,7 +685,11 @@ export class Album extends Thing {
       const albumData = [];
       const trackSectionData = [];
       const trackData = [];
+
       const artworkData = [];
+      const commentaryData = [];
+      const creditingSourceData = [];
+      const lyricsData = [];
 
       for (const {header: album, entries} of results) {
         const trackSections = [];
@@ -707,6 +736,13 @@ export class Album extends Thing {
           entry.album = album;
 
           artworkData.push(...entry.trackArtworks);
+          commentaryData.push(...entry.commentary);
+          creditingSourceData.push(...entry.creditSources);
+
+          // TODO: As exposed, Track.lyrics tries to inherit from the main
+          // release, which is impossible before the data's been linked.
+          // We just use the update value here. But it's icky!
+          lyricsData.push(...CacheableObject.getUpdateValue(entry, 'lyrics') ?? []);
         }
 
         closeCurrentTrackSection();
@@ -723,6 +759,9 @@ export class Album extends Thing {
           artworkData.push(album.wallpaperArtwork);
         }
 
+        commentaryData.push(...album.commentary);
+        creditingSourceData.push(...album.creditSources);
+
         album.trackSections = trackSections;
       }
 
@@ -730,7 +769,11 @@ export class Album extends Thing {
         albumData,
         trackSectionData,
         trackData,
+
         artworkData,
+        commentaryData,
+        creditingSourceData,
+        lyricsData,
       };
     },
 
@@ -761,12 +804,18 @@ export class Album extends Thing {
       ];
     }
 
+    // TODO: using trackCover here is obviously, badly wrong
+    // but we ought to refactor banners and wallpapers similarly
+    // (i.e. depend on those intrinsic artwork paths rather than
+    // accessing media.{albumBanner,albumWallpaper} from content
+    // or other code directly)
     return [
-      'media.albumCover',
+      'media.trackCover',
+      this.directory,
 
       (artwork.unqualifiedDirectory
-        ? this.directory + '-' + artwork.unqualifiedDirectory
-        : this.directory),
+        ? 'cover-' + artwork.unqualifiedDirectory
+        : 'cover'),
 
       artwork.fileExtension,
     ];
diff --git a/src/data/things/art-tag.js b/src/data/things/art-tag.js
index 7944beb0..57e156ee 100644
--- a/src/data/things/art-tag.js
+++ b/src/data/things/art-tag.js
@@ -92,22 +92,11 @@ export class ArtTag extends Thing {
       },
     ],
 
-    directlyTaggedInThings: {
-      flags: {expose: true},
-
-      expose: {
-        dependencies: ['this', 'reverse'],
-        compute: ({this: artTag, reverse}) =>
-          sortAlbumsTracksChronologically(
-            [
-              ...reverse.albumsWhoseArtworksFeature(artTag),
-              ...reverse.tracksWhoseArtworksFeature(artTag),
-            ],
-            {getDate: thing => thing.coverArtDate}),
-      },
-    },
+    directlyFeaturedInArtworks: reverseReferenceList({
+      reverse: soupyReverse.input('artworksWhichFeature'),
+    }),
 
-    indirectlyTaggedInThings: [
+    indirectlyFeaturedInArtworks: [
       withAllDescendantArtTags(),
 
       {
@@ -115,7 +104,7 @@ export class ArtTag extends Thing {
         compute: ({'#allDescendantArtTags': allDescendantArtTags}) =>
           unique(
             allDescendantArtTags
-              .flatMap(artTag => artTag.directlyTaggedInThings)),
+              .flatMap(artTag => artTag.directlyFeaturedInArtworks)),
       },
     ],
 
diff --git a/src/data/things/artwork.js b/src/data/things/artwork.js
index 65032d86..2a97fd6d 100644
--- a/src/data/things/artwork.js
+++ b/src/data/things/artwork.js
@@ -3,7 +3,6 @@ import {inspect} from 'node:util';
 import {input} from '#composite';
 import find from '#find';
 import Thing from '#thing';
-import {parseAnnotatedReferences, parseContributors, parseDate} from '#yaml';
 
 import {
   isContentString,
@@ -18,6 +17,13 @@ import {
   validateReferenceList,
 } from '#validators';
 
+import {
+  parseAnnotatedReferences,
+  parseContributors,
+  parseDate,
+  parseDimensions,
+} from '#yaml';
+
 import {withPropertyFromObject} from '#composite/data';
 
 import {
@@ -132,6 +138,11 @@ export class Artwork extends Thing {
         ['#value']: '#dimensionsFromThing',
       }),
 
+      exitWithoutDependency({
+        dependency: 'dimensionsFromThingProperty',
+        value: input.value(null),
+      }),
+
       exposeDependencyOrContinue({
         dependency: '#dimensionsFromThing',
       }),
@@ -179,6 +190,8 @@ export class Artwork extends Thing {
       }),
     ],
 
+    artTagsFromThingProperty: simpleString(),
+
     artTags: [
       withResolvedReferenceList({
         list: input.updateValue({
@@ -194,13 +207,20 @@ export class Artwork extends Thing {
         mode: input.value('empty'),
       }),
 
+      exitWithoutDependency({
+        dependency: 'artTagsFromThingProperty',
+        value: input.value([]),
+      }),
+
       withPropertyFromObject({
         object: 'thing',
-        property: input.value('artTags'),
+        property: 'artTagsFromThingProperty',
+      }).outputs({
+        ['#value']: '#artTags',
       }),
 
       exposeDependencyOrContinue({
-        dependency: '#thing.artTags',
+        dependency: '#artTags',
       }),
 
       exposeConstant({
@@ -208,6 +228,8 @@ export class Artwork extends Thing {
       }),
     ],
 
+    referencedArtworksFromThingProperty: simpleString(),
+
     referencedArtworks: [
       {
         compute: (continuation) => continuation({
@@ -244,13 +266,20 @@ export class Artwork extends Thing {
         mode: input.value('empty'),
       }),
 
+      exitWithoutDependency({
+        dependency: 'referencedArtworksFromThingProperty',
+        value: input.value([]),
+      }),
+
       withPropertyFromObject({
         object: 'thing',
-        property: input.value('referencedArtworks'),
+        property: 'referencedArtworksFromThingProperty',
+      }).outputs({
+        ['#value']: '#referencedArtworks',
       }),
 
       exposeDependencyOrContinue({
-        dependency: '#thing.referencedArtworks',
+        dependency: '#referencedArtworks',
       }),
 
       exposeConstant({
@@ -280,6 +309,11 @@ export class Artwork extends Thing {
       'Directory': {property: 'unqualifiedDirectory'},
       'File Extension': {property: 'fileExtension'},
 
+      'Dimensions': {
+        property: 'dimensions',
+        transform: parseDimensions,
+      },
+
       'Label': {property: 'label'},
       'Source': {property: 'source'},
 
@@ -323,6 +357,13 @@ export class Artwork extends Thing {
 
       date: ({artwork}) => artwork.date,
     },
+
+    artworksWhichFeature: {
+      bindTo: 'artworkData',
+
+      referencing: artwork => [artwork],
+      referenced: artwork => artwork.artTags,
+    },
   };
 
   get path() {
diff --git a/src/data/things/content.js b/src/data/things/content.js
new file mode 100644
index 00000000..7f352795
--- /dev/null
+++ b/src/data/things/content.js
@@ -0,0 +1,122 @@
+import {input} from '#composite';
+import find from '#find';
+import Thing from '#thing';
+import {is, isDate} from '#validators';
+import {parseDate} from '#yaml';
+
+import {contentString, referenceList, simpleDate, soupyFind, thing}
+  from '#composite/wiki-properties';
+
+import {
+  exposeConstant,
+  exposeDependencyOrContinue,
+  exposeUpdateValueOrContinue,
+  withResultOfAvailabilityCheck,
+} from '#composite/control-flow';
+
+import {withWebArchiveDate} from '#composite/things/commentary-entry';
+
+export class ContentEntry extends Thing {
+  static [Thing.getPropertyDescriptors] = ({Artist}) => ({
+    // Update & expose
+
+    thing: thing(),
+
+    artists: referenceList({
+      class: input.value(Artist),
+      find: soupyFind.input('artist'),
+    }),
+
+    artistText: contentString(),
+
+    annotation: contentString(),
+
+    dateKind: {
+      flags: {update: true, expose: true},
+
+      update: {
+        validate: is(...[
+          'sometime',
+          'throughout',
+          'around',
+        ]),
+      },
+    },
+
+    accessKind: [
+      exposeUpdateValueOrContinue({
+        validate: input.value(
+          is(...[
+            'captured',
+            'accessed',
+          ])),
+      }),
+
+      withWebArchiveDate(),
+
+      withResultOfAvailabilityCheck({
+        from: '#webArchiveDate',
+      }),
+
+      {
+        dependencies: ['#availability'],
+        compute: (continuation, {['#availability']: availability}) =>
+          (availability
+            ? continuation.exit('captured')
+            : continuation()),
+      },
+
+      exposeConstant({
+        value: input.value(null),
+      }),
+    ],
+
+    date: simpleDate(),
+
+    secondDate: simpleDate(),
+
+    accessDate: [
+      exposeUpdateValueOrContinue({
+        validate: input.value(isDate),
+      }),
+
+      withWebArchiveDate(),
+
+      exposeDependencyOrContinue({
+        dependency: '#webArchiveDate',
+      }),
+
+      exposeConstant({
+        value: input.value(null),
+      }),
+    ],
+
+    body: contentString(),
+
+    // Update only
+
+    find: soupyFind(),
+  });
+
+  static [Thing.yamlDocumentSpec] = {
+    fields: {
+      'Artists': {property: 'artists'},
+      'Artist Text': {property: 'artistText'},
+
+      'Annotation': {property: 'annotation'},
+
+      'Date Kind': {property: 'dateKind'},
+      'Access Kind': {property: 'accessKind'},
+
+      'Date': {property: 'date', transform: parseDate},
+      'Second Date': {property: 'secondDate', transform: parseDate},
+      'Access Date': {property: 'accessDate', transform: parseDate},
+
+      'Body': {property: 'body'},
+    },
+  };
+}
+
+export class CommentaryEntry extends ContentEntry {}
+export class LyricsEntry extends ContentEntry {}
+export class CreditingSourcesEntry extends ContentEntry {}
diff --git a/src/data/things/flash.js b/src/data/things/flash.js
index ace18af9..dac674dd 100644
--- a/src/data/things/flash.js
+++ b/src/data/things/flash.js
@@ -10,7 +10,9 @@ import {anyOf, isColor, isContentString, isDirectory, isNumber, isString}
 import {
   parseArtwork,
   parseAdditionalNames,
+  parseCommentary,
   parseContributors,
+  parseCreditingSources,
   parseDate,
   parseDimensions,
 } from '#yaml';
@@ -27,7 +29,6 @@ import {
 import {
   additionalNameList,
   color,
-  commentary,
   commentatorArtists,
   constitutibleArtwork,
   contentString,
@@ -41,6 +42,7 @@ import {
   soupyFind,
   soupyReverse,
   thing,
+  thingList,
   urls,
   wikiData,
 } from '#composite/wiki-properties';
@@ -52,6 +54,8 @@ export class Flash extends Thing {
   static [Thing.referenceType] = 'flash';
 
   static [Thing.getPropertyDescriptors] = ({
+    CommentaryEntry,
+    CreditingSourcesEntry,
     Track,
     FlashAct,
     WikiInfo,
@@ -125,8 +129,13 @@ export class Flash extends Thing {
 
     additionalNames: additionalNameList(),
 
-    commentary: commentary(),
-    creditSources: commentary(),
+    commentary: thingList({
+      class: input.value(CommentaryEntry),
+    }),
+
+    creditSources: thingList({
+      class: input.value(CreditingSourcesEntry),
+    }),
 
     // Update only
 
@@ -240,8 +249,15 @@ export class Flash extends Thing {
         transform: parseContributors,
       },
 
-      'Commentary': {property: 'commentary'},
-      'Credit Sources': {property: 'creditSources'},
+      'Commentary': {
+        property: 'commentary',
+        transform: parseCommentary,
+      },
+
+      'Credit Sources': {
+        property: 'creditSources',
+        transform: parseCreditingSources,
+      },
 
       'Review Points': {ignore: true},
     },
@@ -441,8 +457,18 @@ export class FlashSide extends Thing {
       const flashSideData = results.filter(x => x instanceof FlashSide);
 
       const artworkData = flashData.map(flash => flash.coverArtwork);
-
-      return {flashData, flashActData, flashSideData, artworkData};
+      const commentaryData = flashData.flatMap(flash => flash.commentary);
+      const creditingSourceData = flashData.flatMap(flash => flash.creditSources);
+
+      return {
+        flashData,
+        flashActData,
+        flashSideData,
+
+        artworkData,
+        commentaryData,
+        creditingSourceData,
+      };
     },
 
     sort({flashData}) {
diff --git a/src/data/things/index.js b/src/data/things/index.js
index 96cec88e..b832ab75 100644
--- a/src/data/things/index.js
+++ b/src/data/things/index.js
@@ -13,6 +13,7 @@ import * as albumClasses from './album.js';
 import * as artTagClasses from './art-tag.js';
 import * as artistClasses from './artist.js';
 import * as artworkClasses from './artwork.js';
+import * as contentClasses from './content.js';
 import * as contributionClasses from './contribution.js';
 import * as flashClasses from './flash.js';
 import * as groupClasses from './group.js';
@@ -29,6 +30,7 @@ const allClassLists = {
   'art-tag.js': artTagClasses,
   'artist.js': artistClasses,
   'artwork.js': artworkClasses,
+  'content.js': contentClasses,
   'contribution.js': contributionClasses,
   'flash.js': flashClasses,
   'group.js': groupClasses,
diff --git a/src/data/things/track.js b/src/data/things/track.js
index 2d2cc002..ae7be170 100644
--- a/src/data/things/track.js
+++ b/src/data/things/track.js
@@ -12,10 +12,13 @@ import {
   parseAdditionalNames,
   parseAnnotatedReferences,
   parseArtwork,
+  parseCommentary,
   parseContributors,
+  parseCreditingSources,
   parseDate,
   parseDimensions,
   parseDuration,
+  parseLyrics,
 } from '#yaml';
 
 import {withPropertyFromObject} from '#composite/data';
@@ -37,7 +40,6 @@ import {
 import {
   additionalFiles,
   additionalNameList,
-  commentary,
   commentatorArtists,
   constitutibleArtworkList,
   contentString,
@@ -56,6 +58,7 @@ import {
   soupyFind,
   soupyReverse,
   thing,
+  thingList,
   urls,
   wikiData,
 } from '#composite/wiki-properties';
@@ -86,7 +89,10 @@ export class Track extends Thing {
     Album,
     ArtTag,
     Artwork,
+    CommentaryEntry,
+    CreditingSourcesEntry,
     Flash,
+    LyricsEntry,
     TrackSection,
     WikiInfo,
   }) => ({
@@ -215,12 +221,23 @@ export class Track extends Thing {
       dimensions(),
     ],
 
-    commentary: commentary(),
-    creditSources: commentary(),
+    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(),
-      contentString(),
+
+      thingList({
+        class: input.value(LyricsEntry),
+      }),
     ],
 
     additionalFiles: additionalFiles(),
@@ -480,9 +497,20 @@ export class Track extends Thing {
 
       'Always Reference By Directory': {property: 'alwaysReferenceByDirectory'},
 
-      'Lyrics': {property: 'lyrics'},
-      'Commentary': {property: 'commentary'},
-      'Credit Sources': {property: 'creditSources'},
+      'Lyrics': {
+        property: 'lyrics',
+        transform: parseLyrics,
+      },
+
+      'Commentary': {
+        property: 'commentary',
+        transform: parseCommentary,
+      },
+
+      'Credit Sources': {
+        property: 'creditSources',
+        transform: parseCreditingSources,
+      },
 
       'Additional Files': {
         property: 'additionalFiles',
@@ -533,6 +561,8 @@ export class Track extends Thing {
             dimensionsFromThingProperty: 'coverArtDimensions',
             fileExtensionFromThingProperty: 'coverArtFileExtension',
             dateFromThingProperty: 'coverArtDate',
+            artTagsFromThingProperty: 'artTags',
+            referencedArtworksFromThingProperty: 'referencedArtworks',
             artistContribsFromThingProperty: 'coverArtistContribs',
             artistContribsArtistProperty: 'trackCoverArtistContributions',
           }),