« get me outta code hell

http-music - Command-line music player + utils (not a server!)
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/http-music.js1
-rw-r--r--src/loop-play.js15
-rw-r--r--src/playlist-utils.js87
-rw-r--r--todo.txt4
4 files changed, 71 insertions, 36 deletions
diff --git a/src/http-music.js b/src/http-music.js
index 6457ef7..04d1f57 100755
--- a/src/http-music.js
+++ b/src/http-music.js
@@ -368,6 +368,7 @@ Promise.resolve()
 
           // TODO: It would be nice to have this as a method of
           // PlayController.
+          // Double TODO: This doesn't actually work!!
           downloadController.cancel()
           play.startNextDownload()
         }
diff --git a/src/loop-play.js b/src/loop-play.js
index f36f1fb..7457b41 100644
--- a/src/loop-play.js
+++ b/src/loop-play.js
@@ -4,6 +4,7 @@ const { spawn } = require('child_process')
 const FIFO = require('fifo-js')
 const EventEmitter = require('events')
 const { getDownloaderFor } = require('./downloaders')
+const { getItemPathString } = require('./playlist-utils')
 
 class DownloadController extends EventEmitter {
   waitForDownload() {
@@ -228,16 +229,22 @@ class PlayController {
   }
 
   logTrackInfo() {
+    const getMessage = t => {
+      let path = getItemPathString(t)
+
+      return (
+        `\x1b[1m${t.name} \x1b[0m@ ${path} \x1b[2m${t.downloaderArg}\x1b[0m`
+      )
+    }
+
     if (this.currentTrack) {
-      const t = this.currentTrack
-      console.log(`Playing: \x1b[1m${t.name} \x1b[2m${t.downloaderArg}\x1b[0m`)
+      console.log(`Playing: ${getMessage(this.currentTrack)}`)
     } else {
       console.log("No song currently playing.")
     }
 
     if (this.nextTrack) {
-      const t = this.nextTrack
-      console.log(`Up next: \x1b[1m${t.name} \x1b[2m${t.downloaderArg}\x1b[0m`)
+      console.log(`Up next: ${getMessage(this.nextTrack)}`)
     } else {
       console.log("No song up next.")
     }
diff --git a/src/playlist-utils.js b/src/playlist-utils.js
index c1a8193..1a0d7b8 100644
--- a/src/playlist-utils.js
+++ b/src/playlist-utils.js
@@ -1,5 +1,7 @@
 'use strict'
 
+const parentSymbol = Symbol('parent')
+
 // TODO: Use this when loading playlists. Also grab things from http-music.js.
 function updatePlaylistFormat(playlist) {
   const defaultPlaylist = {
@@ -31,19 +33,40 @@ function updatePlaylistFormat(playlist) {
 
   const fullPlaylistObj = Object.assign(defaultPlaylist, playlistObj)
 
-  const handleGroupContents = groupContents => {
-    return groupContents.map(item => {
-      if (Array.isArray(item[1])) {
-        return {name: item[0], items: handleGroupContents(item[1])}
-      } else {
-        return updateTrackFormat(item)
-      }
-    })
+  return updateGroupFormat(fullPlaylistObj)
+}
+
+function updateGroupFormat(group) {
+  const defaultGroup = {
+    name: '',
+    items: []
+  }
+
+  let groupObj = {}
+
+  if (Array.isArray(group[1])) {
+    groupObj = {name: group[0], items: group[1]}
+  } else {
+    groupObj = group
   }
 
-  fullPlaylistObj.items = handleGroupContents(fullPlaylistObj.items)
+  groupObj = Object.assign(defaultGroup, groupObj)
 
-  return fullPlaylistObj
+  groupObj.items = groupObj.items.map(item => {
+    // Theoretically this wouldn't work on downloader-args where the value
+    // isn't a string..
+    if (typeof group[1] === 'string' || item.downloaderArg) {
+      item = updateTrackFormat(item)
+    } else {
+      item = updateGroupFormat(item)
+    }
+
+    item[parentSymbol] = groupObj
+
+    return item
+  })
+
+  return groupObj
 }
 
 function updateTrackFormat(track) {
@@ -67,27 +90,6 @@ function updateTrackFormat(track) {
   return Object.assign(defaultTrack, trackObj)
 }
 
-function updateGroupFormat(group) {
-  const defaultGroup = {
-    name: '',
-    items: []
-  }
-
-  let groupObj
-
-  if (Array.isArray(group)) {
-    if (group.length === 2) {
-      groupObj = {name: group[0], items: group[1]}
-    } else {
-      throw new Error("Unexpected non-length 2 array-format group")
-    }
-  } else {
-    groupObj = group
-  }
-
-  return Object.assign(defaultGroup, groupObj)
-}
-
 function mapGrouplikeItems(grouplike, handleTrack) {
   if (typeof handleTrack === 'undefined') {
     throw new Error("Missing track handler function")
@@ -114,7 +116,9 @@ function flattenGrouplike(grouplike) {
   return {
     items: grouplike.items.map(item => {
       if (isGroup(item)) {
-        return flattenGrouplike(item).items
+        const flat = flattenGrouplike(item).items
+
+        return flat
       } else {
         return [item]
       }
@@ -232,6 +236,23 @@ function getPlaylistTreeString(playlist, showTracks = false) {
   return recursive(playlist)
 }
 
+function getItemPathString(item) {
+  // Gets the playlist path of an item by following its parent chain.
+  // Returns a string in format Foo/Bar/Baz, where Foo and Bar are group
+  // names, and Baz is the name of the item. Unnamed parents (except for the
+  // top one) are considered to have the name '(Unnamed)'.
+
+  if (item[parentSymbol]) {
+    if (item[parentSymbol].name || item[parentSymbol][parentSymbol]) {
+      return getItemPathString(item[parentSymbol]) + '/' + item.name
+    } else {
+      return item.name
+    }
+  } else {
+    return item.name || '(Unnamed)'
+  }
+}
+
 function parsePathString(pathString) {
   const pathParts = pathString.split('/')
   return pathParts
@@ -250,11 +271,13 @@ function isTrack(obj) {
 }
 
 module.exports = {
+  parentSymbol,
   updatePlaylistFormat, updateTrackFormat,
   flattenGrouplike,
   filterPlaylistByPathString, filterGrouplikeByPath,
   removeGroupByPathString, removeGroupByPath,
   getPlaylistTreeString,
+  getItemPathString,
   parsePathString,
   isGroup, isTrack
 }
diff --git a/todo.txt b/todo.txt
index 6d4da81..a65ad2f 100644
--- a/todo.txt
+++ b/todo.txt
@@ -114,6 +114,7 @@ TODO: A way to kill the up-next song.
 
 TODO: A way to see information about the currently playing song, as well as
       the up-next song.
+      (Done!)
 
 TODO: A way to see the previously played songs, and to skip back (or forwards).
 
@@ -204,6 +205,7 @@ TODO: Tracks should be able to contain more data than the title and downloader
       tracks by sticking Symbols onto the track objects. It'd be particularly
       useful to store the original group path for tracks in flattenGroup, for
       example.
+      (Done!)
 
 TODO: Piping the output of a crawl command into the http-music command would
       be nifty!
@@ -214,3 +216,5 @@ TODO: Having all the http-music commands be stuck into one main command might
 TODO: Figure out how man pages work, and update the syntax in those files.
       Particularly I'd like to make the number of blank lines between headings
       more consistent, and figure out when to use '\-' or '-'.
+
+TODO: Fix skip up-next..!