« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/data/composite/things
diff options
context:
space:
mode:
Diffstat (limited to 'src/data/composite/things')
-rw-r--r--src/data/composite/things/album/index.js1
-rw-r--r--src/data/composite/things/album/withCoverArtDate.js50
-rw-r--r--src/data/composite/things/artwork/index.js6
-rw-r--r--src/data/composite/things/artwork/withArtTags.js99
-rw-r--r--src/data/composite/things/artwork/withAttachedArtwork.js43
-rw-r--r--src/data/composite/things/artwork/withContainingArtworkList.js46
-rw-r--r--src/data/composite/things/artwork/withContentWarningArtTags.js27
-rw-r--r--src/data/composite/things/artwork/withContribsFromAttachedArtwork.js27
-rw-r--r--src/data/composite/things/artwork/withPropertyFromAttachedArtwork.js65
-rw-r--r--src/data/composite/things/content/contentArtists.js40
-rw-r--r--src/data/composite/things/content/hasAnnotationPart.js25
-rw-r--r--src/data/composite/things/content/helpers/withExpressedOrImplicitArtistReferences.js61
-rw-r--r--src/data/composite/things/content/index.js7
-rw-r--r--src/data/composite/things/content/withAnnotationParts.js103
-rw-r--r--src/data/composite/things/content/withHasAnnotationPart.js43
-rw-r--r--src/data/composite/things/content/withSourceText.js53
-rw-r--r--src/data/composite/things/content/withSourceURLs.js62
-rw-r--r--src/data/composite/things/content/withWebArchiveDate.js41
-rw-r--r--src/data/composite/things/contribution/index.js2
-rw-r--r--src/data/composite/things/contribution/thingPropertyMatches.js46
-rw-r--r--src/data/composite/things/contribution/thingReferenceTypeMatches.js66
-rw-r--r--src/data/composite/things/language/index.js1
-rw-r--r--src/data/composite/things/language/withStrings.js111
-rw-r--r--src/data/composite/things/track-section/withContinueCountingFrom.js2
-rw-r--r--src/data/composite/things/track/alwaysReferenceByDirectory.js69
-rw-r--r--src/data/composite/things/track/index.js3
-rw-r--r--src/data/composite/things/track/trackAdditionalNameList.js38
-rw-r--r--src/data/composite/things/track/withAllReleases.js19
-rw-r--r--src/data/composite/things/track/withAlwaysReferenceByDirectory.js97
-rw-r--r--src/data/composite/things/track/withDate.js8
-rw-r--r--src/data/composite/things/track/withDirectorySuffix.js22
-rw-r--r--src/data/composite/things/track/withHasUniqueCoverArt.js23
-rw-r--r--src/data/composite/things/track/withMainRelease.js133
-rw-r--r--src/data/composite/things/track/withMainReleaseTrack.js248
-rw-r--r--src/data/composite/things/track/withOtherReleases.js3
-rw-r--r--src/data/composite/things/track/withPropertyFromMainRelease.js10
-rw-r--r--src/data/composite/things/track/withSuffixDirectoryFromAlbum.js20
37 files changed, 1383 insertions, 337 deletions
diff --git a/src/data/composite/things/album/index.js b/src/data/composite/things/album/index.js
index 8b5098f0..de1d37c3 100644
--- a/src/data/composite/things/album/index.js
+++ b/src/data/composite/things/album/index.js
@@ -1 +1,2 @@
+export {default as withCoverArtDate} from './withCoverArtDate.js';
 export {default as withTracks} from './withTracks.js';
diff --git a/src/data/composite/things/album/withCoverArtDate.js b/src/data/composite/things/album/withCoverArtDate.js
new file mode 100644
index 00000000..978f566a
--- /dev/null
+++ b/src/data/composite/things/album/withCoverArtDate.js
@@ -0,0 +1,50 @@
+import {input, templateCompositeFrom} from '#composite';
+import {isDate} from '#validators';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+import {withHasArtwork} from '#composite/wiki-data';
+
+export default templateCompositeFrom({
+  annotation: `withCoverArtDate`,
+
+  inputs: {
+    from: input({
+      validate: isDate,
+      defaultDependency: 'coverArtDate',
+      acceptsNull: true,
+    }),
+  },
+
+  outputs: ['#coverArtDate'],
+
+  steps: () => [
+    withHasArtwork({
+      contribs: 'coverArtistContribs',
+      artworks: 'coverArtworks',
+    }),
+
+    raiseOutputWithoutDependency({
+      dependency: '#hasArtwork',
+      mode: input.value('falsy'),
+      output: input.value({'#coverArtDate': null}),
+    }),
+
+    {
+      dependencies: [input('from')],
+      compute: (continuation, {
+        [input('from')]: from,
+      }) =>
+        (from
+          ? continuation.raiseOutput({'#coverArtDate': from})
+          : continuation()),
+    },
+
+    {
+      dependencies: ['date'],
+      compute: (continuation, {date}) =>
+        (date
+          ? continuation({'#coverArtDate': date})
+          : continuation({'#coverArtDate': null})),
+    },
+  ],
+});
diff --git a/src/data/composite/things/artwork/index.js b/src/data/composite/things/artwork/index.js
index b92bff72..b5e5e167 100644
--- a/src/data/composite/things/artwork/index.js
+++ b/src/data/composite/things/artwork/index.js
@@ -1 +1,7 @@
+export {default as withArtTags} from './withArtTags.js';
+export {default as withAttachedArtwork} from './withAttachedArtwork.js';
+export {default as withContainingArtworkList} from './withContainingArtworkList.js';
+export {default as withContentWarningArtTags} from './withContentWarningArtTags.js';
+export {default as withContribsFromAttachedArtwork} from './withContribsFromAttachedArtwork.js';
 export {default as withDate} from './withDate.js';
+export {default as withPropertyFromAttachedArtwork} from './withPropertyFromAttachedArtwork.js';
diff --git a/src/data/composite/things/artwork/withArtTags.js b/src/data/composite/things/artwork/withArtTags.js
new file mode 100644
index 00000000..1fed3c31
--- /dev/null
+++ b/src/data/composite/things/artwork/withArtTags.js
@@ -0,0 +1,99 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {raiseOutputWithoutDependency, withResultOfAvailabilityCheck}
+  from '#composite/control-flow';
+import {withPropertyFromObject} from '#composite/data';
+import {withResolvedReferenceList} from '#composite/wiki-data';
+import {soupyFind} from '#composite/wiki-properties';
+
+import withPropertyFromAttachedArtwork
+  from './withPropertyFromAttachedArtwork.js';
+
+export default templateCompositeFrom({
+  annotation: `withArtTags`,
+
+  inputs: {
+    from: input({
+      type: 'array',
+      acceptsNull: true,
+      defaultDependency: 'artTags',
+    }),
+  },
+
+  outputs: ['#artTags'],
+
+  steps: () => [
+    withResolvedReferenceList({
+      list: input('from'),
+      find: soupyFind.input('artTag'),
+    }),
+
+    withResultOfAvailabilityCheck({
+      from: '#resolvedReferenceList',
+      mode: input.value('empty'),
+    }),
+
+    {
+      dependencies: ['#availability', '#resolvedReferenceList'],
+      compute: (continuation, {
+        ['#availability']: availability,
+        ['#resolvedReferenceList']: resolvedReferenceList,
+      }) =>
+        (availability
+          ? continuation.raiseOutput({
+              '#artTags': resolvedReferenceList,
+            })
+          : continuation()),
+    },
+
+    withPropertyFromAttachedArtwork({
+      property: input.value('artTags'),
+    }),
+
+    withResultOfAvailabilityCheck({
+      from: '#attachedArtwork.artTags',
+      mode: input.value('empty'),
+    }),
+
+    {
+      dependencies: ['#availability', '#attachedArtwork.artTags'],
+      compute: (continuation, {
+        ['#availability']: availability,
+        ['#attachedArtwork.artTags']: attachedArtworkArtTags,
+      }) =>
+        (availability
+          ? continuation.raiseOutput({
+              '#artTags': attachedArtworkArtTags,
+            })
+          : continuation()),
+    },
+
+    raiseOutputWithoutDependency({
+      dependency: 'artTagsFromThingProperty',
+      output: input.value({'#artTags': []}),
+    }),
+
+    withPropertyFromObject({
+      object: 'thing',
+      property: 'artTagsFromThingProperty',
+    }).outputs({
+      ['#value']: '#thing.artTags',
+    }),
+
+    withResultOfAvailabilityCheck({
+      from: '#thing.artTags',
+      mode: input.value('empty'),
+    }),
+
+    {
+      dependencies: ['#availability', '#thing.artTags'],
+      compute: (continuation, {
+        ['#availability']: availability,
+        ['#thing.artTags']: thingArtTags,
+      }) =>
+        (availability
+          ? continuation({'#artTags': thingArtTags})
+          : continuation({'#artTags': []})),
+    },
+  ],
+});
diff --git a/src/data/composite/things/artwork/withAttachedArtwork.js b/src/data/composite/things/artwork/withAttachedArtwork.js
new file mode 100644
index 00000000..d7c0d87b
--- /dev/null
+++ b/src/data/composite/things/artwork/withAttachedArtwork.js
@@ -0,0 +1,43 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {flipFilter, raiseOutputWithoutDependency}
+  from '#composite/control-flow';
+import {withNearbyItemFromList, withPropertyFromList} from '#composite/data';
+
+import withContainingArtworkList from './withContainingArtworkList.js';
+
+export default templateCompositeFrom({
+  annotaion: `withContribsFromMainArtwork`,
+
+  outputs: ['#attachedArtwork'],
+
+  steps: () => [
+    raiseOutputWithoutDependency({
+      dependency: 'attachAbove',
+      mode: input.value('falsy'),
+      output: input.value({'#attachedArtwork': null}),
+    }),
+
+    withContainingArtworkList(),
+
+    withPropertyFromList({
+      list: '#containingArtworkList',
+      property: input.value('attachAbove'),
+    }),
+
+    flipFilter({
+      filter: '#containingArtworkList.attachAbove',
+    }).outputs({
+      '#containingArtworkList.attachAbove': '#filterNotAttached',
+    }),
+
+    withNearbyItemFromList({
+      list: '#containingArtworkList',
+      item: input.myself(),
+      offset: input.value(-1),
+      filter: '#filterNotAttached',
+    }).outputs({
+      '#nearbyItem': '#attachedArtwork',
+    }),
+  ],
+});
diff --git a/src/data/composite/things/artwork/withContainingArtworkList.js b/src/data/composite/things/artwork/withContainingArtworkList.js
new file mode 100644
index 00000000..9c928ffd
--- /dev/null
+++ b/src/data/composite/things/artwork/withContainingArtworkList.js
@@ -0,0 +1,46 @@
+// Gets the list of artworks which contains this one, which is functionally
+// equivalent to `this.thing[this.thingProperty]`. If the exposed value is not
+// a list at all (i.e. the property holds a single artwork), this composition
+// outputs null.
+
+import {input, templateCompositeFrom} from '#composite';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+import {withPropertyFromObject} from '#composite/data';
+
+export default templateCompositeFrom({
+  annotation: `withContainingArtworkList`,
+
+  outputs: ['#containingArtworkList'],
+
+  steps: () => [
+    raiseOutputWithoutDependency({
+      dependency: 'thing',
+      output: input.value({'#containingArtworkList': null}),
+    }),
+
+    raiseOutputWithoutDependency({
+      dependency: 'thingProperty',
+      output: input.value({'#containingArtworkList': null}),
+    }),
+
+    withPropertyFromObject({
+      object: 'thing',
+      property: 'thingProperty',
+    }).outputs({
+      '#value': '#containingValue',
+    }),
+
+    {
+      dependencies: ['#containingValue'],
+      compute: (continuation, {
+        ['#containingValue']: containingValue,
+      }) => continuation({
+        ['#containingArtworkList']:
+          (Array.isArray(containingValue)
+            ? containingValue
+            : null),
+      }),
+    },
+  ],
+});
diff --git a/src/data/composite/things/artwork/withContentWarningArtTags.js b/src/data/composite/things/artwork/withContentWarningArtTags.js
new file mode 100644
index 00000000..4c07e837
--- /dev/null
+++ b/src/data/composite/things/artwork/withContentWarningArtTags.js
@@ -0,0 +1,27 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {withFilteredList, withPropertyFromList} from '#composite/data';
+
+import withArtTags from './withArtTags.js';
+
+export default templateCompositeFrom({
+  annotation: `withContentWarningArtTags`,
+
+  outputs: ['#contentWarningArtTags'],
+
+  steps: () => [
+    withArtTags(),
+
+    withPropertyFromList({
+      list: '#artTags',
+      property: input.value('isContentWarning'),
+    }),
+
+    withFilteredList({
+      list: '#artTags',
+      filter: '#artTags.isContentWarning',
+    }).outputs({
+      '#filteredList': '#contentWarningArtTags',
+    }),
+  ],
+});
diff --git a/src/data/composite/things/artwork/withContribsFromAttachedArtwork.js b/src/data/composite/things/artwork/withContribsFromAttachedArtwork.js
new file mode 100644
index 00000000..e9425c95
--- /dev/null
+++ b/src/data/composite/things/artwork/withContribsFromAttachedArtwork.js
@@ -0,0 +1,27 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+import {withRecontextualizedContributionList} from '#composite/wiki-data';
+
+import withPropertyFromAttachedArtwork from './withPropertyFromAttachedArtwork.js';
+
+export default templateCompositeFrom({
+  annotaion: `withContribsFromAttachedArtwork`,
+
+  outputs: ['#attachedArtwork.artistContribs'],
+
+  steps: () => [
+    withPropertyFromAttachedArtwork({
+      property: input.value('artistContribs'),
+    }),
+
+    raiseOutputWithoutDependency({
+      dependency: '#attachedArtwork.artistContribs',
+      output: input.value({'#attachedArtwork.artistContribs': null}),
+    }),
+
+    withRecontextualizedContributionList({
+      list: '#attachedArtwork.artistContribs',
+    }),
+  ],
+});
diff --git a/src/data/composite/things/artwork/withPropertyFromAttachedArtwork.js b/src/data/composite/things/artwork/withPropertyFromAttachedArtwork.js
new file mode 100644
index 00000000..a2f954b9
--- /dev/null
+++ b/src/data/composite/things/artwork/withPropertyFromAttachedArtwork.js
@@ -0,0 +1,65 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {withResultOfAvailabilityCheck} from '#composite/control-flow';
+import {withPropertyFromObject} from '#composite/data';
+
+import withAttachedArtwork from './withAttachedArtwork.js';
+
+function getOutputName({
+  [input.staticValue('property')]: property,
+}) {
+  if (property) {
+    return `#attachedArtwork.${property}`;
+  } else {
+    return '#value';
+  }
+}
+
+export default templateCompositeFrom({
+  annotation: `withPropertyFromAttachedArtwork`,
+
+  inputs: {
+    property: input({type: 'string'}),
+  },
+
+  outputs: inputs => [getOutputName(inputs)],
+
+  steps: () => [
+    {
+      dependencies: [input.staticValue('property')],
+      compute: (continuation, inputs) =>
+        continuation({'#output': getOutputName(inputs)}),
+    },
+
+    withAttachedArtwork(),
+
+    withResultOfAvailabilityCheck({
+      from: '#attachedArtwork',
+    }),
+
+    {
+      dependencies: ['#availability', '#output'],
+      compute: (continuation, {
+        ['#availability']: availability,
+        ['#output']: output,
+      }) =>
+        (availability
+          ? continuation()
+          : continuation.raiseOutput({[output]: null})),
+    },
+
+    withPropertyFromObject({
+      object: '#attachedArtwork',
+      property: input('property'),
+    }),
+
+    {
+      dependencies: ['#value', '#output'],
+      compute: (continuation, {
+        ['#value']: value,
+        ['#output']: output,
+      }) =>
+        continuation.raiseOutput({[output]: value}),
+    },
+  ],
+});
diff --git a/src/data/composite/things/content/contentArtists.js b/src/data/composite/things/content/contentArtists.js
new file mode 100644
index 00000000..8d5db5a5
--- /dev/null
+++ b/src/data/composite/things/content/contentArtists.js
@@ -0,0 +1,40 @@
+import {input, templateCompositeFrom} from '#composite';
+import {validateReferenceList} from '#validators';
+
+import {exitWithoutDependency, exposeDependency}
+  from '#composite/control-flow';
+import {withResolvedReferenceList} from '#composite/wiki-data';
+import {soupyFind} from '#composite/wiki-properties';
+
+import withExpressedOrImplicitArtistReferences
+  from './helpers/withExpressedOrImplicitArtistReferences.js';
+
+export default templateCompositeFrom({
+  annotation: `contentArtists`,
+
+  compose: false,
+
+  update: {
+    validate: validateReferenceList('artist'),
+  },
+
+  steps: () => [
+    withExpressedOrImplicitArtistReferences({
+      from: input.updateValue(),
+    }),
+
+    exitWithoutDependency({
+      dependency: '#artistReferences',
+      value: input.value([]),
+    }),
+
+    withResolvedReferenceList({
+      list: '#artistReferences',
+      find: soupyFind.input('artist'),
+    }),
+
+    exposeDependency({
+      dependency: '#resolvedReferenceList',
+    }),
+  ],
+});
diff --git a/src/data/composite/things/content/hasAnnotationPart.js b/src/data/composite/things/content/hasAnnotationPart.js
new file mode 100644
index 00000000..83d175e3
--- /dev/null
+++ b/src/data/composite/things/content/hasAnnotationPart.js
@@ -0,0 +1,25 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {exposeDependency} from '#composite/control-flow';
+
+import withHasAnnotationPart from './withHasAnnotationPart.js';
+
+export default templateCompositeFrom({
+  annotation: `hasAnnotationPart`,
+
+  compose: false,
+
+  inputs: {
+    part: input({type: 'string'}),
+  },
+
+  steps: () => [
+    withHasAnnotationPart({
+      part: input('part'),
+    }),
+
+    exposeDependency({
+      dependency: '#hasAnnotationPart',
+    }),
+  ],
+});
diff --git a/src/data/composite/things/content/helpers/withExpressedOrImplicitArtistReferences.js b/src/data/composite/things/content/helpers/withExpressedOrImplicitArtistReferences.js
new file mode 100644
index 00000000..69da8c75
--- /dev/null
+++ b/src/data/composite/things/content/helpers/withExpressedOrImplicitArtistReferences.js
@@ -0,0 +1,61 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+import {withFilteredList, withMappedList} from '#composite/data';
+import {withContentNodes} from '#composite/wiki-data';
+
+export default templateCompositeFrom({
+  annotation: `withExpressedOrImplicitArtistReferences`,
+
+  inputs: {
+    from: input({type: 'array', acceptsNull: true}),
+  },
+
+  outputs: ['#artistReferences'],
+
+  steps: () => [
+    {
+      dependencies: [input('from')],
+      compute: (continuation, {
+        [input('from')]: expressedArtistReferences,
+      }) =>
+        (expressedArtistReferences
+          ? continuation.raiseOutput({'#artistReferences': expressedArtistReferences})
+          : continuation()),
+    },
+
+    raiseOutputWithoutDependency({
+      dependency: 'artistText',
+      output: input.value({'#artistReferences': null}),
+    }),
+
+    withContentNodes({
+      from: 'artistText',
+    }),
+
+    withMappedList({
+      list: '#contentNodes',
+      map: input.value(node =>
+        node.type === 'tag' &&
+        node.data.replacerKey?.data === 'artist'),
+    }).outputs({
+      '#mappedList': '#artistTagFilter',
+    }),
+
+    withFilteredList({
+      list: '#contentNodes',
+      filter: '#artistTagFilter',
+    }).outputs({
+      '#filteredList': '#artistTags',
+    }),
+
+    withMappedList({
+      list: '#artistTags',
+      map: input.value(node =>
+        'artist:' +
+        node.data.replacerValue[0].data),
+    }).outputs({
+      '#mappedList': '#artistReferences',
+    }),
+  ],
+});
diff --git a/src/data/composite/things/content/index.js b/src/data/composite/things/content/index.js
new file mode 100644
index 00000000..4176337d
--- /dev/null
+++ b/src/data/composite/things/content/index.js
@@ -0,0 +1,7 @@
+export {default as contentArtists} from './contentArtists.js';
+export {default as hasAnnotationPart} from './hasAnnotationPart.js';
+export {default as withAnnotationParts} from './withAnnotationParts.js';
+export {default as withHasAnnotationPart} from './withHasAnnotationPart.js';
+export {default as withSourceText} from './withSourceText.js';
+export {default as withSourceURLs} from './withSourceURLs.js';
+export {default as withWebArchiveDate} from './withWebArchiveDate.js';
diff --git a/src/data/composite/things/content/withAnnotationParts.js b/src/data/composite/things/content/withAnnotationParts.js
new file mode 100644
index 00000000..0c5a0294
--- /dev/null
+++ b/src/data/composite/things/content/withAnnotationParts.js
@@ -0,0 +1,103 @@
+import {input, templateCompositeFrom} from '#composite';
+import {empty, transposeArrays} from '#sugar';
+import {is} from '#validators';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+import {withPropertyFromList} from '#composite/data';
+import {splitContentNodesAround, withContentNodes} from '#composite/wiki-data';
+
+export default templateCompositeFrom({
+  annotation: `withAnnotationParts`,
+
+  inputs: {
+    mode: input({
+      validate: is('strings', 'nodes'),
+    }),
+  },
+
+  outputs: ['#annotationParts'],
+
+  steps: () => [
+    raiseOutputWithoutDependency({
+      dependency: 'annotation',
+      output: input.value({'#annotationParts': []}),
+    }),
+
+    withContentNodes({
+      from: 'annotation',
+    }),
+
+    splitContentNodesAround({
+      nodes: '#contentNodes',
+      around: input.value(/, */g),
+    }),
+
+    {
+      dependencies: ['#contentNodeLists'],
+      compute: (continuation, {
+        ['#contentNodeLists']: nodeLists,
+      }) => continuation({
+        ['#contentNodeLists']:
+          nodeLists.filter(list => !empty(list)),
+      }),
+    },
+
+    {
+      dependencies: ['#contentNodeLists', input('mode')],
+      compute: (continuation, {
+        ['#contentNodeLists']: nodeLists,
+        [input('mode')]: mode,
+      }) =>
+        (mode === 'nodes'
+          ? continuation.raiseOutput({'#annotationParts': nodeLists})
+          : continuation()),
+    },
+
+    {
+      dependencies: ['#contentNodeLists'],
+
+      compute: (continuation, {
+        ['#contentNodeLists']: nodeLists,
+      }) => continuation({
+        ['#firstNodes']:
+          nodeLists.map(list => list.at(0)),
+
+        ['#lastNodes']:
+          nodeLists.map(list => list.at(-1)),
+      }),
+    },
+
+    withPropertyFromList({
+      list: '#firstNodes',
+      property: input.value('i'),
+    }).outputs({
+      '#firstNodes.i': '#startIndices',
+    }),
+
+    withPropertyFromList({
+      list: '#lastNodes',
+      property: input.value('iEnd'),
+    }).outputs({
+      '#lastNodes.iEnd': '#endIndices',
+    }),
+
+    {
+      dependencies: [
+        'annotation',
+        '#startIndices',
+        '#endIndices',
+      ],
+
+      compute: (continuation, {
+        ['annotation']: annotation,
+        ['#startIndices']: startIndices,
+        ['#endIndices']: endIndices,
+      }) => continuation({
+        ['#annotationParts']:
+          transposeArrays([startIndices, endIndices])
+            .map(([start, end]) =>
+              annotation.slice(start, end)),
+      }),
+    },
+  ],
+});
diff --git a/src/data/composite/things/content/withHasAnnotationPart.js b/src/data/composite/things/content/withHasAnnotationPart.js
new file mode 100644
index 00000000..4af554f3
--- /dev/null
+++ b/src/data/composite/things/content/withHasAnnotationPart.js
@@ -0,0 +1,43 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+
+import withAnnotationParts from './withAnnotationParts.js';
+
+export default templateCompositeFrom({
+  annotation: `withHasAnnotationPart`,
+
+  inputs: {
+    part: input({type: 'string'}),
+  },
+
+  outputs: ['#hasAnnotationPart'],
+
+  steps: () => [
+    withAnnotationParts({
+      mode: input.value('strings'),
+    }),
+
+    raiseOutputWithoutDependency({
+      dependency: '#annotationParts',
+      output: input.value({'#hasAnnotationPart': false}),
+    }),
+
+    {
+      dependencies: [
+        input('part'),
+        '#annotationParts',
+      ],
+
+      compute: (continuation, {
+        [input('part')]: search,
+        ['#annotationParts']: parts,
+      }) => continuation({
+        ['#hasAnnotationPart']:
+          parts.some(part =>
+            part.toLowerCase() ===
+            search.toLowerCase()),
+      }),
+    },
+  ],
+});
diff --git a/src/data/composite/things/content/withSourceText.js b/src/data/composite/things/content/withSourceText.js
new file mode 100644
index 00000000..292306b7
--- /dev/null
+++ b/src/data/composite/things/content/withSourceText.js
@@ -0,0 +1,53 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+
+import withAnnotationParts from './withAnnotationParts.js';
+
+export default templateCompositeFrom({
+  annotation: `withSourceText`,
+
+  outputs: ['#sourceText'],
+
+  steps: () => [
+    withAnnotationParts({
+      mode: input.value('nodes'),
+    }),
+
+    raiseOutputWithoutDependency({
+      dependency: '#annotationParts',
+      output: input.value({'#sourceText': null}),
+    }),
+
+    {
+      dependencies: ['#annotationParts'],
+      compute: (continuation, {
+        ['#annotationParts']: annotationParts,
+      }) => continuation({
+        ['#firstPartWithExternalLink']:
+          annotationParts
+            .find(nodes => nodes
+              .some(node => node.type === 'external-link')) ??
+          null,
+      }),
+    },
+
+    raiseOutputWithoutDependency({
+      dependency: '#firstPartWithExternalLink',
+      output: input.value({'#sourceText': null}),
+    }),
+
+    {
+      dependencies: ['annotation', '#firstPartWithExternalLink'],
+      compute: (continuation, {
+        ['annotation']: annotation,
+        ['#firstPartWithExternalLink']: nodes,
+      }) => continuation({
+        ['#sourceText']:
+          annotation.slice(
+            nodes.at(0).i,
+            nodes.at(-1).iEnd),
+      }),
+    },
+  ],
+});
diff --git a/src/data/composite/things/content/withSourceURLs.js b/src/data/composite/things/content/withSourceURLs.js
new file mode 100644
index 00000000..f85ff9ea
--- /dev/null
+++ b/src/data/composite/things/content/withSourceURLs.js
@@ -0,0 +1,62 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+import {withFilteredList, withMappedList} from '#composite/data';
+
+import withAnnotationParts from './withAnnotationParts.js';
+
+export default templateCompositeFrom({
+  annotation: `withSourceURLs`,
+
+  outputs: ['#sourceURLs'],
+
+  steps: () => [
+    withAnnotationParts({
+      mode: input.value('nodes'),
+    }),
+
+    raiseOutputWithoutDependency({
+      dependency: '#annotationParts',
+      output: input.value({'#sourceURLs': []}),
+    }),
+
+    {
+      dependencies: ['#annotationParts'],
+      compute: (continuation, {
+        ['#annotationParts']: annotationParts,
+      }) => continuation({
+        ['#firstPartWithExternalLink']:
+          annotationParts
+            .find(nodes => nodes
+              .some(node => node.type === 'external-link')) ??
+          null,
+      }),
+    },
+
+    raiseOutputWithoutDependency({
+      dependency: '#firstPartWithExternalLink',
+      output: input.value({'#sourceURLs': []}),
+    }),
+
+    withMappedList({
+      list: '#firstPartWithExternalLink',
+      map: input.value(node => node.type === 'external-link'),
+    }).outputs({
+      '#mappedList': '#externalLinkFilter',
+    }),
+
+    withFilteredList({
+      list: '#firstPartWithExternalLink',
+      filter: '#externalLinkFilter',
+    }).outputs({
+      '#filteredList': '#externalLinks',
+    }),
+
+    withMappedList({
+      list: '#externalLinks',
+      map: input.value(node => node.data.href),
+    }).outputs({
+      '#mappedList': '#sourceURLs',
+    }),
+  ],
+});
diff --git a/src/data/composite/things/content/withWebArchiveDate.js b/src/data/composite/things/content/withWebArchiveDate.js
new file mode 100644
index 00000000..3aaa4f64
--- /dev/null
+++ b/src/data/composite/things/content/withWebArchiveDate.js
@@ -0,0 +1,41 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+
+export default templateCompositeFrom({
+  annotation: `withWebArchiveDate`,
+
+  outputs: ['#webArchiveDate'],
+
+  steps: () => [
+    {
+      dependencies: ['annotation'],
+
+      compute: (continuation, {annotation}) =>
+        continuation({
+          ['#dateText']:
+            annotation
+              ?.match(/https?:\/\/web.archive.org\/web\/([0-9]{8,8})[0-9]*\//)
+              ?.[1] ??
+            null,
+        }),
+    },
+
+    raiseOutputWithoutDependency({
+      dependency: '#dateText',
+      output: input.value({['#webArchiveDate']: null}),
+    }),
+
+    {
+      dependencies: ['#dateText'],
+      compute: (continuation, {['#dateText']: dateText}) =>
+        continuation({
+          ['#webArchiveDate']:
+            new Date(
+              dateText.slice(0, 4) + '/' +
+              dateText.slice(4, 6) + '/' +
+              dateText.slice(6, 8)),
+        }),
+    },
+  ],
+});
diff --git a/src/data/composite/things/contribution/index.js b/src/data/composite/things/contribution/index.js
index 9b22be2e..31d86b8b 100644
--- a/src/data/composite/things/contribution/index.js
+++ b/src/data/composite/things/contribution/index.js
@@ -1,6 +1,4 @@
 export {default as inheritFromContributionPresets} from './inheritFromContributionPresets.js';
-export {default as thingPropertyMatches} from './thingPropertyMatches.js';
-export {default as thingReferenceTypeMatches} from './thingReferenceTypeMatches.js';
 export {default as withContainingReverseContributionList} from './withContainingReverseContributionList.js';
 export {default as withContributionArtist} from './withContributionArtist.js';
 export {default as withContributionContext} from './withContributionContext.js';
diff --git a/src/data/composite/things/contribution/thingPropertyMatches.js b/src/data/composite/things/contribution/thingPropertyMatches.js
deleted file mode 100644
index 1e9019b8..00000000
--- a/src/data/composite/things/contribution/thingPropertyMatches.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import {input, templateCompositeFrom} from '#composite';
-
-import {exitWithoutDependency} from '#composite/control-flow';
-import {withPropertyFromObject} from '#composite/data';
-
-export default templateCompositeFrom({
-  annotation: `thingPropertyMatches`,
-
-  compose: false,
-
-  inputs: {
-    value: input({type: 'string'}),
-  },
-
-  steps: () => [
-    {
-      dependencies: ['thing', 'thingProperty'],
-
-      compute: (continuation, {thing, thingProperty}) =>
-        continuation({
-          ['#thingProperty']:
-            (thing.constructor[Symbol.for('Thing.referenceType')] === 'artwork'
-              ? thing.artistContribsFromThingProperty
-              : thingProperty),
-        }),
-    },
-
-    exitWithoutDependency({
-      dependency: '#thingProperty',
-      value: input.value(false),
-    }),
-
-    {
-      dependencies: [
-        '#thingProperty',
-        input('value'),
-      ],
-
-      compute: ({
-        ['#thingProperty']: thingProperty,
-        [input('value')]: value,
-      }) =>
-        thingProperty === value,
-    },
-  ],
-});
diff --git a/src/data/composite/things/contribution/thingReferenceTypeMatches.js b/src/data/composite/things/contribution/thingReferenceTypeMatches.js
deleted file mode 100644
index 4042e78f..00000000
--- a/src/data/composite/things/contribution/thingReferenceTypeMatches.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import {input, templateCompositeFrom} from '#composite';
-
-import {exitWithoutDependency} from '#composite/control-flow';
-import {withPropertyFromObject} from '#composite/data';
-
-export default templateCompositeFrom({
-  annotation: `thingReferenceTypeMatches`,
-
-  compose: false,
-
-  inputs: {
-    value: input({type: 'string'}),
-  },
-
-  steps: () => [
-    exitWithoutDependency({
-      dependency: 'thing',
-      value: input.value(false),
-    }),
-
-    withPropertyFromObject({
-      object: 'thing',
-      property: input.value('constructor'),
-    }),
-
-    {
-      dependencies: [
-        '#thing.constructor',
-        input('value'),
-      ],
-
-      compute: (continuation, {
-        ['#thing.constructor']: constructor,
-        [input('value')]: value,
-      }) =>
-        (constructor[Symbol.for('Thing.referenceType')] === value
-          ? continuation.exit(true)
-       : constructor[Symbol.for('Thing.referenceType')] === 'artwork'
-          ? continuation()
-          : continuation.exit(false)),
-    },
-
-    withPropertyFromObject({
-      object: 'thing',
-      property: input.value('thing'),
-    }),
-
-    withPropertyFromObject({
-      object: '#thing.thing',
-      property: input.value('constructor'),
-    }),
-
-    {
-      dependencies: [
-        '#thing.thing.constructor',
-        input('value'),
-      ],
-
-      compute: ({
-        ['#thing.thing.constructor']: constructor,
-        [input('value')]: value,
-      }) =>
-        constructor[Symbol.for('Thing.referenceType')] === value,
-    },
-  ],
-});
diff --git a/src/data/composite/things/language/index.js b/src/data/composite/things/language/index.js
new file mode 100644
index 00000000..f22cdaf6
--- /dev/null
+++ b/src/data/composite/things/language/index.js
@@ -0,0 +1 @@
+export {default as withStrings} from './withStrings.js';
diff --git a/src/data/composite/things/language/withStrings.js b/src/data/composite/things/language/withStrings.js
new file mode 100644
index 00000000..3b8d46b3
--- /dev/null
+++ b/src/data/composite/things/language/withStrings.js
@@ -0,0 +1,111 @@
+import {logWarn} from '#cli';
+import {input, templateCompositeFrom} from '#composite';
+import {empty, withEntries} from '#sugar';
+import {languageOptionRegex} from '#wiki-data';
+
+import {withResultOfAvailabilityCheck} from '#composite/control-flow';
+
+export default templateCompositeFrom({
+  annotation: `withStrings`,
+
+  inputs: {
+    from: input({defaultDependency: 'strings'}),
+  },
+
+  outputs: ['#strings'],
+
+  steps: () => [
+    withResultOfAvailabilityCheck({
+      from: input('from'),
+    }).outputs({
+      '#availability': '#stringsAvailability',
+    }),
+
+    withResultOfAvailabilityCheck({
+      from: 'inheritedStrings',
+    }).outputs({
+      '#availability': '#inheritedStringsAvailability',
+    }),
+
+    {
+      dependencies: [
+        '#stringsAvailability',
+        '#inheritedStringsAvailability',
+      ],
+
+      compute: (continuation, {
+        ['#stringsAvailability']: stringsAvailability,
+        ['#inheritedStringsAvailability']: inheritedStringsAvailability,
+      }) =>
+        (stringsAvailability || inheritedStringsAvailability
+          ? continuation()
+          : continuation.raiseOutput({'#strings': null})),
+    },
+
+    {
+      dependencies: [input('from'), '#inheritedStringsAvailability'],
+      compute: (continuation, {
+        [input('from')]: strings,
+        ['#inheritedStringsAvailability']: inheritedStringsAvailability,
+      }) =>
+        (inheritedStringsAvailability
+          ? continuation()
+          : continuation.raiseOutput({'#strings': strings})),
+    },
+
+    {
+      dependencies: ['inheritedStrings', '#stringsAvailability'],
+      compute: (continuation, {
+        ['inheritedStrings']: inheritedStrings,
+        ['#stringsAvailability']: stringsAvailability,
+      }) =>
+        (stringsAvailability
+          ? continuation()
+          : continuation.raiseOutput({'#strings': inheritedStrings})),
+    },
+
+    {
+      dependencies: [input('from'), 'inheritedStrings', 'code'],
+      compute(continuation, {
+        [input('from')]: strings,
+        ['inheritedStrings']: inheritedStrings,
+        ['code']: code,
+      }) {
+        const validStrings = {
+          ...inheritedStrings,
+          ...strings,
+        };
+
+        const optionsFromTemplate = template =>
+          Array.from(template.matchAll(languageOptionRegex))
+            .map(({groups}) => groups.name);
+
+        for (const [key, providedTemplate] of Object.entries(strings)) {
+          const inheritedTemplate = inheritedStrings[key];
+          if (!inheritedTemplate) continue;
+
+          const providedOptions = optionsFromTemplate(providedTemplate);
+          const inheritedOptions = optionsFromTemplate(inheritedTemplate);
+
+          const missingOptionNames =
+            inheritedOptions.filter(name => !providedOptions.includes(name));
+
+          const misplacedOptionNames =
+            providedOptions.filter(name => !inheritedOptions.includes(name));
+
+          if (!empty(missingOptionNames) || !empty(misplacedOptionNames)) {
+            logWarn`Not using ${code ?? '(no code)'} string ${key}:`;
+            if (!empty(missingOptionNames))
+              logWarn`- Missing options: ${missingOptionNames.join(', ')}`;
+            if (!empty(misplacedOptionNames))
+              logWarn`- Unexpected options: ${misplacedOptionNames.join(', ')}`;
+
+            validStrings[key] = inheritedStrings[key];
+          }
+        }
+
+        return continuation({'#strings': validStrings});
+      },
+    },
+  ],
+});
diff --git a/src/data/composite/things/track-section/withContinueCountingFrom.js b/src/data/composite/things/track-section/withContinueCountingFrom.js
index e034b7a5..0ca52b6c 100644
--- a/src/data/composite/things/track-section/withContinueCountingFrom.js
+++ b/src/data/composite/things/track-section/withContinueCountingFrom.js
@@ -1,4 +1,4 @@
-import {input, templateCompositeFrom} from '#composite';
+import {templateCompositeFrom} from '#composite';
 
 import withStartCountingFrom from './withStartCountingFrom.js';
 
diff --git a/src/data/composite/things/track/alwaysReferenceByDirectory.js b/src/data/composite/things/track/alwaysReferenceByDirectory.js
new file mode 100644
index 00000000..a342d38b
--- /dev/null
+++ b/src/data/composite/things/track/alwaysReferenceByDirectory.js
@@ -0,0 +1,69 @@
+// Controls how find.track works - it'll never be matched by a reference
+// just to the track's name, which means you don't have to always reference
+// some *other* (much more commonly referenced) track by directory instead
+// of more naturally by name.
+
+import {input, templateCompositeFrom} from '#composite';
+import {isBoolean} from '#validators';
+import {getKebabCase} from '#wiki-data';
+
+import {withPropertyFromObject} from '#composite/data';
+
+import {
+  exitWithoutDependency,
+  exposeDependencyOrContinue,
+  exposeUpdateValueOrContinue,
+} from '#composite/control-flow';
+
+import withMainReleaseTrack from './withMainReleaseTrack.js';
+import withPropertyFromAlbum from './withPropertyFromAlbum.js';
+
+export default templateCompositeFrom({
+  annotation: `alwaysReferenceByDirectory`,
+
+  compose: false,
+
+  steps: () => [
+    exposeUpdateValueOrContinue({
+      validate: input.value(isBoolean),
+    }),
+
+    withPropertyFromAlbum({
+      property: input.value('alwaysReferenceTracksByDirectory'),
+    }),
+
+    // Falsy mode means this exposes true if the album's property is true,
+    // but continues if the property is false (which is also the default).
+    exposeDependencyOrContinue({
+      dependency: '#album.alwaysReferenceTracksByDirectory',
+      mode: input.value('falsy'),
+    }),
+
+    exitWithoutDependency({
+      dependency: 'mainRelease',
+      value: input.value(false),
+    }),
+
+    withMainReleaseTrack(),
+
+    exitWithoutDependency({
+      dependency: '#mainReleaseTrack',
+      value: input.value(false),
+    }),
+
+    withPropertyFromObject({
+      object: '#mainReleaseTrack',
+      property: input.value('name'),
+    }),
+
+    {
+      dependencies: ['name', '#mainReleaseTrack.name'],
+      compute: ({
+        ['name']: name,
+        ['#mainReleaseTrack.name']: mainReleaseName,
+      }) =>
+        getKebabCase(name) ===
+        getKebabCase(mainReleaseName),
+    },
+  ],
+});
diff --git a/src/data/composite/things/track/index.js b/src/data/composite/things/track/index.js
index e789e736..1c203cd9 100644
--- a/src/data/composite/things/track/index.js
+++ b/src/data/composite/things/track/index.js
@@ -1,14 +1,15 @@
+export {default as alwaysReferenceByDirectory} from './alwaysReferenceByDirectory.js';
 export {default as exitWithoutUniqueCoverArt} from './exitWithoutUniqueCoverArt.js';
 export {default as inheritContributionListFromMainRelease} from './inheritContributionListFromMainRelease.js';
 export {default as inheritFromMainRelease} from './inheritFromMainRelease.js';
 export {default as withAllReleases} from './withAllReleases.js';
-export {default as withAlwaysReferenceByDirectory} from './withAlwaysReferenceByDirectory.js';
 export {default as withContainingTrackSection} from './withContainingTrackSection.js';
 export {default as withCoverArtistContribs} from './withCoverArtistContribs.js';
 export {default as withDate} from './withDate.js';
 export {default as withDirectorySuffix} from './withDirectorySuffix.js';
 export {default as withHasUniqueCoverArt} from './withHasUniqueCoverArt.js';
 export {default as withMainRelease} from './withMainRelease.js';
+export {default as withMainReleaseTrack} from './withMainReleaseTrack.js';
 export {default as withOtherReleases} from './withOtherReleases.js';
 export {default as withPropertyFromAlbum} from './withPropertyFromAlbum.js';
 export {default as withPropertyFromMainRelease} from './withPropertyFromMainRelease.js';
diff --git a/src/data/composite/things/track/trackAdditionalNameList.js b/src/data/composite/things/track/trackAdditionalNameList.js
deleted file mode 100644
index 65a2263d..00000000
--- a/src/data/composite/things/track/trackAdditionalNameList.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// Compiles additional names from various sources.
-
-import {input, templateCompositeFrom} from '#composite';
-import {isAdditionalNameList} from '#validators';
-
-import withInferredAdditionalNames from './withInferredAdditionalNames.js';
-import withSharedAdditionalNames from './withSharedAdditionalNames.js';
-
-export default templateCompositeFrom({
-  annotation: `trackAdditionalNameList`,
-
-  compose: false,
-
-  update: {validate: isAdditionalNameList},
-
-  steps: () => [
-    withInferredAdditionalNames(),
-    withSharedAdditionalNames(),
-
-    {
-      dependencies: [
-        '#inferredAdditionalNames',
-        '#sharedAdditionalNames',
-        input.updateValue(),
-      ],
-
-      compute: ({
-        ['#inferredAdditionalNames']: inferredAdditionalNames,
-        ['#sharedAdditionalNames']: sharedAdditionalNames,
-        [input.updateValue()]: providedAdditionalNames,
-      }) => [
-        ...providedAdditionalNames ?? [],
-        ...sharedAdditionalNames,
-        ...inferredAdditionalNames,
-      ],
-    },
-  ],
-});
diff --git a/src/data/composite/things/track/withAllReleases.js b/src/data/composite/things/track/withAllReleases.js
index b93bf753..bd54384f 100644
--- a/src/data/composite/things/track/withAllReleases.js
+++ b/src/data/composite/things/track/withAllReleases.js
@@ -8,10 +8,9 @@
 import {input, templateCompositeFrom} from '#composite';
 import {sortByDate} from '#sort';
 
-import {exitWithoutDependency} from '#composite/control-flow';
 import {withPropertyFromObject} from '#composite/data';
 
-import withMainRelease from './withMainRelease.js';
+import withMainReleaseTrack from './withMainReleaseTrack.js';
 
 export default templateCompositeFrom({
   annotation: `withAllReleases`,
@@ -19,7 +18,7 @@ export default templateCompositeFrom({
   outputs: ['#allReleases'],
 
   steps: () => [
-    withMainRelease({
+    withMainReleaseTrack({
       selfIfMain: input.value(true),
       notFoundValue: input.value([]),
     }),
@@ -29,18 +28,22 @@ export default templateCompositeFrom({
     // `this.secondaryReleases` from within a data composition.
     // Oooooooooooooooooooooooooooooooooooooooooooooooo
     withPropertyFromObject({
-      object: '#mainRelease',
+      object: '#mainReleaseTrack',
       property: input.value('secondaryReleases'),
     }),
 
     {
-      dependencies: ['#mainRelease', '#mainRelease.secondaryReleases'],
+      dependencies: [
+        '#mainReleaseTrack',
+        '#mainReleaseTrack.secondaryReleases',
+      ],
+
       compute: (continuation, {
-        ['#mainRelease']: mainRelease,
-        ['#mainRelease.secondaryReleases']: secondaryReleases,
+        ['#mainReleaseTrack']: mainReleaseTrack,
+        ['#mainReleaseTrack.secondaryReleases']: secondaryReleases,
       }) => continuation({
         ['#allReleases']:
-          sortByDate([mainRelease, ...secondaryReleases]),
+          sortByDate([mainReleaseTrack, ...secondaryReleases]),
       }),
     },
   ],
diff --git a/src/data/composite/things/track/withAlwaysReferenceByDirectory.js b/src/data/composite/things/track/withAlwaysReferenceByDirectory.js
deleted file mode 100644
index 60faeaf4..00000000
--- a/src/data/composite/things/track/withAlwaysReferenceByDirectory.js
+++ /dev/null
@@ -1,97 +0,0 @@
-// Controls how find.track works - it'll never be matched by a reference
-// just to the track's name, which means you don't have to always reference
-// some *other* (much more commonly referenced) track by directory instead
-// of more naturally by name.
-
-import {input, templateCompositeFrom} from '#composite';
-import find from '#find';
-import {isBoolean} from '#validators';
-
-import {withPropertyFromObject} from '#composite/data';
-import {withResolvedReference} from '#composite/wiki-data';
-import {soupyFind} from '#composite/wiki-properties';
-
-import {
-  exitWithoutDependency,
-  exposeDependencyOrContinue,
-  exposeUpdateValueOrContinue,
-} from '#composite/control-flow';
-
-import withPropertyFromAlbum from './withPropertyFromAlbum.js';
-
-export default templateCompositeFrom({
-  annotation: `withAlwaysReferenceByDirectory`,
-
-  outputs: ['#alwaysReferenceByDirectory'],
-
-  steps: () => [
-    exposeUpdateValueOrContinue({
-      validate: input.value(isBoolean),
-    }),
-
-    withPropertyFromAlbum({
-      property: input.value('alwaysReferenceTracksByDirectory'),
-    }),
-
-    // Falsy mode means this exposes true if the album's property is true,
-    // but continues if the property is false (which is also the default).
-    exposeDependencyOrContinue({
-      dependency: '#album.alwaysReferenceTracksByDirectory',
-      mode: input.value('falsy'),
-    }),
-
-    // Remaining code is for defaulting to true if this track is a rerelease of
-    // another with the same name, so everything further depends on access to
-    // trackData as well as mainReleaseTrack.
-
-    exitWithoutDependency({
-      dependency: 'trackData',
-      mode: input.value('empty'),
-      value: input.value(false),
-    }),
-
-    exitWithoutDependency({
-      dependency: 'mainReleaseTrack',
-      value: input.value(false),
-    }),
-
-    // It's necessary to use the custom trackMainReleasesOnly find function
-    // here, so as to avoid recursion issues - the find.track() function depends
-    // on accessing each track's alwaysReferenceByDirectory, which means it'll
-    // hit *this track* - and thus this step - and end up recursing infinitely.
-    // By definition, find.trackMainReleasesOnly excludes tracks which have
-    // an mainReleaseTrack update value set, which means even though it does
-    // still access each of tracks' `alwaysReferenceByDirectory` property, it
-    // won't access that of *this* track - it will never proceed past the
-    // `exitWithoutDependency` step directly above, so there's no opportunity
-    // for recursion.
-    withResolvedReference({
-      ref: 'mainReleaseTrack',
-      data: 'trackData',
-      find: input.value(find.trackMainReleasesOnly),
-    }).outputs({
-      '#resolvedReference': '#mainRelease',
-    }),
-
-    exitWithoutDependency({
-      dependency: '#mainRelease',
-      value: input.value(false),
-    }),
-
-    withPropertyFromObject({
-      object: '#mainRelease',
-      property: input.value('name'),
-    }),
-
-    {
-      dependencies: ['name', '#mainRelease.name'],
-      compute: (continuation, {
-        name,
-        ['#mainRelease.name']: mainReleaseName,
-      }) => continuation({
-        ['#alwaysReferenceByDirectory']:
-          name === mainReleaseName,
-      }),
-    },
-  ],
-});
diff --git a/src/data/composite/things/track/withDate.js b/src/data/composite/things/track/withDate.js
index b5a770e9..1851c0d2 100644
--- a/src/data/composite/things/track/withDate.js
+++ b/src/data/composite/things/track/withDate.js
@@ -12,6 +12,14 @@ export default templateCompositeFrom({
 
   steps: () => [
     {
+      dependencies: ['disableDate'],
+      compute: (continuation, {disableDate}) =>
+        (disableDate
+          ? continuation.raiseOutput({'#date': null})
+          : continuation()),
+    },
+
+    {
       dependencies: ['dateFirstReleased'],
       compute: (continuation, {dateFirstReleased}) =>
         (dateFirstReleased
diff --git a/src/data/composite/things/track/withDirectorySuffix.js b/src/data/composite/things/track/withDirectorySuffix.js
index c063e158..c3651491 100644
--- a/src/data/composite/things/track/withDirectorySuffix.js
+++ b/src/data/composite/things/track/withDirectorySuffix.js
@@ -1,8 +1,9 @@
 import {input, templateCompositeFrom} from '#composite';
 
 import {raiseOutputWithoutDependency} from '#composite/control-flow';
+import {withPropertyFromObject} from '#composite/data';
 
-import withPropertyFromAlbum from './withPropertyFromAlbum.js';
+import withContainingTrackSection from './withContainingTrackSection.js';
 import withSuffixDirectoryFromAlbum from './withSuffixDirectoryFromAlbum.js';
 
 export default templateCompositeFrom({
@@ -16,21 +17,16 @@ export default templateCompositeFrom({
     raiseOutputWithoutDependency({
       dependency: '#suffixDirectoryFromAlbum',
       mode: input.value('falsy'),
-      output: input.value({['#directorySuffix']: null}),
+      output: input.value({'#directorySuffix': null}),
     }),
 
-    withPropertyFromAlbum({
+    withContainingTrackSection(),
+
+    withPropertyFromObject({
+      object: '#trackSection',
       property: input.value('directorySuffix'),
+    }).outputs({
+      '#trackSection.directorySuffix': '#directorySuffix',
     }),
-
-    {
-      dependencies: ['#album.directorySuffix'],
-      compute: (continuation, {
-        ['#album.directorySuffix']: directorySuffix,
-      }) => continuation({
-        ['#directorySuffix']:
-          directorySuffix,
-      }),
-    },
   ],
 });
diff --git a/src/data/composite/things/track/withHasUniqueCoverArt.js b/src/data/composite/things/track/withHasUniqueCoverArt.js
index af68073e..85d3b92a 100644
--- a/src/data/composite/things/track/withHasUniqueCoverArt.js
+++ b/src/data/composite/things/track/withHasUniqueCoverArt.js
@@ -15,7 +15,8 @@ import {input, templateCompositeFrom} from '#composite';
 
 import {raiseOutputWithoutDependency, withResultOfAvailabilityCheck}
   from '#composite/control-flow';
-import {withFlattenedList, withPropertyFromList} from '#composite/data';
+import {fillMissingListItems, withFlattenedList, withPropertyFromList}
+  from '#composite/data';
 
 import withPropertyFromAlbum from './withPropertyFromAlbum.js';
 
@@ -86,6 +87,13 @@ export default templateCompositeFrom({
       internal: input.value(true),
     }),
 
+    // Since we're getting the update value for each artwork's artistContribs,
+    // it may not be set at all, and in that case won't be exposing as [].
+    fillMissingListItems({
+      list: '#trackArtworks.artistContribs',
+      fill: input.value([]),
+    }),
+
     withFlattenedList({
       list: '#trackArtworks.artistContribs',
     }),
@@ -93,17 +101,8 @@ export default templateCompositeFrom({
     withResultOfAvailabilityCheck({
       from: '#flattenedList',
       mode: input.value('empty'),
+    }).outputs({
+      '#availability': '#hasUniqueCoverArt',
     }),
-
-    {
-      dependencies: ['#availability'],
-      compute: (continuation, {
-        ['#availability']: availability,
-      }) =>
-        continuation({
-          ['#hasUniqueCoverArt']:
-            availability,
-        }),
-    },
   ],
 });
diff --git a/src/data/composite/things/track/withMainRelease.js b/src/data/composite/things/track/withMainRelease.js
index 3a91edae..67a312ae 100644
--- a/src/data/composite/things/track/withMainRelease.js
+++ b/src/data/composite/things/track/withMainRelease.js
@@ -1,13 +1,15 @@
-// Just includes the main release of this track as a dependency.
-// If this track isn't a secondary release, then it'll provide null, unless
-// the {selfIfMain} option is set, in which case it'll provide this track
-// itself. This will early exit (with notFoundValue) if the main release
-// is specified by reference and that reference doesn't resolve to anything.
+// Resolves this track's `mainRelease` reference, using weird-ass atypical
+// machinery that operates on soupyFind and does not operate on findMixed,
+// let alone a prim and proper standalone find spec.
+//
+// Raises null only if there is no `mainRelease` reference provided at all.
+// This will early exit (with notFoundValue) if the reference doesn't resolve.
+//
 
 import {input, templateCompositeFrom} from '#composite';
 
-import {exitWithoutDependency, withResultOfAvailabilityCheck}
-  from '#composite/control-flow';
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+import {withPropertyFromObject} from '#composite/data';
 import {withResolvedReference} from '#composite/wiki-data';
 import {soupyFind} from '#composite/wiki-properties';
 
@@ -15,56 +17,121 @@ export default templateCompositeFrom({
   annotation: `withMainRelease`,
 
   inputs: {
-    selfIfMain: input({type: 'boolean', defaultValue: false}),
+    from: input({
+      defaultDependency: 'mainRelease',
+      acceptsNull: true,
+    }),
+
     notFoundValue: input({defaultValue: null}),
   },
 
   outputs: ['#mainRelease'],
 
   steps: () => [
-    withResultOfAvailabilityCheck({
-      from: 'mainReleaseTrack',
+    raiseOutputWithoutDependency({
+      dependency: input('from'),
+      output: input.value({'#mainRelease': null}),
     }),
 
     {
+      dependencies: [input('from'), 'name'],
+      compute: (continuation, {
+        [input('from')]: ref,
+        ['name']: ownName,
+      }) =>
+        (ref === 'same name single'
+          ? continuation({
+              ['#albumOrTrackReference']: null,
+              ['#sameNameSingleReference']: ownName,
+            })
+          : continuation({
+              ['#albumOrTrackReference']: ref,
+              ['#sameNameSingleReference']: null,
+            })),
+    },
+
+    withResolvedReference({
+      ref: '#albumOrTrackReference',
+      find: soupyFind.input('trackMainReleasesOnly'),
+    }).outputs({
+      '#resolvedReference': '#matchingTrack',
+    }),
+
+    withResolvedReference({
+      ref: '#albumOrTrackReference',
+      find: soupyFind.input('album'),
+    }).outputs({
+      '#resolvedReference': '#matchingAlbum',
+    }),
+
+    withResolvedReference({
+      ref: '#sameNameSingleReference',
+      find: soupyFind.input('albumSinglesOnly'),
+      findOptions: input.value({
+        fuzz: {
+          capitalization: true,
+          kebab: true,
+        },
+      }),
+    }).outputs({
+      '#resolvedReference': '#sameNameSingle',
+    }),
+
+    {
+      dependencies: ['#sameNameSingle'],
+      compute: (continuation, {
+        ['#sameNameSingle']: sameNameSingle,
+      }) =>
+        (sameNameSingle
+          ? continuation.raiseOutput({
+              ['#mainRelease']:
+                sameNameSingle,
+            })
+          : continuation()),
+    },
+
+    {
       dependencies: [
-        input.myself(),
-        input('selfIfMain'),
-        '#availability',
+        '#matchingTrack',
+        '#matchingAlbum',
+        input('notFoundValue'),
       ],
 
       compute: (continuation, {
-        [input.myself()]: track,
-        [input('selfIfMain')]: selfIfMain,
-        '#availability': availability,
+        ['#matchingTrack']: matchingTrack,
+        ['#matchingAlbum']: matchingAlbum,
+        [input('notFoundValue')]: notFoundValue,
       }) =>
-        (availability
+        (matchingTrack && matchingAlbum
           ? continuation()
-          : continuation.raiseOutput({
+       : matchingTrack ?? matchingAlbum
+          ? continuation.raiseOutput({
               ['#mainRelease']:
-                (selfIfMain ? track : null),
-            })),
+                matchingTrack ?? matchingAlbum,
+            })
+          : continuation.exit(notFoundValue)),
     },
 
-    withResolvedReference({
-      ref: 'mainReleaseTrack',
-      find: soupyFind.input('track'),
-    }),
-
-    exitWithoutDependency({
-      dependency: '#resolvedReference',
-      value: input('notFoundValue'),
+    withPropertyFromObject({
+      object: '#matchingAlbum',
+      property: input.value('tracks'),
     }),
 
     {
-      dependencies: ['#resolvedReference'],
+      dependencies: [
+        '#matchingAlbum.tracks',
+        '#matchingTrack',
+        input('notFoundValue'),
+      ],
 
       compute: (continuation, {
-        ['#resolvedReference']: resolvedReference,
+        ['#matchingAlbum.tracks']: matchingAlbumTracks,
+        ['#matchingTrack']: matchingTrack,
+        [input('notFoundValue')]: notFoundValue,
       }) =>
-        continuation({
-          ['#mainRelease']: resolvedReference,
-        }),
+        (matchingAlbumTracks.includes(matchingTrack)
+          ? continuation.raiseOutput({'#mainRelease': matchingTrack})
+          : continuation.exit(notFoundValue)),
     },
   ],
 });
diff --git a/src/data/composite/things/track/withMainReleaseTrack.js b/src/data/composite/things/track/withMainReleaseTrack.js
new file mode 100644
index 00000000..6371e895
--- /dev/null
+++ b/src/data/composite/things/track/withMainReleaseTrack.js
@@ -0,0 +1,248 @@
+// Just provides the main release of this track as a dependency.
+// If this track isn't a secondary release, then it'll provide null, unless
+// the {selfIfMain} option is set, in which case it'll provide this track
+// itself. This will early exit (with notFoundValue) if the main release
+// is specified by reference and that reference doesn't resolve to anything.
+
+import {input, templateCompositeFrom} from '#composite';
+import {onlyItem} from '#sugar';
+import {getKebabCase} from '#wiki-data';
+
+import {
+  exitWithoutDependency,
+  withAvailabilityFilter,
+  withResultOfAvailabilityCheck,
+} from '#composite/control-flow';
+
+import {
+  withFilteredList,
+  withMappedList,
+  withPropertyFromList,
+  withPropertyFromObject,
+} from '#composite/data';
+
+import withMainRelease from './withMainRelease.js';
+
+export default templateCompositeFrom({
+  annotation: `withMainReleaseTrack`,
+
+  inputs: {
+    selfIfMain: input({type: 'boolean', defaultValue: false}),
+    notFoundValue: input({defaultValue: null}),
+  },
+
+  outputs: ['#mainReleaseTrack'],
+
+  steps: () => [
+    withResultOfAvailabilityCheck({
+      from: 'mainRelease',
+    }),
+
+    {
+      dependencies: [
+        input.myself(),
+        input('selfIfMain'),
+        '#availability',
+      ],
+
+      compute: (continuation, {
+        [input.myself()]: track,
+        [input('selfIfMain')]: selfIfMain,
+        '#availability': availability,
+      }) =>
+        (availability
+          ? continuation()
+          : continuation.raiseOutput({
+              ['#mainReleaseTrack']:
+                (selfIfMain ? track : null),
+            })),
+    },
+
+    withMainRelease(),
+
+    exitWithoutDependency({
+      dependency: '#mainRelease',
+      value: input('notFoundValue'),
+    }),
+
+    withPropertyFromObject({
+      object: '#mainRelease',
+      property: input.value('isTrack'),
+    }),
+
+    {
+      dependencies: ['#mainRelease', '#mainRelease.isTrack'],
+
+      compute: (continuation, {
+        ['#mainRelease']: mainRelease,
+        ['#mainRelease.isTrack']: mainReleaseIsTrack,
+      }) =>
+        (mainReleaseIsTrack
+          ? continuation.raiseOutput({
+              ['#mainReleaseTrack']: mainRelease,
+            })
+          : continuation()),
+    },
+
+    {
+      dependencies: ['name', 'directory'],
+      compute: (continuation, {
+        ['name']: ownName,
+        ['directory']: ownDirectory,
+      }) => {
+        const ownNameKebabed = getKebabCase(ownName);
+
+        return continuation({
+          ['#mapItsNameLikeName']:
+            name => getKebabCase(name) === ownNameKebabed,
+
+          ['#mapItsDirectoryLikeDirectory']:
+            (ownDirectory
+              ? directory => directory === ownDirectory
+              : () => false),
+
+          ['#mapItsNameLikeDirectory']:
+            (ownDirectory
+              ? name => getKebabCase(name) === ownDirectory
+              : () => false),
+
+          ['#mapItsDirectoryLikeName']:
+            directory => directory === ownNameKebabed,
+        });
+      },
+    },
+
+    withPropertyFromObject({
+      object: '#mainRelease',
+      property: input.value('tracks'),
+    }),
+
+    withPropertyFromList({
+      list: '#mainRelease.tracks',
+      property: input.value('mainRelease'),
+      internal: input.value(true),
+    }),
+
+    withAvailabilityFilter({
+      from: '#mainRelease.tracks.mainRelease',
+    }),
+
+    withMappedList({
+      list: '#availabilityFilter',
+      map: input.value(item => !item),
+    }).outputs({
+      '#mappedList': '#availabilityFilter',
+    }),
+
+    withFilteredList({
+      list: '#mainRelease.tracks',
+      filter: '#availabilityFilter',
+    }).outputs({
+      '#filteredList': '#mainRelease.tracks',
+    }),
+
+    withPropertyFromList({
+      list: '#mainRelease.tracks',
+      property: input.value('name'),
+    }),
+
+    withPropertyFromList({
+      list: '#mainRelease.tracks',
+      property: input.value('directory'),
+      internal: input.value(true),
+    }),
+
+    withMappedList({
+      list: '#mainRelease.tracks.name',
+      map: '#mapItsNameLikeName',
+    }).outputs({
+      '#mappedList': '#filterItsNameLikeName',
+    }),
+
+    withMappedList({
+      list: '#mainRelease.tracks.directory',
+      map: '#mapItsDirectoryLikeDirectory',
+    }).outputs({
+      '#mappedList': '#filterItsDirectoryLikeDirectory',
+    }),
+
+    withMappedList({
+      list: '#mainRelease.tracks.name',
+      map: '#mapItsNameLikeDirectory',
+    }).outputs({
+      '#mappedList': '#filterItsNameLikeDirectory',
+    }),
+
+    withMappedList({
+      list: '#mainRelease.tracks.directory',
+      map: '#mapItsDirectoryLikeName',
+    }).outputs({
+      '#mappedList': '#filterItsDirectoryLikeName',
+    }),
+
+    withFilteredList({
+      list: '#mainRelease.tracks',
+      filter: '#filterItsNameLikeName',
+    }).outputs({
+      '#filteredList': '#matchingItsNameLikeName',
+    }),
+
+    withFilteredList({
+      list: '#mainRelease.tracks',
+      filter: '#filterItsDirectoryLikeDirectory',
+    }).outputs({
+      '#filteredList': '#matchingItsDirectoryLikeDirectory',
+    }),
+
+    withFilteredList({
+      list: '#mainRelease.tracks',
+      filter: '#filterItsNameLikeDirectory',
+    }).outputs({
+      '#filteredList': '#matchingItsNameLikeDirectory',
+    }),
+
+    withFilteredList({
+      list: '#mainRelease.tracks',
+      filter: '#filterItsDirectoryLikeName',
+    }).outputs({
+      '#filteredList': '#matchingItsDirectoryLikeName',
+    }),
+
+    {
+      dependencies: [
+        '#matchingItsNameLikeName',
+        '#matchingItsDirectoryLikeDirectory',
+        '#matchingItsNameLikeDirectory',
+        '#matchingItsDirectoryLikeName',
+      ],
+
+      compute: (continuation, {
+        ['#matchingItsNameLikeName']:           NLN,
+        ['#matchingItsDirectoryLikeDirectory']: DLD,
+        ['#matchingItsNameLikeDirectory']:      NLD,
+        ['#matchingItsDirectoryLikeName']:      DLN,
+      }) => continuation({
+        ['#mainReleaseTrack']:
+          onlyItem(DLD) ??
+          onlyItem(NLN) ??
+          onlyItem(DLN) ??
+          onlyItem(NLD) ??
+          null,
+      }),
+    },
+
+    {
+      dependencies: ['#mainReleaseTrack', input.myself()],
+
+      compute: (continuation, {
+        ['#mainReleaseTrack']: mainReleaseTrack,
+        [input.myself()]: thisTrack,
+      }) => continuation({
+        ['#mainReleaseTrack']:
+          (mainReleaseTrack === thisTrack
+            ? null
+            : mainReleaseTrack),
+      }),
+    },
+  ],
+});
diff --git a/src/data/composite/things/track/withOtherReleases.js b/src/data/composite/things/track/withOtherReleases.js
index 0639742f..bb3e8983 100644
--- a/src/data/composite/things/track/withOtherReleases.js
+++ b/src/data/composite/things/track/withOtherReleases.js
@@ -3,9 +3,6 @@
 
 import {input, templateCompositeFrom} from '#composite';
 
-import {exitWithoutDependency} from '#composite/control-flow';
-import {withPropertyFromObject} from '#composite/data';
-
 import withAllReleases from './withAllReleases.js';
 
 export default templateCompositeFrom({
diff --git a/src/data/composite/things/track/withPropertyFromMainRelease.js b/src/data/composite/things/track/withPropertyFromMainRelease.js
index 393a4c63..c6f65653 100644
--- a/src/data/composite/things/track/withPropertyFromMainRelease.js
+++ b/src/data/composite/things/track/withPropertyFromMainRelease.js
@@ -10,10 +10,10 @@ import {input, templateCompositeFrom} from '#composite';
 import {withResultOfAvailabilityCheck} from '#composite/control-flow';
 import {withPropertyFromObject} from '#composite/data';
 
-import withMainRelease from './withMainRelease.js';
+import withMainReleaseTrack from './withMainReleaseTrack.js';
 
 export default templateCompositeFrom({
-  annotation: `inheritFromMainRelease`,
+  annotation: `withPropertyFromMainRelease`,
 
   inputs: {
     property: input({type: 'string'}),
@@ -32,12 +32,12 @@ export default templateCompositeFrom({
         : ['#mainReleaseValue'])),
 
   steps: () => [
-    withMainRelease({
+    withMainReleaseTrack({
       notFoundValue: input('notFoundValue'),
     }),
 
     withResultOfAvailabilityCheck({
-      from: '#mainRelease',
+      from: '#mainReleaseTrack',
     }),
 
     {
@@ -61,7 +61,7 @@ export default templateCompositeFrom({
     },
 
     withPropertyFromObject({
-      object: '#mainRelease',
+      object: '#mainReleaseTrack',
       property: input('property'),
     }),
 
diff --git a/src/data/composite/things/track/withSuffixDirectoryFromAlbum.js b/src/data/composite/things/track/withSuffixDirectoryFromAlbum.js
index 7159a3f4..30c777b6 100644
--- a/src/data/composite/things/track/withSuffixDirectoryFromAlbum.js
+++ b/src/data/composite/things/track/withSuffixDirectoryFromAlbum.js
@@ -1,8 +1,9 @@
 import {input, templateCompositeFrom} from '#composite';
 
 import {withResultOfAvailabilityCheck} from '#composite/control-flow';
+import {withPropertyFromObject} from '#composite/data';
 
-import withPropertyFromAlbum from './withPropertyFromAlbum.js';
+import withContainingTrackSection from './withContainingTrackSection.js';
 
 export default templateCompositeFrom({
   annotation: `withSuffixDirectoryFromAlbum`,
@@ -36,18 +37,13 @@ export default templateCompositeFrom({
           : continuation()),
     },
 
-    withPropertyFromAlbum({
+    withContainingTrackSection(),
+
+    withPropertyFromObject({
+      object: '#trackSection',
       property: input.value('suffixTrackDirectories'),
+    }).outputs({
+      '#trackSection.suffixTrackDirectories': '#suffixDirectoryFromAlbum',
     }),
-
-    {
-      dependencies: ['#album.suffixTrackDirectories'],
-      compute: (continuation, {
-        ['#album.suffixTrackDirectories']: suffixTrackDirectories,
-      }) => continuation({
-        ['#suffixDirectoryFromAlbum']:
-          suffixTrackDirectories,
-      }),
-    },
   ],
 });