diff options
-rw-r--r-- | src/cli.js | 37 | ||||
-rwxr-xr-x | src/crawl-http.js | 20 | ||||
-rwxr-xr-x | src/crawl-itunes.js | 12 | ||||
-rwxr-xr-x | src/crawl-local.js | 23 | ||||
-rwxr-xr-x | src/download-playlist.js | 14 | ||||
-rwxr-xr-x | src/http-music.js | 404 | ||||
-rw-r--r-- | src/loop-play.js | 2 | ||||
-rwxr-xr-x | src/play.js | 412 |
8 files changed, 494 insertions, 430 deletions
diff --git a/src/cli.js b/src/cli.js new file mode 100644 index 0000000..d88ddc0 --- /dev/null +++ b/src/cli.js @@ -0,0 +1,37 @@ +#!/usr/bin/env node + +// Let this forever be of use to people who run into +// maxlistenersexceededwarning. +process.on('warning', e => console.warn(e.stack)) + +async function main(args) { + let script + + if (args.length === 0) { + console.error("No command provided.") + console.error("Try 'man http-music'?") + return + } + + switch (args[0]) { + case 'play': script = require('./play'); break + case 'crawl-http': script = require('./crawl-http'); break + case 'crawl-local': script = require('./crawl-local'); break + case 'crawl-itunes': script = require('./crawl-itunes'); break + case 'download-playlist': script = require('./download-playlist'); break + + default: + console.error(`Invalid command "${args[0]}" provided.`) + console.error("Try 'man http-music'?") + return + } + + await script(args.slice(1)) +} + +module.exports = main + +if (require.main === module) { + main(process.argv.slice(2)) + .catch(err => console.error(err)) +} diff --git a/src/crawl-http.js b/src/crawl-http.js index a2bf884..29c59d2 100755 --- a/src/crawl-http.js +++ b/src/crawl-http.js @@ -139,14 +139,20 @@ function getHTMLLinks(text) { }) } -async function main() { - let url = process.argv[2] +async function main(args) { + if (args.length === 0) { + console.log("Usage: crawl-http http://.../example/path/ [opts]") + process.exit(1) + return + } + + let url = args[0] let maxDownloadAttempts = 5 let verbose = false let filterRegex = null - await processArgv(process.argv.slice(3), { + await processArgv(args.slice(1), { '-max-download-attempts': function(util) { // --max-download-attempts <max> (alias: -m) // Sets the maximum number of times to attempt downloading the index for @@ -190,9 +196,9 @@ async function main() { console.log(JSON.stringify(downloadedPlaylist, null, 2)) } -if (process.argv.length === 2) { - console.log("Usage: http-music-crawl-http http://.../example/path/ [opts]") -} else { - main() +module.exports = main + +if (require.main === module) { + main(process.argv.slice(2)) .catch(err => console.error(err)) } diff --git a/src/crawl-itunes.js b/src/crawl-itunes.js index 5cc1b55..6060ffa 100755 --- a/src/crawl-itunes.js +++ b/src/crawl-itunes.js @@ -93,8 +93,8 @@ async function crawl(libraryXML) { return resultGroup } -async function main() { - const libraryPath = process.argv[2] || ( +async function main(args) { + const libraryPath = args[0] || ( `${process.env.HOME}/Music/iTunes/iTunes Music Library.xml` ) @@ -130,5 +130,9 @@ async function main() { console.log(JSON.stringify(playlist, null, 2)) } -main() - .catch(err => console.error(err)) +module.exports = main + +if (require.main === module) { + main(process.argv.slice(2)) + .catch(err => console.error(err)) +} diff --git a/src/crawl-local.js b/src/crawl-local.js index 6d245ae..629e015 100755 --- a/src/crawl-local.js +++ b/src/crawl-local.js @@ -30,13 +30,20 @@ function crawl(dirPath) { }).then(items => ({items})) } -if (process.argv.length === 2) { - console.log("Usage: http-music-crawl-local /example/path..") - console.log("..or, npm run crawl-local /example/path") -} else { - const path = process.argv[2] - - crawl(path) - .then(res => console.log(JSON.stringify(res, null, 2))) +async function main(args) { + if (args.length === 0) { + console.log("Usage: crawl-local /example/path") + } else { + const path = args[0] + + const res = await crawl(path) + console.log(JSON.stringify(res, null, 2)) + } +} + +module.exports = main + +if (require.main === module) { + main(process.argv.slice(2)) .catch(err => console.error(err)) } diff --git a/src/download-playlist.js b/src/download-playlist.js index 18e1a7f..0b5ea58 100755 --- a/src/download-playlist.js +++ b/src/download-playlist.js @@ -102,17 +102,15 @@ async function downloadCrawl(topPlaylist, initialOutPath = './out/') { return recursive(topPlaylist.items, initialOutPath) } -async function main() { +async function main(args) { // TODO: Implement command line stuff here - if (process.argv.length === 2) { + if (args.length === 0) { console.error('Usage: download-playlist <playlistFile> [opts]') return } - const playlist = updatePlaylistFormat( - JSON.parse(await readFile(process.argv[2])) - ) + const playlist = updatePlaylistFormat(JSON.parse(await readFile(args[0]))) const outPlaylist = await downloadCrawl(playlist) @@ -122,5 +120,7 @@ async function main() { process.exit(0) } -main() - .catch(err => console.error(err)) +if (require.main === module) { + main(process.argv.slice(2)) + .catch(err => console.error(err)) +} diff --git a/src/http-music.js b/src/http-music.js deleted file mode 100755 index 04d1f57..0000000 --- a/src/http-music.js +++ /dev/null @@ -1,404 +0,0 @@ -#!/usr/bin/env node - -'use strict' - -const { promisify } = require('util') -const clone = require('clone') -const fs = require('fs') -const fetch = require('node-fetch') -const pickers = require('./pickers') -const loopPlay = require('./loop-play') -const processArgv = require('./process-argv') - -const { - filterPlaylistByPathString, removeGroupByPathString, getPlaylistTreeString, - updatePlaylistFormat -} = require('./playlist-utils') - -const readFile = promisify(fs.readFile) -const writeFile = promisify(fs.writeFile) - -function downloadPlaylistFromURL(url) { - return fetch(url).then(res => res.text()) -} - -function downloadPlaylistFromLocalPath(path) { - return readFile(path) -} - -function downloadPlaylistFromOptionValue(arg) { - // TODO: Verify things! - if (arg.startsWith('http://') || arg.startsWith('https://')) { - return downloadPlaylistFromURL(arg) - } else { - return downloadPlaylistFromLocalPath(arg) - } -} - -// Let this forever be of use to people who run into -// maxlistenersexceededwarning. -process.on('warning', e => console.warn(e.stack)) - -Promise.resolve() - .then(async () => { - let sourcePlaylist = null - let activePlaylist = null - - let pickerType = 'shuffle' - let playOpts = [] - - // WILL play says whether the user has forced playback via an argument. - // SHOULD play says whether the program has automatically decided to play - // or not, if the user hasn't set WILL play. - let shouldPlay = true - let willPlay = null - - async function openPlaylist(arg, silent = false) { - let playlistText - - if (!silent) { - console.log("Opening playlist from: " + arg) - } - - try { - playlistText = await downloadPlaylistFromOptionValue(arg) - } catch(err) { - if (!silent) { - console.error("Failed to open playlist file: " + arg) - console.error(err) - } - - return false - } - - const openedPlaylist = updatePlaylistFormat(JSON.parse(playlistText)) - - // The active playlist is a clone of the source playlist; after all it's - // quite possible we'll be messing with the value of the active playlist, - // and we don't want to reflect those changes in the source playlist. - sourcePlaylist = openedPlaylist - activePlaylist = clone(openedPlaylist) - - processArgv(openedPlaylist.options, optionFunctions) - } - - function requiresOpenPlaylist() { - if (activePlaylist === null) { - throw new Error( - "This action requires an open playlist - try --open (file)" - ) - } - } - - const optionFunctions = { - '-help': function(util) { - // --help (alias: -h, -?) - // Presents a help message. - - console.log('http-music\nTry man http-music!') - - if (util.index === util.argv.length - 1) { - shouldPlay = false - } - }, - - 'h': util => util.alias('-help'), - '?': util => util.alias('-help'), - - '-open-playlist': async function(util) { - // --open-playlist <file> (alias: --open, -o) - // Opens a separate playlist file. - // This sets the source playlist. - - await openPlaylist(util.nextArg()) - }, - - '-open': util => util.alias('-open-playlist'), - 'o': util => util.alias('-open-playlist'), - - '-write-playlist': function(util) { - // --write-playlist <file> (alias: --write, -w, --save) - // Writes the active playlist to a file. This file can later be used - // with --open <file>; you won't need to stick in all the filtering - // options again. - - requiresOpenPlaylist() - - const playlistString = JSON.stringify(activePlaylist, null, 2) - const file = util.nextArg() - - console.log(`Saving playlist to file ${file}...`) - - return writeFile(file, playlistString).then(() => { - console.log("Saved.") - - // If this is the last option, the user probably doesn't actually - // want to play the playlist. (We need to check if this is len - 2 - // rather than len - 1, because of the <file> option that comes - // after --write-playlist.) - if (util.index === util.argv.length - 2) { - shouldPlay = false - } - }) - }, - - '-write': util => util.alias('-write-playlist'), - 'w': util => util.alias('-write-playlist'), - '-save': util => util.alias('-write-playlist'), - - '-print-playlist': function(util) { - // --print-playlist (alias: --log-playlist, --json) - // Prints out the JSON representation of the active playlist. - - requiresOpenPlaylist() - - console.log(JSON.stringify(activePlaylist, null, 2)) - - // As with --write-playlist, the user probably doesn't want to actually - // play anything if this is the last option. - if (util.index === util.argv.length - 1) { - shouldPlay = false - } - }, - - '-log-playlist': util => util.alias('-print-playlist'), - '-json': util => util.alias('-print-playlist'), - - '-clear': function(util) { - // --clear (alias: -c) - // Clears the active playlist. This does not affect the source - // playlist. - - requiresOpenPlaylist() - - activePlaylist.items = [] - }, - - 'c': util => util.alias('-clear'), - - '-keep': function(util) { - // --keep <groupPath> (alias: -k) - // 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 removed an entire parent group, e.g. `-r foo -k foo/baz`. - - requiresOpenPlaylist() - - const pathString = util.nextArg() - const group = filterPlaylistByPathString(sourcePlaylist, pathString) - activePlaylist.items.push(group) - }, - - 'k': util => util.alias('-keep'), - - '-remove': function(util) { - // --remove <groupPath> (alias: -r, -x) - // Filters the playlist so that the given path is removed. - - requiresOpenPlaylist() - - const pathString = util.nextArg() - console.log("Ignoring path: " + pathString) - removeGroupByPathString(activePlaylist, pathString) - }, - - 'r': util => util.alias('-remove'), - 'x': util => util.alias('-remove'), - - '-list-groups': function(util) { - // --list-groups (alias: -l, --list) - // Lists all groups in the playlist. - - requiresOpenPlaylist() - - console.log(getPlaylistTreeString(activePlaylist)) - - // If this is the last item in the argument list, the user probably - // only wants to get the list, so we'll mark the 'should run' flag - // as false. - if (util.index === util.argv.length - 1) { - shouldPlay = false - } - }, - - '-list': util => util.alias('-list-groups'), - 'l': util => util.alias('-list-groups'), - - '-list-all': function(util) { - // --list-all (alias: --list-tracks, -L) - // Lists all groups and tracks in the playlist. - - requiresOpenPlaylist() - - console.log(getPlaylistTreeString(activePlaylist, true)) - - // As with -l, if this is the last item in the argument list, we - // won't actually be playing the playlist. - if (util.index === util.argv.length - 1) { - shouldPlay = false - } - }, - - '-list-tracks': util => util.alias('-list-all'), - 'L': util => util.alias('-list-all'), - - '-play': function(util) { - // --play (alias: -p) - // Forces the playlist to actually play. - - willPlay = true - }, - - 'p': util => util.alias('-play'), - - '-no-play': function(util) { - // --no-play (alias: -np) - // Forces the playlist not to play. - - willPlay = false - }, - - 'np': util => util.alias('-no-play'), - - '-picker': function(util) { - // --picker <picker type> (alias: --selector) - // Selects the mode that the song to play is picked. - // See pickers.js. - - pickerType = util.nextArg() - }, - - '-selector': util => util.alias('-picker'), - - '-play-opts': function(util) { - // --play-opts <opts> - // Sets command line options passed to the `play` command. - - playOpts = util.nextArg().split(' ') - } - } - - await openPlaylist('./playlist.json', true) - - await processArgv(process.argv, optionFunctions) - - if (activePlaylist === null) { - throw new Error( - "Cannot play - no open playlist. Try --open <playlist file>?" - ) - } - - if (willPlay || (willPlay === null && shouldPlay)) { - let picker - if (pickerType === 'shuffle') { - console.log("Using shuffle picker.") - picker = pickers.makeShufflePlaylistPicker(activePlaylist) - } else if (pickerType === 'ordered') { - console.log("Using ordered picker.") - picker = pickers.makeOrderedPlaylistPicker(activePlaylist) - } else { - console.error("Invalid picker type: " + pickerType) - return - } - - const { - promise: playPromise, - playController: play, - downloadController - } = loopPlay(picker, playOpts) - - // We're looking to gather standard input one keystroke at a time. - process.stdin.setRawMode(true) - - process.stdin.on('data', data => { - const escModifier = Buffer.from('\x1b[') - const shiftModifier = Buffer.from('1;2') - - const esc = num => Buffer.concat([escModifier, Buffer.from([num])]) - - const shiftEsc = num => ( - Buffer.concat([escModifier, shiftModifier, Buffer.from([num])]) - ) - - if (Buffer.from([0x20]).equals(data)) { - play.togglePause() - } - - if (esc(0x43).equals(data)) { - play.seekAhead(5) - } - - if (esc(0x44).equals(data)) { - play.seekBack(5) - } - - if (shiftEsc(0x43).equals(data)) { - play.seekAhead(30) - } - - if (shiftEsc(0x44).equals(data)) { - play.seekBack(30) - } - - if (esc(0x41).equals(data)) { - play.volUp(10) - } - - if (esc(0x42).equals(data)) { - play.volDown(10) - } - - if (Buffer.from('s').equals(data)) { - clearConsoleLine() - console.log( - "Skipping the track that's currently playing. " + - "(Press I for track info!)" - ) - - play.skip() - } - - if (Buffer.from([0x7f]).equals(data)) { - clearConsoleLine() - console.log( - "Skipping the track that's up next. " + - "(Press I for track info!)" - ) - - // TODO: It would be nice to have this as a method of - // PlayController. - // Double TODO: This doesn't actually work!! - downloadController.cancel() - play.startNextDownload() - } - - if ( - Buffer.from('i').equals(data) || - Buffer.from('t').equals(data) - ) { - clearConsoleLine() - play.logTrackInfo() - } - - 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 playPromise - } else { - return activePlaylist - } - }) - .catch(err => console.error(err)) - -function clearConsoleLine() { - process.stdout.write('\x1b[1K\r') -} diff --git a/src/loop-play.js b/src/loop-play.js index 7457b41..884d3cb 100644 --- a/src/loop-play.js +++ b/src/loop-play.js @@ -1,3 +1,5 @@ +// This isn't actually the code for the `play` command! That's in `play.js`. + 'use strict' const { spawn } = require('child_process') diff --git a/src/play.js b/src/play.js new file mode 100755 index 0000000..9515d97 --- /dev/null +++ b/src/play.js @@ -0,0 +1,412 @@ +#!/usr/bin/env node + +'use strict' + +const { promisify } = require('util') +const clone = require('clone') +const fs = require('fs') +const fetch = require('node-fetch') +const pickers = require('./pickers') +const loopPlay = require('./loop-play') +const processArgv = require('./process-argv') + +const { + filterPlaylistByPathString, removeGroupByPathString, getPlaylistTreeString, + updatePlaylistFormat +} = require('./playlist-utils') + +const readFile = promisify(fs.readFile) +const writeFile = promisify(fs.writeFile) + +function downloadPlaylistFromURL(url) { + return fetch(url).then(res => res.text()) +} + +function downloadPlaylistFromLocalPath(path) { + return readFile(path) +} + +function downloadPlaylistFromOptionValue(arg) { + // TODO: Verify things! + if (arg.startsWith('http://') || arg.startsWith('https://')) { + return downloadPlaylistFromURL(arg) + } else { + return downloadPlaylistFromLocalPath(arg) + } +} + +function clearConsoleLine() { + process.stdout.write('\x1b[1K\r') +} + +async function main(args) { + let sourcePlaylist = null + let activePlaylist = null + + let pickerType = 'shuffle' + let playOpts = [] + + // WILL play says whether the user has forced playback via an argument. + // SHOULD play says whether the program has automatically decided to play + // or not, if the user hasn't set WILL play. + let shouldPlay = true + let willPlay = null + + async function openPlaylist(arg, silent = false) { + let playlistText + + if (!silent) { + console.log("Opening playlist from: " + arg) + } + + try { + playlistText = await downloadPlaylistFromOptionValue(arg) + } catch(err) { + if (!silent) { + console.error("Failed to open playlist file: " + arg) + console.error(err) + } + + return false + } + + const openedPlaylist = updatePlaylistFormat(JSON.parse(playlistText)) + + // The active playlist is a clone of the source playlist; after all it's + // quite possible we'll be messing with the value of the active playlist, + // and we don't want to reflect those changes in the source playlist. + sourcePlaylist = openedPlaylist + activePlaylist = clone(openedPlaylist) + + processArgv(openedPlaylist.options, optionFunctions) + } + + function requiresOpenPlaylist() { + if (activePlaylist === null) { + throw new Error( + "This action requires an open playlist - try --open (file)" + ) + } + } + + const optionFunctions = { + '-help': function(util) { + // --help (alias: -h, -?) + // Presents a help message. + + console.log('http-music\nTry man http-music!') + + if (util.index === util.argv.length - 1) { + shouldPlay = false + } + }, + + 'h': util => util.alias('-help'), + '?': util => util.alias('-help'), + + '-open-playlist': async function(util) { + // --open-playlist <file> (alias: --open, -o) + // Opens a separate playlist file. + // This sets the source playlist. + + await openPlaylist(util.nextArg()) + }, + + '-open': util => util.alias('-open-playlist'), + 'o': util => util.alias('-open-playlist'), + + '-write-playlist': function(util) { + // --write-playlist <file> (alias: --write, -w, --save) + // Writes the active playlist to a file. This file can later be used + // with --open <file>; you won't need to stick in all the filtering + // options again. + + requiresOpenPlaylist() + + const playlistString = JSON.stringify(activePlaylist, null, 2) + const file = util.nextArg() + + console.log(`Saving playlist to file ${file}...`) + + return writeFile(file, playlistString).then(() => { + console.log("Saved.") + + // If this is the last option, the user probably doesn't actually + // want to play the playlist. (We need to check if this is len - 2 + // rather than len - 1, because of the <file> option that comes + // after --write-playlist.) + if (util.index === util.argv.length - 2) { + shouldPlay = false + } + }) + }, + + '-write': util => util.alias('-write-playlist'), + 'w': util => util.alias('-write-playlist'), + '-save': util => util.alias('-write-playlist'), + + '-print-playlist': function(util) { + // --print-playlist (alias: --log-playlist, --json) + // Prints out the JSON representation of the active playlist. + + requiresOpenPlaylist() + + console.log(JSON.stringify(activePlaylist, null, 2)) + + // As with --write-playlist, the user probably doesn't want to actually + // play anything if this is the last option. + if (util.index === util.argv.length - 1) { + shouldPlay = false + } + }, + + '-log-playlist': util => util.alias('-print-playlist'), + '-json': util => util.alias('-print-playlist'), + + '-clear': function(util) { + // --clear (alias: -c) + // Clears the active playlist. This does not affect the source + // playlist. + + requiresOpenPlaylist() + + activePlaylist.items = [] + }, + + 'c': util => util.alias('-clear'), + + '-keep': function(util) { + // --keep <groupPath> (alias: -k) + // 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 removed an entire parent group, e.g. `-r foo -k foo/baz`. + + requiresOpenPlaylist() + + const pathString = util.nextArg() + const group = filterPlaylistByPathString(sourcePlaylist, pathString) + activePlaylist.items.push(group) + }, + + 'k': util => util.alias('-keep'), + + '-remove': function(util) { + // --remove <groupPath> (alias: -r, -x) + // Filters the playlist so that the given path is removed. + + requiresOpenPlaylist() + + const pathString = util.nextArg() + console.log("Ignoring path: " + pathString) + removeGroupByPathString(activePlaylist, pathString) + }, + + 'r': util => util.alias('-remove'), + 'x': util => util.alias('-remove'), + + '-list-groups': function(util) { + // --list-groups (alias: -l, --list) + // Lists all groups in the playlist. + + requiresOpenPlaylist() + + console.log(getPlaylistTreeString(activePlaylist)) + + // If this is the last item in the argument list, the user probably + // only wants to get the list, so we'll mark the 'should run' flag + // as false. + if (util.index === util.argv.length - 1) { + shouldPlay = false + } + }, + + '-list': util => util.alias('-list-groups'), + 'l': util => util.alias('-list-groups'), + + '-list-all': function(util) { + // --list-all (alias: --list-tracks, -L) + // Lists all groups and tracks in the playlist. + + requiresOpenPlaylist() + + console.log(getPlaylistTreeString(activePlaylist, true)) + + // As with -l, if this is the last item in the argument list, we + // won't actually be playing the playlist. + if (util.index === util.argv.length - 1) { + shouldPlay = false + } + }, + + '-list-tracks': util => util.alias('-list-all'), + 'L': util => util.alias('-list-all'), + + '-play': function(util) { + // --play (alias: -p) + // Forces the playlist to actually play. + + willPlay = true + }, + + 'p': util => util.alias('-play'), + + '-no-play': function(util) { + // --no-play (alias: -np) + // Forces the playlist not to play. + + willPlay = false + }, + + 'np': util => util.alias('-no-play'), + + '-picker': function(util) { + // --picker <picker type> (alias: --selector) + // Selects the mode that the song to play is picked. + // See pickers.js. + + pickerType = util.nextArg() + }, + + '-selector': util => util.alias('-picker'), + + '-play-opts': function(util) { + // --play-opts <opts> + // Sets command line options passed to the `play` command. + + playOpts = util.nextArg().split(' ') + } + } + + await openPlaylist('./playlist.json', true) + + await processArgv(args, optionFunctions) + + if (activePlaylist === null) { + throw new Error( + "Cannot play - no open playlist. Try --open <playlist file>?" + ) + } + + if (willPlay || (willPlay === null && shouldPlay)) { + let picker + if (pickerType === 'shuffle') { + console.log("Using shuffle picker.") + picker = pickers.makeShufflePlaylistPicker(activePlaylist) + } else if (pickerType === 'ordered') { + console.log("Using ordered picker.") + picker = pickers.makeOrderedPlaylistPicker(activePlaylist) + } else { + console.error("Invalid picker type: " + pickerType) + return + } + + const { + promise: playPromise, + playController: play, + downloadController + } = loopPlay(picker, playOpts) + + // We're looking to gather standard input one keystroke at a time. + // But that isn't *always* possible, e.g. when piping into the http-music + // command through the shell. + if ('setRawMode' in process.stdin) { + process.stdin.setRawMode(true) + } else { + console.warn("User input cannot be gotten!") + console.warn("If you're piping into http-music, this is normal.") + } + + process.stdin.on('data', data => { + const escModifier = Buffer.from('\x1b[') + const shiftModifier = Buffer.from('1;2') + + const esc = num => Buffer.concat([escModifier, Buffer.from([num])]) + + const shiftEsc = num => ( + Buffer.concat([escModifier, shiftModifier, Buffer.from([num])]) + ) + + if (Buffer.from([0x20]).equals(data)) { + play.togglePause() + } + + if (esc(0x43).equals(data)) { + play.seekAhead(5) + } + + if (esc(0x44).equals(data)) { + play.seekBack(5) + } + + if (shiftEsc(0x43).equals(data)) { + play.seekAhead(30) + } + + if (shiftEsc(0x44).equals(data)) { + play.seekBack(30) + } + + if (esc(0x41).equals(data)) { + play.volUp(10) + } + + if (esc(0x42).equals(data)) { + play.volDown(10) + } + + if (Buffer.from('s').equals(data)) { + clearConsoleLine() + console.log( + "Skipping the track that's currently playing. " + + "(Press I for track info!)" + ) + + play.skip() + } + + if (Buffer.from([0x7f]).equals(data)) { + clearConsoleLine() + console.log( + "Skipping the track that's up next. " + + "(Press I for track info!)" + ) + + // TODO: It would be nice to have this as a method of + // PlayController. + // Double TODO: This doesn't actually work!! + downloadController.cancel() + play.startNextDownload() + } + + if ( + Buffer.from('i').equals(data) || + Buffer.from('t').equals(data) + ) { + clearConsoleLine() + play.logTrackInfo() + } + + 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 playPromise + } else { + return activePlaylist + } +} + +module.exports = main + +if (require.main === module) { + main(process.argv.slice(2)) + .catch(err => console.error(err)) +} |