« 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/keybinder.js80
-rwxr-xr-xsrc/play.js30
2 files changed, 86 insertions, 24 deletions
diff --git a/src/keybinder.js b/src/keybinder.js
index 784484c..f8d68a4 100644
--- a/src/keybinder.js
+++ b/src/keybinder.js
@@ -1,20 +1,27 @@
 const splitChars = str => str.split('').map(char => char.charCodeAt(0))
 
 const simpleKeybindings = {
-  space: [0x20],
-  esc: [0x1b], escape: [0x1b],
-  up: [0x1b, ...splitChars('[A')],
-  down: [0x1b, ...splitChars('[B')],
-  right: [0x1b, ...splitChars('[C')],
-  left: [0x1b, ...splitChars('[D')],
-  shiftUp: [0x1b, ...splitChars('[1;2A')],
-  shiftDown: [0x1b, ...splitChars('[1;2B')],
-  shiftRight: [0x1b, ...splitChars('[1;2C')],
-  shiftLeft: [0x1b, ...splitChars('[1;2D')],
-  delete: [0x7f]
+  space: ['Space', [0x20]],
+  esc: ['Escape', [0x1b]],
+  escape: ['Escape', [0x1b]],
+  up: ['Up', [0x1b, ...splitChars('[A')]],
+  down: ['Down', [0x1b, ...splitChars('[B')]],
+  right: ['Right', [0x1b, ...splitChars('[C')]],
+  left: ['Left', [0x1b, ...splitChars('[D')]],
+  shiftUp: ['Shift+Up', [0x1b, ...splitChars('[1;2A')]],
+  shiftDown: ['Shift+Down', [0x1b, ...splitChars('[1;2B')]],
+  shiftRight: ['Shift+Right', [0x1b, ...splitChars('[1;2C')]],
+  shiftLeft: ['Shift+Left', [0x1b, ...splitChars('[1;2D')]],
+  delete: ['Backspace', [0x7f]],
+  backspace: ['Backspace', [0x7f]]
 }
 
 module.exports.compileKeybindings = function(bindings, commands) {
+  // The "commands" array is an optional argument - if not given, the resulting
+  // handler function will simply return the keybinding array for whichever
+  // matches the inputted keypress. Too bad this feature isn't used anywhere.
+  // Thanks, old me.
+
   const handlers = bindings.map(binding => {
     const [ keys, command, ...args] = binding
 
@@ -29,12 +36,13 @@ module.exports.compileKeybindings = function(bindings, commands) {
       if (typeof item === 'number') {
         return [item]
       } else if (simpleKeybindings.hasOwnProperty(item)) {
-        return simpleKeybindings[item]
+        return simpleKeybindings[item][1]
       } else if (typeof item === 'string' && item.length === 1) {
         return [item.charCodeAt(0)]
       } else {
-        // Error
-        console.warn('Invalid keybinding part', item, 'in keybinding', bindings)
+        console.warn(
+          'Invalid keybinding part', item, 'in keybinding', bindings
+        )
         failed = true
         return []
       }
@@ -46,19 +54,51 @@ module.exports.compileKeybindings = function(bindings, commands) {
 
     const buffer = Buffer.from(bufferParts)
 
-    return function(inputData) {
+    return async function(inputData) {
       if (buffer.equals(inputData)) {
-        commands[command](...args)
-        return true
+        if (commands) {
+          const result = await commands[command](...args)
+          return typeof result === 'undefined' ? true : result
+        } else {
+          return keybinding
+        }
       }
     }
   }).filter(Boolean)
 
-  return function(inputData) {
+  return async function(inputData) {
     for (const handler of handlers) {
-      if (handler(inputData)) {
-        break
+      const result = await handler(inputData)
+      if (typeof result !== 'undefined') {
+        return result
       }
     }
   }
 }
+
+module.exports.getComboForCommand = function(command, bindings) {
+  const binding = bindings.find(kb => kb[1] === command)
+  if (binding) {
+    return binding[0]
+  } else {
+    return null
+  }
+}
+
+module.exports.stringifyCombo = function(combo) {
+  const stringifiedItems = combo.map(item => {
+    if (typeof item === 'string') {
+      if (item.length === 1) {
+        return item.toUpperCase()
+      } else if (simpleKeybindings.hasOwnProperty(item)) {
+        return simpleKeybindings[item][0]
+      } else {
+        return item
+      }
+    } else {
+      return JSON.stringify(item)
+    }
+  })
+
+  return stringifiedItems.join('+')
+}
diff --git a/src/play.js b/src/play.js
index 2a5cbdd..ff9e76a 100755
--- a/src/play.js
+++ b/src/play.js
@@ -11,7 +11,6 @@ const commandExists = require('./command-exists')
 const startLoopPlay = require('./loop-play')
 const processArgv = require('./process-argv')
 const promisifyProcess = require('./promisify-process')
-const { compileKeybindings } = require('./keybinder')
 const processSmartPlaylist = require('./smart-playlist')
 
 const {
@@ -19,6 +18,10 @@ const {
   updatePlaylistFormat, collapseGrouplike, filterGrouplikeByProperty, isTrack
 } = require('./playlist-utils')
 
+const {
+  compileKeybindings, getComboForCommand, stringifyCombo
+} = require('./keybinder')
+
 const readFile = promisify(fs.readFile)
 const writeFile = promisify(fs.writeFile)
 
@@ -401,6 +404,20 @@ async function main(args) {
     '-list-tracks': util => util.alias('-list-all'),
     'L': util => util.alias('-list-all'),
 
+    '-list-keybindings': function() {
+      console.log('Keybindings:')
+
+      for (const [ combo, command, ...args ] of keybindings) {
+        console.log(`${stringifyCombo(combo)}: ${command}${
+          args ? ' ' + args.join(' ') : ''}`)
+      }
+
+      shouldPlay = false
+    },
+
+    '-show-keybindings': util => util.alias('-list-keybindings'),
+    '-keybindings': util => util.alias('-list-keybindings'),
+
     '-play': function(util) {
       // --play  (alias: -p)
       // Forces the playlist to actually play.
@@ -625,6 +642,12 @@ async function main(args) {
       console.warn("If you're piping into http-music, this is normal.")
     }
 
+    const trackInfoCombo = stringifyCombo(getComboForCommand(
+      'showTrackInfo', keybindings
+    ))
+
+    const trackInfoString = `(Press ${trackInfoCombo} for track info!)`
+
     const commands = {
       'doNothing': function() {},
 
@@ -662,7 +685,7 @@ async function main(args) {
 
       'skipBack': function() {
         clearConsoleLine()
-        console.log("Skipping backwards. (Press I for track info!")
+        console.log("Skipping backwards.", trackInfoString)
 
         playController.skipBack()
       },
@@ -670,8 +693,7 @@ async function main(args) {
       'skipAhead': function() {
         clearConsoleLine()
         console.log(
-          "Skipping the track that's currently playing. " +
-          "(Press I for track info!)"
+          "Skipping the track that's currently playing.", trackInfoString
         )
 
         playController.skip()