« get me outta code hell

http-music - Command-line music player + utils (not a server!)
about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/duration-graph.js105
1 files changed, 85 insertions, 20 deletions
diff --git a/src/duration-graph.js b/src/duration-graph.js
index 2ffdef7..47183c9 100644
--- a/src/duration-graph.js
+++ b/src/duration-graph.js
@@ -4,11 +4,21 @@ const fs = require('fs')
 const util = require('util')
 const processArgv = require('./process-argv')
 
-const { updatePlaylistFormat, isGroup, isItem, getItemPathString } = require('./playlist-utils')
+const {
+  updatePlaylistFormat,
+  isGroup, isItem,
+  getItemPathString,
+  flattenGrouplike
+} = require('./playlist-utils')
 
 const readFile = util.promisify(fs.readFile)
 
-const cachedDuration = Symbol('Cached duration')
+const metrics = {}
+metrics.duration = Symbol('Duration')
+metrics.length = metrics.duration
+metrics.time = metrics.duration
+metrics.tracks = Symbol('# of tracks')
+metrics.items = metrics.tracks
 
 function getUncachedDurationOfItem(item) {
   if (isGroup(item)) {
@@ -26,11 +36,23 @@ function getUncachedDurationOfItem(item) {
 // This is mostly just to avoid logging out "item missing metadata" warnings
 // multiple times.
 function getDurationOfItem(item) {
-  if (cachedDuration in item === false) {
-    item[cachedDuration] = getUncachedDurationOfItem(item)
+  if (metrics.duration in item === false) {
+    item[metrics.duration] = getUncachedDurationOfItem(item)
   }
 
-  return item[cachedDuration]
+  return item[metrics.duration]
+}
+
+function getTrackCount(item) {
+  if (metrics.tracks in item === false) {
+    if (isGroup(item)) {
+      item[metrics.tracks] = flattenGrouplike(item).items.length
+    } else {
+      item[metrics.tracks] = 1
+    }
+  }
+
+  return item[metrics.tracks]
 }
 
 const getHours = n => Math.floor(n / 3600)
@@ -75,19 +97,48 @@ function padStartList(strings) {
   return strings.map(s => s.padStart(len, ' '))
 }
 
+function measureItem(item, metric) {
+  if (metric === metrics.duration) {
+    return getDurationOfItem(item)
+  } else if (metric === metrics.tracks) {
+    return getTrackCount(item)
+  } else {
+    throw new Error('Invalid metric: ' + metric)
+  }
+}
+
 function makePlaylistGraph(playlist, {
   graphWidth = 60,
-  onlyFirst = 20
+  onlyFirst = 20,
+  metric = metrics.duration
 } = {}) {
   const output = []
 
-  const wholePlaylistLength = getDurationOfItem(playlist)
+  const wholePlaylistLength = measureItem(playlist, metric)
 
-  let topThings = playlist.items.map((item, i) => ({
-    item,
-    duration: getDurationOfItem(item),
-    digitalDuration: digitalFormatDuration(getDurationOfItem(item))
-  }))
+  const briefFormatDuration = duration => {
+    if (metric === metrics.duration) {
+      return digitalFormatDuration(duration)
+    } else {
+      return duration.toString()
+    }
+  }
+
+  const longFormatDuration = duration => {
+    if (metric === metrics.duration) {
+      return wordFormatDuration(duration)
+    } else if (metric === metrics.tracks) {
+      return `${duration} tracks`
+    } else {
+      return duration.toString()
+    }
+  }
+
+  let topThings = playlist.items.map((item, i) => {
+    const duration = measureItem(item, metric)
+    const briefDuration = briefFormatDuration(duration)
+    return {item, duration, briefDuration}
+  })
 
   topThings.sort((a, b) => b.duration - a.duration)
 
@@ -97,11 +148,11 @@ function makePlaylistGraph(playlist, {
 
   const displayLength = topThings.reduce((a, b) => a + b.duration, 0)
 
-  // Left-pad the digital durations so they're all the same length.
+  // Left-pad the brief durations so they're all the same length.
   {
-    const len = topThings.reduce((a, b) => Math.max(a, b.digitalDuration.length), 0)
+    const len = topThings.reduce((a, b) => Math.max(a, b.briefDuration.length), 0)
     for (const obj of topThings) {
-      obj.digitalDuration = obj.digitalDuration.padStart(len, ' ')
+      obj.padDuration = obj.briefDuration.padStart(len, ' ')
     }
   }
 
@@ -122,7 +173,7 @@ function makePlaylistGraph(playlist, {
     topThings[i].visualWidth = w
   }
 
-  output.push('    Whole length: ' + wordFormatDuration(wholePlaylistLength), '')
+  output.push('    Whole length: ' + longFormatDuration(wholePlaylistLength), '')
 
   output.push('    ' + topThings.map(({ bgColor, fgColor, visualWidth }) => {
     return bgColor + fgColor + '-'.repeat(visualWidth)
@@ -130,15 +181,16 @@ function makePlaylistGraph(playlist, {
 
   output.push('    Length by item:')
 
-  output.push(...topThings.map(({ item, digitalDuration, visualWidth, fgColor }) =>
+  output.push(...topThings.map(({ item, padDuration, visualWidth, fgColor }) =>
     `    ${fgColor}${
       // Dim the row if it doesn't show up in the graph.
       visualWidth === 0 ? '\x1b[2m- ' : '  '
-    }${digitalDuration}  ${item.name}\x1b[0m`
+    }${padDuration}  ${item.name}\x1b[0m`
   ))
 
   if (ignoredThings.length) {
-    const dur = wordFormatDuration(ignoredThings.reduce((a, b) => a + b.duration, 0))
+    const totalDuration = ignoredThings.reduce((a, b) => a + b.duration, 0)
+    const dur = longFormatDuration(totalDuration)
     output.push(
       `    \x1b[2m(* Plus ${ignoredThings.length} skipped items, accounting `,
       `       for ${dur}.)\x1b[0m`
@@ -163,8 +215,21 @@ async function main(args) {
 
   let graphWidth = 60
   let onlyFirst = 20
+  let metric = metrics.duration
 
   await processArgv(args.slice(1), {
+    '-metric': util => {
+      const arg = util.nextArg()
+      if (Object.keys(metrics).includes(arg)) {
+        metric = metrics[arg]
+      } else {
+        console.warn('Didn\'t set metric because it isn\'t recognized:', arg)
+      }
+    },
+
+    '-measure': util => util.alias('-metric'),
+    'm': util => util.alias('-metric'),
+
     '-graph-width': util => {
       const arg = util.nextArg()
       const newVal = parseInt(arg)
@@ -201,7 +266,7 @@ async function main(args) {
   const playlist = updatePlaylistFormat(JSON.parse(await readFile(args[0])))
 
   for (const line of makePlaylistGraph(playlist, {
-    graphWidth, onlyFirst
+    graphWidth, onlyFirst, metric
   })) {
     console.log(line)
   }