« get me outta code hell

mtui - Music Text User Interface - user-friendly command line music player
about summary refs log tree commit diff
path: root/playlist-utils.js
diff options
context:
space:
mode:
Diffstat (limited to 'playlist-utils.js')
-rw-r--r--playlist-utils.js133
1 files changed, 73 insertions, 60 deletions
diff --git a/playlist-utils.js b/playlist-utils.js
index 5279177..b813181 100644
--- a/playlist-utils.js
+++ b/playlist-utils.js
@@ -166,15 +166,30 @@ function flattenGrouplike(grouplike) {
   // levels in the group tree and returns them as a new group containing those
   // tracks.
 
-  return {
-    items: grouplike.items.map(item => {
-      if (isGroup(item)) {
-        return flattenGrouplike(item).items
-      } else {
-        return [item]
-      }
-    }).reduce((a, b) => a.concat(b), [])
-  }
+  return {items: getFlatTrackList(grouplike)}
+}
+
+function getFlatTrackList(grouplike) {
+  // Underlying function for flattenGrouplike. Can be used if you just want to
+  // get an array and not a grouplike, too.
+
+  return grouplike.items.map(item => {
+    if (isGroup(item)) {
+      return getFlatTrackList(item)
+    } else {
+      return [item]
+    }
+  }).reduce((a, b) => a.concat(b), [])
+}
+
+function getFlatGroupList(grouplike) {
+  // Analogue of getFlatTrackList for groups instead of tracks. Returns a flat
+  // array of all the groups in each level of the provided grouplike.
+
+  return grouplike.items
+    .filter(isGroup)
+    .map(item => [item, ...getFlatGroupList(item)])
+    .reduce((a, b) => a.concat(b), [])
 }
 
 function countTotalTracks(item) {
@@ -782,20 +797,17 @@ function getPathScore(path1, path2) {
   return scores.reduce((a, b) => a < b ? a : b)
 }
 
-function findTrackObject(referenceData, sourcePlaylist, flattenedSourcePlaylist = null) {
-  // Finds the track object in the source playlist which most closely resembles
+function findItemObject(referenceData, possibleChoices) {
+  // Finds the item object in the provided choices which most closely resembles
   // the provided reference data. This is used for maintaining the identity of
-  // track objects when reloading a playlist (see serialized-backend.js). It's
-  // also usable in synchronizing the identity of tracks across linked clients
+  // item objects when reloading a playlist (see serialized-backend.js). It's
+  // also usable in synchronizing the identity of items across linked clients
   // (see socket.js).
-  //
-  // NB: This function is many times more efficient if you pass a preemptively
-  // flattened version of the source playlist in as well!
 
-  // Reference data includes track NAME, track SOURCE (downloaderArg), and
-  // track PATH (names of parent groups). Specifics of how existing track
-  // objects are determined to resemble this data are laid out next to the
-  // relevant implementation code.
+  // Reference data includes item NAME, item SOURCE (downloaderArg), and item
+  // PATH (names of parent groups). Specifics of how existing item  objects are
+  // determined to resemble this data are laid out next to the relevant
+  // implementation code.
   //
   // TODO: Should track number be considered here?
   // TODO: Should track "metadata" (duration, md5?) be considered too?
@@ -803,41 +815,37 @@ function findTrackObject(referenceData, sourcePlaylist, flattenedSourcePlaylist
   //       tracks *is*, and in considering those I lean towards "no" here, but
   //       it's probably worth looking at more in the future. (TM.)
 
-  function getTrackPathScore(track) {
+  function getItemPathScore(item) {
     if (!referenceData.path) {
       return null
     }
 
     const path1 = referenceData.path.slice()
-    const path2 = getItemPath(track).slice(0, -1).map(group => group.name)
+    const path2 = getItemPath(item).slice(0, -1).map(group => group.name)
     return getPathScore(path1, path2)
   }
 
-  if (!flattenedSourcePlaylist) {
-    flattenedSourcePlaylist = flattenGrouplike(sourcePlaylist)
-  }
-
-  // The only tracks which will be considered at all are those which match at
+  // The only items which will be considered at all are those which match at
   // least one of the reference name/source.
-  const baselineResemble = flattenedSourcePlaylist.items.filter(track =>
-    track.name === referenceData.name ||
-    track.downloaderArg === referenceData.downloaderArg)
+  const baselineResemble = possibleChoices.filter(item =>
+    item.name === referenceData.name ||
+    item.downloaderArg && item.downloaderArg === referenceData.downloaderArg)
 
-  // If no track matches the baseline conditions for resemblance at all,
+  // If no item matches the baseline conditions for resemblance at all,
   // return null. It's up to the caller to decide what to do in this case,
-  // e.g. reporting that no track was found, or creating a new track object
+  // e.g. reporting that no item was found, or creating a new item object
   // from the reference data altogether.
   if (!baselineResemble.length) {
     return null
   }
 
-  // Find the "reasons" these tracks resemble the reference data; these will
-  // be used as the factors in calculating which track resembles closest.
-  const reasons = baselineResemble.map(track => ({
-    track,
-    nameMatches: track.name === referenceData.name,
-    sourceMatches: track.downloaderArg === referenceData.downloaderArg,
-    pathScore: getTrackPathScore(track)
+  // Find the "reasons" these items resemble the reference data; these will
+  // be used as the factors in calculating which item resembles closest.
+  const reasons = baselineResemble.map(item => ({
+    item,
+    nameMatches: item.name === referenceData.name,
+    sourceMatches: item.downloaderArg && item.downloaderArg === referenceData.downloaderArg,
+    pathScore: getItemPathScore(item)
   }))
 
   // TODO: The algorithm for determining which track matches closest is
@@ -868,7 +876,7 @@ function findTrackObject(referenceData, sourcePlaylist, flattenedSourcePlaylist
     mostResembles = reasons[0]
   }
 
-  return mostResembles.track
+  return mostResembles.item
 }
 
 module.exports = {
@@ -876,7 +884,10 @@ module.exports = {
   updatePlaylistFormat, updateGroupFormat, updateTrackFormat,
   cloneGrouplike,
   filterTracks,
-  flattenGrouplike, countTotalTracks,
+  flattenGrouplike,
+  getFlatTrackList,
+  getFlatGroupList,
+  countTotalTracks,
   shuffleOrderOfGroups,
   reverseOrderOfGroups,
   partiallyFlattenGrouplike, collapseGrouplike,
@@ -892,7 +903,7 @@ module.exports = {
   getCorrespondingFileForItem,
   getCorrespondingPlayableForFile,
   getPathScore,
-  findTrackObject,
+  findItemObject,
   isGroup, isTrack,
   isOpenable, isPlayable
 }
@@ -902,25 +913,27 @@ if (require.main === module) {
   console.log(getPathScore(['A', 'B', 'C'], ['A', 'B', 'C', 'D']))
   console.log(getPathScore(['A', 'B', 'C', 'E'], ['A', 'B', 'C']))
   console.log(getPathScore(['W', 'X'], ['Y', 'Z']))
-  console.log(findTrackObject(
-    {name: 'T', downloaderArg: 'foo', path: ['A', 'B', 'C']},
-    updateGroupFormat({items: [
-      {id: 1, name: 'T'},
-      {id: 2, name: 'T'},
-      {id: 3, name: 'T'},
-      // {id: 4, name: 'T', downloaderArg: 'foo'},
-      {id: 5, name: 'T'},
-      {id: 6, name: 'Y', downloaderArg: 'foo'},
-      /*
-      {name: 'A', items: [
-        {name: 'B', items: [
-          {name: 'C', items: [
+  console.log(findItemObject(
+    // {name: 'T', downloaderArg: 'foo', path: ['A', 'B', 'C']},
+    {name: 'B'},
+    // getFlatTrackList(
+    getFlatGroupList(
+      updateGroupFormat({items: [
+        {id: 1, name: 'T'},
+        {id: 2, name: 'T'},
+        {id: 3, name: 'T'},
+        // {id: 4, name: 'T', downloaderArg: 'foo'},
+        {id: 5, name: 'T'},
+        {id: 6, name: 'Y', downloaderArg: 'foo'},
+        {name: 'A', items: [
+          {name: 'B', items: [
+            {name: 'C', items: [
+              {name: 'T'}
+            ]},
             {name: 'T'}
-          ]},
-          {name: 'T'}
+          ]}
         ]}
-      ]}
-      */
-    ]})
+      ]})
+    )
   ))
 }