« 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/album
diff options
context:
space:
mode:
Diffstat (limited to 'src/data/things/album')
-rw-r--r--src/data/things/album/Album.js319
-rw-r--r--src/data/things/album/TrackSection.js16
2 files changed, 251 insertions, 84 deletions
diff --git a/src/data/things/album/Album.js b/src/data/things/album/Album.js
index f07d552c..4f3dd770 100644
--- a/src/data/things/album/Album.js
+++ b/src/data/things/album/Album.js
@@ -1,14 +1,18 @@
 import {input, V} from '#composite';
 import {empty} from '#sugar';
 import Thing from '#thing';
+import {getKebabCase} from '#wiki-data';
 
 import {
+  anyOf,
   is,
+  isBoolean,
   isContributionList,
   isDate,
   isExcludingURLsReason,
   isDirectory,
   isNumber,
+  isString,
 } from '#validators';
 
 import {
@@ -29,8 +33,6 @@ import {
 } from '#yaml';
 
 import {withFlattenedList, withPropertyFromList} from '#composite/data';
-import {withRecontextualizedContributionList, withResolvedContribs}
-  from '#composite/wiki-data';
 
 import {
   exitWithoutDependency,
@@ -41,6 +43,12 @@ import {
 } from '#composite/control-flow';
 
 import {
+  withDirectory,
+  withRecontextualizedContributionList,
+  withResolvedContribs,
+} from '#composite/wiki-data';
+
+import {
   color,
   commentatorArtists,
   constitutibleArtwork,
@@ -48,7 +56,6 @@ import {
   contentString,
   contributionList,
   dimensions,
-  directory,
   fileExtension,
   flag,
   hasArtwork,
@@ -77,7 +84,6 @@ export class Album extends Thing {
   ];
 
   static [Thing.getPropertyDescriptors] = ({
-    AdditionalFile,
     AdditionalName,
     AlbumArtistContribution,
     AlbumBannerArtistContribution,
@@ -87,6 +93,7 @@ export class Album extends Thing {
     CommentaryEntry,
     CreditingSourcesEntry,
     Group,
+    MiscellaneousAdditionalFile,
     MusicVideo,
     TrackArtistContribution,
     TrackSection,
@@ -99,16 +106,89 @@ export class Album extends Thing {
     // > Update & expose - Identifying metadata
 
     name: name(V('Unnamed Album')),
-    directory: directory(),
+    nameDetail: simpleString(),
+
+    nameDetailForTracks: {
+      flags: {update: true, expose: true},
+
+      update: {validate: isString},
+
+      expose: {
+        dependencies: ['name', 'nameDetail'],
+        transform: (value, {name, nameDetail}) =>
+          (value
+            ? value
+         : nameDetail
+            ? `${name}, ${nameDetail}`
+            : name),
+      },
+    },
 
-    directorySuffix: [
+    directory: [
+      withDirectory({
+        directory:
+          input.updateValue({
+            validate(value) {
+              isDirectory(value);
+
+              if (value === 'vgm') {
+                throw new Error(
+                  `"vgm" is a reserved directory and can't be used albums`);
+              }
+
+              return true;
+            },
+          }),
+
+        suffix: 'suffixDirectory',
+      }),
+
+      exposeDependency('#directory'),
+    ],
+
+    suffixDirectory: [
+      exposeUpdateValueOrContinue({
+        validate: input.value(
+          anyOf(is(false), isDirectory)),
+      }),
+
+      {
+        transform: (value, continuation) =>
+          (value === false
+            ? null
+            : continuation()),
+      },
+
+      {
+        dependencies: ['nameDetail'],
+        compute(continuation, {nameDetail}) {
+          if (nameDetail) {
+            return getKebabCase(nameDetail);
+          }
+
+          return continuation();
+        },
+      },
+
+      exposeConstant(V(null)),
+    ],
+
+    directorySuffixForTracks: [
       exposeUpdateValueOrContinue({
         validate: input.value(isDirectory),
       }),
 
-      exposeDependency('directory'),
+      {
+        dependencies: ['directory', 'name', 'nameDetailForTracks'],
+        compute: ({directory, name, nameDetailForTracks}) =>
+          (nameDetailForTracks === name
+            ? directory
+            : getKebabCase(nameDetailForTracks)),
+      },
     ],
 
+    suffixTrackDirectoriesByDefault: flag(V(false)),
+
     alwaysReferenceByDirectory: flag(V(false)),
 
     referenceTracksByDirectory: [
@@ -125,14 +205,13 @@ export class Album extends Thing {
       exposeConstant(V('normally')),
     ],
 
-    suffixTrackDirectories: flag(V(false)),
-
     style: [
       exposeUpdateValueOrContinue({
         validate: input.value(is(...[
           'album',
           'single',
           'meta',
+          'in-game vgm',
         ])),
       }),
 
@@ -144,7 +223,8 @@ export class Album extends Thing {
 
     additionalNames: thingList(V(AdditionalName)),
 
-    date: simpleDate(),
+    dateReleased: simpleDate(),
+    datePosted: simpleDate(),
     dateAddedToWiki: simpleDate(),
 
     // > Update & expose - Credits and contributors
@@ -188,11 +268,37 @@ export class Album extends Thing {
     isListedOnHomepage: flag(V(true)),
     isListedInGalleries: flag(V(true)),
 
-    hasTrackNumbers: flag(V(true)),
+    hasTrackNumbers: [
+      exposeUpdateValueOrContinue({
+        validate: input.value(isBoolean),
+      }),
+
+      {
+        dependencies: ['style'],
+        compute: ({style}) =>
+          (style === 'in-game vgm'
+            ? false
+            : true),
+      },
+    ],
+
     showAlbumInTracksWithoutArtists: flag(V(false)),
     showTrackSectionInNavBar: flag(V(false)),
     showArtistsInTrackList: flag(V(true)),
-    hideDuration: flag(V(false)),
+
+    hideDuration: [
+      exposeUpdateValueOrContinue({
+        validate: input.value(isBoolean),
+      }),
+
+      {
+        dependencies: ['style'],
+        compute: ({style}) =>
+          (style === 'in-game vgm'
+            ? true
+            : false),
+      },
+    ],
 
     // > Update & expose - General metadata
 
@@ -212,6 +318,11 @@ export class Album extends Thing {
         .call(this, 'Cover Artwork'),
     ],
 
+    hasCoverArt: hasArtwork({
+      contribs: '_coverArtistContribs',
+      artworks: '_coverArtworks',
+    }),
+
     coverArtistContribs: contributionList({
       date: 'coverArtDate',
       artistProperty: input.value('albumCoverArtistContributions'),
@@ -300,6 +411,11 @@ export class Album extends Thing {
         .call(this, 'Wallpaper Artwork'),
     ],
 
+    hasWallpaperArt: hasArtwork({
+      contribs: '_wallpaperArtistContribs',
+      artwork: '_wallpaperArtwork',
+    }),
+
     wallpaperArtistContribs: contributionList({
       class: input.value(AlbumWallpaperArtistContribution),
       date: 'coverArtDate',
@@ -343,6 +459,11 @@ export class Album extends Thing {
         .call(this, 'Banner Artwork'),
     ],
 
+    hasBannerArt: hasArtwork({
+      contribs: '_bannerArtistContribs',
+      artwork: '_bannerArtwork',
+    }),
+
     bannerArtistContribs: contributionList({
       class: input.value(AlbumBannerArtistContribution),
       date: 'coverArtDate',
@@ -394,7 +515,7 @@ export class Album extends Thing {
 
     // > Update & expose - Additional files
 
-    additionalFiles: thingList(V(AdditionalFile)),
+    additionalFiles: thingList(V(MiscellaneousAdditionalFile)),
 
     // > Update only
 
@@ -411,22 +532,31 @@ export class Album extends Thing {
 
     isAlbum: exposeConstant(V(true)),
 
-    commentatorArtists: commentatorArtists(),
+    nameForReferencingAcrossWiki: [
+      {
+        dependencies: ['alwaysReferenceByDirectory'],
+        compute: (continuation, {alwaysReferenceByDirectory}) =>
+          (alwaysReferenceByDirectory
+            ? continuation.exit(null)
+            : continuation()),
+      },
 
-    hasCoverArt: hasArtwork({
-      contribs: '_coverArtistContribs',
-      artworks: '_coverArtworks',
-    }),
+      exposeDependency('nameForSorting'),
+    ],
 
-    hasWallpaperArt: hasArtwork({
-      contribs: '_wallpaperArtistContribs',
-      artwork: '_wallpaperArtwork',
-    }),
+    nameForSorting: [
+      {
+        dependencies: ['name', 'nameDetail'],
+        compute: (continuation, {name, nameDetail}) =>
+          (nameDetail
+            ? continuation.exit(`${name} (${nameDetail})`)
+            : continuation()),
+      },
 
-    hasBannerArt: hasArtwork({
-      contribs: '_bannerArtistContribs',
-      artwork: '_bannerArtwork',
-    }),
+      exposeDependency('name'),
+    ],
+
+    commentatorArtists: commentatorArtists(),
 
     tracks: [
       exitWithoutDependency('trackSections', V([])),
@@ -435,6 +565,27 @@ export class Album extends Thing {
       withFlattenedList('#trackSections.tracks'),
       exposeDependency('#flattenedList'),
     ],
+
+    date: [
+      exposeDependencyOrContinue('dateReleased'),
+      exposeDependencyOrContinue('datePosted'),
+      exposeConstant(V(null)),
+    ],
+
+    dateStyle: [
+      exitWithoutDependency('date'),
+
+      {
+        dependencies: ['_dateReleased', '_datePosted'],
+        compute: ({
+          ['_dateReleased']: dateReleased,
+          ['_datePosted']: datePosted,
+        }) =>
+          (dateReleased ? 'released'
+         : datePosted   ? 'posted'
+                        : null),
+      },
+    ]
   });
 
   static [Thing.getSerializeDescriptors] = ({
@@ -594,9 +745,15 @@ export class Album extends Thing {
       // Identifying metadata
 
       'Album': {property: 'name'},
+      'Name Detail': {property: 'nameDetail'},
+      'Name Detail For Tracks': {property: 'nameDetailForTracks'},
+
       'Directory': {property: 'directory'},
-      'Directory Suffix': {property: 'directorySuffix'},
-      'Suffix Track Directories': {property: 'suffixTrackDirectories'},
+      'Suffix Own Directory': {property: 'suffixDirectory'},
+
+      'Directory Suffix': {property: 'directorySuffixForTracks'},
+      'Suffix Track Directories': {property: 'suffixTrackDirectoriesByDefault'},
+
       'Always Reference By Directory': {property: 'alwaysReferenceByDirectory'},
 
       'Reference Tracks By Directory': {property: 'referenceTracksByDirectory'},
@@ -623,15 +780,9 @@ export class Album extends Thing {
         transform: parseAdditionalNames,
       },
 
-      'Date': {
-        property: 'date',
-        transform: parseDate,
-      },
-
-      'Date Added': {
-        property: 'dateAddedToWiki',
-        transform: parseDate,
-      },
+      'Date': {property: 'dateReleased', transform: parseDate},
+      'Date Posted': {property: 'datePosted', transform: parseDate},
+      'Date Added': {property: 'dateAddedToWiki', transform: parseDate},
 
       // Credits and contributors
 
@@ -722,6 +873,8 @@ export class Album extends Thing {
           }),
       },
 
+      'Has Cover Art': {property: 'hasCoverArt'},
+
       'Cover Artists': {
         property: 'coverArtistContribs',
         transform: parseContributors,
@@ -752,6 +905,8 @@ export class Album extends Thing {
         transform: parseDimensions,
       },
 
+      'Has Wallpaper Art': {property: 'hasWallpaperArt'},
+
       'Wallpaper Artists': {
         property: 'wallpaperArtistContribs',
         transform: parseContributors,
@@ -764,6 +919,8 @@ export class Album extends Thing {
         transform: parseWallpaperParts,
       },
 
+      'Has Banner Art': {property: 'hasBannerArt'},
+
       'Banner Artists': {
         property: 'bannerArtistContribs',
         transform: parseContributors,
@@ -852,62 +1009,72 @@ export class Album extends Thing {
     ],
   };
 
-  getOwnAdditionalFilePath(_file, filename) {
-    return [
-      'media.albumAdditionalFile',
-      this.directory,
-      filename,
-    ];
+  getOwnAdditionalFilePath(file, filename) {
+    if (file.folder) {
+      return [
+        'media.albumAdditionalFileInFolder',
+        this.directory,
+        file.folder,
+        filename,
+      ];
+    } else {
+      return [
+        'media.albumAdditionalFile',
+        this.directory,
+        filename,
+      ];
+    }
   }
 
   getOwnArtworkPath(artwork) {
+    const ext = artwork.fileExtension;
+
     if (artwork === this.bannerArtwork) {
-      return [
-        'media.albumBanner',
-        this.directory,
-        artwork.fileExtension,
-      ];
+      return this.getAlbumArtPath(`banner.${ext}`);
     }
 
     if (artwork === this.wallpaperArtwork) {
-      if (!empty(this.wallpaperParts)) {
+      if (empty(this.wallpaperParts)) {
+        return this.getAlbumArtPath(`bg.${ext}`);
+      } else {
         return null;
       }
-
-      return [
-        'media.albumWallpaper',
-        this.directory,
-        artwork.fileExtension,
-      ];
     }
 
-    // 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.trackCover',
-      this.directory,
-
+    const basename =
       (artwork.unqualifiedDirectory
         ? 'cover-' + artwork.unqualifiedDirectory
-        : 'cover'),
+        : 'cover');
+
+    return this.getAlbumArtPath(`${basename}.${ext}`);
+  }
 
-      artwork.fileExtension,
-    ];
+  getWallpaperPartPath(part) {
+    return this.getAlbumArtPath(part.asset);
   }
 
   getOwnMusicVideoCoverPath(musicVideo) {
-    // Lala, same shenanigan as above, this is media.trackCover
-    // where it shouldn't be.
-
-    return [
-      'media.trackCover',
-      this.directory,
-      musicVideo.unqualifiedDirectory,
-      musicVideo.coverArtFileExtension,
-    ];
+    const filename =
+      musicVideo.unqualifiedDirectory +
+      `.${musicVideo.coverArtFileExtension}`;
+
+    return this.getAlbumArtPath(filename);
+  }
+
+  getAlbumArtPath(filename) {
+    const key = this.#getArtworkPathKey();
+    const front = [key, this.directory];
+    return [...front, filename];
+  }
+
+  #getArtworkPathKey(artwork) {
+    switch (this.style) {
+      case 'in-game vgm':
+        return 'media.vgmAlbumArt';
+
+      default:
+        return 'media.albumArt';
+    }
   }
 
   // As of writing, albums don't even have a `duration` property...
diff --git a/src/data/things/album/TrackSection.js b/src/data/things/album/TrackSection.js
index 451f8f7b..00963d1b 100644
--- a/src/data/things/album/TrackSection.js
+++ b/src/data/things/album/TrackSection.js
@@ -50,22 +50,22 @@ export class TrackSection extends Thing {
 
     unqualifiedDirectory: directory(),
 
-    directorySuffix: [
+    directorySuffixForTracks: [
       exposeUpdateValueOrContinue({
         validate: input.value(isDirectory),
       }),
 
-      withPropertyFromObject('album', V('directorySuffix')),
-      exposeDependency('#album.directorySuffix'),
+      withPropertyFromObject('album', V('directorySuffixForTracks')),
+      exposeDependency('#album.directorySuffixForTracks'),
     ],
 
-    suffixTrackDirectories: [
+    suffixTrackDirectoriesByDefault: [
       exposeUpdateValueOrContinue({
         validate: input.value(isBoolean),
       }),
 
-      withPropertyFromObject('album', V('suffixTrackDirectories')),
-      exposeDependency('#album.suffixTrackDirectories'),
+      withPropertyFromObject('album', V('suffixTrackDirectoriesByDefault')),
+      exposeDependency('#album.suffixTrackDirectoriesByDefault'),
     ],
 
     color: [
@@ -204,8 +204,8 @@ export class TrackSection extends Thing {
   static [Thing.yamlDocumentSpec] = {
     fields: {
       'Section': {property: 'name'},
-      'Directory Suffix': {property: 'directorySuffix'},
-      'Suffix Track Directories': {property: 'suffixTrackDirectories'},
+      'Directory Suffix': {property: 'directorySuffixForTracks'},
+      'Suffix Track Directories': {property: 'suffixTrackDirectoriesByDefault'},
 
       'Color': {property: 'color'},
       'Has Track Numbers': {property: 'hasTrackNumbers'},