From b11539dadd64cf283eb056414df383cfa15f7b3d Mon Sep 17 00:00:00 2001 From: Florrie Date: Fri, 24 Nov 2017 23:07:11 -0400 Subject: Gracefully exit if the active playlist has no items --- src/play.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'src/play.js') diff --git a/src/play.js b/src/play.js index ff9e76a..c910b08 100755 --- a/src/play.js +++ b/src/play.js @@ -15,7 +15,8 @@ const processSmartPlaylist = require('./smart-playlist') const { filterPlaylistByPathString, removeGroupByPathString, getPlaylistTreeString, - updatePlaylistFormat, collapseGrouplike, filterGrouplikeByProperty, isTrack + updatePlaylistFormat, collapseGrouplike, filterGrouplikeByProperty, isTrack, + flattenGrouplike } = require('./playlist-utils') const { @@ -609,6 +610,22 @@ async function main(args) { } if (willPlay || (willPlay === null && shouldPlay)) { + // Quick and simple test - if there are no items in the playlist, don't + // continue. This is mainly to catch incomplete user-entered commands + // (like `http-music play -c`). + if (flattenGrouplike(activePlaylist).items.length === 0) { + console.error( + 'Your playlist doesn\'t have any tracks in it, so it can\'t be ' + + 'played.' + ) + console.error( + '(Make sure your http-music command doesn\'t have any typos ' + + 'and isn\'t incomplete? You might have used -c or --clear but not ' + + '--keep to actually pick tracks to play!)' + ) + return false + } + console.log(`Using sort: ${pickerSortMode} and loop: ${pickerLoopMode}.`) console.log(`Using ${playerCommand} player.`) console.log(`Using ${converterCommand} converter.`) -- cgit 1.3.0-6-gf8a5 From 31692325a13be15d75d739c34ab47047ac45fde2 Mon Sep 17 00:00:00 2001 From: Florrie Date: Mon, 27 Nov 2017 22:00:11 -0400 Subject: Publish smart-playlist command properly --- src/play.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/play.js') diff --git a/src/play.js b/src/play.js index c910b08..5b71c94 100755 --- a/src/play.js +++ b/src/play.js @@ -11,7 +11,7 @@ const commandExists = require('./command-exists') const startLoopPlay = require('./loop-play') const processArgv = require('./process-argv') const promisifyProcess = require('./promisify-process') -const processSmartPlaylist = require('./smart-playlist') +const { processSmartPlaylist } = require('./smart-playlist') const { filterPlaylistByPathString, removeGroupByPathString, getPlaylistTreeString, -- cgit 1.3.0-6-gf8a5 From 0a514c53087e40453089dbf223267655a89b3076 Mon Sep 17 00:00:00 2001 From: Florrie Date: Wed, 24 Jan 2018 17:07:23 -0400 Subject: MAKE DEFAULT KEYBINDINGS WORK WITH CAPS LOCK --- src/play.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/play.js') diff --git a/src/play.js b/src/play.js index 5b71c94..788f6bc 100755 --- a/src/play.js +++ b/src/play.js @@ -111,11 +111,11 @@ async function main(args) { [['shiftRight'], 'seek', +30], [['up'], 'skipBack'], [['down'], 'skipAhead'], - [['s'], 'skipAhead'], [['delete'], 'skipUpNext'], - [['i'], 'showTrackInfo'], - [['t'], 'showTrackInfo'], - [['q'], 'quit'] + [['s'], 'skipAhead'], [['S'], 'skipAhead'], + [['i'], 'showTrackInfo'], [['I'], 'showTrackInfo'], + [['t'], 'showTrackInfo'], [['T'], 'showTrackInfo'], + [['q'], 'quit'], [['Q'], 'quit'] ] async function openPlaylist(arg, silent = false) { -- cgit 1.3.0-6-gf8a5 From 2ec0349909be5c8cca63008f18603555493b95d9 Mon Sep 17 00:00:00 2001 From: Florrie Date: Wed, 24 Jan 2018 17:19:20 -0400 Subject: Add --playlist-string alias, update man page --- src/play.js | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/play.js') diff --git a/src/play.js b/src/play.js index 788f6bc..75099f9 100755 --- a/src/play.js +++ b/src/play.js @@ -235,6 +235,8 @@ async function main(args) { await loadPlaylist(JSON.parse(util.nextArg())) }, + '-playlist-string': util => util.alias('-open-playlist-string'), + '-write-playlist': function(util) { // --write-playlist (alias: --write, -w, --save) // Writes the active playlist to a file. This file can later be used -- cgit 1.3.0-6-gf8a5 From e249bda854212d9ba29015b0c895b72aa2ee3cad Mon Sep 17 00:00:00 2001 From: Florrie Date: Mon, 12 Feb 2018 19:45:52 -0400 Subject: Add --track-display-file option for meme OBS livestreams --- src/play.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'src/play.js') diff --git a/src/play.js b/src/play.js index 75099f9..a3481ea 100755 --- a/src/play.js +++ b/src/play.js @@ -103,6 +103,9 @@ async function main(args) { // keybinding files. let mayTrustShellCommands = true + // The file to output the playlist path to the current file. + let trackDisplayFile + const keybindings = [ [['space'], 'togglePause'], [['left'], 'seek', -5], @@ -571,6 +574,24 @@ async function main(args) { '-hide-playback-status': util => util.alias('-disable-playback-status'), + '-track-display-file': async function(util) { + // --track-display-file (alias: --display-track-file) + // Sets the file to output the current track's path to every time a new + // track is played. This is mostly useful for using tools like OBS to + // interface with http-music, for example so that you can display the + // name/path of the track that is currently playing in a live stream. + const file = util.nextArg() + try { + await writeFile(file, 'Not yet playing.') + } catch (error) { + console.log(`Failed to set track display file to "${file}".`) + return + } + trackDisplayFile = file + }, + + '-display-track-file': util => util.alias('-track-display-file'), + '-trust-shell-commands': function(util) { // --trust-shell-commands (alias: --trust) // Lets keybindings run shell commands. Only use this when loading @@ -648,7 +669,8 @@ async function main(args) { willUseConverterOptions === null && shouldUseConverterOptions ), disablePlaybackStatus, - startTrack + startTrack, + trackDisplayFile }) // We're looking to gather standard input one keystroke at a time. -- cgit 1.3.0-6-gf8a5 From 1b5d6f8f96baae53367a7a7d0f9485a42029eaa3 Mon Sep 17 00:00:00 2001 From: Florrie Date: Fri, 16 Feb 2018 11:02:51 -0400 Subject: Make --track-display-file show SOURCE path to track This also means we're keeping track of the source item of items, which is sure to be useful more later. --- src/play.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/play.js') diff --git a/src/play.js b/src/play.js index a3481ea..d5df591 100755 --- a/src/play.js +++ b/src/play.js @@ -158,7 +158,9 @@ async function main(args) { // ..And finally, we have to update the playlist format again, since // processSmartPlaylist might have added new (un-updated) items: - const finalPlaylist = updatePlaylistFormat(processedPlaylist) + const finalPlaylist = updatePlaylistFormat(processedPlaylist, true) + // We also pass true so that the playlist-format-updater knows that this + // is the source playlist. sourcePlaylist = finalPlaylist -- cgit 1.3.0-6-gf8a5 From 820b7940db2f1d533848f024fbb1d6f4841ce598 Mon Sep 17 00:00:00 2001 From: Florrie Date: Sun, 18 Feb 2018 19:31:30 -0400 Subject: Move downloadPlaylistFromOptionValue into general-util --- src/play.js | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) (limited to 'src/play.js') diff --git a/src/play.js b/src/play.js index d5df591..158c887 100755 --- a/src/play.js +++ b/src/play.js @@ -19,6 +19,10 @@ const { flattenGrouplike } = require('./playlist-utils') +const { + downloadPlaylistFromOptionValue +} = require('./general-util') + const { compileKeybindings, getComboForCommand, stringifyCombo } = require('./keybinder') @@ -26,23 +30,6 @@ const { 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') } -- cgit 1.3.0-6-gf8a5 From b2ac9246886f72bef8b96cf218ed2d803397dafa Mon Sep 17 00:00:00 2001 From: Florrie Date: Sun, 18 Feb 2018 23:44:07 -0400 Subject: Make completely new filter system See the man page for how it works now. --- src/play.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'src/play.js') diff --git a/src/play.js b/src/play.js index 158c887..462cd88 100755 --- a/src/play.js +++ b/src/play.js @@ -335,16 +335,25 @@ async function main(args) { 'r': util => util.alias('-remove'), 'x': util => util.alias('-remove'), - '-filter': function(util) { - // --filter (alias: -f) - // Filters the playlist so that only tracks with the given property- - // value pair are kept. + '-filter': async function(util) { + // --filter + // Filters the playlist so that only tracks that match the given filter + // are kept. FilterJSON should be a JSON object as described in the + // man page section "filters". - const property = util.nextArg() - const value = util.nextArg() + const filterJSON = util.nextArg() - const p = filterGrouplikeByProperty(activePlaylist, property, value) - activePlaylist = updatePlaylistFormat(p) + let filterObj + try { + filterObj = JSON.parse(filterJSON) + } catch (error) { + console.error('Invalid JSON for filter:', filterJSON) + return + } + + activePlaylist.filters = [filterObj] + activePlaylist = await processSmartPlaylist(activePlaylist) + activePlaylist = updatePlaylistFormat(activePlaylist) }, 'f': util => util.alias('-filter'), -- cgit 1.3.0-6-gf8a5 From 0c3ded953b9d2a5b127d62f2fccdc3b4658f6c7b Mon Sep 17 00:00:00 2001 From: Florrie Date: Thu, 22 Feb 2018 01:40:50 -0400 Subject: Only open default playlist when needed That is, only when no playlist has already been loaded, and the action requires a playilst. Also a todo.txt note. --- src/play.js | 58 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 22 deletions(-) (limited to 'src/play.js') diff --git a/src/play.js b/src/play.js index 462cd88..b049a0d 100755 --- a/src/play.js +++ b/src/play.js @@ -131,6 +131,8 @@ async function main(args) { const importedPlaylist = JSON.parse(playlistText) + hasOpenedPlaylist = true + await loadPlaylist(importedPlaylist) } @@ -185,14 +187,24 @@ async function main(args) { keybindings.unshift(...openedKeybindings) } - function requiresOpenPlaylist() { + let hasOpenedPlaylist = false + + async function requiresOpenPlaylist() { if (activePlaylist === null) { - throw new Error( - "This action requires an open playlist - try --open (file)" - ) + if (hasOpenedPlaylist === false) { + await openDefaultPlaylist() + } else { + throw new Error( + "This action requires an open playlist - try --open (file)" + ) + } } } + function openDefaultPlaylist() { + return openPlaylist('./playlist.json', true) + } + const optionFunctions = { '-help': function(util) { // --help (alias: -h, -?) @@ -229,13 +241,13 @@ async function main(args) { '-playlist-string': util => util.alias('-open-playlist-string'), - '-write-playlist': function(util) { + '-write-playlist': async function(util) { // --write-playlist (alias: --write, -w, --save) // Writes the active playlist to a file. This file can later be used // with --open ; you won't need to stick in all the filtering // options again. - requiresOpenPlaylist() + await requiresOpenPlaylist() const playlistString = JSON.stringify(activePlaylist, null, 2) const file = util.nextArg() @@ -259,11 +271,11 @@ async function main(args) { 'w': util => util.alias('-write-playlist'), '-save': util => util.alias('-write-playlist'), - '-print-playlist': function(util) { + '-print-playlist': async function(util) { // --print-playlist (alias: --log-playlist, --json) // Prints out the JSON representation of the active playlist. - requiresOpenPlaylist() + await requiresOpenPlaylist() console.log(JSON.stringify(activePlaylist, null, 2)) @@ -290,26 +302,26 @@ async function main(args) { await openKeybindings(util.nextArg(), false) }, - '-clear': function(util) { + '-clear': async function(util) { // --clear (alias: -c) // Clears the active playlist. This does not affect the source // playlist. - requiresOpenPlaylist() + await requiresOpenPlaylist() activePlaylist.items = [] }, 'c': util => util.alias('-clear'), - '-keep': function(util) { + '-keep': async function(util) { // --keep (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() + await requiresOpenPlaylist() const pathString = util.nextArg() const group = filterPlaylistByPathString(sourcePlaylist, pathString) @@ -321,11 +333,11 @@ async function main(args) { 'k': util => util.alias('-keep'), - '-remove': function(util) { + '-remove': async function(util) { // --remove (alias: -r, -x) // Filters the playlist so that the given path is removed. - requiresOpenPlaylist() + await requiresOpenPlaylist() const pathString = util.nextArg() console.log("Ignoring path: " + pathString) @@ -358,24 +370,24 @@ async function main(args) { 'f': util => util.alias('-filter'), - '-collapse-groups': function() { + '-collapse-groups': async function() { // --collapse-groups (alias: --collapse) // Collapses groups in the active playlist so that there is only one // level of sub-groups. Handy for shuffling the order groups play in; // try `--collapse-groups --sort shuffle-groups`. - requiresOpenPlaylist() + await requiresOpenPlaylist() activePlaylist = updatePlaylistFormat(collapseGrouplike(activePlaylist)) }, '-collapse': util => util.alias('-collapse-groups'), - '-list-groups': function(util) { + '-list-groups': async function(util) { // --list-groups (alias: -l, --list) // Lists all groups in the playlist. - requiresOpenPlaylist() + await requiresOpenPlaylist() console.log(getPlaylistTreeString(activePlaylist)) @@ -390,11 +402,11 @@ async function main(args) { '-list': util => util.alias('-list-groups'), 'l': util => util.alias('-list-groups'), - '-list-all': function(util) { + '-list-all': async function(util) { // --list-all (alias: --list-tracks, -L) // Lists all groups and tracks in the playlist. - requiresOpenPlaylist() + await requiresOpenPlaylist() console.log(getPlaylistTreeString(activePlaylist, true)) @@ -615,10 +627,12 @@ async function main(args) { '-trust': util => util.alias('-trust-shell-commands') } - await openPlaylist('./playlist.json', true) - await processArgv(args, optionFunctions) + if (!hasOpenedPlaylist) { + await openDefaultPlaylist() + } + if (activePlaylist === null) { console.error( "Cannot play - no open playlist. Try --open ?" -- cgit 1.3.0-6-gf8a5 From ee1fb8297bbd71faa51985556232bb75cae11274 Mon Sep 17 00:00:00 2001 From: Florrie Date: Thu, 22 Feb 2018 01:46:05 -0400 Subject: Add comment explaining what hasOpenedPlaylist does I can totally see myself forgetting what on earth the variable exists for without this documentation. --- src/play.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/play.js') diff --git a/src/play.js b/src/play.js index b049a0d..d54004a 100755 --- a/src/play.js +++ b/src/play.js @@ -93,6 +93,16 @@ async function main(args) { // The file to output the playlist path to the current file. let trackDisplayFile + // Whether or not a playlist has been opened yet. This is just used to + // decide when exactly to load the default playlist. (We don't want to load + // it as soon as the process starts, since there might be an --open-playlist + // option that specifies opening a *different* playlist! But if we encounter + // an action that requires a playlist, and no playlist has yet been opened, + // we assume that the user probably wants to do something with the default + // playlist, and that's when we open it. See requiresOpenPlaylist for the + // implementation of this.) + let hasOpenedPlaylist = false + const keybindings = [ [['space'], 'togglePause'], [['left'], 'seek', -5], @@ -187,8 +197,6 @@ async function main(args) { keybindings.unshift(...openedKeybindings) } - let hasOpenedPlaylist = false - async function requiresOpenPlaylist() { if (activePlaylist === null) { if (hasOpenedPlaylist === false) { -- cgit 1.3.0-6-gf8a5 From c48e8e5e6f20e056c34996a49628777050454c1b Mon Sep 17 00:00:00 2001 From: Florrie Date: Mon, 26 Feb 2018 10:18:41 -0400 Subject: Add fancy duration graph utility --- src/play.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/play.js') diff --git a/src/play.js b/src/play.js index d54004a..28fd2c7 100755 --- a/src/play.js +++ b/src/play.js @@ -391,6 +391,18 @@ async function main(args) { '-collapse': util => util.alias('-collapse-groups'), + '-flatten-tracks': async function() { + // --flatten-tracks (alias: --flatten) + // Flattens the entire active playlist, so that only tracks remain, + // and there are no groups. + + await requiresOpenPlaylist() + + activePlaylist = updatePlaylistFormat(flattenGrouplike(activePlaylist)) + }, + + '-flatten': util => util.alias('-flatten-tracks'), + '-list-groups': async function(util) { // --list-groups (alias: -l, --list) // Lists all groups in the playlist. -- cgit 1.3.0-6-gf8a5 From 7c7d32d0136092d06f9747cc44c5a37bcc9832b8 Mon Sep 17 00:00:00 2001 From: Florrie Date: Wed, 7 Mar 2018 19:15:26 -0400 Subject: Make (t) key only show information about one track The showTrackInfo keybinding command can now take a number of next/ previous tracks to show. --- src/play.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/play.js') diff --git a/src/play.js b/src/play.js index 28fd2c7..0b521b4 100755 --- a/src/play.js +++ b/src/play.js @@ -114,7 +114,7 @@ async function main(args) { [['delete'], 'skipUpNext'], [['s'], 'skipAhead'], [['S'], 'skipAhead'], [['i'], 'showTrackInfo'], [['I'], 'showTrackInfo'], - [['t'], 'showTrackInfo'], [['T'], 'showTrackInfo'], + [['t'], 'showTrackInfo', 0, 0], [['T'], 'showTrackInfo', 0, 0], [['q'], 'quit'], [['Q'], 'quit'] ] @@ -788,10 +788,9 @@ async function main(args) { }) }, - // TODO: Number of history/up-next tracks to show. - 'showTrackInfo': function() { + 'showTrackInfo': function(previousTrackCount = 3, upNextTrackCount = undefined) { clearConsoleLine() - playController.logTrackInfo() + playController.logTrackInfo(previousTrackCount, upNextTrackCount) }, 'runShellCommand': async function(command, args) { -- cgit 1.3.0-6-gf8a5 From 033109a6bf959541e6855abe613dc29c4cec4bbc Mon Sep 17 00:00:00 2001 From: Florrie Date: Wed, 14 Mar 2018 14:07:03 -0300 Subject: Add alphabetic sort This automatically flattens the playlist, so you don't need to pass --flatten-tracks if you also pass --sort a-z. Unfortunately this means there's no particularly convenient way to sort groups alphabetically. --- src/play.js | 1 + 1 file changed, 1 insertion(+) (limited to 'src/play.js') diff --git a/src/play.js b/src/play.js index 0b521b4..cccb89e 100755 --- a/src/play.js +++ b/src/play.js @@ -115,6 +115,7 @@ async function main(args) { [['s'], 'skipAhead'], [['S'], 'skipAhead'], [['i'], 'showTrackInfo'], [['I'], 'showTrackInfo'], [['t'], 'showTrackInfo', 0, 0], [['T'], 'showTrackInfo', 0, 0], + [['%'], 'showTrackInfo', 20, 0], [['q'], 'quit'], [['Q'], 'quit'] ] -- cgit 1.3.0-6-gf8a5 From 7831b28be25aae1e890ee1f4d3bd6969023c10da Mon Sep 17 00:00:00 2001 From: Florrie Date: Wed, 14 Mar 2018 14:11:15 -0300 Subject: (play) Add -S as alias --sort-mode --- src/play.js | 1 + 1 file changed, 1 insertion(+) (limited to 'src/play.js') diff --git a/src/play.js b/src/play.js index cccb89e..30a151e 100755 --- a/src/play.js +++ b/src/play.js @@ -482,6 +482,7 @@ async function main(args) { }, '-sort': util => util.alias('-sort-mode'), + 'S': util => util.alias('-sort-mode'), '-shuffle-seed': function(util) { // --shuffle-seed (alias: --seed) -- cgit 1.3.0-6-gf8a5