diff options
author | (quasar) nebula <towerofnix@gmail.com> | 2021-06-03 11:47:08 -0300 |
---|---|---|
committer | (quasar) nebula <towerofnix@gmail.com> | 2021-06-03 11:48:11 -0300 |
commit | 6dc67afaf4f8d90152bf973b0264a46f68fb07b2 (patch) | |
tree | 0c0a5cb8434a93575a32f4f37579f5e600752970 | |
parent | 9f81855af35aaf1dc5ef3773e263b7a505c85396 (diff) |
module-ify artist and artist alias pages
-rw-r--r-- | src/page/artist-alias.js | 22 | ||||
-rw-r--r-- | src/page/artist.js | 512 | ||||
-rw-r--r-- | src/page/index.js | 2 | ||||
-rwxr-xr-x | src/upd8.js | 474 |
4 files changed, 536 insertions, 474 deletions
diff --git a/src/page/artist-alias.js b/src/page/artist-alias.js new file mode 100644 index 00000000..d03510a8 --- /dev/null +++ b/src/page/artist-alias.js @@ -0,0 +1,22 @@ +// Artist alias redirect pages. +// (Makes old permalinks bring visitors to the up-to-date page.) + +export function targets({wikiData}) { + return wikiData.artistAliasData; +} + +export function write(aliasArtist, {wikiData}) { + // This function doesn't actually use wikiData, 8ut, um, consistency? + + const { alias: targetArtist } = aliasArtist; + + const redirect = { + type: 'redirect', + fromPath: ['artist', aliasArtist.directory], + toPath: ['artist', targetArtist.directory], + title: () => aliasArtist.name + }; + + return [redirect]; +} + diff --git a/src/page/artist.js b/src/page/artist.js new file mode 100644 index 00000000..695fddf0 --- /dev/null +++ b/src/page/artist.js @@ -0,0 +1,512 @@ +// Artist page specification. +// +// NB: See artist-alias.js for artist alias redirect pages. + +// Imports + +import fixWS from 'fix-whitespace'; + +import * as html from '../util/html.js'; + +import { + UNRELEASED_TRACKS_DIRECTORY +} from '../util/magic-constants.js'; + +import { + bindOpts, + unique +} from '../util/sugar.js'; + +import { + chunkByProperties, + getTotalDuration, + sortByDate +} from '../util/wiki-data.js'; + +// Page exports + +export function targets({wikiData}) { + return wikiData.artistData; +} + +export function write(artist, {wikiData}) { + const { groupData, wikiInfo } = wikiData; + + const { + name, + urls = [], + note = '' + } = artist; + + const artThingsAll = sortByDate(unique([...artist.albums.asCoverArtist, ...artist.albums.asWallpaperArtist, ...artist.albums.asBannerArtist, ...artist.tracks.asCoverArtist])); + const artThingsGallery = sortByDate([...artist.albums.asCoverArtist, ...artist.tracks.asCoverArtist]); + const commentaryThings = sortByDate([...artist.albums.asCommentator, ...artist.tracks.asCommentator]); + + const hasGallery = artThingsGallery.length > 0; + + const getArtistsAndContrib = (thing, key) => ({ + artists: thing[key]?.filter(({ who }) => who !== artist), + contrib: thing[key]?.find(({ who }) => who === artist), + thing, + key + }); + + const artListChunks = chunkByProperties(artThingsAll.flatMap(thing => + (['coverArtists', 'wallpaperArtists', 'bannerArtists'] + .map(key => getArtistsAndContrib(thing, key)) + .filter(({ contrib }) => contrib) + .map(props => ({ + album: thing.album || thing, + track: thing.album ? thing : null, + date: +(thing.coverArtDate || thing.date), + ...props + }))) + ), ['date', 'album']); + + const commentaryListChunks = chunkByProperties(commentaryThings.map(thing => ({ + album: thing.album || thing, + track: thing.album ? thing : null + })), ['album']); + + const allTracks = sortByDate(unique([...artist.tracks.asArtist, ...artist.tracks.asContributor])); + const unreleasedTracks = allTracks.filter(track => track.album.directory === UNRELEASED_TRACKS_DIRECTORY); + const releasedTracks = allTracks.filter(track => track.album.directory !== UNRELEASED_TRACKS_DIRECTORY); + + const chunkTracks = tracks => ( + chunkByProperties(tracks.map(track => ({ + track, + 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)), + contrib: { + who: artist, + what: [ + track.artists.find(({ who }) => who === artist)?.what, + track.contributors.find(({ who }) => who === artist)?.what + ].filter(Boolean).join(', ') + } + })), ['date', 'album']) + .map(({date, album, chunk}) => ({ + date, album, chunk, + duration: getTotalDuration(chunk), + }))); + + const unreleasedTrackListChunks = chunkTracks(unreleasedTracks); + const releasedTrackListChunks = chunkTracks(releasedTracks); + + const totalReleasedDuration = getTotalDuration(releasedTracks); + + const countGroups = things => { + const usedGroups = things.flatMap(thing => thing.groups || thing.album?.groups || []); + return groupData + .map(group => ({ + group, + contributions: usedGroups.filter(g => g === group).length + })) + .filter(({ contributions }) => contributions > 0) + .sort((a, b) => b.contributions - a.contributions); + }; + + const musicGroups = countGroups(releasedTracks); + const artGroups = countGroups(artThingsAll); + + let flashes, flashListChunks; + if (wikiInfo.features.flashesAndGames) { + flashes = sortByDate(artist.flashes.asContributor.slice()); + flashListChunks = ( + chunkByProperties(flashes.map(flash => ({ + act: flash.act, + flash, + date: flash.date, + // Manual artists/contrib properties here, 8ecause we don't + // 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) + })), ['act']) + .map(({ act, chunk }) => ({ + act, chunk, + dateFirst: chunk[0].date, + dateLast: chunk[chunk.length - 1].date + }))); + } + + const generateEntryAccents = ({ + getArtistString, strings, + aka, entry, artists, contrib + }) => + (aka + ? strings('artistPage.creditList.entry.rerelease', {entry}) + : (artists.length + ? (contrib.what + ? strings('artistPage.creditList.entry.withArtists.withContribution', { + entry, + artists: getArtistString(artists), + contribution: contrib.what + }) + : strings('artistPage.creditList.entry.withArtists', { + entry, + artists: getArtistString(artists) + })) + : (contrib.what + ? strings('artistPage.creditList.entry.withContribution', { + entry, + contribution: contrib.what + }) + : entry))); + + const unbound_generateTrackList = (chunks, { + getArtistString, link, strings + }) => fixWS` + <dl> + ${chunks.map(({date, album, chunk, duration}) => fixWS` + <dt>${strings('artistPage.creditList.album.withDate.withDuration', { + album: link.album(album), + date: strings.count.date(date), + duration: strings.count.duration(duration, {approximate: true}) + })}</dt> + <dd><ul> + ${(chunk + .map(({track, ...props}) => ({ + aka: track.aka, + entry: strings('artistPage.creditList.entry.track.withDuration', { + track: link.track(track), + duration: strings.count.duration(track.duration) + }), + ...props + })) + .map(({aka, ...opts}) => html.tag('li', + {class: aka && 'rerelease'}, + generateEntryAccents({getArtistString, strings, aka, ...opts}))) + .join('\n'))} + </ul></dd> + `).join('\n')} + </dl> + `; + + const unbound_serializeArtistsAndContrib = (key, { + serializeContribs, + serializeLink + }) => thing => { + const { artists, contrib } = getArtistsAndContrib(thing, key); + const ret = {}; + ret.link = serializeLink(thing); + if (contrib.what) ret.contribution = contrib.what; + if (artists.length) ret.otherArtists = serializeContribs(artists); + return ret; + }; + + const unbound_serializeTrackListChunks = (chunks, {serializeLink}) => + chunks.map(({date, album, chunk, duration}) => ({ + album: serializeLink(album), + date, + duration, + tracks: chunk.map(({ track }) => ({ + link: serializeLink(track), + duration: track.duration + })) + })); + + const data = { + type: 'data', + path: ['artist', artist.directory], + data: ({ + serializeContribs, + serializeLink + }) => { + const serializeArtistsAndContrib = bindOpts(unbound_serializeArtistsAndContrib, { + serializeContribs, + serializeLink + }); + + const serializeTrackListChunks = bindOpts(unbound_serializeTrackListChunks, { + serializeLink + }); + + return { + albums: { + asCoverArtist: artist.albums.asCoverArtist.map(serializeArtistsAndContrib('coverArtists')), + asWallpaperArtist: artist.albums.asWallpaperArtist.map(serializeArtistsAndContrib('wallpaperArtists')), + asBannerArtist: artist.albums.asBannerArtist.map(serializeArtistsAndContrib('bannerArtists')) + }, + flashes: wikiInfo.features.flashesAndGames ? { + asContributor: artist.flashes.asContributor + .map(flash => getArtistsAndContrib(flash, 'contributors')) + .map(({ contrib, thing: flash }) => ({ + link: serializeLink(flash), + contribution: contrib.what + })) + } : null, + tracks: { + asArtist: artist.tracks.asArtist.map(serializeArtistsAndContrib('artists')), + asContributor: artist.tracks.asContributor.map(serializeArtistsAndContrib('contributors')), + chunked: { + released: serializeTrackListChunks(releasedTrackListChunks), + unreleased: serializeTrackListChunks(unreleasedTrackListChunks) + } + } + }; + } + }; + + const infoPage = { + type: 'page', + path: ['artist', artist.directory], + page: ({ + fancifyURL, + generateCoverLink, + generateInfoGalleryLinks, + getArtistString, + link, + strings, + to, + transformMultiline + }) => { + const generateTrackList = bindOpts(unbound_generateTrackList, { + getArtistString, + link, + strings + }); + + return { + title: strings('artistPage.title', {artist: name}), + + main: { + content: fixWS` + ${artist.hasAvatar && generateCoverLink({ + path: ['localized.artistAvatar', artist.directory], + alt: strings('misc.alt.artistAvatar') + })} + <h1>${strings('artistPage.title', {artist: name})}</h1> + ${note && fixWS` + <p>${strings('releaseInfo.note')}</p> + <blockquote> + ${transformMultiline(note)} + </blockquote> + <hr> + `} + ${urls.length && `<p>${strings('releaseInfo.visitOn', { + links: strings.list.or(urls.map(url => fancifyURL(url, {strings}))) + })}</p>`} + ${hasGallery && `<p>${strings('artistPage.viewArtGallery', { + link: link.artistGallery(artist, { + text: strings('artistPage.viewArtGallery.link') + }) + })}</p>`} + <p>${strings('misc.jumpTo.withLinks', { + links: strings.list.unit([ + [ + [...releasedTracks, ...unreleasedTracks].length && `<a href="#tracks">${strings('artistPage.trackList.title')}</a>`, + unreleasedTracks.length && `(<a href="#unreleased-tracks">${strings('artistPage.unreleasedTrackList.title')}</a>)` + ].filter(Boolean).join(' '), + artThingsAll.length && `<a href="#art">${strings('artistPage.artList.title')}</a>`, + wikiInfo.features.flashesAndGames && flashes.length && `<a href="#flashes">${strings('artistPage.flashList.title')}</a>`, + commentaryThings.length && `<a href="#commentary">${strings('artistPage.commentaryList.title')}</a>` + ].filter(Boolean)) + })}</p> + ${(releasedTracks.length || unreleasedTracks.length) && fixWS` + <h2 id="tracks">${strings('artistPage.trackList.title')}</h2> + `} + ${releasedTracks.length && fixWS` + <p>${strings('artistPage.contributedDurationLine', { + artist: artist.name, + duration: strings.count.duration(totalReleasedDuration, {approximate: true, unit: true}) + })}</p> + <p>${strings('artistPage.musicGroupsLine', { + groups: strings.list.unit(musicGroups + .map(({ group, contributions }) => strings('artistPage.groupsLine.item', { + group: link.groupInfo(group), + contributions: strings.count.contributions(contributions) + }))) + })}</p> + ${generateTrackList(releasedTrackListChunks)} + `} + ${unreleasedTracks.length && fixWS` + <h3 id="unreleased-tracks">${strings('artistPage.unreleasedTrackList.title')}</h3> + ${generateTrackList(unreleasedTrackListChunks)} + `} + ${artThingsAll.length && fixWS` + <h2 id="art">${strings('artistPage.artList.title')}</h2> + ${hasGallery && `<p>${strings('artistPage.viewArtGallery.orBrowseList', { + link: link.artistGallery(artist, { + text: strings('artistPage.viewArtGallery.link') + }) + })}</p>`} + <p>${strings('artistPage.artGroupsLine', { + groups: strings.list.unit(artGroups + .map(({ group, contributions }) => strings('artistPage.groupsLine.item', { + group: link.groupInfo(group), + contributions: strings.count.contributions(contributions) + }))) + })}</p> + <dl> + ${artListChunks.map(({date, album, chunk}) => fixWS` + <dt>${strings('artistPage.creditList.album.withDate', { + album: link.album(album), + date: strings.count.date(date) + })}</dt> + <dd><ul> + ${(chunk + .map(({album, track, key, ...props}) => ({ + entry: (track + ? strings('artistPage.creditList.entry.track', { + track: link.track(track) + }) + : `<i>${strings('artistPage.creditList.entry.album.' + { + wallpaperArtists: 'wallpaperArt', + bannerArtists: 'bannerArt', + coverArtists: 'coverArt' + }[key])}</i>`), + ...props + })) + .map(opts => generateEntryAccents({getArtistString, strings, ...opts})) + .map(row => `<li>${row}</li>`) + .join('\n'))} + </ul></dd> + `).join('\n')} + </dl> + `} + ${wikiInfo.features.flashesAndGames && flashes.length && fixWS` + <h2 id="flashes">${strings('artistPage.flashList.title')}</h2> + <dl> + ${flashListChunks.map(({act, chunk, dateFirst, dateLast}) => fixWS` + <dt>${strings('artistPage.creditList.flashAct.withDateRange', { + act: link.flash(chunk[0].flash, {text: act.name}), + dateRange: strings.count.dateRange([dateFirst, dateLast]) + })}</dt> + <dd><ul> + ${(chunk + .map(({flash, ...props}) => ({ + entry: strings('artistPage.creditList.entry.flash', { + flash: link.flash(flash) + }), + ...props + })) + .map(opts => generateEntryAccents({getArtistString, strings, ...opts})) + .map(row => `<li>${row}</li>`) + .join('\n'))} + </ul></dd> + `).join('\n')} + </dl> + `} + ${commentaryThings.length && fixWS` + <h2 id="commentary">${strings('artistPage.commentaryList.title')}</h2> + <dl> + ${commentaryListChunks.map(({album, chunk}) => fixWS` + <dt>${strings('artistPage.creditList.album', { + album: link.album(album) + })}</dt> + <dd><ul> + ${(chunk + .map(({album, track, ...props}) => track + ? strings('artistPage.creditList.entry.track', { + track: link.track(track) + }) + : `<i>${strings('artistPage.creditList.entry.album.commentary')}</i>`) + .map(row => `<li>${row}</li>`) + .join('\n'))} + </ul></dd> + `).join('\n')} + </dl> + `} + ` + }, + + nav: generateNavForArtist(artist, false, hasGallery, { + generateInfoGalleryLinks, + link, + strings, + wikiData + }) + }; + } + }; + + const galleryPage = hasGallery && { + type: 'page', + path: ['artistGallery', artist.directory], + page: ({ + generateInfoGalleryLinks, + getAlbumCover, + getGridHTML, + getTrackCover, + link, + strings, + to + }) => ({ + title: strings('artistGalleryPage.title', {artist: name}), + + main: { + classes: ['top-index'], + content: fixWS` + <h1>${strings('artistGalleryPage.title', {artist: name})}</h1> + <p class="quick-info">${strings('artistGalleryPage.infoLine', { + coverArts: strings.count.coverArts(artThingsGallery.length, {unit: true}) + })}</p> + <div class="grid-listing"> + ${getGridHTML({ + entries: artThingsGallery.map(item => ({item})), + srcFn: thing => (thing.album + ? getTrackCover(thing) + : getAlbumCover(thing)), + hrefFn: thing => (thing.album + ? to('localized.track', thing.directory) + : to('localized.album', thing.directory)) + })} + </div> + ` + }, + + nav: generateNavForArtist(artist, true, hasGallery, { + generateInfoGalleryLinks, + link, + strings, + wikiData + }) + }) + }; + + return [data, infoPage, galleryPage].filter(Boolean); +} + +// Utility functions + +function generateNavForArtist(artist, isGallery, hasGallery, { + generateInfoGalleryLinks, + link, + strings, + wikiData +}) { + const { wikiInfo } = wikiData; + + const infoGalleryLinks = (hasGallery && + generateInfoGalleryLinks(artist, isGallery, { + link, strings, + linkKeyGallery: 'artistGallery', + linkKeyInfo: 'artist' + })) + + return { + links: [ + {toHome: true}, + wikiInfo.features.listings && + { + path: ['localized.listingIndex'], + title: strings('listingIndex.title') + }, + { + html: strings('artistPage.nav.artist', { + artist: link.artist(artist, {class: 'current'}) + }) + }, + hasGallery && + { + divider: false, + html: `(${infoGalleryLinks})` + } + ] + }; +} diff --git a/src/page/index.js b/src/page/index.js index e6565766..d74dad57 100644 --- a/src/page/index.js +++ b/src/page/index.js @@ -26,5 +26,7 @@ // pertain only to site page generation. export * as album from './album.js'; +export * as artist from './artist.js'; +export * as artistAlias from './artist-alias.js'; export * as group from './group.js'; export * as track from './track.js'; diff --git a/src/upd8.js b/src/upd8.js index e6a880dc..2e164fac 100755 --- a/src/upd8.js +++ b/src/upd8.js @@ -2652,480 +2652,6 @@ function getAlbumStylesheet(album, {to}) { ].filter(Boolean).join('\n'); } -function writeArtistPages({wikiData}) { - return [ - ...wikiData.artistData.map(artist => writeArtistPage(artist, {wikiData})), - ...wikiData.artistAliasData.map(artist => writeArtistAliasPage(artist, {wikiData})) - ]; -} - -function writeArtistPage(artist, {wikiData}) { - const { groupData, wikiInfo } = wikiData; - - const { - name, - urls = [], - note = '' - } = artist; - - const artThingsAll = sortByDate(unique([...artist.albums.asCoverArtist, ...artist.albums.asWallpaperArtist, ...artist.albums.asBannerArtist, ...artist.tracks.asCoverArtist])); - const artThingsGallery = sortByDate([...artist.albums.asCoverArtist, ...artist.tracks.asCoverArtist]); - const commentaryThings = sortByDate([...artist.albums.asCommentator, ...artist.tracks.asCommentator]); - - const hasGallery = artThingsGallery.length > 0; - - const getArtistsAndContrib = (thing, key) => ({ - artists: thing[key]?.filter(({ who }) => who !== artist), - contrib: thing[key]?.find(({ who }) => who === artist), - thing, - key - }); - - const artListChunks = chunkByProperties(artThingsAll.flatMap(thing => - (['coverArtists', 'wallpaperArtists', 'bannerArtists'] - .map(key => getArtistsAndContrib(thing, key)) - .filter(({ contrib }) => contrib) - .map(props => ({ - album: thing.album || thing, - track: thing.album ? thing : null, - date: +(thing.coverArtDate || thing.date), - ...props - }))) - ), ['date', 'album']); - - const commentaryListChunks = chunkByProperties(commentaryThings.map(thing => ({ - album: thing.album || thing, - track: thing.album ? thing : null - })), ['album']); - - const allTracks = sortByDate(unique([...artist.tracks.asArtist, ...artist.tracks.asContributor])); - const unreleasedTracks = allTracks.filter(track => track.album.directory === UNRELEASED_TRACKS_DIRECTORY); - const releasedTracks = allTracks.filter(track => track.album.directory !== UNRELEASED_TRACKS_DIRECTORY); - - const chunkTracks = tracks => ( - chunkByProperties(tracks.map(track => ({ - track, - 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)), - contrib: { - who: artist, - what: [ - track.artists.find(({ who }) => who === artist)?.what, - track.contributors.find(({ who }) => who === artist)?.what - ].filter(Boolean).join(', ') - } - })), ['date', 'album']) - .map(({date, album, chunk}) => ({ - date, album, chunk, - duration: getTotalDuration(chunk), - }))); - - const unreleasedTrackListChunks = chunkTracks(unreleasedTracks); - const releasedTrackListChunks = chunkTracks(releasedTracks); - - const totalReleasedDuration = getTotalDuration(releasedTracks); - - const countGroups = things => { - const usedGroups = things.flatMap(thing => thing.groups || thing.album?.groups || []); - return groupData - .map(group => ({ - group, - contributions: usedGroups.filter(g => g === group).length - })) - .filter(({ contributions }) => contributions > 0) - .sort((a, b) => b.contributions - a.contributions); - }; - - const musicGroups = countGroups(releasedTracks); - const artGroups = countGroups(artThingsAll); - - let flashes, flashListChunks; - if (wikiInfo.features.flashesAndGames) { - flashes = sortByDate(artist.flashes.asContributor.slice()); - flashListChunks = ( - chunkByProperties(flashes.map(flash => ({ - act: flash.act, - flash, - date: flash.date, - // Manual artists/contrib properties here, 8ecause we don't - // 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) - })), ['act']) - .map(({ act, chunk }) => ({ - act, chunk, - dateFirst: chunk[0].date, - dateLast: chunk[chunk.length - 1].date - }))); - } - - const generateEntryAccents = ({ - getArtistString, strings, - aka, entry, artists, contrib - }) => - (aka - ? strings('artistPage.creditList.entry.rerelease', {entry}) - : (artists.length - ? (contrib.what - ? strings('artistPage.creditList.entry.withArtists.withContribution', { - entry, - artists: getArtistString(artists), - contribution: contrib.what - }) - : strings('artistPage.creditList.entry.withArtists', { - entry, - artists: getArtistString(artists) - })) - : (contrib.what - ? strings('artistPage.creditList.entry.withContribution', { - entry, - contribution: contrib.what - }) - : entry))); - - const unbound_generateTrackList = (chunks, { - getArtistString, link, strings - }) => fixWS` - <dl> - ${chunks.map(({date, album, chunk, duration}) => fixWS` - <dt>${strings('artistPage.creditList.album.withDate.withDuration', { - album: link.album(album), - date: strings.count.date(date), - duration: strings.count.duration(duration, {approximate: true}) - })}</dt> - <dd><ul> - ${(chunk - .map(({track, ...props}) => ({ - aka: track.aka, - entry: strings('artistPage.creditList.entry.track.withDuration', { - track: link.track(track), - duration: strings.count.duration(track.duration) - }), - ...props - })) - .map(({aka, ...opts}) => `<li ${classes(aka && 'rerelease')}>${generateEntryAccents({getArtistString, strings, aka, ...opts})}</li>`) - .join('\n'))} - </ul></dd> - `).join('\n')} - </dl> - `; - - const serializeArtistsAndContrib = key => thing => { - const { artists, contrib } = getArtistsAndContrib(thing, key); - const ret = {}; - ret.link = serializeLink(thing); - if (contrib.what) ret.contribution = contrib.what; - if (artists.length) ret.otherArtists = serializeContribs(artists); - return ret; - }; - - const serializeTrackListChunks = chunks => - chunks.map(({date, album, chunk, duration}) => ({ - album: serializeLink(album), - date, - duration, - tracks: chunk.map(({ track }) => ({ - link: serializeLink(track), - duration: track.duration - })) - })); - - const data = { - type: 'data', - path: ['artist', artist.directory], - data: () => ({ - albums: { - asCoverArtist: artist.albums.asCoverArtist.map(serializeArtistsAndContrib('coverArtists')), - asWallpaperArtist: artist.albums.asWallpaperArtist.map(serializeArtistsAndContrib('wallpaperArtists')), - asBannerArtist: artist.albums.asBannerArtist.map(serializeArtistsAndContrib('bannerArtists')) - }, - flashes: wikiInfo.features.flashesAndGames ? { - asContributor: artist.flashes.asContributor - .map(flash => getArtistsAndContrib(flash, 'contributors')) - .map(({ contrib, thing: flash }) => ({ - link: serializeLink(flash), - contribution: contrib.what - })) - } : null, - tracks: { - asArtist: artist.tracks.asArtist.map(serializeArtistsAndContrib('artists')), - asContributor: artist.tracks.asContributor.map(serializeArtistsAndContrib('contributors')), - chunked: { - released: serializeTrackListChunks(releasedTrackListChunks), - unreleased: serializeTrackListChunks(unreleasedTrackListChunks) - } - } - }) - }; - - const infoPage = { - type: 'page', - path: ['artist', artist.directory], - page: ({ - generateCoverLink, - getArtistString, - link, - strings, - to, - transformMultiline - }) => { - const generateTrackList = bindOpts(unbound_generateTrackList, { - getArtistString, - link, - strings - }); - - return { - title: strings('artistPage.title', {artist: name}), - - main: { - content: fixWS` - ${artist.hasAvatar && generateCoverLink({ - path: ['localized.artistAvatar', artist.directory], - alt: strings('misc.alt.artistAvatar') - })} - <h1>${strings('artistPage.title', {artist: name})}</h1> - ${note && fixWS` - <p>${strings('releaseInfo.note')}</p> - <blockquote> - ${transformMultiline(note)} - </blockquote> - <hr> - `} - ${urls.length && `<p>${strings('releaseInfo.visitOn', { - links: strings.list.or(urls.map(url => fancifyURL(url, {strings}))) - })}</p>`} - ${hasGallery && `<p>${strings('artistPage.viewArtGallery', { - link: link.artistGallery(artist, { - text: strings('artistPage.viewArtGallery.link') - }) - })}</p>`} - <p>${strings('misc.jumpTo.withLinks', { - links: strings.list.unit([ - [ - [...releasedTracks, ...unreleasedTracks].length && `<a href="#tracks">${strings('artistPage.trackList.title')}</a>`, - unreleasedTracks.length && `(<a href="#unreleased-tracks">${strings('artistPage.unreleasedTrackList.title')}</a>)` - ].filter(Boolean).join(' '), - artThingsAll.length && `<a href="#art">${strings('artistPage.artList.title')}</a>`, - wikiInfo.features.flashesAndGames && flashes.length && `<a href="#flashes">${strings('artistPage.flashList.title')}</a>`, - commentaryThings.length && `<a href="#commentary">${strings('artistPage.commentaryList.title')}</a>` - ].filter(Boolean)) - })}</p> - ${(releasedTracks.length || unreleasedTracks.length) && fixWS` - <h2 id="tracks">${strings('artistPage.trackList.title')}</h2> - `} - ${releasedTracks.length && fixWS` - <p>${strings('artistPage.contributedDurationLine', { - artist: artist.name, - duration: strings.count.duration(totalReleasedDuration, {approximate: true, unit: true}) - })}</p> - <p>${strings('artistPage.musicGroupsLine', { - groups: strings.list.unit(musicGroups - .map(({ group, contributions }) => strings('artistPage.groupsLine.item', { - group: link.groupInfo(group), - contributions: strings.count.contributions(contributions) - }))) - })}</p> - ${generateTrackList(releasedTrackListChunks)} - `} - ${unreleasedTracks.length && fixWS` - <h3 id="unreleased-tracks">${strings('artistPage.unreleasedTrackList.title')}</h3> - ${generateTrackList(unreleasedTrackListChunks)} - `} - ${artThingsAll.length && fixWS` - <h2 id="art">${strings('artistPage.artList.title')}</h2> - ${hasGallery && `<p>${strings('artistPage.viewArtGallery.orBrowseList', { - link: link.artistGallery(artist, { - text: strings('artistPage.viewArtGallery.link') - }) - })}</p>`} - <p>${strings('artistPage.artGroupsLine', { - groups: strings.list.unit(artGroups - .map(({ group, contributions }) => strings('artistPage.groupsLine.item', { - group: link.groupInfo(group), - contributions: strings.count.contributions(contributions) - }))) - })}</p> - <dl> - ${artListChunks.map(({date, album, chunk}) => fixWS` - <dt>${strings('artistPage.creditList.album.withDate', { - album: link.album(album), - date: strings.count.date(date) - })}</dt> - <dd><ul> - ${(chunk - .map(({album, track, key, ...props}) => ({ - entry: (track - ? strings('artistPage.creditList.entry.track', { - track: link.track(track) - }) - : `<i>${strings('artistPage.creditList.entry.album.' + { - wallpaperArtists: 'wallpaperArt', - bannerArtists: 'bannerArt', - coverArtists: 'coverArt' - }[key])}</i>`), - ...props - })) - .map(opts => generateEntryAccents({getArtistString, strings, ...opts})) - .map(row => `<li>${row}</li>`) - .join('\n'))} - </ul></dd> - `).join('\n')} - </dl> - `} - ${wikiInfo.features.flashesAndGames && flashes.length && fixWS` - <h2 id="flashes">${strings('artistPage.flashList.title')}</h2> - <dl> - ${flashListChunks.map(({act, chunk, dateFirst, dateLast}) => fixWS` - <dt>${strings('artistPage.creditList.flashAct.withDateRange', { - act: link.flash(chunk[0].flash, {text: act.name}), - dateRange: strings.count.dateRange([dateFirst, dateLast]) - })}</dt> - <dd><ul> - ${(chunk - .map(({flash, ...props}) => ({ - entry: strings('artistPage.creditList.entry.flash', { - flash: link.flash(flash) - }), - ...props - })) - .map(opts => generateEntryAccents({getArtistString, strings, ...opts})) - .map(row => `<li>${row}</li>`) - .join('\n'))} - </ul></dd> - `).join('\n')} - </dl> - `} - ${commentaryThings.length && fixWS` - <h2 id="commentary">${strings('artistPage.commentaryList.title')}</h2> - <dl> - ${commentaryListChunks.map(({album, chunk}) => fixWS` - <dt>${strings('artistPage.creditList.album', { - album: link.album(album) - })}</dt> - <dd><ul> - ${(chunk - .map(({album, track, ...props}) => track - ? strings('artistPage.creditList.entry.track', { - track: link.track(track) - }) - : `<i>${strings('artistPage.creditList.entry.album.commentary')}</i>`) - .map(row => `<li>${row}</li>`) - .join('\n'))} - </ul></dd> - `).join('\n')} - </dl> - `} - ` - }, - - nav: generateNavForArtist(artist, false, { - link, strings, wikiData, - hasGallery - }) - }; - } - }; - - const galleryPage = hasGallery && { - type: 'page', - path: ['artistGallery', artist.directory], - page: ({ - getAlbumCover, - getGridHTML, - getTrackCover, - link, - strings, - to - }) => ({ - title: strings('artistGalleryPage.title', {artist: name}), - - main: { - classes: ['top-index'], - content: fixWS` - <h1>${strings('artistGalleryPage.title', {artist: name})}</h1> - <p class="quick-info">${strings('artistGalleryPage.infoLine', { - coverArts: strings.count.coverArts(artThingsGallery.length, {unit: true}) - })}</p> - <div class="grid-listing"> - ${getGridHTML({ - entries: artThingsGallery.map(item => ({item})), - srcFn: thing => (thing.album - ? getTrackCover(thing) - : getAlbumCover(thing)), - hrefFn: thing => (thing.album - ? to('localized.track', thing.directory) - : to('localized.album', thing.directory)) - })} - </div> - ` - }, - - nav: generateNavForArtist(artist, true, { - link, strings, wikiData, - hasGallery - }) - }) - }; - - return [data, infoPage, galleryPage].filter(Boolean); -} - -function generateNavForArtist(artist, isGallery, { - link, strings, wikiData, - hasGallery -}) { - const { wikiInfo } = wikiData; - - const infoGalleryLinks = (hasGallery && - generateInfoGalleryLinks(artist, isGallery, { - link, strings, - linkKeyGallery: 'artistGallery', - linkKeyInfo: 'artist' - })) - - return { - links: [ - {toHome: true}, - wikiInfo.features.listings && - { - path: ['localized.listingIndex'], - title: strings('listingIndex.title') - }, - { - html: strings('artistPage.nav.artist', { - artist: link.artist(artist, {class: 'current'}) - }) - }, - hasGallery && - { - divider: false, - html: `(${infoGalleryLinks})` - } - ] - }; -} - -function writeArtistAliasPage(aliasArtist, {wikiData}) { - // This function doesn't actually use wikiData, 8ut, um, consistency? - - const { alias: targetArtist } = aliasArtist; - - const redirect = { - type: 'redirect', - fromPath: ['artist', aliasArtist.directory], - toPath: ['artist', targetArtist.directory], - title: () => aliasArtist.name - }; - - return [redirect]; -} - function generateRedirectPage(title, target, {strings}) { return fixWS` <!DOCTYPE html> |