« 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/data/things.js96
-rw-r--r--src/data/validators.js6
-rw-r--r--src/page/artist.js26
-rwxr-xr-xsrc/upd8.js66
-rw-r--r--src/util/cli.js49
5 files changed, 152 insertions, 91 deletions
diff --git a/src/data/things.js b/src/data/things.js
index 02697319..5aa43f30 100644
--- a/src/data/things.js
+++ b/src/data/things.js
@@ -358,7 +358,27 @@ Thing.common = {
         update: {
             validate: validateArrayItems(validateInstanceOf(thingClass))
         }
-    })
+    }),
+
+    // This one's kinda tricky: it parses artist "references" from the
+    // commentary content, and finds the matching artist for each reference.
+    // This is mostly useful for credits and listings on artist pages.
+    commentatorArtists: () => ({
+        flags: {expose: true},
+
+        expose: {
+            dependencies: ['artistData', 'commentary'],
+
+            compute: ({ artistData, commentary }) => (
+                (artistData && commentary
+                    ? Array.from(new Set((Array
+                        .from(commentary
+                            .replace(/<\/?b>/g, '')
+                            .matchAll(/<i>(?<who>.*?):<\/i>/g))
+                        .map(({ groups: {who} }) => find.artist(who, {wikiData: {artistData}, quiet: true})))))
+                    : []))
+        }
+    }),
 };
 
 // Get a reference to a thing (e.g. track:showtime-piano-refrain), using its
@@ -445,6 +465,8 @@ Album.propertyDescriptors = {
     // Previously known as: (album).artists
     artistContribs: Thing.common.dynamicContribs('artistContribsByRef'),
 
+    commentatorArtists: Thing.common.commentatorArtists(),
+
     tracks: {
         flags: {expose: true},
 
@@ -587,6 +609,8 @@ Track.propertyDescriptors = {
 
     // Expose only
 
+    commentatorArtists: Thing.common.commentatorArtists(),
+
     album: {
         flags: {expose: true},
 
@@ -688,6 +712,18 @@ Track.propertyDescriptors = {
 
 // -> Artist
 
+Artist.filterByContrib = (thingDataProperty, contribsProperty) => ({
+    flags: {expose: true},
+
+    expose: {
+        dependencies: [thingDataProperty],
+
+        compute: ({ [thingDataProperty]: thingData, [Artist.instance]: artist }) => (
+            thingData?.filter(({ [contribsProperty]: contribs }) => (
+                contribs?.some(contrib => contrib.who === artist))))
+    }
+});
+
 Artist.propertyDescriptors = {
     // Update & expose
 
@@ -708,7 +744,10 @@ Artist.propertyDescriptors = {
 
     // Update only
 
+    albumData: Thing.common.wikiData(Album),
     artistData: Thing.common.wikiData(Artist),
+    flashData: Thing.common.wikiData(Flash),
+    trackData: Thing.common.wikiData(Track),
 
     // Expose only
 
@@ -725,18 +764,53 @@ Artist.propertyDescriptors = {
         }
     },
 
-    // albumsAsCoverArtist
-    // albumsAsWallpaperArtist
-    // albumsAsBannerArtist
-    // albumsAsCommentator
+    tracksAsArtist: Artist.filterByContrib('trackData', 'artistContribs'),
+    tracksAsContributor: Artist.filterByContrib('trackData', 'contributorContribs'),
+    tracksAsCoverArtist: Artist.filterByContrib('trackData', 'coverArtistContribs'),
+
+    tracksAsAny: {
+        flags: {expose: true},
 
-    // tracksAsAny
-    // tracksAsArtist
-    // tracksAsContributor
-    // tracksAsCoverArtist
-    // tracksAsCommentator
+        expose: {
+            dependencies: ['trackData'],
+
+            compute: ({ trackData, [Artist.instance]: artist }) => (
+                trackData?.filter(track => (
+                    [
+                        ...track.artistContribs,
+                        ...track.contributorContribs,
+                        ...track.coverArtistContribs
+                    ].some(({ who }) => who === artist))))
+        }
+    },
+
+    tracksAsCommentator: {
+        flags: {expose: true},
+
+        expose: {
+            dependencies: ['trackData'],
+
+            compute: ({ trackData, [Artist.instance]: artist }) => (
+                trackData.filter(({ commentatorArtists }) => commentatorArtists?.includes(artist)))
+        }
+    },
+
+    albumsAsCoverArtist: Artist.filterByContrib('albumData', 'coverArtistContribs'),
+    albumsAsWallpaperArtist: Artist.filterByContrib('albumData', 'wallpaperArtistContribs'),
+    albumsAsBannerArtist: Artist.filterByContrib('albumData', 'bannerArtistContribs'),
+
+    albumsAsCommentator: {
+        flags: {expose: true},
+
+        expose: {
+            dependencies: ['albumData'],
+
+            compute: ({ albumData, [Artist.instance]: artist }) => (
+                albumData.filter(({ commentatorArtists }) => commentatorArtists?.includes(artist)))
+        }
+    },
 
-    // flashesAsContributor
+    flashesAsContributor: Artist.filterByContrib('flashData', 'contributorContribs'),
 };
 
 // -> Group
diff --git a/src/data/validators.js b/src/data/validators.js
index ca10833e..8f4d06d7 100644
--- a/src/data/validators.js
+++ b/src/data/validators.js
@@ -1,6 +1,6 @@
 import { withAggregate } from '../util/sugar.js';
 
-import { color, ENABLE_COLOR } from '../util/cli.js';
+import { color, ENABLE_COLOR, decorateTime } from '../util/cli.js';
 
 import { inspect as nodeInspect } from 'util';
 
@@ -155,7 +155,7 @@ function validateArrayItemsHelper(itemValidator) {
 export function validateArrayItems(itemValidator) {
     const fn = validateArrayItemsHelper(itemValidator);
 
-    return array => {
+    return decorateTime('validateArrayItems -> work', array => {
         isArray(array);
 
         withAggregate({message: 'Errors validating array items'}, ({ wrap }) => {
@@ -163,7 +163,7 @@ export function validateArrayItems(itemValidator) {
         });
 
         return true;
-    };
+    });
 }
 
 export function validateInstanceOf(constructor) {
diff --git a/src/page/artist.js b/src/page/artist.js
index d0128050..6a465604 100644
--- a/src/page/artist.js
+++ b/src/page/artist.js
@@ -61,7 +61,7 @@ export function write(artist, {wikiData}) {
     });
 
     const artListChunks = chunkByProperties(sortByDate(artThingsAll.flatMap(thing =>
-        (['coverArtists', 'wallpaperArtists', 'bannerArtists']
+        (['coverArtistContribs', 'wallpaperArtistContribs', 'bannerArtistContribs']
             .map(key => getArtistsAndContrib(thing, key))
             .filter(({ contrib }) => contrib)
             .map(props => ({
@@ -91,14 +91,14 @@ export function write(artist, {wikiData}) {
             date: +track.date,
             album: track.album,
             duration: track.duration,
-            artists: (track.artists.some(({ who }) => who === artist)
-                ? track.artists.filter(({ who }) => who !== artist)
-                : track.contributors.filter(({ who }) => who !== artist)),
+            artists: (track.artistContribs.some(({ who }) => who === artist)
+                ? track.artistContribs.filter(({ who }) => who !== artist)
+                : track.contributorContribs.filter(({ who }) => who !== artist)),
             contrib: {
                 who: artist,
                 what: [
-                    track.artists.find(({ who }) => who === artist)?.what,
-                    track.contributors.find(({ who }) => who === artist)?.what
+                    track.artistContribs.find(({ who }) => who === artist)?.what,
+                    track.contributorContribs.find(({ who }) => who === artist)?.what
                 ].filter(Boolean).join(', ')
             }
         })), ['date', 'album'])
@@ -138,7 +138,7 @@ export function write(artist, {wikiData}) {
                 // want to show the full list of other contri8utors inline.
                 // (It can often 8e very, very large!)
                 artists: [],
-                contrib: flash.contributors.find(({ who }) => who === artist)
+                contrib: flash.contributorContribs.find(({ who }) => who === artist)
             })), ['act'])
             .map(({ act, chunk }) => ({
                 act, chunk,
@@ -241,21 +241,21 @@ export function write(artist, {wikiData}) {
 
             return {
                 albums: {
-                    asCoverArtist: artist.albumsAsCoverArtist?.map(serializeArtistsAndContrib('coverArtists')),
-                    asWallpaperArtist: artist.albumsAsWallpaperArtist?.map(serializeArtistsAndContrib('wallpaperArtists')),
-                    asBannerArtist: artist.albumsAsBannerArtist?.map(serializeArtistsAndContrib('bannerArtists'))
+                    asCoverArtist: artist.albumsAsCoverArtist?.map(serializeArtistsAndContrib('coverArtistContribs')),
+                    asWallpaperArtist: artist.albumsAsWallpaperArtist?.map(serializeArtistsAndContrib('wallpaperArtistContribs')),
+                    asBannerArtist: artist.albumsAsBannerArtist?.map(serializeArtistsAndContrib('bannerArtistContribs'))
                 },
                 flashes: wikiInfo.enableFlashesAndGames ? {
                     asContributor: (artist.flashesAsContributor
-                        ?.map(flash => getArtistsAndContrib(flash, 'contributors'))
+                        ?.map(flash => getArtistsAndContrib(flash, 'contributorContribs'))
                         .map(({ contrib, thing: flash }) => ({
                             link: serializeLink(flash),
                             contribution: contrib.what
                         })))
                 } : null,
                 tracks: {
-                    asArtist: artist.tracksAsArtist.map(serializeArtistsAndContrib('artists')),
-                    asContributor: artist.tracksAsContributor.map(serializeArtistsAndContrib('contributors')),
+                    asArtist: artist.tracksAsArtist.map(serializeArtistsAndContrib('artistContribs')),
+                    asContributor: artist.tracksAsContributor.map(serializeArtistsAndContrib('contributorContribs')),
                     chunked: {
                         released: serializeTrackListChunks(releasedTrackListChunks),
                         unreleased: serializeTrackListChunks(unreleasedTrackListChunks)
diff --git a/src/upd8.js b/src/upd8.js
index 81275ad3..45538260 100755
--- a/src/upd8.js
+++ b/src/upd8.js
@@ -2736,56 +2736,26 @@ async function main() {
     // result (many of which are required for page HTML generation).
 
     function linkDataArrays() {
-        for (const album of WD.albumData) {
-            album.artistData = WD.artistData;
-            album.groupData = WD.groupData;
-            album.trackData = WD.trackData;
-
-            for (const trackGroup of album.trackGroups) {
-                trackGroup.trackData = WD.trackData;
+        function assignWikiData(things, ...keys) {
+            for (let i = 0; i < things.length; i++) {
+                for (let j = 0; j < keys.length; j++) {
+                    const key = keys[j];
+                    things[i][key] = wikiData[key];
+                }
             }
         }
 
-        for (const track of WD.trackData) {
-            track.albumData = WD.albumData;
-            track.artistData = WD.artistData;
-            track.artTagData = WD.artTagData;
-            track.flashData = WD.flashData;
-            track.trackData = WD.trackData;
-        }
-
-        for (const artist of WD.artistData) {
-            artist.artistData = WD.artistData;
-        }
-
-        for (const group of WD.groupData) {
-            group.albumData = WD.albumData;
-            group.groupCategoryData = WD.groupCategoryData;
-        }
-
-        for (const category of WD.groupCategoryData) {
-            category.groupData = WD.groupData;
-        }
-
-        for (const flash of WD.flashData) {
-            flash.artistData = WD.artistData;
-            flash.trackData = WD.trackData;
-            flash.flashActData = WD.flashActData;
-        }
-
-        for (const act of WD.flashActData) {
-            act.flashData = WD.flashData;
-        }
+        assignWikiData(WD.albumData, 'artistData', 'groupData', 'trackData');
+        WD.albumData.forEach(album => assignWikiData(album.trackGroups, 'trackData'));
 
-        for (const artTag of WD.artTagData) {
-            artTag.albumData = WD.albumData;
-            artTag.trackData = WD.trackData;
-        }
-
-        for (const row of WD.homepageLayout.rows) {
-            row.albumData = WD.albumData;
-            row.groupData = WD.groupData;
-        }
+        assignWikiData(WD.trackData, 'albumData', 'artistData', 'artTagData', 'flashData', 'trackData');
+        assignWikiData(WD.artistData, 'albumData', 'artistData', 'flashData', 'trackData');
+        assignWikiData(WD.groupData, 'albumData', 'groupCategoryData');
+        assignWikiData(WD.groupCategoryData, 'groupData');
+        assignWikiData(WD.flashData, 'artistData', 'flashActData', 'trackData');
+        assignWikiData(WD.flashActData, 'flashData');
+        assignWikiData(WD.artTagData, 'albumData', 'trackData');
+        assignWikiData(WD.homepageLayout.rows, 'albumData', 'groupData');
     }
 
     // Extra organization stuff needed for listings and the like.
@@ -2820,6 +2790,7 @@ async function main() {
     // console.log(WD.homepageLayout.rows[0].countAlbumsFromGroup);
     // console.log(WD.albumData.map(a => `${a.name} (${a.date.toDateString()})`).join('\n'));
     // console.log(WD.groupData.find(g => g.name === 'Fandom').albums.map(a => `${a.name} (${a.date.toDateString()})`).join('\n'));
+    // console.log(WD.trackData.find(t => t.name === 'Another Chance').commentatorArtists.map(artist => `${artist.name} - commentated ${artist.tracksAsCommentator.length} tracks, ${artist.albumsAsCommentator.length} albums`).join('\n'));
     // return;
 
     // Update languages o8ject with the wiki-specified default language!
@@ -3430,8 +3401,6 @@ async function main() {
         wikiData
     });
 
-    decorateTime.displayTime();
-
     // The single most important step.
     logInfo`Written!`;
 }
@@ -3443,5 +3412,6 @@ main().catch(error => {
         console.error(error);
     }
 }).then(() => {
+    decorateTime.displayTime();
     CacheableObject.showInvalidAccesses();
 });
diff --git a/src/util/cli.js b/src/util/cli.js
index b6335726..4b2d3498 100644
--- a/src/util/cli.js
+++ b/src/util/cli.js
@@ -179,35 +179,52 @@ export async function parseOptions(options, optionDescriptorMap) {
 export const handleDashless = Symbol();
 export const handleUnknown = Symbol();
 
-export function decorateTime(functionToBeWrapped) {
+export function decorateTime(arg1, arg2) {
+    const [ id, functionToBeWrapped ] =
+        ((typeof arg1 === 'string' || typeof arg1 === 'symbol')
+            ? [arg1, arg2]
+            : [Symbol(arg1.name), arg1]);
+
+    const meta = decorateTime.idMetaMap[id] ?? {
+        wrappedName: functionToBeWrapped.name,
+        timeSpent: 0,
+        timesCalled: 0,
+        displayTime() {
+            const averageTime = meta.timeSpent / meta.timesCalled;
+            console.log(`\x1b[1m${typeof id === 'symbol' ? id.description : id}(...):\x1b[0m ${meta.timeSpent} ms / ${meta.timesCalled} calls \x1b[2m(avg: ${averageTime} ms)\x1b[0m`);
+        }
+    };
+
+    decorateTime.idMetaMap[id] = meta;
+
     const fn = function(...args) {
         const start = Date.now();
         const ret = functionToBeWrapped(...args);
         const end = Date.now();
-        fn.timeSpent += end - start;
-        fn.timesCalled++;
+        meta.timeSpent += end - start;
+        meta.timesCalled++;
         return ret;
     };
 
-    fn.wrappedName = functionToBeWrapped.name;
-    fn.timeSpent = 0;
-    fn.timesCalled = 0;
-    fn.displayTime = function() {
-        const averageTime = fn.timeSpent / fn.timesCalled;
-        console.log(`\x1b[1m${fn.wrappedName}(...):\x1b[0m ${fn.timeSpent} ms / ${fn.timesCalled} calls \x1b[2m(avg: ${averageTime} ms)\x1b[0m`);
-    };
-
-    decorateTime.decoratedFunctions.push(fn);
+    fn.displayTime = meta.displayTime;
 
     return fn;
 }
 
-decorateTime.decoratedFunctions = [];
+decorateTime.idMetaMap = Object.create(null);
+
 decorateTime.displayTime = function() {
-    if (decorateTime.decoratedFunctions.length) {
+    const map = decorateTime.idMetaMap;
+
+    const keys = [
+        ...Object.getOwnPropertySymbols(map),
+        ...Object.getOwnPropertyNames(map)
+    ];
+
+    if (keys.length) {
         console.log(`\x1b[1mdecorateTime results: ` + '-'.repeat(40) + '\x1b[0m');
-        for (const fn of decorateTime.decoratedFunctions) {
-            fn.displayTime();
+        for (const key of keys) {
+            map[key].displayTime();
         }
     }
 };