package ui import ( "fmt" "strings" "time" "github.com/gdamore/tcell/v2" "github.com/zmb3/spotify/v2" "github.com/aditya-K2/gspt/config" "github.com/aditya-K2/gspt/spt" "github.com/aditya-K2/tview" "github.com/aditya-K2/utils" ) var ( state *spotify.PlayerState ctrackId spotify.ID devices map[string]string playIcons map[bool]string shuffleIcons map[bool]string repeatIcons map[string]string ) // ProgressBar is a two-lined Box. First line is the BarTitle // Second being the actual progress done. // Use SetProgressFunc to provide the callback which provides the Fields each time the ProgressBar will be Drawn. // The progressFunc must return (BarTitle, BarTopTitle, BarText, percentage) respectively type ProgressBar struct { *tview.Box BarTitle string BarText string BarTopTitle string progressFunc func() (BarTitle string, BarTopTitle string, BarText string, percentage float64) } func (self *ProgressBar) SetProgressFunc(pfunc func() (string, string, string, float64)) *ProgressBar { self.progressFunc = pfunc return self } func NewProgressBar() *ProgressBar { return &ProgressBar{ Box: tview.NewBox(), } } func getProgressGlyph(width, percentage float64, btext string) string { q := "[black:white:b]" var a string a += strings.Repeat(" ", int(width)-len(btext)) a = utils.InsertAt(a, btext, int(width/2)-10) a = utils.InsertAt(a, "[-:-:-]", int(width*percentage/100)) q += a return q } func (self *ProgressBar) Draw(screen tcell.Screen) { var ( OFFSET int = 1 ) self.Box.SetBorder(true) self.Box.SetBackgroundColor(tcell.ColorDefault) var percentage float64 self.BarTitle, self.BarTopTitle, self.BarText, percentage = self.progressFunc() self.DrawForSubclass(screen, self.Box) self.Box.SetTitle(self.BarTopTitle) self.Box.SetTitleAlign(tview.AlignRight) x, y, _width, _ := self.Box.GetInnerRect() tview.Print(screen, self.BarTitle, x+OFFSET, y, _width, tview.AlignLeft, tcell.ColorWhite) tview.Print(screen, getProgressGlyph(float64(_width-OFFSET-1), percentage, self.BarText), x, y+2, _width-OFFSET, tview.AlignRight, tcell.ColorWhite) } func (self *ProgressBar) RefreshState() { RefreshProgress(false) } func RefreshProgress(force bool) { s, err := spt.GetPlayerState() if err != nil { SendNotification(err.Error()) return } state = s // Reset the "cached" if nothing is playing (TODO: better name)[ if s.Item == nil { ctrackId = "" } if coverArt != nil { // If No Item is playing if (state.Item == nil) || // An Item is Playing but doesn't match the cached Track ID (state.Item != nil && state.Item.ID != ctrackId) || // Forced Redrawing force { if state.Item != nil { ctrackId = state.Item.ID } if cfg.Image == config.ImageShow { coverArt.RefreshState() } } } } func refreshProgressLocal() { if state != nil { if state.Item != nil && state.Playing { if state.Item.Duration-state.Progress >= 1000 { state.Progress += 1000 } else { state.Progress += state.Item.Duration - state.Progress go func() { RefreshProgress(false) }() } } } } func progressRoutine() { RefreshProgress(false) go func() { localTicker := time.NewTicker(time.Second) spotifyTicker := time.NewTicker(time.Second * 5) for { select { case <-spotifyTicker.C: { RefreshProgress(false) } case <-localTicker.C: { refreshProgressLocal() } } } }() } func deviceIcon(d spotify.PlayerDevice) string { if val, ok := devices[strings.ToLower(d.Type)]; cfg.UseIcons && ok { return val } return "Device:" } func topTitle(playing, shuffle bool, repeat string, device spotify.PlayerDevice) ( playState, deviceIcn, deviceName, shuffleState, repeatState string) { icon := "" playState = "Paused" shuffleState = fmt.Sprintf("%t", shuffle) if playing { playState = "Playing" } if cfg.UseIcons { icon = playIcons[playing] icon += " " repeat = repeatIcons[repeat] shuffleState = shuffleIcons[shuffle] } playState = icon + playState deviceIcn = deviceIcon(device) deviceName = device.Name repeatState = repeat return } func setIcons() { devices = map[string]string{ "computer": cfg.Icons.Computer, "tablet": cfg.Icons.Tablet, "smartphone": cfg.Icons.Smartphone, "speaker": cfg.Icons.Speaker, "tv": cfg.Icons.Tv, "avr": cfg.Icons.Avr, "stb": cfg.Icons.Stb, "audio_dongle": cfg.Icons.AudioDongle, "game_console": cfg.Icons.GameConsole, "cast_video": cfg.Icons.CastVideo, "cast_audio": cfg.Icons.CastAudio, "automobile": cfg.Icons.Automobile, } playIcons = map[bool]string{ true: cfg.Icons.Playing, false: cfg.Icons.Paused, } shuffleIcons = map[bool]string{ true: cfg.Icons.ShuffleOn, false: cfg.Icons.ShuffleOff, } repeatIcons = map[string]string{ "track": cfg.Icons.RepeatOne, "off": cfg.Icons.RepeatAll, "context": cfg.Icons.RepeatOff, } } func progressFunc() (barTitle, barTopTitle, barText string, percentage float64) { percentage = 0 barTitle = " - " barText = "---:---" barTopTitle = "[]" if state != nil { barTopTitle = fmt.Sprintf("[ %s %s %s Shuffle: %s Repeat: %s ]", wrap( topTitle( state.Playing, state.ShuffleState, state.RepeatState, state.Device))...) if state.Item != nil { barTitle = fmt.Sprintf("%s%s[-:-:-] - %s%s", cfg.Colors.PBarTrack.String(), state.Item.Name, cfg.Colors.PBarArtist.String(), artistName(state.Item.Artists)) barText = utils.StrTime(float64(state.Progress/1000)) + "/" + utils.StrTime(float64(state.Item.Duration/1000)) percentage = (float64(state.Progress) / float64(state.Item.Duration)) * 100 } } return }