« get me outta code hell

mtui - Music Text User Interface - user-friendly command line music player
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--README.md1
-rw-r--r--players.js28
-rw-r--r--todo.txt3
-rw-r--r--ui.js13
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()
   }