« 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--backend.js9
-rw-r--r--todo.txt41
-rw-r--r--ui.js87
3 files changed, 117 insertions, 20 deletions
diff --git a/backend.js b/backend.js
index 36344be..59c4a48 100644
--- a/backend.js
+++ b/backend.js
@@ -338,9 +338,11 @@ class QueuePlayer extends EventEmitter {
     }
   }
 
-  shuffleQueue() {
+  shuffleQueue(pastPlayingTrackOnly = true) {
     const queue = this.queueGrouplike
-    const index = queue.items.indexOf(this.playingTrack) + 1 // This is 0 if no track is playing
+    const index = (pastPlayingTrackOnly
+      ? queue.items.indexOf(this.playingTrack) + 1 // This is 0 if no track is playing
+      : 0)
     const initialItems = queue.items.slice(0, index)
     const remainingItems = queue.items.slice(index)
     const newItems = initialItems.concat(shuffleArray(remainingItems))
@@ -517,8 +519,7 @@ class QueuePlayer extends EventEmitter {
         this.playFirst()
         return true
       case 'shuffle':
-        this.clearPlayingTrack()
-        this.shuffleQueue()
+        this.shuffleQueue(false)
         this.playFirst()
         return true
       case 'end':
diff --git a/todo.txt b/todo.txt
index a6ce5ef..b10c614 100644
--- a/todo.txt
+++ b/todo.txt
@@ -649,3 +649,44 @@ TODO: The "From: <downloaderArg>" text in the playback info element *does* cut
 
 TODO: "Play later" has a slight chance of keeping the track in the same place,
       which is accentuated when there's only a couple tracks left in the queue.
+
+TODO: "Loop mode" should be an option under the Queue menu, not Playback.
+      (Done!)
+
+TODO: "Loop mode" setting should be displayed in the queue's length label!
+      Probably on the same line as ex. "2 / 3", and only when the currently
+      playing track is selected.
+      (Done!)
+
+TODO: "Clear past current" and "clear up to current" should probably be visible
+      from the Queue menu!
+
+TODO: The queue length lebel is kinda busy, and doesn't fit everything so well
+      on thinner screens. That should get checked out!
+      (Done!)
+
+TODO: When the last track in the queue finishes playing and the queue is set to
+      shuffle, the currently selected index in the queue listing won't be moved
+      to the new first track (so, reset to zero). The cursor just ends up on
+      whatever track had been the last in the queue (which is obviously now in
+      some random location - even possibly the first track, but usually not).
+      I have a feeling this is the result of shuffling first - which updates
+      the selected index to go to wherever the last track ended up - and then
+      playing the first track, but not moving the cursor back to the start
+      because it's apparently not at the end anymore. But I could be totally
+      misremembering how this code works. :P --- Nope not even related LOL.
+      Good guess though! We don't even have to worry about that situation, with
+      the way selecting the new playing track works. It checks against the
+      track which *was* playing... but that was getting cleared to make the
+      shuffle work properly (applying to the whole queue instead of just the
+      stuff past the current track, which is nothing when you're at its end).
+      Now we just use a flag to ignore the current playback position. Since the
+      currently playing track is retained for the 'playing track' event, the
+      existing code does the rest of the work and selects the newly playing
+      track (whatever's been shuffled to the start) all on its own!
+      (Done!)
+
+TODO: Apparently pressing any key while the UI is booting up will make the
+      screen totally black and unresponsive (and apparently inactive) until the
+      screen is resized. I think we're interrupting a control sequence somehow,
+      and that isn't being handled very well?
diff --git a/ui.js b/ui.js
index 18652f5..dc9dab9 100644
--- a/ui.js
+++ b/ui.js
@@ -351,7 +351,6 @@ class AppElement extends FocusElement {
         return [
           {label: playingTrack ? `("${playingTrack.name}")` : '(No track playing.)'},
           {divider: true},
-          {element: this.loopModeControl},
           {element: this.volumeSlider},
           {divider: true},
           playingTrack && {element: this.playingControl},
@@ -373,6 +372,8 @@ class AppElement extends FocusElement {
         return [
           {label: `(Queue - ${curIndex >= 0 ? `${curIndex + 1}/` : ''}${items.length} items.)`},
           {divider: true},
+          {element: this.loopModeControl},
+          {divider: true},
           items.length && {label: 'Shuffle', action: () => this.shuffleQueue()},
           items.length && {label: 'Clear', action: () => this.clearQueue()}
         ]
@@ -406,7 +407,7 @@ class AppElement extends FocusElement {
       getEnabled: () => this.config.canControlPlayback
     })
 
-    this.loopModeControl = new InlineListPickerElement('Loop mode', [
+    this.loopModeControl = new InlineListPickerElement('Loop queue?', [
       {value: 'end', label: 'Don\'t loop'},
       {value: 'loop', label: 'Loop (same order)'},
       {value: 'shuffle', label: 'Loop (shuffle)'}
@@ -1973,7 +1974,7 @@ class AppElement extends FocusElement {
       return
     }
 
-    const { playingTrack, timeData } = this.SQP
+    const { playingTrack, timeData, queueEndMode } = this.SQP
     const { items } = this.SQP.queueGrouplike
     const {
       currentInput: currentInput,
@@ -2073,25 +2074,34 @@ class AppElement extends FocusElement {
     const { duration: durationString } = getTimeStringsFromSec(0, durationTotal)
     this.queueTimeLabel.text = `(${durationSymbol + durationString + approxSymbol})`
 
-    let collapseExtraInfo = false
     if (playingTrack) {
       let trackPart
+      let trackPartShort
+      let trackPartReallyShort
 
       {
         const distance = Math.abs(selectedIndex - playingIndex)
 
         let insertString
+        let insertStringShort
         if (selectedIndex < playingIndex) {
           insertString = ` (-${distance})`
-          collapseExtraInfo = true
+          insertStringShort = `-${distance}`
         } else if (selectedIndex > playingIndex) {
           insertString = ` (+${distance})`
-          collapseExtraInfo = true
+          insertStringShort = `+${distance}`
         } else {
           insertString = ''
+          insertStringShort = ''
         }
 
         trackPart = `${playingIndex + 1 + insertString} / ${items.length}`
+        trackPartShort = (insertString
+          ? `${playingIndex + 1 + insertStringShort}/${items.length}`
+          : `${playingIndex + 1}/${items.length}`)
+        trackPartReallyShort = (insertString
+          ? insertStringShort
+          : `#${playingIndex + 1}`)
       }
 
       let timestampPart
@@ -2106,10 +2116,8 @@ class AppElement extends FocusElement {
         let insertString
         if (selectedTimestampIndex < playingTimestampIndex) {
           insertString = ` (-${distance})`
-          collapseExtraInfo = true
         } else if (selectedTimestampIndex > playingTimestampIndex) {
           insertString = ` (+${distance})`
-          collapseExtraInfo = true
         } else {
           insertString = ''
         }
@@ -2117,19 +2125,66 @@ class AppElement extends FocusElement {
         timestampPart = `${playingTimestampIndex + 1 + insertString} / ${timestampData.length}`
       }
 
-      if (timestampPart) {
-        this.queueLengthLabel.text = `(${this.SQP.playSymbol} ${trackPart} : ${timestampPart})`
-      } else {
-        this.queueLengthLabel.text = `(${this.SQP.playSymbol} ${trackPart})`
+      let queueLoopPart
+      let queueLoopPartShort
+
+      if (selectedIndex === playingIndex) {
+        switch (queueEndMode) {
+          case 'loop':
+            queueLoopPart = 'Repeat'
+            queueLoopPartShort = 'R'
+            break
+          case 'shuffle':
+            queueLoopPart = 'Shuffle'
+            queueLoopPartShort = 'S'
+            break
+          case 'end':
+          default:
+            break
+        }
+      }
+
+      let partsTogether
+
+      const all = () => `(${this.SQP.playSymbol} ${partsTogether})`
+      const tooWide = () => all().length > this.queuePane.contentW
+
+      // goto irl
+      determineParts: {
+        if (timestampPart) {
+          if (queueLoopPart) {
+            partsTogether = `${trackPart} : ${timestampPart} »${queueLoopPartShort}`
+          } else {
+            partsTogether = `(${this.SQP.playSymbol} ${trackPart} : ${timestampPart})`
+          }
+          break determineParts
+        }
+
+        if (queueLoopPart) includeQueueLoop: {
+          partsTogether = `${trackPart} » ${queueLoopPart}`
+          if (tooWide()) {
+            partsTogether = `${trackPart} »${queueLoopPartShort}`
+            if (tooWide()) {
+              break includeQueueLoop
+            }
+          }
+          break determineParts
+        }
+
+        partsTogether = trackPart
+        if (tooWide()) {
+          partsTogether = trackPartShort
+          if (tooWide()) {
+            partsTogether = trackPartReallyShort
+          }
+        }
       }
+
+      this.queueLengthLabel.text = all()
     } else {
       this.queueLengthLabel.text = `(${items.length})`
     }
 
-    if (this.SQP.loopQueueAtEnd) {
-      this.queueLengthLabel.text += (collapseExtraInfo ? ` [L${unic.ELLIPSIS}]` : ` [Looping]`)
-    }
-
     // Layout stuff to position the length and time labels correctly.
     this.queueLengthLabel.centerInParent()
     this.queueTimeLabel.centerInParent()