spogo/internal/cli/render.go
StandardGage c82902fce0 feat: add shuffle option to play command and enhance playback functionality
- Introduced a `--shuffle` flag for the `play` command to enable shuffle before playing.
- Updated playback logic to handle context URIs for playlists and tracks.
- Enhanced error handling and added support for library tracks and albums in the Connect client.
- Improved rendering of playlist information in the CLI output.
- Added tests for new playback features and context URI handling.
2026-03-08 04:10:53 +00:00

103 lines
3.3 KiB
Go

package cli
import (
"fmt"
"strings"
"time"
"github.com/steipete/spogo/internal/output"
"github.com/steipete/spogo/internal/spotify"
)
func renderItems(w *output.Writer, items []spotify.Item) (plain []string, human []string) {
plain = make([]string, 0, len(items))
human = make([]string, 0, len(items))
for _, item := range items {
plain = append(plain, itemPlain(item))
human = append(human, itemHuman(w, item))
}
return plain, human
}
func itemPlain(item spotify.Item) string {
switch item.Type {
case "track":
return fmt.Sprintf("track\t%s\t%s\t%s\t%s\t%s", item.ID, item.Name, strings.Join(item.Artists, ", "), item.Album, item.URI)
case "album":
return fmt.Sprintf("album\t%s\t%s\t%s\t%s\t%d", item.ID, item.Name, strings.Join(item.Artists, ", "), item.ReleaseDate, item.TotalTracks)
case "artist":
return fmt.Sprintf("artist\t%s\t%s\t%d", item.ID, item.Name, item.Followers)
case "playlist":
return fmt.Sprintf("playlist\t%s\t%s\t%s\t%d", item.ID, item.Name, item.Owner, item.TotalTracks)
case "show":
return fmt.Sprintf("show\t%s\t%s\t%s\t%d", item.ID, item.Name, item.Publisher, item.TotalEpisodes)
case "episode":
return fmt.Sprintf("episode\t%s\t%s\t%d", item.ID, item.Name, item.DurationMS)
default:
return fmt.Sprintf("item\t%s\t%s\t%s", item.ID, item.Name, item.URI)
}
}
func itemHuman(w *output.Writer, item spotify.Item) string {
accent := w.Theme.Accent
muted := w.Theme.Muted
switch item.Type {
case "track":
return fmt.Sprintf("%s — %s %s", accent(item.Name), strings.Join(item.Artists, ", "), muted("· "+item.Album))
case "album":
return fmt.Sprintf("%s — %s %s", accent(item.Name), strings.Join(item.Artists, ", "), muted("· "+item.ReleaseDate))
case "artist":
return fmt.Sprintf("%s %s", accent(item.Name), muted(fmt.Sprintf("· %d followers", item.Followers)))
case "playlist":
if item.TotalTracks > 0 {
return fmt.Sprintf("%s — %s %s", accent(item.Name), item.Owner, muted(fmt.Sprintf("· %d tracks", item.TotalTracks)))
}
return fmt.Sprintf("%s — %s", accent(item.Name), item.Owner)
case "show":
return fmt.Sprintf("%s — %s %s", accent(item.Name), item.Publisher, muted(fmt.Sprintf("· %d episodes", item.TotalEpisodes)))
case "episode":
return fmt.Sprintf("%s %s", accent(item.Name), muted(fmt.Sprintf("· %s", humanDuration(item.DurationMS))))
default:
return accent(item.Name)
}
}
func humanDuration(ms int) string {
if ms <= 0 {
return "0s"
}
d := time.Duration(ms) * time.Millisecond
h := int(d.Hours())
m := int(d.Minutes()) % 60
s := int(d.Seconds()) % 60
if h > 0 {
return fmt.Sprintf("%dh%02dm%02ds", h, m, s)
}
if m > 0 {
return fmt.Sprintf("%dm%02ds", m, s)
}
return fmt.Sprintf("%ds", s)
}
func playbackPlain(status spotify.PlaybackStatus) string {
track := ""
if status.Item != nil {
track = status.Item.Name
}
return fmt.Sprintf("%t\t%d\t%s\t%s", status.IsPlaying, status.ProgressMS, status.Device.Name, track)
}
func playbackHuman(w *output.Writer, status spotify.PlaybackStatus) string {
accent := w.Theme.Accent
muted := w.Theme.Muted
state := "paused"
if status.IsPlaying {
state = "playing"
}
track := ""
if status.Item != nil {
track = fmt.Sprintf("%s — %s", accent(status.Item.Name), strings.Join(status.Item.Artists, ", "))
}
return fmt.Sprintf("%s %s %s", accent(strings.ToUpper(state)), track, muted("· "+status.Device.Name))
}