From a48c6bbbbcca48ddc0bade66b33781e13f80213d Mon Sep 17 00:00:00 2001 From: liam4 Date: Sun, 11 Jun 2017 10:04:09 -0300 Subject: Beginnings of keyboard controls --- man/http-music.1 | 10 ++++++++++ src/http-music.js | 23 ++++++++++++++++++++++- src/loop-play.js | 47 +++++++++++++++++++++++++++++++++++++++-------- todo.txt | 11 +++++++++++ 4 files changed, 82 insertions(+), 9 deletions(-) diff --git a/man/http-music.1 b/man/http-music.1 index 625b174..5fc93fb 100644 --- a/man/http-music.1 +++ b/man/http-music.1 @@ -22,6 +22,16 @@ It can be used anywhere with a Node environment, requiring only two commonly ins +.SH KEYBOARD CONTROLS +.TP +.BR s +Skips the currently playing track. +.TP +.BR q +Quits the http-music process and stops music currently being played. (\fB^C\fR and \fB^D\fR also work.) + + + .SH OPTIONS .TP .BR \-c ", " \-\-clear diff --git a/src/http-music.js b/src/http-music.js index fee1b79..3d7e217 100755 --- a/src/http-music.js +++ b/src/http-music.js @@ -239,7 +239,28 @@ setupDefaultPlaylist('./playlist.json') return } - return loopPlay(picker, downloader, playOpts) + const play = loopPlay(picker, downloader, playOpts) + + // We're looking to gather standard input one keystroke at a time. + process.stdin.setRawMode(true) + + process.stdin.on('data', data => { + if (Buffer.from('s').equals(data)) { + play.skip() + } + + if ( + Buffer.from('q').equals(data) || + Buffer.from([0x03]).equals(data) || // ^C + Buffer.from([0x04]).equals(data) // ^D + ) { + play.kill() + process.stdout.write('\n') + process.exit(0) + } + }) + + return play.promise } else { return activePlaylist } diff --git a/src/loop-play.js b/src/loop-play.js index 5205025..ff77940 100644 --- a/src/loop-play.js +++ b/src/loop-play.js @@ -12,7 +12,7 @@ const sanitize = require('sanitize-filename') const writeFile = promisify(fs.writeFile) -module.exports = async function loopPlay(picker, downloader, playArgs = []) { +module.exports = function loopPlay(picker, downloader, playArgs = []) { // Looping play function. Takes one argument, the "pick" function, // which returns a track to play. Preemptively downloads the next // track while the current one is playing for seamless continuation @@ -20,6 +20,8 @@ module.exports = async function loopPlay(picker, downloader, playArgs = []) { // function is null (or similar). Optionally takes a second argument // used as arguments to the `play` process (before the file name). + let playProcess, convertProcess + async function downloadNext() { const picked = picker() @@ -36,7 +38,9 @@ module.exports = async function loopPlay(picker, downloader, playArgs = []) { const wavFile = tempDir + `/.${sanitize(title)}.wav` try { - await convert(downloadFile, wavFile) + const convertPromise = convert(downloadFile, wavFile) + convertProcess = convertPromise.process + await convertPromise } catch(err) { console.warn("Failed to convert " + title) console.warn("Selecting a new track\n") @@ -47,12 +51,39 @@ module.exports = async function loopPlay(picker, downloader, playArgs = []) { return wavFile } - let wavFile = await downloadNext() + async function main() { + let wavFile = await downloadNext() + + while (wavFile) { + const nextPromise = downloadNext() + + // What a mouthful! + const playPromise = playFile(wavFile, playArgs) + playProcess = playPromise.process - while (wavFile) { - const nextPromise = downloadNext() - await playFile(wavFile, playArgs) - wavFile = await nextPromise + try { + await playPromise + } catch(err) { + console.warn(err) + } + + wavFile = await nextPromise + } + } + + const promise = main() + + return { + promise, + + skip: function() { + if (playProcess) playProcess.kill() + }, + + kill: function() { + if (playProcess) playProcess.kill() + if (convertProcess) convertProcess.kill() + } } } @@ -63,5 +94,5 @@ function convert(fromFile, toFile) { function playFile(file, opts = []) { const play = spawn('play', [...opts, file]) - return promisifyProcess(play) + return Object.assign(promisifyProcess(play), {process: play}) } diff --git a/todo.txt b/todo.txt index 02df97a..adf4456 100644 --- a/todo.txt +++ b/todo.txt @@ -96,3 +96,14 @@ TODO: Make a --help/-h/-? option that directs helpless users to the man page. TODO: Make a way to write the current playlist to a file. I think just renaming the debug-playlist-log option could work, since you could pipe that to a file through your shell. + +TODO: Figure out a less "hacky" way to kill the process. Ideally we shouldn't + have to handle ^C and ^D ourselves; for instance right now ^Z is actually + broken, since we aren't using the shell's normal way of handling any + keyboard controls such as those! + +TODO: A way to kill the up-next song. + +TODO: Separate the code in loop-play.js to be a bit nicer. + +TODO: Cleaning up http-music.js would be nice as well! -- cgit 1.3.0-6-gf8a5