« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/content/dependencies/generateAbsoluteDatetimestamp.js12
-rw-r--r--src/content/dependencies/generateMusicVideo.js44
-rw-r--r--src/content/dependencies/generateMusicVideoReleaseLine.js61
-rw-r--r--src/content/dependencies/generateSingleArtworkColumn.js16
-rw-r--r--src/content/dependencies/generateTrackArtworkColumn.js2
-rw-r--r--src/content/dependencies/generateTrackInfoPage.js2
-rw-r--r--src/content/dependencies/listTracksWithExtra.js3
-rw-r--r--src/content/dependencies/listTracksWithMusicVideos.js9
-rw-r--r--src/data/things/music-video.js15
-rw-r--r--src/data/things/track.js12
-rw-r--r--src/listing-spec.js9
-rw-r--r--src/static/css/site.css16
-rw-r--r--src/strings-default.yaml40
13 files changed, 191 insertions, 50 deletions
diff --git a/src/content/dependencies/generateAbsoluteDatetimestamp.js b/src/content/dependencies/generateAbsoluteDatetimestamp.js
index d006374a..52b524e0 100644
--- a/src/content/dependencies/generateAbsoluteDatetimestamp.js
+++ b/src/content/dependencies/generateAbsoluteDatetimestamp.js
@@ -21,6 +21,7 @@ export default {
         'year',
         'minimal-difference',
         'year-difference',
+        'full-difference',
       ]),
       default: 'full',
     },
@@ -78,6 +79,17 @@ export default {
           label = language.formatYear(data.date);
           tooltip = language.formatDate(data.date);
         }
+
+        break;
+      }
+
+      case 'full-difference': {
+        if (data.date.toDateString() === data.contextDate?.toDateString()) {
+          return html.blank();
+        }
+
+        label = language.formatDate(data.date);
+        break;
       }
     }
 
diff --git a/src/content/dependencies/generateMusicVideo.js b/src/content/dependencies/generateMusicVideo.js
index a61cd5b7..e83fdf80 100644
--- a/src/content/dependencies/generateMusicVideo.js
+++ b/src/content/dependencies/generateMusicVideo.js
@@ -1,5 +1,5 @@
 export default {
-  relations: (relation, musicVideo) => ({
+  relations: (relation, musicVideo, thing) => ({
     image:
       relation('image', {
         path: musicVideo.path,
@@ -7,14 +7,14 @@ export default {
         dimensions: musicVideo.coverArtDimensions,
       }),
 
-    artistCredit:
-      relation('generateArtistCredit', musicVideo.artistContribs, []),
+    releaseLine:
+      relation('generateMusicVideoReleaseLine', musicVideo, thing),
 
     contributorCredit:
       relation('generateArtistCredit', musicVideo.contributorContribs, []),
   }),
 
-  data: (musicVideo) => ({
+  data: (musicVideo, _track) => ({
     label:
       musicVideo.label,
 
@@ -45,33 +45,25 @@ export default {
           {[html.joinChildren]: html.tag('br')},
 
           [
-            language.encapsulate(capsule, 'by', workingCapsule => {
-              const additionalStringOptions = {};
+            html.tag('span', {class: 'release-line'},
+              {[html.onlyIfContent]: true},
 
-              if (data.label) {
-                workingCapsule += '.customLabel';
-                additionalStringOptions.label = data.label;
-              }
+              relations.releaseLine),
 
-              return relations.artistCredit.slots({
-                normalStringKey: workingCapsule,
-                additionalStringOptions,
+            language.encapsulate(capsule, 'contributorsLine', capsule =>
+              language.$(capsule, {
+                [language.onlyIfOptions]: ['credit'],
 
-                showAnnotation: true,
-                showChronology: true,
+                credit:
+                  relations.contributorCredit.slots({
+                    normalStringKey: language.encapsulate(capsule, 'credit'),
 
-                chronologyKind: 'musicVideo',
-              });
-            }),
+                    showAnnotation: true,
+                    showChronology: true,
 
-            relations.contributorCredit.slots({
-              normalStringKey: language.encapsulate(capsule, 'contributors'),
-
-              showAnnotation: true,
-              showChronology: true,
-
-              chronologyKind: 'musicVideoContribution',
-            }),
+                    chronologyKind: 'musicVideoContribution',
+                  }),
+              })),
           ]),
       ])),
 };
diff --git a/src/content/dependencies/generateMusicVideoReleaseLine.js b/src/content/dependencies/generateMusicVideoReleaseLine.js
new file mode 100644
index 00000000..e4e196e9
--- /dev/null
+++ b/src/content/dependencies/generateMusicVideoReleaseLine.js
@@ -0,0 +1,61 @@
+export default {
+  relations: (relation, musicVideo, thing) => ({
+    datetimestamp:
+      relation('generateAbsoluteDatetimestamp', musicVideo.date, thing.date),
+
+    artistCredit:
+      relation('generateArtistCredit', musicVideo.artistContribs, []),
+  }),
+
+  data: (data) => ({
+    label:
+      (data.label !== 'Music video'
+        ? data.label
+        : null),
+  }),
+
+  generate(data, relations, {html, language}) {
+    const {artistCredit, datetimestamp} = relations;
+    const capsule = language.encapsulate('misc.musicVideo');
+
+    datetimestamp.setSlot('style', 'full-difference');
+
+    let artistsLineCapsule = language.encapsulate(capsule, 'artistsLine');
+    let artistsLineOptions = {[language.onlyIfOptions]: ['credit']};
+
+    if (data.label) {
+      artistsLineCapsule += '.customLabel';
+      artistsLineOptions.label = data.label;
+    }
+
+    if (!html.isBlank(datetimestamp)) {
+      artistsLineCapsule += '.withDate';
+      artistsLineOptions.date = datetimestamp;
+    }
+
+    artistsLineOptions.credit =
+      html.tag('span', {class: 'by'},
+        {[html.onlyIfContent]: true},
+
+        artistCredit.slots({
+          normalStringKey: language.encapsulate(capsule, 'artistsLine.credit'),
+
+          showAnnotation: true,
+          showChronology: true,
+
+          chronologyKind: 'musicVideo',
+        }));
+
+    const artistsLine = language.$(artistsLineCapsule, artistsLineOptions);
+
+    if (!html.isBlank(artistsLine)) {
+      return artistsLine;
+    }
+
+    if (!html.isBlank(datetimestamp)) {
+      return language.$(capsule, 'date', {date: datetimestamp});
+    }
+
+    return html.blank();
+  },
+}
diff --git a/src/content/dependencies/generateSingleArtworkColumn.js b/src/content/dependencies/generateSingleArtworkColumn.js
new file mode 100644
index 00000000..32c448ad
--- /dev/null
+++ b/src/content/dependencies/generateSingleArtworkColumn.js
@@ -0,0 +1,16 @@
+export default {
+  relations: (relation, track) => ({
+    albumArtworkColumn:
+      relation('generateAlbumArtworkColumn', track.album),
+
+    trackMusicVideos:
+      track.musicVideos.map(musicVideo =>
+        relation('generateMusicVideo', musicVideo, track)),
+  }),
+
+  generate: (relations, {html}) =>
+    html.tags([
+      relations.albumArtworkColumn,
+      relations.trackMusicVideos,
+    ]),
+};
diff --git a/src/content/dependencies/generateTrackArtworkColumn.js b/src/content/dependencies/generateTrackArtworkColumn.js
index dde37376..feaed604 100644
--- a/src/content/dependencies/generateTrackArtworkColumn.js
+++ b/src/content/dependencies/generateTrackArtworkColumn.js
@@ -13,7 +13,7 @@ export default {
 
     trackMusicVideos:
       track.musicVideos.map(musicVideo =>
-        relation('generateMusicVideo', musicVideo)),
+        relation('generateMusicVideo', musicVideo, track)),
   }),
 
   generate: (relations, {html}) =>
diff --git a/src/content/dependencies/generateTrackInfoPage.js b/src/content/dependencies/generateTrackInfoPage.js
index 0937c42c..77adff02 100644
--- a/src/content/dependencies/generateTrackInfoPage.js
+++ b/src/content/dependencies/generateTrackInfoPage.js
@@ -65,7 +65,7 @@ export default {
 
     artworkColumn:
       (query.firstTrackInSingle
-        ? relation('generateAlbumArtworkColumn', track.album)
+        ? relation('generateSingleArtworkColumn', track)
         : relation('generateTrackArtworkColumn', track)),
 
     contentHeading:
diff --git a/src/content/dependencies/listTracksWithExtra.js b/src/content/dependencies/listTracksWithExtra.js
index 09d8ee21..27f03ee9 100644
--- a/src/content/dependencies/listTracksWithExtra.js
+++ b/src/content/dependencies/listTracksWithExtra.js
@@ -52,6 +52,7 @@ export default {
 
   slots: {
     hash: {type: 'string'},
+    showAlbumDates: {type: 'boolean', default: true},
   },
 
   generate(data, relations, slots, {language}) {
@@ -63,7 +64,7 @@ export default {
           albumLink: relations.albumLinks,
           date: data.dates,
         }).map(({albumLink, date}) =>
-            (date
+            (slots.showAlbumDates && date
               ? {
                   stringsKey: 'withDate',
                   album: albumLink,
diff --git a/src/content/dependencies/listTracksWithMusicVideos.js b/src/content/dependencies/listTracksWithMusicVideos.js
new file mode 100644
index 00000000..e7aa1f5e
--- /dev/null
+++ b/src/content/dependencies/listTracksWithMusicVideos.js
@@ -0,0 +1,9 @@
+export default {
+  relations: (relation, spec) =>
+    ({page: relation('listTracksWithExtra', spec, 'musicVideos', 'array')}),
+
+  generate: (relations) =>
+    relations.page.slots({
+      showAlbumDates: false,
+    }),
+};
diff --git a/src/data/things/music-video.js b/src/data/things/music-video.js
index 267349e8..6c1e3ba6 100644
--- a/src/data/things/music-video.js
+++ b/src/data/things/music-video.js
@@ -5,7 +5,7 @@ import {input, V} from '#composite';
 import find from '#find';
 import Thing from '#thing';
 import {isDate, isStringNonEmpty, isURL} from '#validators';
-import {parseContributors} from '#yaml';
+import {parseContributors, parseDate} from '#yaml';
 
 import {exposeConstant, exposeUpdateValueOrContinue}
   from '#composite/control-flow';
@@ -72,21 +72,14 @@ export class MusicVideo extends Thing {
     fields: {
       'Label': {property: 'label'},
       'Directory': {property: 'unqualifiedDirectory'},
-      'Date': {property: 'date'},
+      'Date': {property: 'date', transform: parseDate},
       'URL': {property: 'url'},
 
       'Cover Art File Extension': {property: 'coverArtFileExtension'},
       'Cover Art Dimensions': {property: 'coverArtDimensions'},
 
-      'Artists': {
-        property: 'artistContribs',
-        transform: parseContributors,
-      },
-
-      'Contributors': {
-        property: 'contributorContribs',
-        transform: parseContributors,
-      },
+      'Artists': {property: 'artistContribs', transform: parseContributors},
+      'Contributors': {property: 'contributorContribs', transform: parseContributors},
     },
   };
 
diff --git a/src/data/things/track.js b/src/data/things/track.js
index 8652fbdf..5b40ca4d 100644
--- a/src/data/things/track.js
+++ b/src/data/things/track.js
@@ -492,7 +492,17 @@ export class Track extends Thing {
 
     // > Update & expose - Music videos
 
-    musicVideos: thingList(V(MusicVideo)),
+    musicVideos: [
+      exposeUpdateValueOrContinue(),
+
+      // TODO: Same situation as lyrics. Inherited music videos don't set
+      // the proper .thing property back to this track... but then, it needs
+      // to keep a reference to its original .thing to get its proper path,
+      // so maybe this is okay...
+      inheritFromMainRelease(),
+
+      thingList(V(MusicVideo)),
+    ],
 
     // > Update & expose - Additional files
 
diff --git a/src/listing-spec.js b/src/listing-spec.js
index a301845b..019b34ab 100644
--- a/src/listing-spec.js
+++ b/src/listing-spec.js
@@ -178,7 +178,14 @@ listingSpec.push({
   directory: 'tracks/with-lyrics',
   stringsKey: 'listTracks.withLyrics',
   contentFunction: 'listTracksWithLyrics',
-  seeAlso: ['tracks/needing-lyrics'],
+  seeAlso: ['tracks/needing-lyrics', 'tracks/with-music-videos'],
+});
+
+listingSpec.push({
+  directory: 'tracks/with-music-videos',
+  stringsKey: 'listTracks.withMusicVideos',
+  contentFunction: 'listTracksWithMusicVideos',
+  seeAlso: ['tracks/with-lyrics'],
 });
 
 listingSpec.push({
diff --git a/src/static/css/site.css b/src/static/css/site.css
index d57f5712..1b4f3a84 100644
--- a/src/static/css/site.css
+++ b/src/static/css/site.css
@@ -1929,8 +1929,7 @@ p.image-details.origin-details .filename-line {
   align-items: center;
 }
 
-.music-video .image-link:hover::after,
-.music-video .image-link:focus::after {
+.music-video .image-link:hover::after {
   font-size: 1.4em;
   background: #0006;
 }
@@ -1941,11 +1940,20 @@ p.image-details.origin-details .filename-line {
   box-shadow: 0 0 4px inset black;
 }
 
-.music-video .image-link:hover .image,
-.music-video .image-link:focus .image {
+.music-video .image-link:hover .image {
   transform: scale(1.02);
 }
 
+.music-video .release-line {
+  display: block;
+  padding-left: 1.2ch;
+  text-indent: -1.2ch;
+}
+
+.music-video .release-line > * {
+  text-indent: 0;
+}
+
 .album-art-info {
   font-size: 0.8em;
   border: 2px solid var(--deep-color);
diff --git a/src/strings-default.yaml b/src/strings-default.yaml
index dc6bffdd..4ddd6e48 100644
--- a/src/strings-default.yaml
+++ b/src/strings-default.yaml
@@ -988,16 +988,36 @@ misc:
 
   # musicVideo:
   #   Strings for music videos, which are presented in a very similar
-  #   fashion as cover artworks.
+  #   fashion as cover artworks, although the strings are structured
+  #   a bit differently.
 
   musicVideo:
     label: "Music video!"
     label.customLabel: "{LABEL}!"
 
-    by: "Music video by {ARTISTS}"
-    by.customLabel: "{LABEL} by {ARTISTS}"
+    artistsLine: >-
+      Music video {CREDIT}
 
-    contributors: "Contributors: {ARTISTS}"
+    artistsLine.customLabel: >-
+      {LABEL} {CREDIT}
+
+    artistsLine.withDate: >-
+      Music video ({DATE}) {CREDIT}
+
+    artistsLine.customLabel.withDate: >-
+      {LABEL} ({DATE}) {CREDIT}
+
+    artistsLine.credit: >-
+      by {ARTISTS}
+
+    date: >-
+      Released {DATE}
+
+    contributorsLine: >-
+      Contributors: {CREDIT}
+
+    contributorsLine.credit: >-
+      {ARTISTS}
 
   # coverGrid:
   #   Generic strings for various sorts of gallery grids, displayed
@@ -2305,6 +2325,18 @@ listingPage:
         title.withDate: "{ALBUM} ({DATE})"
         item: "{TRACK}"
 
+    # listTracks.withMusicVideos:
+    #   The same as withLyrics, but for music videos.
+
+    withMusicVideos:
+      title: "Tracks - with Music Videos"
+      title.short: "...with Music Videos"
+
+      chunk:
+        title: "{ALBUM}"
+        title.withDate: "{ALBUM} ({DATE})"
+        item: "{TRACK}"
+
     # listTracks.withSheetMusicFiles:
     #   List tracks, chunked by album (which are sorted by date,
     #   falling back alphabetically) and in their usual track order,