« 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.js77
-rw-r--r--todo.txt2
2 files changed, 63 insertions, 16 deletions
diff --git a/src/http-music.js b/src/http-music.js
index 68bfa77..6abda75 100755
--- a/src/http-music.js
+++ b/src/http-music.js
@@ -17,17 +17,12 @@ const {
 
 const readFile = promisify(fs.readFile)
 
-function setupDefaultPlaylist(file) {
-  return readFile(file, 'utf-8').then(
-    text => JSON.parse(text),
-    err => null
-  )
-}
+Promise.resolve()
+  .then(async () => {
+    let sourcePlaylist = null
+    let activePlaylist = null
 
-setupDefaultPlaylist('./playlist.json')
-  .then(async playlist => {
-    let sourcePlaylist = playlist
-    let activePlaylist = playlist
+    await openPlaylist('./playlist.json')
 
     let pickerType = 'shuffle'
     let downloaderType = 'http'
@@ -39,6 +34,57 @@ setupDefaultPlaylist('./playlist.json')
     let shouldPlay = true
     let willPlay = null
 
+    async function openPlaylist(file, silent = false) {
+      let playlistText
+
+      try {
+        playlistText = await readFile(file, 'utf-8')
+      } catch(err) {
+        if (silent) {
+          console.error("Failed to read playlist file: " + file)
+        }
+
+        return false
+      }
+
+      const openedPlaylist = JSON.parse(playlistText)
+
+      // Playlists can be in two formats...
+      if (Array.isArray(openedPlaylist)) {
+        // ..the first, a simple array of tracks and groups;
+
+        sourcePlaylist = openedPlaylist
+        activePlaylist = openedPlaylist
+      } else if (typeof openedPlaylist === 'object') {
+        // ..or an object including metadata and configuration as well as the
+        // array described in the first.
+
+        if (!('tracks' in openedPlaylist)) {
+          throw new Error(
+            "Trackless object-type playlist (requires 'tracks' property)"
+          )
+        }
+
+        sourcePlaylist = openedPlaylist.tracks
+        activePlaylist = openedPlaylist.tracks
+
+        // What's handy about the object-type playlist is that you can pass
+        // options that will be run every time the playlist is opened:
+        if ('options' in openedPlaylist) {
+          if (Array.isArray(openedPlaylist.options)) {
+            processArgv(openedPlaylist.options, optionFunctions)
+          } else {
+            throw new Error(
+              "Invalid 'options' property (expected array): " + file
+            )
+          }
+        }
+      } else {
+        // Otherwise something's gone horribly wrong..!
+        throw new Error("Invalid playlist file contents: " + file)
+      }
+    }
+
     function requiresOpenPlaylist() {
       if (activePlaylist === null) {
         throw new Error(
@@ -47,7 +93,7 @@ setupDefaultPlaylist('./playlist.json')
       }
     }
 
-    await processArgv(process.argv, {
+    const optionFunctions = {
       '-help': function(util) {
         // --help  (alias: -h, -?)
         // Presents a help message.
@@ -67,10 +113,7 @@ setupDefaultPlaylist('./playlist.json')
         // Opens a separate playlist file.
         // This sets the source playlist.
 
-        const playlistText = await readFile(util.nextArg(), 'utf-8')
-        const openedPlaylist = JSON.parse(playlistText)
-        sourcePlaylist = openedPlaylist
-        activePlaylist = openedPlaylist
+        await openPlaylist(util.nextArg())
       },
 
       'o': util => util.alias('-open'),
@@ -203,7 +246,9 @@ setupDefaultPlaylist('./playlist.json')
 
         console.log(JSON.stringify(activePlaylist, null, 2))
       }
-    })
+    }
+
+    await processArgv(process.argv, optionFunctions)
 
     if (activePlaylist === null) {
       throw new Error(
diff --git a/todo.txt b/todo.txt
index 0054aca..3961c78 100644
--- a/todo.txt
+++ b/todo.txt
@@ -143,3 +143,5 @@ TODO: Make max download attempts variable by the user (without requiring
 
 TODO: Update HTTP crawl man page to include new options, and maybe update
       the HTTP crawler itself to reveal more options to the command line.
+
+TODO: Fix the bug in loop-play.js where wasDestroyed doesn't exist!