« 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:
-rw-r--r--crawl-recursive.js22
-rw-r--r--package.json3
-rw-r--r--src/loop-play.js4
-rw-r--r--src/play.js35
-rw-r--r--src/playlist-utils.js56
-rw-r--r--src/process-argv.js2
-rw-r--r--src/promisify-process.js5
-rw-r--r--todo.txt2
-rw-r--r--yarn.lock9
9 files changed, 82 insertions, 56 deletions
diff --git a/crawl-recursive.js b/crawl-recursive.js
index d3b0127..d53f7d1 100644
--- a/crawl-recursive.js
+++ b/crawl-recursive.js
@@ -3,6 +3,7 @@
 const MAX_DOWNLOAD_ATTEMPTS = 5
 
 const fetch = require('node-fetch')
+const $ = require('cheerio')
 
 function crawl(absURL, attempts = 0) {
   // Recursively crawls a given URL, following every link to a deeper path and
@@ -21,33 +22,34 @@ function crawl(absURL, attempts = 0) {
           if (href.endsWith('/')) {
             // It's a directory!
 
-            if (verbose) console.log('[Dir] ' + absURL + href)
+            if (verbose) console.log("[Dir] " + absURL + href)
             return crawl(absURL + href)
               .then(res => [title, res])
           } else {
             // It's a file!
 
-            if (verbose) console.log('[File] ' + absURL + href)
+            if (verbose) console.log("[File] " + absURL + href)
             return Promise.resolve([title, absURL + href])
           }
         }))
       }),
 
       err => {
-        console.error('Failed to download: ' + absURL)
+        console.warn("Failed to download: " + absURL)
 
         if (attempts < MAX_DOWNLOAD_ATTEMPTS) {
-          console.error(
-            'Trying again. Attempt ' + (attempts + 1) +
-            '/' + MAX_DOWNLOAD_ATTEMPTS + '...'
+          console.warn(
+            "Trying again. Attempt " + (attempts + 1) +
+            "/" + MAX_DOWNLOAD_ATTEMPTS + "..."
           )
+
           return crawl(absURL, attempts + 1)
         } else {
           console.error(
-            'We\'ve hit the download attempt limit (' +
-            MAX_DOWNLOAD_ATTEMPTS + '). Giving up on ' +
-            'this path.'
+            "We've hit the download attempt limit (" +
+            MAX_DOWNLOAD_ATTEMPTS + "). Giving up on this path."
           )
+
           throw 'FAILED_DOWNLOAD'
         }
       }
@@ -72,7 +74,7 @@ function getHTMLLinks(text) {
 }
 
 if (process.argv.length === 2) {
-  console.log('Usage: crawl-recursive http://example.com/example/path')
+  console.log("Usage: crawl-recursive http://example.com/example/path")
 } else {
   let url = process.argv[2]
 
diff --git a/package.json b/package.json
index 4af34d5..5efd88e 100644
--- a/package.json
+++ b/package.json
@@ -1,4 +1,7 @@
 {
+  "name": "http-music",
+  "version": "0.0.1",
+  "main": "src/play.js",
   "dependencies": {
     "cheerio": "^1.0.0-rc.1",
     "node-fetch": "^1.7.0",
diff --git a/src/loop-play.js b/src/loop-play.js
index e59fbc2..48b5790 100644
--- a/src/loop-play.js
+++ b/src/loop-play.js
@@ -37,8 +37,8 @@ module.exports = async function loopPlay(fn) {
     try {
       await convert('./.temp-track', wavFile)
     } catch(err) {
-      console.warn('Failed to convert ' + title)
-      console.warn('Selecting a new track\n')
+      console.warn("Failed to convert " + title)
+      console.warn("Selecting a new track\n")
 
       return await downloadNext()
     }
diff --git a/src/play.js b/src/play.js
index 9b9a5cf..5e3e04b 100644
--- a/src/play.js
+++ b/src/play.js
@@ -8,7 +8,7 @@ const processArgv = require('./process-argv')
 const pickers = require('./pickers')
 
 const {
-  filterPlaylistByPathString, ignoreGroupByPathString, getPlaylistTreeString
+  filterPlaylistByPathString, removeGroupByPathString, getPlaylistTreeString
 } = require('./playlist-utils')
 
 const readFile = promisify(fs.readFile)
@@ -56,7 +56,7 @@ readFile('./playlist.json', 'utf-8')
         // Keeps a group by loading it from the source playlist into the
         // active playlist. This is usually useful after clearing the
         // active playlist; it can also be used to keep a subgroup when
-        // you've ignored an entire parent group, e.g. `-i foo -k foo/baz`.
+        // you've removed an entire parent group, e.g. `-r foo -k foo/baz`.
 
         const pathString = util.nextArg()
         const group = filterPlaylistByPathString(sourcePlaylist, pathString)
@@ -65,16 +65,17 @@ readFile('./playlist.json', 'utf-8')
 
       'k': util => util.alias('-keep'),
 
-      '-ignore': function(util) {
-        // --ignore <groupPath>  (alias: -i)
+      '-remove': function(util) {
+        // --remove <groupPath>  (alias: -r, -x)
         // Filters the playlist so that the given path is removed.
 
         const pathString = util.nextArg()
-        console.log('Ignoring path: ' + pathString)
-        ignoreGroupByPathString(curPlaylist, pathString)
+        console.log("Ignoring path: " + pathString)
+        removeGroupByPathString(curPlaylist, pathString)
       },
 
-      'i': util => util.alias('-ignore'),
+      'r': util => util.alias('-remove'),
+      'x': util => util.alias('-remove'),
 
       '-list-groups': function(util) {
         // --list-groups  (alias: -l, --list)
@@ -127,13 +128,6 @@ readFile('./playlist.json', 'utf-8')
 
       'np': util => util.alias('-no-play'),
 
-      '-debug-list': function(util) {
-        // --debug-list
-        // Prints out the JSON representation of the active playlist.
-
-        console.log(JSON.stringify(curPlaylist, null, 2))
-      },
-
       '-picker': function(util) {
         // --picker <shuffle|ordered>
         // Selects the mode that the song to play is picked.
@@ -141,19 +135,26 @@ readFile('./playlist.json', 'utf-8')
         // playlist.
 
         pickerType = util.nextArg()
+      },
+
+      '-debug-list': function(util) {
+        // --debug-list
+        // Prints out the JSON representation of the active playlist.
+
+        console.log(JSON.stringify(curPlaylist, null, 2))
       }
     })
 
     if (willPlay || (willPlay === null && shouldPlay)) {
       let picker
       if (pickerType === 'shuffle') {
-        console.log('Using shuffle picker')
+        console.log("Using shuffle picker.")
         picker = pickers.makeShufflePlaylistPicker(curPlaylist)
       } else if (pickerType === 'ordered') {
-        console.log('Using ordered picker')
+        console.log("Using ordered picker.")
         picker = pickers.makeOrderedPlaylistPicker(curPlaylist)
       } else {
-        console.error('Invalid picker type: ' + pickerType)
+        console.error("Invalid picker type: " + pickerType)
       }
 
       return loopPlay(picker)
diff --git a/src/playlist-utils.js b/src/playlist-utils.js
index 5266f1a..f65e247 100644
--- a/src/playlist-utils.js
+++ b/src/playlist-utils.js
@@ -5,9 +5,10 @@ function flattenPlaylist(playlist) {
   // levels in the playlist tree and returns them as a single-level array of
   // tracks.
 
-  const groups = playlist.filter(x => Array.isArray(x[1]))
-  const nonGroups = playlist.filter(x => x[1] && !(Array.isArray(x[1])))
-  return groups.map(g => flattenPlaylist(g[1]))
+  const groups = playlist.filter(x => isGroup(x))
+  const nonGroups = playlist.filter(x => !isGroup(x))
+
+  return groups.map(g => flattenPlaylist(getGroupContents(g)))
     .reduce((a, b) => a.concat(b), nonGroups)
 }
 
@@ -25,13 +26,15 @@ function filterPlaylistByPath(playlist, pathParts) {
 
   let cur = pathParts[0]
 
-  const match = playlist.find(g => g[0] === cur || g[0] === cur + '/')
+  const match = playlist.find(group => {
+    const title = getGroupTitle(group)
+    return title === cur || title === cur + '/'
+  })
 
   if (match) {
-    const groupContents = match[1]
     if (pathParts.length > 1) {
       const rest = pathParts.slice(1)
-      return filterPlaylistByPath(groupContents, rest)
+      return filterPlaylistByPath(getGroupContents(match), rest)
     } else {
       return match
     }
@@ -58,7 +61,7 @@ function removeGroupByPath(playlist, pathParts) {
   if (parentPath.length === 0) {
     parent = playlist
   } else {
-    parent = filterPlaylistByPath(playlist, pathParts.slice(0, -1))
+    parent = getGroupContents(filterPlaylistByPath(playlist, parentPath))
   }
 
   const index = parent.indexOf(groupToRemove)
@@ -67,31 +70,32 @@ function removeGroupByPath(playlist, pathParts) {
     parent.splice(index, 1)
   } else {
     console.error(
-      'Group ' + pathParts.join('/') + ' doesn\'t exist, so we can\'t ' +
-      'explicitly ignore it.'
+      `Group ${pathParts.join('/')} doesn't exist, so we can't explicitly ` +
+      "ignore it."
     )
   }
 }
 
 function getPlaylistTreeString(playlist, showTracks = false) {
   function recursive(group) {
-    const groups = group.filter(x => Array.isArray(x[1]))
-    const nonGroups = group.filter(x => x[1] && !(Array.isArray(x[1])))
+    const groups = group.filter(x => isGroup(x))
+    const nonGroups = group.filter(x => !isGroup(x))
 
-    const childrenString = groups.map(g => {
-      const groupString = recursive(g[1])
+    const childrenString = groups.map(group => {
+      const title = getGroupTitle(group)
+      const groupString = recursive(getGroupContents(group))
 
       if (groupString) {
         const indented = groupString.split('\n').map(l => '| ' + l).join('\n')
-        return '\n' + g[0] + '\n' + indented
+        return '\n' + title + '\n' + indented
       } else {
-        return g[0]
+        return title
       }
     }).join('\n')
 
-    let trackString = ''
+    let tracksString = ''
     if (showTracks) {
-      trackString = nonGroups.map(g => g[0]).join('\n')
+      tracksString = nonGroups.map(g => getGroupTitle(g)).join('\n')
     }
 
     if (tracksString && childrenString) {
@@ -113,9 +117,23 @@ function parsePathString(pathString) {
   return pathParts
 }
 
+function getGroupTitle(group) {
+  return group[0]
+}
+
+function getGroupContents(group) {
+  return group[1]
+}
+
+function isGroup(array) {
+  return Array.isArray(array[1])
+}
+
 module.exports = {
   flattenPlaylist,
   filterPlaylistByPathString, filterPlaylistByPath,
-  ignoreGroupByPathString, ignoreGroupByPath,
-  parsePathString
+  removeGroupByPathString, removeGroupByPath,
+  getPlaylistTreeString,
+  parsePathString,
+  getGroupTitle, getGroupContents
 }
diff --git a/src/process-argv.js b/src/process-argv.js
index d5f86f9..201f927 100644
--- a/src/process-argv.js
+++ b/src/process-argv.js
@@ -34,7 +34,7 @@ module.exports = async function processArgv(argv, handlers) {
         }
       })
     } else {
-      console.warn('Option not understood: ' + opt)
+      console.warn("Option not understood: " + opt)
     }
   }
 
diff --git a/src/promisify-process.js b/src/promisify-process.js
index ca49b31..ef8d0c6 100644
--- a/src/promisify-process.js
+++ b/src/promisify-process.js
@@ -2,7 +2,8 @@
 
 module.exports = function promisifyProcess(proc, showLogging = true) {
   // Takes a process (from child_process) and returns a promise that resolves
-  // when the process exits.
+  // when the process exits (or rejects with a warning, if the exit code is
+  // non-zero).
 
   return new Promise((resolve, reject) => {
     if (showLogging) {
@@ -14,7 +15,7 @@ module.exports = function promisifyProcess(proc, showLogging = true) {
       if (code === 0) {
         resolve()
       } else {
-        console.error('Process failed!', proc.spawnargs)
+        console.error("Process failed!", proc.spawnargs)
         reject(code)
       }
     })
diff --git a/todo.txt b/todo.txt
index cf984fc..3c62c68 100644
--- a/todo.txt
+++ b/todo.txt
@@ -67,3 +67,5 @@ TODO: Play-in-order track picker.
 TODO: Volume controls. Who knows how to do this? It might have to be an
       argument passed to `play`. Being able to change the volume while it's
       playing would be nice, but I'm not sure if that's really possible.
+
+TODO: Tempfiles, maybe?
diff --git a/yarn.lock b/yarn.lock
index 4f46d69..c18f89b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1,7 +1,5 @@
 # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
 # yarn lockfile v1
-
-
 "@types/node@^6.0.46":
   version "6.0.73"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.73.tgz#85dc4bb6f125377c75ddd2519a1eeb63f0a4ed70"
@@ -42,14 +40,14 @@ css-what@2.1:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd"
 
-dom-serializer@0, dom-serializer@~0.1.0:
+dom-serializer@~0.1.0, dom-serializer@0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
   dependencies:
     domelementtype "~1.1.1"
     entities "~1.1.1"
 
-domelementtype@1, domelementtype@^1.3.0:
+domelementtype@^1.3.0, domelementtype@1:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
 
@@ -63,7 +61,7 @@ domhandler@^2.3.0:
   dependencies:
     domelementtype "1"
 
-domutils@1.5.1, domutils@^1.5.1:
+domutils@^1.5.1, domutils@1.5.1:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
   dependencies:
@@ -175,3 +173,4 @@ utf8-byte-length@^1.0.1:
 util-deprecate@~1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+