From e56b564efe888e028e4b01891d9074e8532360d2 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 19 May 2026 21:52:18 -0300 Subject: data, content: additional file artists --- .../dependencies/generateAdditionalFilesList.js | 2 + .../generateAdditionalFilesListChunk.js | 34 +++++- src/content/dependencies/generateArtistInfoPage.js | 57 ++++++++++ .../generateArtistInfoPageAdditionalFilesChunk.js | 41 ++++++++ ...nerateArtistInfoPageAdditionalFilesChunkItem.js | 115 +++++++++++++++++++++ ...ateArtistInfoPageMidiProjectFilesChunkedList.js | 68 ++++++++++++ ...oPageMiscellaneousAdditionalFilesChunkedList.js | 68 ++++++++++++ ...rateArtistInfoPageSheetMusicFilesChunkedList.js | 69 +++++++++++++ .../generateListAllAdditionalFilesChunk.js | 112 ++++++++++++-------- src/content/dependencies/generateTrackInfoPage.js | 22 ++-- 10 files changed, 534 insertions(+), 54 deletions(-) create mode 100644 src/content/dependencies/generateArtistInfoPageAdditionalFilesChunk.js create mode 100644 src/content/dependencies/generateArtistInfoPageAdditionalFilesChunkItem.js create mode 100644 src/content/dependencies/generateArtistInfoPageMidiProjectFilesChunkedList.js create mode 100644 src/content/dependencies/generateArtistInfoPageMiscellaneousAdditionalFilesChunkedList.js create mode 100644 src/content/dependencies/generateArtistInfoPageSheetMusicFilesChunkedList.js (limited to 'src/content/dependencies') diff --git a/src/content/dependencies/generateAdditionalFilesList.js b/src/content/dependencies/generateAdditionalFilesList.js index 699c5f86..9eb602f7 100644 --- a/src/content/dependencies/generateAdditionalFilesList.js +++ b/src/content/dependencies/generateAdditionalFilesList.js @@ -6,6 +6,7 @@ export default { }), slots: { + string: {type: 'string', default: 'miscellaneousAdditionalFiles'}, showFileSizes: {type: 'boolean', default: true}, }, @@ -14,6 +15,7 @@ export default { {[html.onlyIfContent]: true}, relations.chunks.map(chunk => chunk.slots({ + string: slots.string, showFileSizes: slots.showFileSizes, }))), }; diff --git a/src/content/dependencies/generateAdditionalFilesListChunk.js b/src/content/dependencies/generateAdditionalFilesListChunk.js index 466a5d8d..7d0e41c1 100644 --- a/src/content/dependencies/generateAdditionalFilesListChunk.js +++ b/src/content/dependencies/generateAdditionalFilesListChunk.js @@ -8,6 +8,9 @@ export default { links: file.filenames .map(filename => relation('linkAdditionalFile', file, filename)), + + artistCredit: + relation('generateArtistCredit', file.artistContribs, []), }), data: (file) => ({ @@ -19,6 +22,11 @@ export default { }), slots: { + string: { + type: 'string', + default: 'miscellaneousAdditionalFiles', + }, + showFileSizes: { type: 'boolean', }, @@ -34,9 +42,29 @@ export default { [ html.tag('summary', html.tag('span', - language.$(capsule, 'entry', { - title: - html.tag('b', data.title), + language.encapsulate(capsule, 'entry', workingCapsule => { + const workingOptions = {}; + const entryCapsule = workingCapsule; + + const titlePart = + (data.title + ? language.sanitize(data.title) + : language.$('releaseInfo', slots.string, 'entry.placeholderTitle')); + + workingOptions.title = + html.tag('b', titlePart); + + relations.artistCredit.setSlots({ + normalStringKey: + entryCapsule + '.credit', + }); + + if (!html.isBlank(relations.artistCredit)) { + workingCapsule += '.withCredit'; + workingOptions.credit = relations.artistCredit; + } + + return language.$(workingCapsule, workingOptions); }))), html.tag('ul', [ diff --git a/src/content/dependencies/generateArtistInfoPage.js b/src/content/dependencies/generateArtistInfoPage.js index ae21b361..c3ac0b9f 100644 --- a/src/content/dependencies/generateArtistInfoPage.js +++ b/src/content/dependencies/generateArtistInfoPage.js @@ -92,6 +92,15 @@ export default { flashesChunkedList: relation('generateArtistInfoPageFlashesChunkedList', artist), + sheetMusicFilesChunkedList: + relation('generateArtistInfoPageSheetMusicFilesChunkedList', artist), + + midiProjectFilesChunkedList: + relation('generateArtistInfoPageMidiProjectFilesChunkedList', artist), + + miscellaneousAdditionalFilesChunkedList: + relation('generateArtistInfoPageMiscellaneousAdditionalFilesChunkedList', artist), + commentaryChunkedList: relation('generateArtistInfoPageCommentaryChunkedList', artist, false), @@ -225,6 +234,21 @@ export default { {href: '#music-videos'}, language.$(pageCapsule, 'musicVideoList.title')), + !html.isBlank(relations.sheetMusicFilesChunkedList) && + html.tag('a', + {href: '#sheet-music-files'}, + language.$(pageCapsule, 'sheetMusicFileList.title')), + + !html.isBlank(relations.midiProjectFilesChunkedList) && + html.tag('a', + {href: '#midi-project-files'}, + language.$(pageCapsule, 'midiProjectFileList.title')), + + !html.isBlank(relations.miscellaneousAdditionalFilesChunkedList) && + html.tag('a', + {href: '#additional-files'}, + language.$(pageCapsule, 'miscellaneousAdditionalFileList.title')), + !html.isBlank(relations.flashesChunkedList) && html.tag('a', {href: '#flashes'}, @@ -354,6 +378,39 @@ export default { }), ]), + html.tags([ + relations.contentHeading.clone() + .slots({ + tag: 'h2', + attributes: {id: 'sheet-music-files'}, + title: language.$(pageCapsule, 'sheetMusicFileList.title'), + }), + + relations.sheetMusicFilesChunkedList, + ]), + + html.tags([ + relations.contentHeading.clone() + .slots({ + tag: 'h2', + attributes: {id: 'midi-project-files'}, + title: language.$(pageCapsule, 'midiProjectFileList.title'), + }), + + relations.midiProjectFilesChunkedList, + ]), + + html.tags([ + relations.contentHeading.clone() + .slots({ + tag: 'h2', + attributes: {id: 'additional-files'}, + title: language.$(pageCapsule, 'miscellaneousAdditionalFileList.title'), + }), + + relations.miscellaneousAdditionalFilesChunkedList, + ]), + html.tags([ relations.contentHeading.clone() .slots({ diff --git a/src/content/dependencies/generateArtistInfoPageAdditionalFilesChunk.js b/src/content/dependencies/generateArtistInfoPageAdditionalFilesChunk.js new file mode 100644 index 00000000..353ad047 --- /dev/null +++ b/src/content/dependencies/generateArtistInfoPageAdditionalFilesChunk.js @@ -0,0 +1,41 @@ +export default { + relations: (relation, artist, album, contribs) => ({ + template: + relation('generateArtistInfoPageChunk'), + + albumLink: + relation('linkAlbum', album), + + items: + contribs.map(contribs => + relation('generateArtistInfoPageAdditionalFilesChunkItem', + artist, + contribs)), + }), + + slots: { + string: { + type: 'string', + default: 'additionalFile', + }, + + disableStandaloneWithFiles: { + type: 'boolean', + default: false, + }, + }, + + generate: (relations, slots, {html}) => + relations.template.slots({ + mode: 'album', + link: relations.albumLink, + + list: + html.tag('ul', + relations.items + .map(item => item.slots({ + string: slots.string, + disableStandaloneWithFiles: slots.disableStandaloneWithFiles, + }))), + }), +}; diff --git a/src/content/dependencies/generateArtistInfoPageAdditionalFilesChunkItem.js b/src/content/dependencies/generateArtistInfoPageAdditionalFilesChunkItem.js new file mode 100644 index 00000000..8352edba --- /dev/null +++ b/src/content/dependencies/generateArtistInfoPageAdditionalFilesChunkItem.js @@ -0,0 +1,115 @@ +export default { + query(_artist, contribs) { + const query = {}; + + query.additionalFile = contribs[0].thing; + + query.albumOrTrack = query.additionalFile.thing; + + query.album = + (query.albumOrTrack.isAlbum + ? query.albumOrTrack + : query.albumOrTrack.album); + + return query; + }, + + relations: (relation, query, artist, _contribs) => ({ + template: + relation('generateArtistInfoPageChunkItem'), + + trackLink: + (query.albumOrTrack.isTrack + ? relation('linkTrack', query.albumOrTrack) + : null), + + artistCredit: + relation('generateArtistCredit', + query.additionalFile.artistContribs, + [artist.mockSimpleContribution]), + }), + + data: (query, _artist, contribs) => ({ + for: + (query.albumOrTrack.isAlbum + ? 'album' + : 'track'), + + title: + query.additionalFile.title, + + files: + query.additionalFile.filenames.length, + + contribAnnotationParts: + contribs.flatMap(contrib => contrib.annotationParts), + }), + + slots: { + string: { + type: 'string', + default: 'additionalFile', + }, + + disableStandaloneWithFiles: { + type: 'boolean', + default: false, + }, + }, + + generate: (data, relations, slots, {html, language}) => + relations.template.slots({ + annotation: + (data.contribAnnotationParts + ? language.formatUnitList(data.contribAnnotationParts) + : html.blank()), + + content: + language.encapsulate('artistPage.creditList.entry', entryCapsule => { + let workingCapsule = entryCapsule; + let workingOptions = {}; + + workingCapsule += '.' + data.for + '.' + slots.string; + + const additionalFileCapsule = workingCapsule; + + if (data.for === 'track') { + workingOptions.track = + relations.trackLink; + } + + if (data.title) { + relations.artistCredit.setSlots({ + normalStringKey: + additionalFileCapsule + '.credit.alongsideTitle', + }); + } else if (data.files && !slots.disableStandaloneWithFiles) { + relations.artistCredit.setSlots({ + normalStringKey: + additionalFileCapsule + '.credit.standaloneWithFiles', + + additionalStringOptions: { + files: language.countFiles(data.files, {unitOnly: true}), + }, + }); + } else { + relations.artistCredit.setSlots({ + normalStringKey: + additionalFileCapsule + '.credit', + }); + } + + if (!html.isBlank(relations.artistCredit)) { + workingCapsule += '.withCredit'; + workingOptions.credit = relations.artistCredit; + } + + if (data.title) { + workingCapsule += '.withTitle'; + workingOptions.title = language.sanitize(data.title); + } + + return language.$(workingCapsule, workingOptions); + }), + }), +}; diff --git a/src/content/dependencies/generateArtistInfoPageMidiProjectFilesChunkedList.js b/src/content/dependencies/generateArtistInfoPageMidiProjectFilesChunkedList.js new file mode 100644 index 00000000..475350a7 --- /dev/null +++ b/src/content/dependencies/generateArtistInfoPageMidiProjectFilesChunkedList.js @@ -0,0 +1,68 @@ +import {chunkByConditions, stitchArrays} from '#sugar'; +import {sortAlbumsTracksChronologically, sortContributionsChronologically} + from '#sort'; + +export default { + query(artist) { + const query = {}; + + const allContributions = [ + ...artist.midiProjectFileArtistContributions, + ]; + + const getAdditionalFile = contrib => + contrib.thing; + + const getAlbumOrTrack = contrib => + getAdditionalFile(contrib).thing; + + sortContributionsChronologically( + allContributions, + sortAlbumsTracksChronologically, + {getThing: getAlbumOrTrack}); + + const getAlbum = contrib => + (getAlbumOrTrack(contrib).isTrack + ? getAlbumOrTrack(contrib).album + : getAlbumOrTrack(contrib)); + + query.contribs = + chunkByConditions(allContributions, [ + (a, b) => getAlbum(a) !== getAlbum(b), + ]).map(contribs => + chunkByConditions(contribs, [ + (a, b) => getAdditionalFile(a) !== getAdditionalFile(b), + ])); + + query.albums = + query.contribs + .map(contribs => contribs[0][0]) + .map(contrib => getAlbum(contrib)); + + return query; + }, + + relations: (relation, query, artist) => ({ + template: + relation('generateArtistInfoPageChunkedList'), + + chunks: + stitchArrays({ + album: query.albums, + contribs: query.contribs, + }).map(({album, contribs}) => + relation('generateArtistInfoPageAdditionalFilesChunk', + artist, + album, + contribs)), + }), + + generate: (relations) => + relations.template.slots({ + chunks: + relations.chunks.map(chunk => + chunk.slots({ + string: 'midiProjectFile', + })), + }), +}; diff --git a/src/content/dependencies/generateArtistInfoPageMiscellaneousAdditionalFilesChunkedList.js b/src/content/dependencies/generateArtistInfoPageMiscellaneousAdditionalFilesChunkedList.js new file mode 100644 index 00000000..55a3119e --- /dev/null +++ b/src/content/dependencies/generateArtistInfoPageMiscellaneousAdditionalFilesChunkedList.js @@ -0,0 +1,68 @@ +import {chunkByConditions, stitchArrays} from '#sugar'; +import {sortAlbumsTracksChronologically, sortContributionsChronologically} + from '#sort'; + +export default { + query(artist) { + const query = {}; + + const allContributions = [ + ...artist.miscellaneousAdditionalFileArtistContributions, + ]; + + const getAdditionalFile = contrib => + contrib.thing; + + const getAlbumOrTrack = contrib => + getAdditionalFile(contrib).thing; + + sortContributionsChronologically( + allContributions, + sortAlbumsTracksChronologically, + {getThing: getAlbumOrTrack}); + + const getAlbum = contrib => + (getAlbumOrTrack(contrib).isTrack + ? getAlbumOrTrack(contrib).album + : getAlbumOrTrack(contrib)); + + query.contribs = + chunkByConditions(allContributions, [ + (a, b) => getAlbum(a) !== getAlbum(b), + ]).map(contribs => + chunkByConditions(contribs, [ + (a, b) => getAdditionalFile(a) !== getAdditionalFile(b), + ])); + + query.albums = + query.contribs + .map(contribs => contribs[0][0]) + .map(contrib => getAlbum(contrib)); + + return query; + }, + + relations: (relation, query, artist) => ({ + template: + relation('generateArtistInfoPageChunkedList'), + + chunks: + stitchArrays({ + album: query.albums, + contribs: query.contribs, + }).map(({album, contribs}) => + relation('generateArtistInfoPageAdditionalFilesChunk', + artist, + album, + contribs)), + }), + + generate: (relations) => + relations.template.slots({ + chunks: + relations.chunks.map(chunk => + chunk.slots({ + string: 'miscellaneousAdditionalFile', + })), + }), +}; diff --git a/src/content/dependencies/generateArtistInfoPageSheetMusicFilesChunkedList.js b/src/content/dependencies/generateArtistInfoPageSheetMusicFilesChunkedList.js new file mode 100644 index 00000000..63a6b23a --- /dev/null +++ b/src/content/dependencies/generateArtistInfoPageSheetMusicFilesChunkedList.js @@ -0,0 +1,69 @@ +import {chunkByConditions, stitchArrays} from '#sugar'; +import {sortAlbumsTracksChronologically, sortContributionsChronologically} + from '#sort'; + +export default { + query(artist) { + const query = {}; + + const allContributions = [ + ...artist.sheetMusicFileArtistContributions, + ]; + + const getAdditionalFile = contrib => + contrib.thing; + + const getAlbumOrTrack = contrib => + getAdditionalFile(contrib).thing; + + sortContributionsChronologically( + allContributions, + sortAlbumsTracksChronologically, + {getThing: getAlbumOrTrack}); + + const getAlbum = contrib => + (getAlbumOrTrack(contrib).isTrack + ? getAlbumOrTrack(contrib).album + : getAlbumOrTrack(contrib)); + + query.contribs = + chunkByConditions(allContributions, [ + (a, b) => getAlbum(a) !== getAlbum(b), + ]).map(contribs => + chunkByConditions(contribs, [ + (a, b) => getAdditionalFile(a) !== getAdditionalFile(b), + ])); + + query.albums = + query.contribs + .map(contribs => contribs[0][0]) + .map(contrib => getAlbum(contrib)); + + return query; + }, + + relations: (relation, query, artist) => ({ + template: + relation('generateArtistInfoPageChunkedList'), + + chunks: + stitchArrays({ + album: query.albums, + contribs: query.contribs, + }).map(({album, contribs}) => + relation('generateArtistInfoPageAdditionalFilesChunk', + artist, + album, + contribs)), + }), + + generate: (relations) => + relations.template.slots({ + chunks: + relations.chunks.map(chunk => + chunk.slots({ + string: 'sheetMusicFile', + disableStandaloneWithFiles: true, + })), + }), +}; diff --git a/src/content/dependencies/generateListAllAdditionalFilesChunk.js b/src/content/dependencies/generateListAllAdditionalFilesChunk.js index d68e3bc1..fea565cb 100644 --- a/src/content/dependencies/generateListAllAdditionalFilesChunk.js +++ b/src/content/dependencies/generateListAllAdditionalFilesChunk.js @@ -6,6 +6,11 @@ export default { additionalFiles .map(file => file.filenames .map(filename => relation('linkAdditionalFile', file, filename))), + + artistCredits: + additionalFiles + .map(file => + relation('generateArtistCredit', file.artistContribs, [])), }), data: (additionalFiles) => ({ @@ -42,55 +47,76 @@ export default { stitchArrays({ title: data.titles, + artistCredit: relations.artistCredits, links: relations.links, filenames: data.filenames, }).map(({ title, + artistCredit, links, filenames, }) => - language.encapsulate(pageCapsule, 'file', capsule => - (links.length === 1 - ? html.tag('li', - links[0].slots({ - content: - language.$(capsule, { - title: title, - }), - })) - - : links.length === 0 - ? html.tag('li', - language.$(capsule, 'withNoFiles', { - title: title, - })) - - : html.tag('li', {class: 'has-details'}, - html.tag('details', [ - html.tag('summary', - html.tag('span', - language.$(capsule, 'withMultipleFiles', { - title: - html.tag('b', title), - - files: - language.countAdditionalFiles( - links.length, - {unit: true}), - }))), - - html.tag('ul', - stitchArrays({ - link: links, - filename: filenames, - }).map(({link, filename}) => - html.tag('li', - link.slots({ - content: - language.$(capsule, { - title: filename, - }), - })))), - ]))))))), + language.encapsulate(pageCapsule, 'file', capsule => { + const titleLine = + language.encapsulate(capsule, workingCapsule => { + const workingOptions = {}; + + const titlePart = + (title + ? language.sanitize(title) + : language.$(capsule, 'placeholderTitle')); + + workingOptions.title = + (links.length <= 1 + ? links[0].slot('content', titlePart) + : html.tag('b', titlePart)); + + artistCredit.setSlots({ + normalStringKey: capsule + '.credit', + }); + + if (!html.isBlank(artistCredit)) { + workingCapsule += '.withCredit'; + workingOptions.credit = artistCredit; + } + + if (links.length === 0) { + workingCapsule += '.withNoFiles'; + } else if (links.length >= 2) { + workingCapsule += '.withMultipleFiles'; + workingOptions.files = + language.countFiles(links.length, {unit: true}); + } + + return language.$(workingCapsule, workingOptions); + }); + + if (links.length <= 1) { + return html.tag('li', titleLine); + } + + const summary = + html.tag('summary', + html.tag('span', titleLine)); + + const list = + html.tag('ul', + stitchArrays({ + link: links, + filename: filenames, + }).map(({link, filename}) => + html.tag('li', + link.slots({ + content: + language.$(capsule, { + title: filename, + }), + })))); + + return ( + html.tag('li', {class: 'has-details'}, + html.tag('details', [summary, list])) + ); + })))), ])), }; diff --git a/src/content/dependencies/generateTrackInfoPage.js b/src/content/dependencies/generateTrackInfoPage.js index 1a21cc72..33242b4e 100644 --- a/src/content/dependencies/generateTrackInfoPage.js +++ b/src/content/dependencies/generateTrackInfoPage.js @@ -122,7 +122,7 @@ export default { midiProjectFilesList: relation('generateAdditionalFilesList', track.midiProjectFiles), - additionalFilesList: + miscellaneousAdditionalFilesList: relation('generateAdditionalFilesList', track.additionalFiles), artistCommentarySection: @@ -219,12 +219,12 @@ export default { language.$(capsule, 'link')), })), - !html.isBlank(relations.additionalFilesList) && - language.encapsulate(capsule, 'additionalFiles.shortcut', capsule => + !html.isBlank(relations.miscellaneousAdditionalFilesList) && + language.encapsulate(capsule, 'miscellaneousAdditionalFiles.shortcut', capsule => language.$(capsule, { link: html.tag('a', - {href: '#midi-project-files'}, + {href: '#additional-files'}, language.$(capsule, 'link')), })), @@ -346,7 +346,9 @@ export default { title: language.$('releaseInfo.sheetMusicFiles.heading'), }), - relations.sheetMusicFilesList, + relations.sheetMusicFilesList.slots({ + string: 'sheetMusicFiles', + }), ]), html.tags([ @@ -355,16 +357,20 @@ export default { title: language.$('releaseInfo.midiProjectFiles.heading'), }), - relations.midiProjectFilesList, + relations.midiProjectFilesList.slots({ + string: 'midiProjectFiles', + }), ]), html.tags([ relations.contentHeading.clone().slots({ attributes: {id: 'additional-files'}, - title: language.$('releaseInfo.additionalFiles.heading'), + title: language.$('releaseInfo.miscellaneousAdditionalFiles.heading'), }), - relations.additionalFilesList, + relations.miscellaneousAdditionalFilesList.slots({ + string: 'miscellaneousAdditionalFiles', + }), ]), relations.artistCommentarySection, -- cgit 1.3.0-6-gf8a5