« get me outta code hell

loads of code shenanigans - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorrie <towerofnix@gmail.com>2020-06-06 18:40:05 -0300
committerFlorrie <towerofnix@gmail.com>2020-06-06 18:40:05 -0300
commit0cd7e34d0fa2fe019a37472bd2ecf14db05f63fb (patch)
treec1078ad7d5525930f97e17c9b3156072b4be3af3
parent6592c70fd5143808b1c238a8ace9038baae599ef (diff)
loads of code shenanigans
-rw-r--r--album/act-7/album.txt7
-rw-r--r--album/ancestral/album.txt3
-rw-r--r--album/beforus/album.txt2
-rw-r--r--album/cherubim/album.txt4
-rw-r--r--album/homestuck-vol-10/album.txt2
-rw-r--r--album/homestuck-vol-5/album.txt3
-rw-r--r--album/homestuck-vol-7/album.txt2
-rw-r--r--album/homestuck-vol-8/album.txt4
-rw-r--r--album/lofam/album.txt2
-rw-r--r--album/lofam2/album.txt6
-rw-r--r--album/lofam3/album.txt4
-rw-r--r--album/lofam4/album.txt2
-rw-r--r--album/one-year-older/album.txt2
-rw-r--r--album/symphony-impossible-to-play/album.txt2
-rw-r--r--album/the-wanderers/album.txt2
-rw-r--r--album/unreleased-tracks/album.txt30
-rw-r--r--artists.txt10
-rw-r--r--client.js2
-rw-r--r--common.js5
-rw-r--r--upd8.js221
20 files changed, 180 insertions, 135 deletions
diff --git a/album/act-7/album.txt b/album/act-7/album.txt
index 3e2d0f8f..cb698028 100644
--- a/album/act-7/album.txt
+++ b/album/act-7/album.txt
@@ -1,4 +1,5 @@
 Album: Act 7
+Artist: Clark Powell, Toby Fox
 Cover Art: Homestuck
 Date: April 13, 2016
 FG: #c7ff43
@@ -7,10 +8,10 @@ URLs:
 - https://www.youtube.com/watch?v=FPsMeamXcE8
 ------------------------------------------------------------------------------
 Track: Overture (Canon Edit)
-Artist: Clark Powell, Toby Fox
-Contributors: Toby Fox (additional work)
 Track Art: Homestuck
-URLs: https://homestuck.bandcamp.com/track/overture-canon-edit-2
+URLs:
+- https://homestuck.bandcamp.com/track/overture-canon-edit-2
+- https://www.youtube.com/watch?v=FPsMeamXcE8
 References:
 - I - Overture
 - Doctor
diff --git a/album/ancestral/album.txt b/album/ancestral/album.txt
index 8035cd1b..45f23d90 100644
--- a/album/ancestral/album.txt
+++ b/album/ancestral/album.txt
@@ -455,7 +455,7 @@ Track: E%ile
 Directory: exile
 Artist: ndividedbyzero
 Track Art: Xagave
-References: Ruins (With Strings)
+References: Ruins
 URLs:
 - https://unofficialmspafans.bandcamp.com/track/e-ile
 - https://youtu.be/r_wqTbLDEAE?list=PL1fTYKSo2grBk7cJ3aIoYT2tc8eikCUKV
@@ -708,6 +708,7 @@ Commentary:
     The track made me feel like she was doing her time travel thing and wreckin crud, so here she is in some important part of history screwing everything up, getting ready to cull you if necessary 8]
 -------------------------------------------------------------------------------
 Track: Mutiny
+Directory: mutiny-ancestral
 Artist: Zan Beaver
 Track Art: Elanor Pam
 URLs:
diff --git a/album/beforus/album.txt b/album/beforus/album.txt
index 6ea17a02..f9aaaa46 100644
--- a/album/beforus/album.txt
+++ b/album/beforus/album.txt
@@ -412,7 +412,7 @@ Track: The Explored Ruins
 Artist: RowBird, one-imperfect-rose
 Track Art: Neiratina
 URLs: https://unofficialmspafans.bandcamp.com/track/the-explored-ruins
-References: Explore, Ruins (With Strings)
+References: Explore, Ruins
 Commentary:
     <i>RowBird:</i>
     This song was composed with the intent of having a "Homestuck" feel to it, while being my own creation at the same time. The floating left-hand chord pattern that is heard throughout the song is inspired by [[Flare]], and [[Three in the Morning]]. The piano portion was composed before I even knew of the fan album, so it was mostly in the additional elements (the strings, woodwinds, percussion, etc.) that the character was shaped.
diff --git a/album/cherubim/album.txt b/album/cherubim/album.txt
index 1b2cfba8..e0dcb60c 100644
--- a/album/cherubim/album.txt
+++ b/album/cherubim/album.txt
@@ -86,7 +86,7 @@ Commentary:
     <i>Michael Guy Bowman:</i>
     I floated the idea for a UU + uu album last year in about June as a concept record divided in half with equal and analogous parts Calliope and Caliborn. While we really wanted to jump in and get to making more Homestuck music following Volume 9, we decided to wait and discover more of the two characters until December when Radiation re-organized the effort. He proposed the Jekyll / Hyde alternating pace of the album culminating in “Eternity Served Cold” and had the group split into pairs, each musician working with a partner on a track with the same themes and motifs.
     I worked with Erik, who sent me the at-the-time unfinished Calliope composition “Constant Confinement" for me to base my Caliborn track around. Erik’s track was so meditative, it was actually difficult to recognize the slow-paced woodwind melody over the course of the piece, which pulses onward without any strict meter. This melody had to be expanded upon a lot to match the pace of "Constant Conquest" - it actually flies by at breakneck pace in the first measure of the piece before settling into its groove.
-    Conquest in many ways resembles “Ruins Rising”, another electronic track based around one of Erik’s piano compositions. Like in my approach to “Ruins" I arranged the melody sparsely and created a very dense environment of drums and percussion sounds to convert Erik’s pulse into a groove. Same input, same solution, although this time I went much, much heavier than ever before. I had a handful of different drum kits going all at once, each with unique effects chains, to create more of an ensemble sound than a clean dance beat.
+    Conquest in many ways resembles [[Ruins Rising]], another electronic track based around one of Erik’s piano compositions. Like in my approach to “Ruins" I arranged the melody sparsely and created a very dense environment of drums and percussion sounds to convert Erik’s pulse into a groove. Same input, same solution, although this time I went much, much heavier than ever before. I had a handful of different drum kits going all at once, each with unique effects chains, to create more of an ensemble sound than a clean dance beat.
     What really sealed in the atmosphere of this track was the huge slather of pads that adorned the track, most of which were created using analog equipment. I fed the signal from an old Radio Shack keyboard with basic midi patches through a pawn shop guitar pedal to create the many layers of sound that fill the background. Some of the most excellent sounds on the track (especially that snarling synth at 2:57) were created by hammering the mod knobs and wah-wah pedal while playing atonal pitches. The result is distorted synth excellence.
 -------------------------------------------------------------------------------
 Track: The Lyrist
@@ -110,7 +110,7 @@ References:
 - Revelawesome
 - Black Rose / Green Sun
 - Endless Climb
-- Ruins (With Strings)
+- Ruins
 - Penumbra Phantasm
 - Doctor
 Commentary:
diff --git a/album/homestuck-vol-10/album.txt b/album/homestuck-vol-10/album.txt
index bde9a512..c67d3d78 100644
--- a/album/homestuck-vol-10/album.txt
+++ b/album/homestuck-vol-10/album.txt
@@ -284,7 +284,7 @@ Commentary:
 -------------------------------------------------------------------------------
 Track: Solar Voyage
 Artist: Marcy Nabors
-References: Ruins (With Strings), Flare, Explore
+References: Ruins, Flare, Explore
 Track Art: Seth Massey
 URLs: https://homestuck.bandcamp.com/track/solar-voyage-2
 Contributors:
diff --git a/album/homestuck-vol-5/album.txt b/album/homestuck-vol-5/album.txt
index e119f671..03b573de 100644
--- a/album/homestuck-vol-5/album.txt
+++ b/album/homestuck-vol-5/album.txt
@@ -236,6 +236,7 @@ Track: How Do I Live (Bunny Back in the Box Version)
 Artist: Michael Guy Bowman
 Track Art: copiko-art
 Contributors: Nick Smalley (guitar solo), Trisha Yearwood (How Do I Live)
+References: How Do I Live
 URLs: https://homestuck.bandcamp.com/track/how-do-i-live-bunny-back-in-the-box-version-2
 -------------------------------------------------------------------------------
 Track: Dupliblaze COMAGMA
@@ -263,7 +264,7 @@ URLs: https://homestuck.bandcamp.com/track/lotus-2
 Track: Ruins (With Strings)
 Artist: Erik Scheele, Michael Guy Bowman
 Track Art: ghostpressure
-References: Ruins (by Erik Scheele), Sburban Jungle
+References: Ruins, Sburban Jungle
 URLs: https://homestuck.bandcamp.com/track/ruins-with-strings-2
 Commentary:
     <i>Erik Scheele:</i>
diff --git a/album/homestuck-vol-7/album.txt b/album/homestuck-vol-7/album.txt
index dda58e1c..5ff5d639 100644
--- a/album/homestuck-vol-7/album.txt
+++ b/album/homestuck-vol-7/album.txt
@@ -95,7 +95,7 @@ Commentary:
 Track: Awakening
 Artist: Erik Scheele
 Track Art: Devin H-S
-References: Ruins (With Strings)
+References: Ruins
 URLs: https://homestuck.bandcamp.com/track/awakening-2
 -------------------------------------------------------------------------------
 Track: Havoc To Be Wrought
diff --git a/album/homestuck-vol-8/album.txt b/album/homestuck-vol-8/album.txt
index 219111f2..d7ab6526 100644
--- a/album/homestuck-vol-8/album.txt
+++ b/album/homestuck-vol-8/album.txt
@@ -21,7 +21,7 @@ Commentary:
 -------------------------------------------------------------------------------
 Track: Do You Remem8er Me
 Artist: Malcolm Brown
-References: Lifdoff, Showtime (Original Mix), Death of the Lusii, Ruins (With Strings)
+References: Lifdoff, Showtime (Original Mix), Death of the Lusii, Ruins
 Track Art: Richard Gung
 URLs: https://homestuck.bandcamp.com/track/do-you-remem8er-me-2
 Commentary:
@@ -332,7 +332,7 @@ Commentary:
 Track: Judgment Day
 Artist: Toby Fox
 Contributors: Kelly Sadwin (violin)
-References: Ruins (With Strings)
+References: Ruins
 Track Art: Lazylaz
 URLs: https://homestuck.bandcamp.com/track/judgment-day-2
 -------------------------------------------------------------------------------
diff --git a/album/lofam/album.txt b/album/lofam/album.txt
index d64b0fe1..3193e58c 100644
--- a/album/lofam/album.txt
+++ b/album/lofam/album.txt
@@ -669,7 +669,7 @@ References:
 - Hearts Flush
 - Crystalanthemums
 - Homestuck Anthem
-- Ruins (With Strings)
+- Ruins
 - Liquid Negrocity
 URLs: https://homestuckgaiden.bandcamp.com/track/growing-up
 Commentary:
diff --git a/album/lofam2/album.txt b/album/lofam2/album.txt
index c4801027..766b8b1e 100644
--- a/album/lofam2/album.txt
+++ b/album/lofam2/album.txt
@@ -146,7 +146,7 @@ Track Art: Cap
 References:
 - Endless Expanse
 - Carapacian Dominion
-- Ruins (With Strings)
+- Ruins
 - Growing Up
 - Gilded Sands
 - Nightmare
@@ -423,7 +423,7 @@ References:
 - Courser
 - Earthsea Borealis
 - Upward Movement (Dave Owns)
-- Ruins (With Strings)
+- Ruins
 - Dawn of Man
 - the rose rap (by Toby Fox)
 - Cascade
@@ -541,7 +541,7 @@ References:
 - Gaia Queen
 - Endless Climb
 - Olive Rogue
-- Ruins (With Strings)
+- Ruins
 - Lotus
 - BL1ND JUST1C3 : 1NV3ST1G4T1ON !!
 - Chorale for Jaspers
diff --git a/album/lofam3/album.txt b/album/lofam3/album.txt
index f9bcb1be..a28745b4 100644
--- a/album/lofam3/album.txt
+++ b/album/lofam3/album.txt
@@ -499,7 +499,7 @@ References:
 - Dawn of Man
 - Cascade (Beta)
 - Liquid Negrocity
-- Ruins (With Strings)
+- Ruins
 - Black Rose / Green Sun
 URLs: https://unofficialmspafans.bandcamp.com/track/garden-of-eden-part-1
 Commentary:
@@ -587,7 +587,7 @@ Track: Tombs & Krypton
 Directory: tombs-and-krypton
 Artist: Rob Little
 Track Art: fueledbyanimation
-References: Ruins (With Strings)
+References: Ruins
 URLs: https://unofficialmspafans.bandcamp.com/track/tombs-krypton
 Commentary:
     <i>Rob Little:</i>
diff --git a/album/lofam4/album.txt b/album/lofam4/album.txt
index e50c03ce..9eaaf456 100644
--- a/album/lofam4/album.txt
+++ b/album/lofam4/album.txt
@@ -959,7 +959,7 @@ References:
 - Sunsetter
 - Oppa Toby Style
 - Hate You
-- Ruins (With Strings)
+- Ruins
 URLs: https://unofficialmspafans.bandcamp.com/track/noble-ascendance
 Commentary:
     <i>WHATISLOSTINTHEMINES:</i>
diff --git a/album/one-year-older/album.txt b/album/one-year-older/album.txt
index 9e5ec05e..bd33613d 100644
--- a/album/one-year-older/album.txt
+++ b/album/one-year-older/album.txt
@@ -100,7 +100,7 @@ Commentary:
 -------------------------------------------------------------------------------
 Track: Skaian Shrapnel
 Track Art: Amethyst Barron
-References: Ruins (With Strings), Walk-Stab-Walk (R&E), Crystalanthemums
+References: Ruins, Walk-Stab-Walk (R&E), Crystalanthemums
 URLs: https://erikscheele.bandcamp.com/track/skiain-shrapnel
 Commentary:
     <i>Erik Scheele:</i>
diff --git a/album/symphony-impossible-to-play/album.txt b/album/symphony-impossible-to-play/album.txt
index 5d75b877..d95bb2b5 100644
--- a/album/symphony-impossible-to-play/album.txt
+++ b/album/symphony-impossible-to-play/album.txt
@@ -30,7 +30,7 @@ Directory: sarabande-symphony
 URLs: https://homestuck.bandcamp.com/track/ii-sarabande-2
 -------------------------------------------------------------------------------
 Track: III - Serenade
-References: Serenade, Ruins (With Strings)
+References: Serenade, Ruins
 Directory: serenade-symphony
 URLs: https://homestuck.bandcamp.com/track/iii-serenade-2
 -------------------------------------------------------------------------------
diff --git a/album/the-wanderers/album.txt b/album/the-wanderers/album.txt
index 9e890ed2..619e4513 100644
--- a/album/the-wanderers/album.txt
+++ b/album/the-wanderers/album.txt
@@ -95,7 +95,7 @@ Commentary:
 -------------------------------------------------------------------------------
 Track: Ruins Rising
 Artist: Michael Guy Bowman
-References: Ruins (With Strings)
+References: Ruins
 Track Art: Raizor
 URLs: https://youtu.be/pDYCL68d3wI
 -------------------------------------------------------------------------------
diff --git a/album/unreleased-tracks/album.txt b/album/unreleased-tracks/album.txt
index 52a8a5cc..7d326fa4 100644
--- a/album/unreleased-tracks/album.txt
+++ b/album/unreleased-tracks/album.txt
@@ -95,6 +95,10 @@ Artist: Toby Fox
 References: GameGrl (Original 1993 Mix), Teal Seer
 URLs: https://www.youtube.com/watch?v=Nc4nIdsuG7o
 -------------------------------------------------------------------------------
+Track: Guardian
+Artist: Bill Bolin
+Has URLs: no
+-------------------------------------------------------------------------------
 Track: Ham And Steak
 Artist: Malcolm Brown
 References:
@@ -134,6 +138,11 @@ Artist: James Roach
 References: Horschestra
 URLs: https://www.youtube.com/watch?v=EjCbsleXpuk
 -------------------------------------------------------------------------------
+Track: How Do I Live
+Artist: Diane Warren
+Contributors: Trisha Yearwood (performance in Con Air)
+URLs: https://www.youtube.com/watch?v=Sjx-T7_CGQA
+-------------------------------------------------------------------------------
 Track: I'm a Member of the Midnight Crew
 Artist: Eddie Morton
 URLs: https://www.youtube.com/watch?v=ARLPT5rKjWo
@@ -158,6 +167,14 @@ Track: Mannequin
 Artist: Perry Sullivan
 URLs: https://www.youtube.com/watch?v=gAyPFa3nEK4
 -------------------------------------------------------------------------------
+Track: Mutiny
+Artist: Bill Bolin
+Has URLs: no
+-------------------------------------------------------------------------------
+Track: Non Compos Mentis
+Artist: Bill Bolin
+Has URLs: no
+-------------------------------------------------------------------------------
 Track: not a creature was stirring
 Artist: Alexander Rosetti
 URLs: https://www.youtube.com/watch?v=hdlh6FRPnCw
@@ -174,6 +191,10 @@ URLs:
 - https://www.youtube.com/watch?v=RIq4GrMv96I
 - https://www.youtube.com/watch?v=OdntMzdkFnk
 -------------------------------------------------------------------------------
+Track: Problem Sleuth Theme
+Artist: Mark Hadley
+URLs: https://www.youtube.com/watch?v=DAj-vYcKNYY
+-------------------------------------------------------------------------------
 Track: Requiem
 Artist: Clark Powell
 Directory: requiem-labyrinths-heart
@@ -192,6 +213,10 @@ Artist: Toby Fox
 References: Riches to Ruins Movements I & II
 URLs: https://www.youtube.com/watch?v=mNu1KAi0Prk
 -------------------------------------------------------------------------------
+Track: Ruins
+Artist: Erik Scheele
+URLs: https://youtu.be/gbiS4WkCOVo?t=4987
+-------------------------------------------------------------------------------
 Track: Secret ROM
 Artist: Toby Fox
 URLs: https://www.youtube.com/watch?v=ercVSU3rjrc
@@ -206,6 +231,11 @@ Track: Skaian Shuffle
 Artist: Clark Powell
 URLs: https://archive.homestuck.net/wl/?id=bBs1qkrvHtZIpfhl7XCLoBX7o41d3nEE&path=Homestuck%20Sound%20Test%2F~Disc%203~%20Unreleased%20Homestuck%20Music%20Team%20Music%2F059%20Skaian%20Shuffle.mp3&mode=default
 -------------------------------------------------------------------------------
+Track: Strider Showdown
+Artist: Bill Bolin
+References: Beatdown (Strider Style)
+Has URLs: no
+-------------------------------------------------------------------------------
 Track: The Brave and the Bronze
 Artist: Yan Rodriguez
 URLs: https://www.youtube.com/watch?v=pK4-g7TiWQA
diff --git a/artists.txt b/artists.txt
index 0ab5ab01..28b042bf 100644
--- a/artists.txt
+++ b/artists.txt
@@ -225,6 +225,8 @@ URLs:
 -------------------------------------------------------------------------------
 Artist: Beta
 -------------------------------------------------------------------------------
+Artist: Bill Bolin
+-------------------------------------------------------------------------------
 Artist: Blackhole
 URLs:
 - https://not-terezi-pyrope.tumblr.com/
@@ -533,6 +535,9 @@ Artist: Devin H-S
 -------------------------------------------------------------------------------
 Artist: devonianecho
 -------------------------------------------------------------------------------
+Artist: Diane Warren
+URLs: https://en.wikipedia.org/wiki/Diane_Warren
+-------------------------------------------------------------------------------
 Artist: Difarem
 URLs:
 - https://soundcloud.com/difarem
@@ -1692,8 +1697,6 @@ Artist: Richard Gung
 Artist: Rikuru
 -------------------------------------------------------------------------------
 Artist: rilez
--------------------------------------------------------------------------------
-Artist: Rilez
 URLs:
 - https://soundcloud.com/rilezfp
 -------------------------------------------------------------------------------
@@ -2137,6 +2140,9 @@ URLs:
 - https://soundcloud.com/musical_panini
 - https://musicalpaninigrill.tumblr.com/
 -------------------------------------------------------------------------------
+Artist: Trisha Yearwood
+URLs: https://en.wikipedia.org/wiki/Trisha_Yearwood
+-------------------------------------------------------------------------------
 Artist: Tristan Scroggins
 URLs:
 - https://tristanscroggins.bandcamp.com/
diff --git a/client.js b/client.js
index ade15878..ffdab814 100644
--- a/client.js
+++ b/client.js
@@ -7,7 +7,7 @@
 
 const officialAlbumData = albumData.filter(album => !album.isFanon);
 const fandomAlbumData = albumData.filter(album => album.isFanon);
-const artistNames = C.getArtistNames(albumData, flashData);
+const artistNames = artistData.filter(artist => !artist.alias).map(artist => artist.name);
 const allTracks = C.getAllTracks(albumData);
 
 function pick(array) {
diff --git a/common.js b/common.js
index 208e33b6..505cfb83 100644
--- a/common.js
+++ b/common.js
@@ -70,11 +70,6 @@ const C = {
     // sorted 8y date.
     getAllTracks: albumData => C.sortByDate(albumData.reduce((acc, album) => acc.concat(album.tracks), [])),
 
-    getArtistNames: (albumData, flashData) => Array.from(new Set([
-        ...albumData.reduce((acc, album) => acc.concat((album.coverArtists || []).map(({ who }) => who), album.tracks.reduce((acc, track) => acc.concat(track.artists, (track.coverArtists || []).map(({ who }) => who)), [])), []),
-        ...flashData.filter(flash => !flash.act8r8k).reduce((acc, flash) => acc.concat(flash.contributors.map(({ who }) => who)), [])
-    ])),
-
     getKebabCase: name => name.split(' ').join('-').replace(/&/g, 'and').replace(/[^a-zA-Z0-9\-]/g, '').replace(/-{2,}/g, '-').replace(/^-+|-+$/g, '').toLowerCase(),
 
     // Terri8le hack: since artists aren't really o8jects and don't have proper
diff --git a/upd8.js b/upd8.js
index 26e1314d..fecdc442 100644
--- a/upd8.js
+++ b/upd8.js
@@ -543,9 +543,11 @@ async function processAlbumDataFile(file) {
 
         const artDateValue = new Date(artDate);
 
-        const trackURLs = (getListField(section, 'URLs') || []).filter(Boolean);
+        const hasURLs = getBasicField(section, 'Has URLs') !== 'no';
 
-        if (!trackURLs.length) {
+        const trackURLs = hasURLs && (getListField(section, 'URLs') || []).filter(Boolean);
+
+        if (hasURLs && !trackURLs.length) {
             return {error: `The track "${trackName}" should have at least one URL specified.`};
         }
 
@@ -682,6 +684,10 @@ function stringifyFlashData() {
     }, 1);
 }
 
+function stringifyArtistData() {
+    return JSON.stringify(artistData, null, 1);
+}
+
 // 8asic function for writing any site page. Handles all the 8asename,
 // directory, and site-template shenanigans!
 async function writePage(directoryParts, titleOrHead, body) {
@@ -836,6 +842,7 @@ function writeMiscellaneousPages() {
             // Yo, this file is gener8ted. Don't mess around with it!
             window.albumData = ${stringifyAlbumData()};
             window.flashData = ${stringifyFlashData()};
+            window.artistData = ${stringifyArtistData()};
         `)
     ]);
 }
@@ -864,21 +871,21 @@ async function writeAlbumPage(album) {
                     <a id="cover-art" href="${getAlbumCover(album)}"><img src="${getAlbumCover(album)}" alt="cover art"></a>
                     <h1>${album.name}</h1>
                     <p>
-                        ${album.artists && `By ${getArtistString(album.artists)}.<br>`}
+                        ${album.artists && `By ${getArtistString(album.artists)}.<br>` || `<!-- (here: Full-album musician credits) -->`}
                         ${album.coverArtists && `Cover art by ${joinNoOxford(album.coverArtists.map(({ who, what }) => fixWS`
                             <a href="${C.ARTIST_DIRECTORY}/${C.getArtistDirectory(who)}/index.html">${who}</a>${what && ` (${getContributionString({what})})`}
-                        `))}.<br>`}
+                        `))}.<br>` || `<!-- (here: Cover art credits) -->`}
                         Released ${getDateString(album)}.
-                        ${+album.artDate !== +album.date && `<br>Art released ${getDateString({date: album.artDate})}.`}
+                        ${+album.artDate !== +album.date && `<br>Art released ${getDateString({date: album.artDate})}.` || `<!-- (here: Cover art release date) -->`}
                     </p>
-                    ${album.urls.length && `<p>Listen on ${joinNoOxford(album.urls.map(url => fancifyURL(url, {album: true})), 'or')}.</p>`}
+                    ${album.urls.length && `<p>Listen on ${joinNoOxford(album.urls.map(url => fancifyURL(url, {album: true})), 'or')}.</p>` || `<!-- (here: Listen on...) -->`}
                     <${listTag}>
                         ${album.tracks.map(track => fixWS`
                             <li>
                                 <a href="${C.TRACK_DIRECTORY}/${track.directory}/index.html">${track.name}</a>
                                 ${track.artists !== album.artists && fixWS`
                                     <span class="by">by ${getArtistString(track.artists)}</span>
-                                `}
+                                ` || `<!-- (here: Track-specific musician credits) -->`}
                             </li>
                         `).join('\n')}
                     </${listTag}>
@@ -887,7 +894,7 @@ async function writeAlbumPage(album) {
                         <blockquote>
                             ${transformMultiline(album.commentary)}
                         </blockquote>
-                    `}
+                    ` || `<!-- (here: Full-album commentary) -->`}
                 </div>
             </div>
         </body>
@@ -913,25 +920,32 @@ async function writeTrackPage(track) {
                     <a href="${getTrackCover(track)}" id="cover-art"><img src="${getTrackCover(track)}" alt="cover art"></a>
                     <h1>${track.name}</h1>
                     <p>
-                        By ${getArtistString(track.artists)}.<br>
-                        ${track.coverArtists && `Cover art by ${joinNoOxford(track.coverArtists.map(({ who, what }) => fixWS`
+                        By ${getArtistString(track.artists)}.
+                        ${track.coverArtists && `<br>Cover art by ${joinNoOxford(track.coverArtists.map(({ who, what }) => fixWS`
                             <a href="${C.ARTIST_DIRECTORY}/${C.getArtistDirectory(who)}/index.html">${who}</a>${what && ` (${getContributionString({what})})`}
-                        `))}.<br>`}
-                        Released ${getDateString(track)}.
-                        ${+track.artDate !== +track.date && `<br>Art released ${getDateString({date: track.artDate})}.`}
+                        `))}.` || `<!-- (here: Cover art credits) -->`}
+                        ${track.album.directory !== C.UNRELEASED_TRACKS_DIRECTORY && `<br>Released ${getDateString(track)}.` || `<!-- (here: Track release date) -->`}
+                        ${+track.artDate !== +track.date && `<br>Art released ${getDateString({date: track.artDate})}.` || `<!-- (here: Cover art release date, if it differs) -->`}
                     </p>
-                    <p>Listen on ${joinNoOxford(track.urls.map(fancifyURL), 'or')}.</p>
+                    ${track.urls.length ? fixWS`
+                        <p>Listen on ${joinNoOxford(track.urls.map(fancifyURL), 'or')}.</p>
+                    ` : fixWS`
+                        <p>This track has no URLs at which it can be listened.</p>
+                    `}
                     ${track.contributors.length && fixWS`
                         <p>Contributors:</p>
                         <ul>
                             ${track.contributors.map(({ who, what }) => fixWS`
-                                <li>${artistNames.includes(who)
-                                    ? `<a href="${C.ARTIST_DIRECTORY}/${C.getArtistDirectory(who)}/index.html">${who}</a>`
-                                    : who
-                                } ${what && `(${getContributionString({what})})`}</li>
+                                <li>
+                                    ${artistNames.includes(who)
+                                        ? `<a href="${C.ARTIST_DIRECTORY}/${C.getArtistDirectory(who)}/index.html">${who}</a>`
+                                        : who
+                                    }
+                                    ${what && `(${getContributionString({what})})` || `<!-- (here: Contribution details) -->`}
+                                </li>
                             `).join('\n')}
                         </ul>
-                    `}
+                    ` || `<!-- (here: Track contributor credits) -->`}
                     ${tracksReferenced.length && fixWS`
                         <p>Tracks that <i>${track.name}</i> references:</p>
                         <ul>
@@ -942,7 +956,7 @@ async function writeTrackPage(track) {
                                 </li>
                             `).join('\n')}
                         </ul>
-                    `}
+                    ` || `<!-- (here: List of tracks referenced) -->`}
                     ${tracksThatReference.length && fixWS`
                         <p>Tracks that reference <i>${track.name}</i>:</p>
                         <dl>
@@ -956,7 +970,7 @@ async function writeTrackPage(track) {
                                         </li>
                                     `).join('\n')}
                                 </ul></dd>
-                            `}
+                            ` || `<!-- (here: Official tracks) -->`}
                             ${ttrFanon.length && fixWS`
                                 <dt>Fandom:</dt>
                                 <dd><ul>
@@ -967,27 +981,27 @@ async function writeTrackPage(track) {
                                         </li>
                                     `).join('\n')}
                                 </ul></dd>
-                            `}
+                            ` || `<!-- (here: Fandom tracks) -->`}
                         </dl>
-                    `}
+                    ` || `<!-- (here: Tracks that reference this track) -->`}
                     ${flashesThatFeature.length && fixWS`
                         <p>Flashes &amp; games that feature <i>${track.name}</i>:</p>
                         <ul>
                             ${flashesThatFeature.map(flash => `<li>${getFlashLinkHTML(flash)}</li>`).join('\n')}
                         </ul>
-                    `}
+                    ` || `<!-- (here: Flashes that feature this track) -->`}
                     ${track.lyrics && fixWS`
                         <p>Lyrics:</p>
                         <blockquote>
                             ${transformMultiline(track.lyrics)}
                         </blockquote>
-                    `}
+                    ` || `<!-- (here: Track lyrics) -->`}
                     ${track.commentary && fixWS`
                         <p>Artist commentary:</p>
                         <blockquote>
                             ${transformMultiline(track.commentary)}
                         </blockquote>
-                    `}
+                    ` || `<!-- (here: Track commentary) -->`}
                 </div>
             </div>
         </body>
@@ -1023,9 +1037,9 @@ async function writeArtistPage(artistName) {
         return fixWS`
             <li title="${th(i + 1)} track by ${artistName}; ${th(track.album.tracks.indexOf(track) + 1)} in ${track.album.name}">
                 <a href="${C.TRACK_DIRECTORY}/${track.directory}/index.html" style="${getThemeString(track.album.theme)}">${track.name}</a>
-                ${track.artists.includes(artistName) && track.artists.length > 1 && `<span class="contributed">(with ${getArtistString(track.artists.filter(a => a !== artistName))})</span>`}
-                ${contrib.what && `<span class="contributed">(${getContributionString(contrib) || 'contributed'})</span>`}
-                ${flashes.length && `<br><span class="flashes">(Featured in ${joinNoOxford(flashes.map(getFlashLinkHTML))})</span></br>`}
+                ${track.artists.includes(artistName) && track.artists.length > 1 && `<span class="contributed">(with ${getArtistString(track.artists.filter(a => a !== artistName))})</span>` || `<!-- (here: Co-artist credits) -->`}
+                ${contrib.what && `<span class="contributed">(${getContributionString(contrib) || 'contributed'})</span>` || `<!-- (here: Contribution details) -->`}
+                ${flashes.length && `<br><span class="flashes">(Featured in ${joinNoOxford(flashes.map(getFlashLinkHTML))})</span></br>` || `<!-- (here: Flashes featuring this track) -->`}
             </li>
         `;
     });
@@ -1184,7 +1198,7 @@ async function writeFlashPage(flash) {
                     / <a href="${C.FLASH_DIRECTORY}/${kebab}/index.html">${flash.name}</a>
                 </h2>
                 <div>
-                    ${chronologyButtons(flash, {
+                    ${chronologyLinks(flash, {
                         headingWord: 'flash/game',
                         sourceData: flashData,
                         filters: [
@@ -1193,47 +1207,46 @@ async function writeFlashPage(flash) {
                                 toArtist: ({ who }) => who
                             }
                         ]
-                    })}
+                    }) || `<!-- (here: Contributor chronology links) -->`}
                 </div>
             </div>
             <div class="columns">
                 <div id="sidebar">
                     <h1><a href="${C.FLASH_DIRECTORY}/index.html">Flashes &amp; Games</a></h1>
                     <dl>
-                        ${flashData.filter(f => f.act8r8k).map(({ act, theme }) => fixWS`
-                            ${act.startsWith('Act 1') && fixWS`
-                                <dt class="side ${side === 1 && 'current'}"><a href="${C.FLASH_DIRECTORY}/${getFlashDirectory(flashData.find(f => !f.act8r8k && f.act.startsWith('Act 1')))}/index.html" style="--fg-color: #4ac925">Side 1 (Acts 1-5)</a></dt>
-                            `}
-                            ${act.startsWith('Act 6 Act 1') && fixWS`
-                                <dt class="side ${side === 2 && 'current'}"><a href="${C.FLASH_DIRECTORY}/${getFlashDirectory(flashData.find(f => !f.act8r8k && f.act.startsWith('Act 6')))}/index.html" style="--fg-color: #1076a2">Side 2 (Acts 6-7)</a></dt>
-                            `}
-                            ${act.startsWith('Hiveswap') && fixWS`
-                                <dt class="side ${side === 0 && 'current'}"><a href="${C.FLASH_DIRECTORY}/${getFlashDirectory(flashData.find(f => !f.act8r8k && f.act.startsWith('Hiveswap')))}/index.html" style="--fg-color: #008282">Outside Canon (Misc. Games)</a></dt>
-                            `}
-                            ${(
-                                (flashData.findIndex(f => f.act === act) < act6) ? (side === 1) :
-                                ((flashData.findIndex(f => f.act === act) < outsideCanon) ? (side === 2) :
-                                true)
-                            ) && fixWS`
-                                <dt class="${act === flash.act ? 'current' : ''}"><a href="${C.FLASH_DIRECTORY}/${getFlashDirectory(flashData.find(f => !f.act8r8k && f.act === act))}/index.html" style="${getThemeString(theme)}">${act}</a></dt>
-                            `}
-                            ${act === flash.act && fixWS`
+                        ${flashData.filter(f => f.act8r8k).filter(({ act }) =>
+                            act.startsWith('Act 1') ||
+                            act.startsWith('Act 6 Act 1') ||
+                            act.startsWith('Hiveswap') ||
+                            (
+                                flashData.findIndex(f => f.act === act) < act6 ? side === 1 :
+                                flashData.findIndex(f => f.act === act) < outsideCanon ? side === 2 :
+                                true
+                            )
+                        ).flatMap(({ act, theme }) => [
+                            act.startsWith('Act 1') && `<dt${classes('side', side === 1 && 'current')}><a href="${C.FLASH_DIRECTORY}/${getFlashDirectory(flashData.find(f => !f.act8r8k && f.act.startsWith('Act 1')))}/index.html" style="--fg-color: #4ac925">Side 1 (Acts 1-5)</a></dt>`
+                            || act.startsWith('Act 6 Act 1') && `<dt${classes('side', side === 2 && 'current')}><a href="${C.FLASH_DIRECTORY}/${getFlashDirectory(flashData.find(f => !f.act8r8k && f.act.startsWith('Act 6')))}/index.html" style="--fg-color: #1076a2">Side 2 (Acts 6-7)</a></dt>`
+                            || act.startsWith('Hiveswap') && `<dt${classes('side', side === 0 && 'current')}><a href="${C.FLASH_DIRECTORY}/${getFlashDirectory(flashData.find(f => !f.act8r8k && f.act.startsWith('Hiveswap')))}/index.html" style="--fg-color: #008282">Outside Canon (Misc. Games)</a></dt>`,
+                            (
+                                flashData.findIndex(f => f.act === act) < act6 ? side === 1 :
+                                flashData.findIndex(f => f.act === act) < outsideCanon ? side === 2 :
+                                true
+                            ) && `<dt${classes(act === flash.act && 'current')}><a href="${C.FLASH_DIRECTORY}/${getFlashDirectory(flashData.find(f => !f.act8r8k && f.act === act))}/index.html" style="${getThemeString(theme)}">${act}</a></dt>`,
+                            act === flash.act && fixWS`
                                 <dd><ul>
                                     ${flashData.filter(f => !f.act8r8k && f.act === act).map(f => fixWS`
-                                        <li class="${f === flash ? 'current' : ''}">
-                                            <a href="${C.FLASH_DIRECTORY}/${getFlashDirectory(f)}/index.html" style="${getThemeString(f.theme)}">${f.name}</a>
-                                        </li>
+                                        <li${classes(f === flash && 'current')}><a href="${C.FLASH_DIRECTORY}/${getFlashDirectory(f)}/index.html" style="${getThemeString(f.theme)}">${f.name}</a></li>
                                     `).join('\n')}
                                 </ul></dd>
-                            `}
-                        `).join('\n')}
+                            `
+                        ]).filter(Boolean).join('\n')}
                     </dl>
                 </div>
                 <div id="content">
                     <h1>${flash.name}</h1>
                     <a id="cover-art" href="${getFlashCover(flash)}"><img src="${getFlashCover(flash)}" alt="cover art"></a>
                     <p>Released ${getDateString(flash)}.</p>
-                    ${flash.page && `<p>Play on <a href="${getFlashLink(flash)}">Homestuck</a> (${isNaN(Number(flash.page)) ? 'secret page' : `page ${flash.page}`}).</p>`}
+                    ${flash.page && `<p>Play on <a href="${getFlashLink(flash)}">Homestuck</a> (${isNaN(Number(flash.page)) ? 'secret page' : `page ${flash.page}`}).</p>` || `<!-- (here: Play-online links) -->`}
                     ${flash.contributors.length && fixWS`
                         <p>Contributors:</p>
                         <ul>
@@ -1241,10 +1254,10 @@ async function writeFlashPage(flash) {
                                 <li>${artistNames.includes(who)
                                     ? `<a href="${C.ARTIST_DIRECTORY}/${C.getArtistDirectory(who)}/index.html">${who}</a>`
                                     : who
-                                } ${what && `(${getContributionString({what})})`}</li>
+                                }${what && ` (${getContributionString({what})})`}</li>
                             `).join('\n')}
                         </ul>
-                    `}
+                    ` || `<!-- (here: Flash contributor details) -->`}
                     <p>Tracks featured in <i>${flash.name.replace(/\.$/, '')}</i>:</p>
                     <ul>
                         ${flash.tracks.map(ref => {
@@ -1429,13 +1442,13 @@ function writeListingPages() {
                                     <blockquote style="${getThemeString(album.theme)}">
                                         ${transformMultiline(album.commentary)}
                                     </blockquote>
-                                `}
+                                ` || `<!-- (here: Full-album commentary) -->`}
                                 ${tracks.filter(t => t.commentary).map(track => fixWS`
                                     <h3 id="${track.directory}"><a href="${C.TRACK_DIRECTORY}/${track.directory}/index.html" style="${getThemeString(album.theme)}">${track.name}</a></h3>
                                     <blockquote style="${getThemeString(album.theme)}">
                                         ${transformMultiline(track.commentary)}
                                     </blockquote>
-                                `).join('\n')}
+                                `).join('\n') || `<!-- (here: Per-track commentary) -->`}
                             `)
                             .join('\n')
                         }
@@ -1516,10 +1529,10 @@ function generateHeaderForListings(listingDescriptors, currentDirectoryParts) {
                 ? currentDirectoryParts.join('/')
                 : currentDirectoryParts
             }/index.html">` + (
-                (currentDirectoryParts === 'all-commentary') ? `All Commentary` :
-                (currentDirectoryParts === 'random') ? `Random Pages` :
+                currentDirectoryParts === 'all-commentary' ? `All Commentary` :
+                currentDirectoryParts === 'random' ? `Random Pages` :
                 listingDescriptors.find(([ ldDirectoryParts ]) => ldDirectoryParts === currentDirectoryParts)[1]
-            ) + `</a>`}
+            ) + `</a>` || `<!-- (here: Link to current listing) -->`}
         </h2>
     `;
 }
@@ -1535,14 +1548,14 @@ function generateLinkIndexForListings(listingDescriptors, currentDirectoryParts)
     return fixWS`
         <ul>
             ${listingDescriptors.map(([ ldDirectoryParts, ldTitle ]) => fixWS`
-                <li class="${currentDirectoryParts === ldDirectoryParts && 'current'}">
+                <li${classes(currentDirectoryParts === ldDirectoryParts && 'current')}>
                     <a href="${C.LISTING_DIRECTORY}/${ldDirectoryParts.join('/')}/index.html">${ldTitle}</a>
                 </li>
             `).join('\n')}
-            <li class="${currentDirectoryParts === 'all-commentary' && 'current'}">
+            <li${classes(currentDirectoryParts === 'all-commentary' && 'current')}>
                 <a href="${C.LISTING_DIRECTORY}/all-commentary/index.html">All Commentary</a>
             </li>
-            <li class="${currentDirectoryParts === 'random' && 'current'}">
+            <li${classes(currentDirectoryParts === 'random' && 'current')}>
                 <a href="${C.LISTING_DIRECTORY}/random/index.html">Random Pages</a>
             </li>
         </ul>
@@ -1664,7 +1677,7 @@ function fancifyURL(url, {album = false} = {}) {
     }</a>`;
 }
 
-function chronologyButtons(currentTrack, {
+function chronologyLinks(currentTrack, {
     mapProperty,
     toArtist,
     filters, // {property, toArtist}
@@ -1703,7 +1716,7 @@ function chronologyButtons(currentTrack, {
         return fixWS`
             <div class="chronology">
                 <span class="heading">${heading}</span>
-                ${parts.length && `<span class="buttons">(${parts.join(', ')})</span>`}
+                ${parts.length && `<span class="buttons">(${parts.join(', ')})</span>` || `<!-- (here: Next/previous links) -->`}
             </div>
         `;
     }).filter(Boolean).join('\n');
@@ -1717,15 +1730,31 @@ function generateHeaderForAlbum(album, currentTrack = null) {
         <h2>
             <a href="index.html">Home</a>
             / <a href="${C.ALBUM_DIRECTORY}/${album.directory}/index.html">${album.name}</a>
-            ${currentTrack && `/ <a href="${C.TRACK_DIRECTORY}/${currentTrack.directory}/index.html">${currentTrack.name}</a>`}
-            <span>${album.tracks.length > 1 && `(${[
-                previous && `<a href="${C.TRACK_DIRECTORY}/${previous.directory}/index.html" title="${previous.name}">Previous</a>`,
-                next && `<a href="${C.TRACK_DIRECTORY}/${next.directory}/index.html" title="${next.name}">Next</a>`,
-                `<a href="${C.JS_DISABLED_DIRECTORY}/index.html" data-random="track-in-album">${currentTrack ? 'Random' : 'Random Track'}</a>`
-            ].filter(Boolean).join(', ')})`}</span>
+            ${currentTrack && `/ <a href="${C.TRACK_DIRECTORY}/${currentTrack.directory}/index.html">${currentTrack.name}</a>` || `<!-- (here: Link to current track) --> `}
+            ${album.tracks.length > 1 && fixWS`
+                <span>(${[
+                    previous && `<a href="${C.TRACK_DIRECTORY}/${previous.directory}/index.html" title="${previous.name}">Previous</a>`,
+                    next && `<a href="${C.TRACK_DIRECTORY}/${next.directory}/index.html" title="${next.name}">Next</a>`,
+                    `<a href="${C.JS_DISABLED_DIRECTORY}/index.html" data-random="track-in-album">${currentTrack ? 'Random' : 'Random Track'}</a>`
+                ].filter(Boolean).join(', ')})</span>
+            ` || `<!-- (here: Album navigation links) -->`}
         </h2>
         <div>
-            ${currentTrack === null && chronologyButtons(album, {
+            ${currentTrack && chronologyLinks(currentTrack, {
+                headingWord: 'track',
+                sourceData: allTracks,
+                filters: [
+                    {
+                        mapProperty: 'artists',
+                        toArtist: artist => artist
+                    },
+                    {
+                        mapProperty: 'contributors',
+                        toArtist: ({ who }) => who
+                    }
+                ]
+            }) || `<!-- (here: Musician & contributors chronology links) -->`}
+            ${chronologyLinks(currentTrack || album, {
                 headingWord: 'cover art',
                 sourceData: justEverythingSortedByArtDateMan,
                 filters: [
@@ -1734,33 +1763,7 @@ function generateHeaderForAlbum(album, currentTrack = null) {
                         toArtist: ({ who }) => who
                     }
                 ]
-            })}
-            ${currentTrack && fixWS`
-                ${chronologyButtons(currentTrack, {
-                    headingWord: 'track',
-                    sourceData: allTracks,
-                    filters: [
-                        {
-                            mapProperty: 'artists',
-                            toArtist: artist => artist
-                        },
-                        {
-                            mapProperty: 'contributors',
-                            toArtist: ({ who }) => who
-                        }
-                    ]
-                })}
-                ${chronologyButtons(currentTrack, {
-                    headingWord: 'cover art',
-                    sourceData: justEverythingSortedByArtDateMan,
-                    filters: [
-                        {
-                            mapProperty: 'coverArtists',
-                            toArtist: ({ who }) => who
-                        }
-                    ]
-                })}
-            `}
+            }) || `<!-- (here: Cover art chronology links) -->`}
         </div>
     `;
 }
@@ -1770,9 +1773,7 @@ function generateSidebarForAlbum(album, currentTrack = null) {
     return fixWS`
         <h1><a href="${C.ALBUM_DIRECTORY}/${album.directory}/index.html">${album.name}</a></h1>
         <${listTag}>
-            ${album.tracks.map(track => fixWS`
-                <li class="${track === currentTrack ? 'current' : ''}"><a href="${C.TRACK_DIRECTORY}/${track.directory}/index.html">${track.name}</a></li>
-            `).join('\n')}
+            ${album.tracks.map(track => `<li${classes(track === currentTrack && 'current')}><a href="${C.TRACK_DIRECTORY}/${track.directory}/index.html">${track.name}</a></li>`).join('\n')}
         </${listTag}>
     `
 }
@@ -1858,6 +1859,11 @@ function rebaseURLs(directory, html) {
     });
 }
 
+function classes(...args) {
+    const values = args.filter(Boolean);
+    return values.length ? ` class="${values.join(' ')}"` : '';
+}
+
 async function main() {
     // 8ut wait, you might say, how do we know which al8um these data files
     // correspond to???????? You wouldn't dare suggest we parse the actual
@@ -1925,7 +1931,12 @@ async function main() {
     }
 
     allTracks = C.getAllTracks(albumData);
-    artistNames = C.getArtistNames(albumData, flashData);
+    artistNames = Array.from(new Set([
+        ...albumData.reduce((acc, album) => acc.concat((album.coverArtists || []).map(({ who }) => who), album.tracks.reduce((acc, track) => acc.concat(track.artists, (track.coverArtists || []).map(({ who }) => who)), [])), []),
+        ...flashData.filter(flash => !flash.act8r8k).reduce((acc, flash) => acc.concat(flash.contributors.map(({ who }) => who)), []),
+        ...artistData.filter(artist => !artist.alias).map(artist => artist.name)
+    ]));
+
     artistNames.sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : a.toLowerCase() > b.toLowerCase() ? 1 : 0);
 
     officialAlbumData = albumData.filter(album => !album.isFanon);