From be421bae8d73ae06e66c705a6c78858bcf8568c3 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 14 Apr 2025 21:16:30 -0300 Subject: data: LyricsEntry & co --- src/data/checks.js | 20 +--- .../composite/wiki-data/withParsedLyricsEntries.js | 4 +- src/data/things/album.js | 14 ++- src/data/things/commentary-entry.js | 118 -------------------- src/data/things/content.js | 122 +++++++++++++++++++++ src/data/things/flash.js | 6 +- src/data/things/index.js | 4 +- src/data/things/track.js | 22 +++- src/data/yaml.js | 27 ++++- 9 files changed, 185 insertions(+), 152 deletions(-) delete mode 100644 src/data/things/commentary-entry.js create mode 100644 src/data/things/content.js (limited to 'src/data') diff --git a/src/data/checks.js b/src/data/checks.js index 510bbb30..ecdfdb10 100644 --- a/src/data/checks.js +++ b/src/data/checks.js @@ -19,12 +19,6 @@ import { withAggregate, } from '#aggregate'; -import { - combineWikiDataArrays, - commentaryRegexCaseSensitive, - oldStyleLyricsDetectionRegex, -} from '#wiki-data'; - function inspect(value, opts = {}) { return nodeInspect(value, {colors: ENABLE_COLOR, ...opts}); } @@ -572,7 +566,7 @@ export function reportContentTextErrors(wikiData, { annotation: 'commentary annotation', }; - const newStyleLyricsShape = { + const lyricsShape = { body: 'lyrics body', artistDisplayText: 'lyrics artist display text', annotation: 'lyrics annotation', @@ -624,7 +618,7 @@ export function reportContentTextErrors(wikiData, { additionalFiles: additionalFileShape, commentary: commentaryShape, creditSources: commentaryShape, - lyrics: '_lyrics', + lyrics: lyricsShape, midiProjectFiles: additionalFileShape, sheetMusicFiles: additionalFileShape, }], @@ -748,7 +742,6 @@ export function reportContentTextErrors(wikiData, { nest({message: `Content text errors in ${inspect(thing)}`}, ({nest, push}) => { for (let [property, shape] of Object.entries(propSpec)) { - const rawValue = CacheableObject.getUpdateValue(thing, property); let value = thing[property]; if (value === undefined) { @@ -760,15 +753,6 @@ export function reportContentTextErrors(wikiData, { continue; } - if (shape === '_lyrics') { - if (oldStyleLyricsDetectionRegex.test(rawValue)) { - value = rawValue; - shape = '_content'; - } else { - shape = newStyleLyricsShape; - } - } - const fieldPropertyMessage = getFieldPropertyMessage( thing.constructor[Thing.yamlDocumentSpec], diff --git a/src/data/composite/wiki-data/withParsedLyricsEntries.js b/src/data/composite/wiki-data/withParsedLyricsEntries.js index d13bfbaa..419ceb84 100644 --- a/src/data/composite/wiki-data/withParsedLyricsEntries.js +++ b/src/data/composite/wiki-data/withParsedLyricsEntries.js @@ -1,7 +1,7 @@ import {input, templateCompositeFrom} from '#composite'; import {stitchArrays} from '#sugar'; import {isLyrics} from '#validators'; -import {commentaryRegexCaseSensitive, oldStyleLyricsDetectionRegex} +import {commentaryRegexCaseSensitive, multipleLyricsDetectionRegex} from '#wiki-data'; import { @@ -45,7 +45,7 @@ export default templateCompositeFrom({ compute: (continuation, { [input('from')]: lyrics, }) => - (oldStyleLyricsDetectionRegex.test(lyrics) + (multipleLyricsDetectionRegex.test(lyrics) ? continuation() : continuation.raiseOutput({ ['#parsedLyricsEntries']: diff --git a/src/data/things/album.js b/src/data/things/album.js index 2f5e1093..8a25a8ac 100644 --- a/src/data/things/album.js +++ b/src/data/things/album.js @@ -3,6 +3,7 @@ export const DATA_ALBUM_DIRECTORY = 'album'; import * as path from 'node:path'; import {inspect} from 'node:util'; +import CacheableObject from '#cacheable-object'; import {colors} from '#cli'; import {input} from '#composite'; import {traverse} from '#node-utils'; @@ -18,6 +19,7 @@ import { parseArtwork, parseCommentary, parseContributors, + parseCreditingSources, parseDate, parseDimensions, parseWallpaperParts, @@ -70,6 +72,7 @@ export class Album extends Thing { ArtTag, Artwork, CommentaryEntry, + CreditingSourcesEntry, Group, Track, TrackSection, @@ -210,7 +213,7 @@ export class Album extends Thing { }), creditSources: thingList({ - class: input.value(CommentaryEntry), + class: input.value(CreditingSourcesEntry), }), additionalFiles: additionalFiles(), @@ -610,7 +613,7 @@ export class Album extends Thing { 'Credit Sources': { property: 'creditSources', - transform: parseCommentary, + transform: parseCreditingSources, }, 'Additional Files': { @@ -686,6 +689,7 @@ export class Album extends Thing { const artworkData = []; const commentaryData = []; const creditingSourceData = []; + const lyricsData = []; for (const {header: album, entries} of results) { const trackSections = []; @@ -734,6 +738,11 @@ export class Album extends Thing { artworkData.push(...entry.trackArtworks); commentaryData.push(...entry.commentary); creditingSourceData.push(...entry.creditSources); + + // TODO: As exposed, Track.lyrics tries to inherit from the main + // release, which is impossible before the data's been linked. + // We just use the update value here. But it's icky! + lyricsData.push(...CacheableObject.getUpdateValue(entry, 'lyrics') ?? []); } closeCurrentTrackSection(); @@ -764,6 +773,7 @@ export class Album extends Thing { artworkData, commentaryData, creditingSourceData, + lyricsData, }; }, diff --git a/src/data/things/commentary-entry.js b/src/data/things/commentary-entry.js deleted file mode 100644 index 3cc53d85..00000000 --- a/src/data/things/commentary-entry.js +++ /dev/null @@ -1,118 +0,0 @@ -import {input} from '#composite'; -import find from '#find'; -import Thing from '#thing'; -import {is, isDate} from '#validators'; -import {parseDate} from '#yaml'; - -import {contentString, referenceList, simpleDate, soupyFind, thing} - from '#composite/wiki-properties'; - -import { - exposeConstant, - exposeDependencyOrContinue, - exposeUpdateValueOrContinue, - withResultOfAvailabilityCheck, -} from '#composite/control-flow'; - -import {withWebArchiveDate} from '#composite/things/commentary-entry'; - -export class CommentaryEntry extends Thing { - static [Thing.getPropertyDescriptors] = ({Artist}) => ({ - // Update & expose - - thing: thing(), - - artists: referenceList({ - class: input.value(Artist), - find: soupyFind.input('artist'), - }), - - artistText: contentString(), - - annotation: contentString(), - - dateKind: { - flags: {update: true, expose: true}, - - update: { - validate: is(...[ - 'sometime', - 'throughout', - 'around', - ]), - }, - }, - - accessKind: [ - exposeUpdateValueOrContinue({ - validate: input.value( - is(...[ - 'captured', - 'accessed', - ])), - }), - - withWebArchiveDate(), - - withResultOfAvailabilityCheck({ - from: '#webArchiveDate', - }), - - { - dependencies: ['#availability'], - compute: (continuation, {['#availability']: availability}) => - (availability - ? continuation.exit('captured') - : continuation()), - }, - - exposeConstant({ - value: input.value(null), - }), - ], - - date: simpleDate(), - - secondDate: simpleDate(), - - accessDate: [ - exposeUpdateValueOrContinue({ - validate: input.value(isDate), - }), - - withWebArchiveDate(), - - exposeDependencyOrContinue({ - dependency: '#webArchiveDate', - }), - - exposeConstant({ - value: input.value(null), - }), - ], - - body: contentString(), - - // Update only - - find: soupyFind(), - }); - - static [Thing.yamlDocumentSpec] = { - fields: { - 'Artists': {property: 'artists'}, - 'Artist Text': {property: 'artistText'}, - - 'Annotation': {property: 'annotation'}, - - 'Date Kind': {property: 'dateKind'}, - 'Access Kind': {property: 'accessKind'}, - - 'Date': {property: 'date', transform: parseDate}, - 'Second Date': {property: 'secondDate', transform: parseDate}, - 'Access Date': {property: 'accessDate', transform: parseDate}, - - 'Body': {property: 'body'}, - }, - }; -} diff --git a/src/data/things/content.js b/src/data/things/content.js new file mode 100644 index 00000000..7f352795 --- /dev/null +++ b/src/data/things/content.js @@ -0,0 +1,122 @@ +import {input} from '#composite'; +import find from '#find'; +import Thing from '#thing'; +import {is, isDate} from '#validators'; +import {parseDate} from '#yaml'; + +import {contentString, referenceList, simpleDate, soupyFind, thing} + from '#composite/wiki-properties'; + +import { + exposeConstant, + exposeDependencyOrContinue, + exposeUpdateValueOrContinue, + withResultOfAvailabilityCheck, +} from '#composite/control-flow'; + +import {withWebArchiveDate} from '#composite/things/commentary-entry'; + +export class ContentEntry extends Thing { + static [Thing.getPropertyDescriptors] = ({Artist}) => ({ + // Update & expose + + thing: thing(), + + artists: referenceList({ + class: input.value(Artist), + find: soupyFind.input('artist'), + }), + + artistText: contentString(), + + annotation: contentString(), + + dateKind: { + flags: {update: true, expose: true}, + + update: { + validate: is(...[ + 'sometime', + 'throughout', + 'around', + ]), + }, + }, + + accessKind: [ + exposeUpdateValueOrContinue({ + validate: input.value( + is(...[ + 'captured', + 'accessed', + ])), + }), + + withWebArchiveDate(), + + withResultOfAvailabilityCheck({ + from: '#webArchiveDate', + }), + + { + dependencies: ['#availability'], + compute: (continuation, {['#availability']: availability}) => + (availability + ? continuation.exit('captured') + : continuation()), + }, + + exposeConstant({ + value: input.value(null), + }), + ], + + date: simpleDate(), + + secondDate: simpleDate(), + + accessDate: [ + exposeUpdateValueOrContinue({ + validate: input.value(isDate), + }), + + withWebArchiveDate(), + + exposeDependencyOrContinue({ + dependency: '#webArchiveDate', + }), + + exposeConstant({ + value: input.value(null), + }), + ], + + body: contentString(), + + // Update only + + find: soupyFind(), + }); + + static [Thing.yamlDocumentSpec] = { + fields: { + 'Artists': {property: 'artists'}, + 'Artist Text': {property: 'artistText'}, + + 'Annotation': {property: 'annotation'}, + + 'Date Kind': {property: 'dateKind'}, + 'Access Kind': {property: 'accessKind'}, + + 'Date': {property: 'date', transform: parseDate}, + 'Second Date': {property: 'secondDate', transform: parseDate}, + 'Access Date': {property: 'accessDate', transform: parseDate}, + + 'Body': {property: 'body'}, + }, + }; +} + +export class CommentaryEntry extends ContentEntry {} +export class LyricsEntry extends ContentEntry {} +export class CreditingSourcesEntry extends ContentEntry {} diff --git a/src/data/things/flash.js b/src/data/things/flash.js index d115db9f..dac674dd 100644 --- a/src/data/things/flash.js +++ b/src/data/things/flash.js @@ -12,6 +12,7 @@ import { parseAdditionalNames, parseCommentary, parseContributors, + parseCreditingSources, parseDate, parseDimensions, } from '#yaml'; @@ -54,6 +55,7 @@ export class Flash extends Thing { static [Thing.getPropertyDescriptors] = ({ CommentaryEntry, + CreditingSourcesEntry, Track, FlashAct, WikiInfo, @@ -132,7 +134,7 @@ export class Flash extends Thing { }), creditSources: thingList({ - class: input.value(CommentaryEntry), + class: input.value(CreditingSourcesEntry), }), // Update only @@ -254,7 +256,7 @@ export class Flash extends Thing { 'Credit Sources': { property: 'creditSources', - transform: parseCommentary, + transform: parseCreditingSources, }, 'Review Points': {ignore: true}, diff --git a/src/data/things/index.js b/src/data/things/index.js index 59d8a490..b832ab75 100644 --- a/src/data/things/index.js +++ b/src/data/things/index.js @@ -13,7 +13,7 @@ import * as albumClasses from './album.js'; import * as artTagClasses from './art-tag.js'; import * as artistClasses from './artist.js'; import * as artworkClasses from './artwork.js'; -import * as commentaryEntryClasses from './commentary-entry.js'; +import * as contentClasses from './content.js'; import * as contributionClasses from './contribution.js'; import * as flashClasses from './flash.js'; import * as groupClasses from './group.js'; @@ -30,7 +30,7 @@ const allClassLists = { 'art-tag.js': artTagClasses, 'artist.js': artistClasses, 'artwork.js': artworkClasses, - 'commentary-entry.js': commentaryEntryClasses, + 'content.js': contentClasses, 'contribution.js': contributionClasses, 'flash.js': flashClasses, 'group.js': groupClasses, diff --git a/src/data/things/track.js b/src/data/things/track.js index 4a30433c..ae7be170 100644 --- a/src/data/things/track.js +++ b/src/data/things/track.js @@ -14,9 +14,11 @@ import { parseArtwork, parseCommentary, parseContributors, + parseCreditingSources, parseDate, parseDimensions, parseDuration, + parseLyrics, } from '#yaml'; import {withPropertyFromObject} from '#composite/data'; @@ -46,7 +48,6 @@ import { directory, duration, flag, - lyrics, name, referenceList, referencedArtworkList, @@ -89,7 +90,9 @@ export class Track extends Thing { ArtTag, Artwork, CommentaryEntry, + CreditingSourcesEntry, Flash, + LyricsEntry, TrackSection, WikiInfo, }) => ({ @@ -223,12 +226,18 @@ export class Track extends Thing { }), creditSources: thingList({ - class: input.value(CommentaryEntry), + class: input.value(CreditingSourcesEntry), }), lyrics: [ + // TODO: Inherited lyrics are literally the same objects, so of course + // their .thing properties aren't going to point back to this one, and + // certainly couldn't be recontextualized... inheritFromMainRelease(), - lyrics(), + + thingList({ + class: input.value(LyricsEntry), + }), ], additionalFiles: additionalFiles(), @@ -488,7 +497,10 @@ export class Track extends Thing { 'Always Reference By Directory': {property: 'alwaysReferenceByDirectory'}, - 'Lyrics': {property: 'lyrics'}, + 'Lyrics': { + property: 'lyrics', + transform: parseLyrics, + }, 'Commentary': { property: 'commentary', @@ -497,7 +509,7 @@ export class Track extends Thing { 'Credit Sources': { property: 'creditSources', - transform: parseCommentary, + transform: parseCreditingSources, }, 'Additional Files': { diff --git a/src/data/yaml.js b/src/data/yaml.js index e6b3fa71..847489f0 100644 --- a/src/data/yaml.js +++ b/src/data/yaml.js @@ -11,7 +11,8 @@ import {colors, ENABLE_COLOR, logInfo, logWarn} from '#cli'; import {sortByName} from '#sort'; import Thing from '#thing'; import thingConstructors from '#things'; -import {matchCommentaryEntries} from '#wiki-data'; +import {matchCommentaryEntries, multipleLyricsDetectionRegex} + from '#wiki-data'; import { aggregateThrows, @@ -825,7 +826,7 @@ export function parseArtwork({ return transform; } -export function parseCommentary(sourceText, {subdoc, CommentaryEntry}) { +export function parseContentEntries(thingClass, sourceText, {subdoc}) { const map = matchEntry => ({ 'Artists': matchEntry.artistReferences @@ -869,11 +870,29 @@ export function parseCommentary(sourceText, {subdoc, CommentaryEntry}) { const subdocs = documents.map(document => - subdoc(CommentaryEntry, document, {bindInto: 'thing'})); + subdoc(thingClass, document, {bindInto: 'thing'})); return subdocs; } +export function parseCommentary(sourceText, {subdoc, CommentaryEntry}) { + return parseContentEntries(CommentaryEntry, sourceText, {subdoc}); +} + +export function parseCreditingSources(sourceText, {subdoc, CreditingSourcesEntry}) { + return parseContentEntries(CreditingSourcesEntry, sourceText, {subdoc}); +} + +export function parseLyrics(sourceText, {subdoc, LyricsEntry}) { + if (!multipleLyricsDetectionRegex.test(sourceText)) { + const document = {'Body': sourceText}; + + return [subdoc(LyricsEntry, document, {bindInto: 'thing'})]; + } + + return parseContentEntries(LyricsEntry, sourceText, {subdoc}); +} + // documentModes: Symbols indicating sets of behavior for loading and processing // data files. export const documentModes = { @@ -1567,6 +1586,8 @@ export function linkWikiDataArrays(wikiData, {bindFind, bindReverse}) { ['homepageLayout.sections.rows', [/* find */]], + ['lyricsData', [/* find */]], + ['trackData', [ 'artworkData', 'trackData', -- cgit 1.3.0-6-gf8a5