From 6e2df0d972dcfa45be1e5f7f070fdebb969f4498 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Fri, 25 Feb 2022 22:01:01 -0400 Subject: fill out data for artist pages + internal tweaks --- src/data/things.js | 96 ++++++++++++++++++++++++++++++++++++++++++++------ src/data/validators.js | 6 ++-- src/page/artist.js | 26 +++++++------- src/upd8.js | 66 ++++++++++------------------------ src/util/cli.js | 49 +++++++++++++++++--------- 5 files changed, 152 insertions(+), 91 deletions(-) diff --git a/src/data/things.js b/src/data/things.js index 0269731..5aa43f3 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>/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 ca10833..8f4d06d 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 d012805..6a46560 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 81275ad..4553826 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 b633572..4b2d349 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(); } } }; -- cgit 1.3.0-6-gf8a5