diff options
-rw-r--r-- | README.md | 1 | ||||
-rw-r--r-- | players.js | 28 | ||||
-rw-r--r-- | todo.txt | 3 | ||||
-rw-r--r-- | ui.js | 13 |
4 files changed, 42 insertions, 3 deletions
diff --git a/README.md b/README.md index 6bd33d3..abd8183 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ You're also welcome to share any ideas, suggestions, and questions through there * <kbd><kbd>Shift</kbd>+<kbd>Down</kbd></kbd> or <kbd>n</kbd> - play next track * <kbd>Space</kbd> - toggle pause * <kbd>Escape</kbd> - stop playing the current track +* <kbd>l</kbd> - toggle track loop * <kbd>Right</kbd> - seek ahead * <kbd>Left</kbd> - seek back * <kbd><kbd>Ctrl</kbd>+<kbd>F</kbd></kbd> or <kbd>/</kbd> - jump to a track or group by entering (part of) its name diff --git a/players.js b/players.js index be5205f..fdcd038 100644 --- a/players.js +++ b/players.js @@ -49,6 +49,7 @@ class Player extends EventEmitter { super() this.disablePlaybackStatus = false + this.isLooping = false } set process(newProcess) { @@ -72,6 +73,7 @@ class Player extends EventEmitter { volUp(amount) {} volDown(amount) {} togglePause() {} + toggleLoop() {} async kill() { if (this.process) { @@ -92,7 +94,11 @@ class Player extends EventEmitter { module.exports.MPVPlayer = class extends Player { getMPVOptions(file) { - return ['--no-video', file] + const opts = ['--no-video', file] + if (this.isLooping) { + opts.unshift('--loop') + } + return opts } playFile(file) { @@ -101,6 +107,8 @@ module.exports.MPVPlayer = class extends Player { this.process = spawn('mpv', this.getMPVOptions(file)) + let lastPercent = 0 + this.process.stderr.on('data', data => { if (this.disablePlaybackStatus) { return @@ -117,6 +125,19 @@ module.exports.MPVPlayer = class extends Player { percent // ###% ] = match.slice(1) + if (lastPercent < parseInt(percent)) { + lastPercent = parseInt(percent) + // mpv forgets commands you sent it whenever it loops, so you + // have to specify them every time it loops. We do that whenever the + // position in the song decreases, since that means it may have + // looped. + if (this.isLooping) { + this.sendCommand('set loop yes') + } else { + this.sendCommand('set loop no') + } + } + this.printStatusLine(getTimeStrings({curHour, curMin, curSec, lenHour, lenMin, lenSec})) } }) @@ -164,6 +185,11 @@ module.exports.ControllableMPVPlayer = class extends module.exports.MPVPlayer { this.sendCommand('cycle pause') } + toggleLoop() { + this.isLooping = !this.isLooping + this.sendCommand('cycle loop') + } + kill() { if (this.fifo) { this.fifo.close() diff --git a/todo.txt b/todo.txt index 3683ecc..dfb14d6 100644 --- a/todo.txt +++ b/todo.txt @@ -188,3 +188,6 @@ TODO: A "play later" option for songs in the queue, identical to distributing randomly the single track; if the track is already playing, it should skip to the next song before shuffling. (Done!) + +TODO: Loop one song! + (Done!) diff --git a/ui.js b/ui.js index 00aa403..4a8f0a4 100644 --- a/ui.js +++ b/ui.js @@ -364,7 +364,7 @@ class AppElement extends FocusElement { this.player.on('printStatusLine', data => { if (this.playingTrack) { - this.playbackInfoElement.updateProgress(data) + this.playbackInfoElement.updateProgress(data, this.player) } }) @@ -407,6 +407,8 @@ class AppElement extends FocusElement { this.seekBack(10) } else if (telc.isSpace(keyBuf)) { this.togglePause() + } else if (telc.isCaselessLetter(keyBuf, 'l')) { + this.toggleLoop() } else if (telc.isEscape(keyBuf)) { this.clearPlayingTrack() } else if (telc.isShiftUp(keyBuf) || telc.isCaselessLetter(keyBuf, 'p')) { @@ -497,6 +499,10 @@ class AppElement extends FocusElement { this.player.togglePause() } + toggleLoop() { + this.player.toggleLoop() + } + stopPlaying() { // We emit this so playTrack doesn't immediately start a new track. // We aren't *actually* about to play a new track. @@ -1652,9 +1658,12 @@ class PlaybackInfoElement extends DisplayElement { } } - updateProgress({timeDone, timeLeft, duration, lenSecTotal, curSecTotal}) { + updateProgress({timeDone, timeLeft, duration, lenSecTotal, curSecTotal}, player) { this.progressBarLabel.text = '-'.repeat(Math.floor(this.w / lenSecTotal * curSecTotal)) this.progressTextLabel.text = timeDone + ' / ' + duration + if (player.isLooping) { + this.progressTextLabel.text += ' [Looping]' + } this.fixLayout() } |