« get me outta code hell

content, data: generateArtistInfoPageMusicVideosChunkedList - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/content/dependencies
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2026-04-14 20:26:02 -0300
committer(quasar) nebula <qznebula@protonmail.com>2026-04-14 20:26:02 -0300
commit2336e252d25536d3678119ff070189e666b98927 (patch)
tree788b9c689b8c7ac4324af092c73e95f20b5abd6f /src/content/dependencies
parented38f9529084cdd3ff6cdfb56148fd9a99c259b2 (diff)
content, data: generateArtistInfoPageMusicVideosChunkedList
Diffstat (limited to 'src/content/dependencies')
-rw-r--r--src/content/dependencies/generateArtistInfoPage.js36
-rw-r--r--src/content/dependencies/generateArtistInfoPageMusicVideosChunk.js58
-rw-r--r--src/content/dependencies/generateArtistInfoPageMusicVideosChunkItem.js118
-rw-r--r--src/content/dependencies/generateArtistInfoPageMusicVideosChunkedList.js66
-rw-r--r--src/content/dependencies/generateArtistInfoPageTracksChunkItem.js48
5 files changed, 281 insertions, 45 deletions
diff --git a/src/content/dependencies/generateArtistInfoPage.js b/src/content/dependencies/generateArtistInfoPage.js
index cf8ce994..29bc34e6 100644
--- a/src/content/dependencies/generateArtistInfoPage.js
+++ b/src/content/dependencies/generateArtistInfoPage.js
@@ -14,6 +14,11 @@ export default {
       ...artist.trackCoverArtistContributions,
     ],
 
+    musicVideoContributions: [
+      ...artist.musicVideoArtistContributions,
+      ...artist.musicVideoContributorContributions,
+    ],
+
     // Banners and wallpapers don't show up in the artist gallery page, only
     // cover art.
     hasGallery:
@@ -79,6 +84,12 @@ export default {
         ? relation('linkArtistGallery', artist)
         : null),
 
+    musicVideosChunkedList:
+      relation('generateArtistInfoPageMusicVideosChunkedList', artist),
+
+    musicVideosGroupInfo:
+      relation('generateArtistGroupContributionsInfo', query.musicVideoContributions),
+
     flashesChunkedList:
       relation('generateArtistInfoPageFlashesChunkedList', artist),
 
@@ -216,6 +227,11 @@ export default {
                         {href: '#art'},
                         language.$(pageCapsule, 'artList.title')),
 
+                  !html.isBlank(relations.musicVideosChunkedList) &&
+                    html.tag('a',
+                      {href: '#music-videos'},
+                      language.$(pageCapsule, 'musicVideoList.title')),
+
                   !html.isBlank(relations.flashesChunkedList) &&
                     html.tag('a',
                       {href: '#flashes'},
@@ -329,6 +345,26 @@ export default {
             relations.contentHeading.clone()
               .slots({
                 tag: 'h2',
+                attributes: {id: 'music-videos'},
+                title: language.$(pageCapsule, 'musicVideoList.title'),
+              }),
+
+            relations.musicVideosChunkedList.slots({
+              groupInfo:
+                language.encapsulate(pageCapsule, 'groupContributions', capsule =>
+                  relations.musicVideosGroupInfo.slots({
+                    title: language.$(capsule, 'title.artworks'),
+                    showBothColumns: false,
+                    sort: 'count',
+                    countUnit: 'artworks',
+                  })),
+            }),
+          ]),
+
+          html.tags([
+            relations.contentHeading.clone()
+              .slots({
+                tag: 'h2',
                 attributes: {id: 'flashes'},
                 title: language.$(pageCapsule, 'flashList.title'),
               }),
diff --git a/src/content/dependencies/generateArtistInfoPageMusicVideosChunk.js b/src/content/dependencies/generateArtistInfoPageMusicVideosChunk.js
new file mode 100644
index 00000000..6912d4d6
--- /dev/null
+++ b/src/content/dependencies/generateArtistInfoPageMusicVideosChunk.js
@@ -0,0 +1,58 @@
+export default {
+  relations: (relation, artist, album, contribs) => ({
+    template:
+      relation('generateArtistInfoPageChunk'),
+
+    albumLink:
+      relation('linkAlbum', album),
+
+    albumArtistCredit:
+      relation('generateArtistCredit', album.artistContribs, []),
+
+    items:
+      contribs.map(contribs =>
+        relation('generateArtistInfoPageMusicVideosChunkItem',
+          artist,
+          contribs)),
+  }),
+
+  data: (_artist, album, contribs) => ({
+    albumDate:
+      album.date,
+
+    contribDates:
+      contribs
+        .flat()
+        .map(contrib => contrib.date),
+  }),
+
+  generate: (data, relations, {html, language}) =>
+    relations.template.slots({
+      mode: 'album',
+
+      link:
+        language.encapsulate('artistPage.creditList.album', workingCapsule => {
+          const creditCapsule = workingCapsule + '.credit';
+          const workingOptions = {album: relations.albumLink};
+
+          relations.albumArtistCredit.setSlots({
+            normalStringKey: creditCapsule + '.by',
+          });
+
+          if (!html.isBlank(relations.albumArtistCredit)) {
+            workingCapsule += '.withCredit';
+            workingOptions.credit =
+              html.tag('span', {class: 'by'},
+                relations.albumArtistCredit);
+          }
+
+          return language.$(workingCapsule, workingOptions);
+        }),
+
+      date: data.albumDate,
+      dates: data.contribDates,
+
+      list:
+        html.tag('ul', relations.items),
+    }),
+};
diff --git a/src/content/dependencies/generateArtistInfoPageMusicVideosChunkItem.js b/src/content/dependencies/generateArtistInfoPageMusicVideosChunkItem.js
new file mode 100644
index 00000000..8bae860d
--- /dev/null
+++ b/src/content/dependencies/generateArtistInfoPageMusicVideosChunkItem.js
@@ -0,0 +1,118 @@
+import {empty} from '#sugar';
+import {selectRepresentativeArtistContributorContribs} from '#wiki-data';
+
+export default {
+  query(_artist, contribs) {
+    const query = {};
+
+    query.musicVideo = contribs[0].thing;
+
+    query.albumOrTrack = query.musicVideo.thing;
+
+    query.album =
+      (query.albumOrTrack.isAlbum
+        ? query.albumOrTrack
+        : query.albumOrTrack.album);
+
+    query.displayedContributions =
+      selectRepresentativeArtistContributorContribs(contribs);
+
+    return query;
+  },
+
+  relations: (relation, query, artist, _contribs) => ({
+    template:
+      relation('generateArtistInfoPageChunkItem'),
+
+    trackLink:
+      (query.albumOrTrack.isTrack
+        ? relation('linkTrack', query.albumOrTrack)
+        : null),
+
+    artistCredit:
+      relation('generateArtistCredit',
+        query.musicVideo.artistContribs,
+        (empty(query.album.artistContribs)
+          ? [artist.mockSimpleContribution]
+          : query.album.artistContribs)),
+
+    externalLinks:
+      query.musicVideo.urls
+        .map(url => relation('linkExternal', url)),
+  }),
+
+  data: (query, _artist, contribs) => ({
+    date: contribs[0].date,
+
+    for:
+      (query.albumOrTrack.isAlbum
+        ? 'album'
+        : 'track'),
+
+    title: query.musicVideo.title,
+    label: query.musicVideo.label,
+
+    contribAnnotationParts:
+      (query.displayedContributions
+        ? query.displayedContributions
+            .flatMap(contrib => contrib.annotationParts)
+        : null),
+  }),
+
+  generate: (data, relations, {html, language}) =>
+    relations.template.slots({
+      annotation:
+        (data.contribAnnotationParts
+          ? language.formatUnitList(data.contribAnnotationParts)
+          : html.blank()),
+
+      content:
+        language.encapsulate('artistPage.creditList.entry', entryCapsule => {
+          let workingCapsule = entryCapsule;
+          let workingOptions = {};
+
+          workingCapsule += '.' + data.for + '.musicVideo';
+
+          const musicVideoCapsule = workingCapsule;
+
+          if (data.for === 'track') {
+            workingOptions.track =
+              relations.trackLink;
+          }
+
+          if (data.date) {
+            workingCapsule += '.withDate';
+            workingOptions.date = language.formatDate(data.date);
+          }
+
+          relations.artistCredit.setSlots({
+            normalStringKey:
+              musicVideoCapsule + '.credit' +
+                (data.title ? '.alongsideTitle'
+               : data.label ? '.alongsideLabel'
+                            : ''),
+          });
+
+          if (!html.isBlank(relations.artistCredit)) {
+            workingCapsule += '.withCredit';
+            workingOptions.credit = relations.artistCredit;
+          }
+
+          if (data.title) {
+            workingCapsule += '.withTitle';
+            workingOptions.title = language.sanitize(data.title);
+          } else if (data.label) {
+            workingCapsule += '.withLabel';
+            workingOptions.label = language.sanitize(data.label);
+          }
+
+          if (!empty(relations.externalLinks)) {
+            workingCapsule += '.withLinks';
+            workingOptions.links =
+              language.formatUnitList(relations.externalLinks);
+          }
+
+          return language.$(workingCapsule, workingOptions);
+        }),
+    }),
+};
diff --git a/src/content/dependencies/generateArtistInfoPageMusicVideosChunkedList.js b/src/content/dependencies/generateArtistInfoPageMusicVideosChunkedList.js
new file mode 100644
index 00000000..588fbbeb
--- /dev/null
+++ b/src/content/dependencies/generateArtistInfoPageMusicVideosChunkedList.js
@@ -0,0 +1,66 @@
+import {chunkByConditions, stitchArrays} from '#sugar';
+import {sortAlbumsTracksChronologically, sortContributionsChronologically}
+  from '#sort';
+
+export default {
+  query(artist) {
+    const query = {};
+
+    const allContributions = [
+      ...artist.musicVideoArtistContributions,
+      ...artist.musicVideoContributorContributions,
+      ...artist.otherMusicVideoArtistContributionsToOwnAlbums,
+    ];
+
+    const getMusicVideo = contrib =>
+      contrib.thing;
+
+    const getAlbumOrTrack = contrib =>
+      getMusicVideo(contrib).thing;
+
+    sortContributionsChronologically(
+      allContributions,
+      sortAlbumsTracksChronologically,
+      {getThing: getAlbumOrTrack});
+
+    const getAlbum = contrib =>
+      (getAlbumOrTrack(contrib).isTrack
+        ? getAlbumOrTrack(contrib).album
+        : getAlbumOrTrack(contrib));
+
+    query.contribs =
+      chunkByConditions(allContributions, [
+        (a, b) => getAlbum(a) !== getAlbum(b),
+      ]).map(contribs =>
+          chunkByConditions(contribs, [
+            (a, b) => getMusicVideo(a) !== getMusicVideo(b),
+          ]));
+
+    query.albums =
+      query.contribs
+        .map(contribs => contribs[0][0])
+        .map(contrib => getAlbum(contrib));
+
+    return query;
+  },
+
+  relations: (relation, query, artist) => ({
+    template:
+      relation('generateArtistInfoPageChunkedList'),
+
+    chunks:
+      stitchArrays({
+        album: query.albums,
+        contribs: query.contribs,
+      }).map(({album, contribs}) =>
+          relation('generateArtistInfoPageMusicVideosChunk',
+            artist,
+            album,
+            contribs)),
+  }),
+
+  generate: (relations) =>
+    relations.template.slots({
+      chunks: relations.chunks,
+    }),
+};
diff --git a/src/content/dependencies/generateArtistInfoPageTracksChunkItem.js b/src/content/dependencies/generateArtistInfoPageTracksChunkItem.js
index 69d8eebd..22a4a228 100644
--- a/src/content/dependencies/generateArtistInfoPageTracksChunkItem.js
+++ b/src/content/dependencies/generateArtistInfoPageTracksChunkItem.js
@@ -1,5 +1,6 @@
 import {sortAlbumsTracksChronologically} from '#sort';
 import {empty} from '#sugar';
+import {selectRepresentativeArtistContributorContribs} from '#wiki-data';
 
 export default {
   query(artist, contribs, chunkContribs) {
@@ -15,51 +16,8 @@ export default {
       chunkContribs.flat()
         .some(contrib => +contrib.date !== +query.track.album.date);
 
-    const creditedAsNormalArtist =
-      contribs
-        .some(contrib =>
-          contrib.thingProperty === 'artistContribs' &&
-         !contrib.isFeaturingCredit);
-
-    const creditedAsContributor =
-      contribs
-        .some(contrib => contrib.thingProperty === 'contributorContribs');
-
-    const annotatedContribs =
-      contribs
-        .filter(contrib => !empty(contrib.annotationParts));
-
-    const annotatedArtistContribs =
-      annotatedContribs
-        .filter(contrib => contrib.thingProperty === 'artistContribs');
-
-    const annotatedContributorContribs =
-      annotatedContribs
-        .filter(contrib => contrib.thingProperty === 'contributorContribs');
-
-    // Don't display annotations associated with crediting in the
-    // Contributors field if the artist is also credited as an Artist
-    // *and* the Artist-field contribution is non-annotated. This is
-    // so that we don't misrepresent the artist - the contributor
-    // annotation tends to be for "secondary" and performance roles.
-    // For example, this avoids crediting Marcy Nabors on Renewed
-    // Return seemingly only for "bass clarinet" when they're also
-    // the one who composed and arranged Renewed Return!
-    if (
-      creditedAsNormalArtist &&
-      creditedAsContributor &&
-      empty(annotatedArtistContribs)
-    ) {
-      query.displayedContributions = null;
-    } else if (
-      !empty(annotatedArtistContribs) ||
-      !empty(annotatedContributorContribs)
-    ) {
-      query.displayedContributions = [
-        ...annotatedArtistContribs,
-        ...annotatedContributorContribs,
-      ];
-    }
+    query.displayedContributions =
+      selectRepresentativeArtistContributorContribs(contribs);
 
     // It's kinda awkward to perform this chronological sort here,
     // per track, rather than just reusing the one that's done to