1package prog 2 3import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/ambientsound/visp/api" 9 "github.com/ambientsound/visp/clipboard" 10 "github.com/ambientsound/visp/db" 11 "github.com/ambientsound/visp/input/keys" 12 "github.com/ambientsound/visp/list" 13 "github.com/ambientsound/visp/log" 14 "github.com/ambientsound/visp/multibar" 15 "github.com/ambientsound/visp/options" 16 "github.com/ambientsound/visp/player" 17 "github.com/ambientsound/visp/spotify/library" 18 "github.com/ambientsound/visp/spotify/proxyclient" 19 "github.com/ambientsound/visp/spotify/tracklist" 20 "github.com/ambientsound/visp/style" 21 "github.com/ambientsound/visp/topbar" 22 "github.com/zmb3/spotify" 23 "golang.org/x/oauth2" 24) 25 26func (v *Visp) Authenticate(token *oauth2.Token) error { 27 log.Infof("Configured Spotify access token, expires at %s", token.Expiry.Format(time.RFC1123)) 28 29 cli := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(token)) 30 scli := spotify.NewClient(cli) 31 32 v.client = &scli 33 34 next := spotify_proxyclient.TokenTTL(token) 35 36 v.tokenRefresh = time.After(next) 37 38 v.Changed(api.ChangePlayerStateInvalid, nil) 39 40 err := v.Tokencache.Write(*token) 41 if err != nil { 42 return fmt.Errorf("write Spotify token to file: %s", err) 43 } 44 45 return nil 46} 47 48func (v *Visp) Clipboards() *clipboard.List { 49 return v.clipboards 50} 51 52func (v *Visp) Db() *db.List { 53 return v.db 54} 55 56func (v *Visp) Exec(command string) error { 57 log.Debugf("Run command: %s", command) 58 return v.interpreter.Exec(command) 59} 60 61func (v *Visp) Library() *spotify_library.List { 62 return v.library 63} 64 65func (v *Visp) List() list.List { 66 return v.list 67} 68 69func (v *Visp) Changed(change api.ChangeType, data interface{}) { 70 switch change { 71 case api.ChangeList: 72 lst, ok := data.(list.List) 73 if !ok { 74 log.Debugf("BUG: list was changed, but is '%T', not 'list.List'", data) 75 return 76 } 77 v.db.Cache(lst) 78 v.clipboards.Update(lst) 79 // TODO: playlists indexes 80 81 case api.ChangeOption: 82 s, ok := data.(string) 83 if !ok { 84 log.Debugf("BUG: option '#v' changed, but is not of type 'string'", data) 85 return 86 } 87 v.optionChanged(s) 88 89 case api.ChangePlayerStateInvalid: 90 v.player.Invalidate() 91 v.ticker.Reset(changePlayerStateDelay) 92 93 case api.ChangeDevice: 94 v.player.Invalidate() 95 v.ticker.Reset(changePlayerStateDelay) 96 // TODO: refresh devices window 97 98 } 99} 100 101func (v *Visp) optionChanged(key string) { 102 switch key { 103 case options.LogFile: 104 logFile := options.GetString(options.LogFile) 105 overwrite := options.GetBool(options.LogOverwrite) 106 if len(logFile) == 0 { 107 break 108 } 109 err := log.Configure(logFile, overwrite) 110 if err != nil { 111 log.Errorf("log configuration: %s", err) 112 break 113 } 114 log.Infof("Note: log file will be backfilled with existing log") 115 log.Infof("Writing debug log to %s", logFile) 116 117 case options.Topbar: 118 config := options.GetString(options.Topbar) 119 matrix, err := topbar.Parse(v, config) 120 if err == nil { 121 v.Termui.Widgets.Topbar.SetMatrix(matrix) 122 v.Termui.Resize() 123 } else { 124 log.Errorf("topbar configuration: %s", err) 125 } 126 127 case options.ExpandColumns: 128 // Re-render columns 129 v.UI().TableWidget().SetColumns(v.UI().TableWidget().ColumnNames()) 130 } 131} 132 133func (v *Visp) PlayerStatus() player.State { 134 return *v.player 135} 136 137func (v *Visp) Quit() { 138 v.quit <- new(interface{}) 139} 140 141func (v *Visp) Sequencer() *keys.Sequencer { 142 return v.sequencer 143} 144 145func (v *Visp) Multibar() *multibar.Multibar { 146 return v.multibar 147} 148 149func (v *Visp) History() *spotify_tracklist.List { 150 if v.history == nil { 151 v.history = spotify_tracklist.NewHistory() 152 } 153 return v.history 154} 155 156func (v *Visp) SetList(lst list.List) { 157 if lst == nil { 158 return 159 } 160 cur := v.db.Current() 161 if cur != nil && cur != lst && cur != v.db && cur != v.clipboards { 162 log.Debugf("Setting last used list to '%s'", cur.Name()) 163 v.db.SetLast(v.db.Current()) 164 } 165 c := v.db.Cache(lst) 166 v.db.SetCursor(c) 167 v.list = lst 168 v.Termui.TableWidget().SetList(lst) 169} 170 171func (v *Visp) Spotify() (*spotify.Client, error) { 172 if v.client == nil { 173 return nil, fmt.Errorf("please authenticate with Spotify at: %s/authorize", options.GetString("spotifyauthserver")) 174 } 175 token, err := v.client.Token() 176 if err != nil { 177 return nil, fmt.Errorf("unable to refresh Spotify token: %s", err) 178 } 179 _ = v.Tokencache.Write(*token) 180 return v.client, nil 181} 182 183func (v *Visp) Styles() style.Stylesheet { 184 return v.stylesheet 185} 186 187func (v *Visp) Tracklist() *spotify_tracklist.List { 188 switch v := v.List().(type) { 189 case *spotify_tracklist.List: 190 return v 191 default: 192 return nil 193 } 194} 195 196func (v *Visp) UI() api.UI { 197 return v.Termui 198} 199