« 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/track
diff options
context:
space:
mode:
Diffstat (limited to 'src/data/composite/things/track')
-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/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
12 files changed, 460 insertions, 210 deletions
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/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,
-      }),
-    },
   ],
 });