mirror of
https://github.com/taigrr/wtf
synced 2025-01-18 04:03:14 -08:00
Define help with keys This means that keys and help are automatically in sync This means that you can't define keys, but forget help This unfortunately also means that formatting may not be quite as good
180 lines
5.3 KiB
Go
180 lines
5.3 KiB
Go
package spotifyweb
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/rivo/tview"
|
|
"github.com/wtfutil/wtf/logger"
|
|
"github.com/wtfutil/wtf/wtf"
|
|
"github.com/zmb3/spotify"
|
|
)
|
|
|
|
// Info is the struct that contains all the information the Spotify player displays to the user
|
|
type Info struct {
|
|
Artists string
|
|
Title string
|
|
Album string
|
|
TrackNumber int
|
|
Status string
|
|
}
|
|
|
|
// Widget is the struct used by all WTF widgets to transfer to the main widget controller
|
|
type Widget struct {
|
|
wtf.KeyboardWidget
|
|
wtf.TextWidget
|
|
|
|
Info
|
|
|
|
client *spotify.Client
|
|
clientChan chan *spotify.Client
|
|
playerState *spotify.PlayerState
|
|
settings *Settings
|
|
}
|
|
|
|
var (
|
|
auth spotify.Authenticator
|
|
tempClientChan = make(chan *spotify.Client)
|
|
state = "wtfSpotifyWebStateString"
|
|
authURL string
|
|
callbackPort string
|
|
redirectURI string
|
|
)
|
|
|
|
func authHandler(w http.ResponseWriter, r *http.Request) {
|
|
logger.Log("[SpotifyWeb] Got an authentication hit!")
|
|
tok, err := auth.Token(state, r)
|
|
if err != nil {
|
|
http.Error(w, "Couldn't get token", http.StatusForbidden)
|
|
logger.Log(err.Error())
|
|
}
|
|
if st := r.FormValue("state"); st != state {
|
|
http.NotFound(w, r)
|
|
logger.Log(fmt.Sprintf("State mismatch: %s != %s\n", st, state))
|
|
}
|
|
// use the token to get an authenticated client
|
|
client := auth.NewClient(tok)
|
|
fmt.Fprintf(w, "Login Completed!")
|
|
tempClientChan <- &client
|
|
}
|
|
|
|
// NewWidget creates a new widget for WTF
|
|
func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *Widget {
|
|
redirectURI = "http://localhost:" + settings.callbackPort + "/callback"
|
|
|
|
auth = spotify.NewAuthenticator(redirectURI, spotify.ScopeUserReadCurrentlyPlaying, spotify.ScopeUserReadPlaybackState, spotify.ScopeUserModifyPlaybackState)
|
|
auth.SetAuthInfo(settings.clientID, settings.secretKey)
|
|
authURL = auth.AuthURL(state)
|
|
|
|
var client *spotify.Client
|
|
var playerState *spotify.PlayerState
|
|
|
|
widget := Widget{
|
|
KeyboardWidget: wtf.NewKeyboardWidget(app, pages, settings.common),
|
|
TextWidget: wtf.NewTextWidget(app, settings.common, true),
|
|
|
|
Info: Info{},
|
|
|
|
client: client,
|
|
clientChan: tempClientChan,
|
|
playerState: playerState,
|
|
settings: settings,
|
|
}
|
|
|
|
http.HandleFunc("/callback", authHandler)
|
|
go http.ListenAndServe(":"+callbackPort, nil)
|
|
|
|
go func() {
|
|
// wait for auth to complete
|
|
logger.Log("[SpotifyWeb] Waiting for authentication... URL: " + authURL)
|
|
client = <-tempClientChan
|
|
|
|
// use the client to make calls that require authorization
|
|
_, err := client.CurrentUser()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
playerState, err = client.PlayerState()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
logger.Log("[SpotifyWeb] Authentication complete.")
|
|
widget.client = client
|
|
widget.playerState = playerState
|
|
widget.Refresh()
|
|
}()
|
|
|
|
// While I wish I could find the reason this doesn't work, I can't.
|
|
//
|
|
// Normally, this should open the URL to the browser, however it opens the Explorer window in Windows.
|
|
// This mostly likely has to do with the fact that the URL includes some very special characters that no terminal likes.
|
|
// The only solution would be to include quotes in the command, which is why I do here, but it doesn't work.
|
|
//
|
|
// If inconvenient, I'll remove this option and save the URL in a file or some other method.
|
|
wtf.OpenFile(`"` + authURL + `"`)
|
|
|
|
widget.settings.common.RefreshInterval = 5
|
|
|
|
widget.initializeKeyboardControls()
|
|
widget.View.SetInputCapture(widget.InputCapture)
|
|
|
|
widget.View.SetWrap(true)
|
|
widget.View.SetWordWrap(true)
|
|
widget.View.SetTitle("[green]Spotify Web[white]")
|
|
|
|
widget.KeyboardWidget.SetView(widget.View)
|
|
|
|
return &widget
|
|
}
|
|
|
|
func (w *Widget) refreshSpotifyInfos() error {
|
|
if w.client == nil || w.playerState == nil {
|
|
return errors.New("Authentication failed! Please log in to Spotify by visiting the following page in your browser: " + authURL)
|
|
}
|
|
var err error
|
|
w.playerState, err = w.client.PlayerState()
|
|
if err != nil {
|
|
return errors.New("Extracting player state failed! Please refresh or restart WTF")
|
|
}
|
|
w.Info.Album = fmt.Sprint(w.playerState.CurrentlyPlaying.Item.Album.Name)
|
|
artists := ""
|
|
for _, artist := range w.playerState.CurrentlyPlaying.Item.Artists {
|
|
artists += artist.Name + ", "
|
|
}
|
|
artists = artists[:len(artists)-2]
|
|
w.Info.Artists = artists
|
|
w.Info.Title = fmt.Sprint(w.playerState.CurrentlyPlaying.Item.Name)
|
|
w.Info.TrackNumber = w.playerState.CurrentlyPlaying.Item.TrackNumber
|
|
if w.playerState.CurrentlyPlaying.Playing {
|
|
w.Info.Status = "Playing"
|
|
} else {
|
|
w.Info.Status = "Paused"
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Refresh refreshes the current view of the widget
|
|
func (w *Widget) Refresh() {
|
|
err := w.refreshSpotifyInfos()
|
|
if err != nil {
|
|
w.Redraw(w.CommonSettings.Title, err.Error(), true)
|
|
} else {
|
|
w.Redraw(w.CommonSettings.Title, w.createOutput(), false)
|
|
}
|
|
}
|
|
|
|
func (w *Widget) createOutput() string {
|
|
output := wtf.CenterText(fmt.Sprintf("[green]Now %v [white]\n", w.Info.Status), w.Width())
|
|
output += wtf.CenterText(fmt.Sprintf("[green]Title:[white] %v\n", w.Info.Title), w.Width())
|
|
output += wtf.CenterText(fmt.Sprintf("[green]Artist:[white] %v\n", w.Info.Artists), w.Width())
|
|
output += wtf.CenterText(fmt.Sprintf("[green]Album:[white] %v\n", w.Info.Album), w.Width())
|
|
if w.playerState.ShuffleState {
|
|
output += wtf.CenterText(fmt.Sprintf("[green]Shuffle:[white] on\n"), w.Width())
|
|
} else {
|
|
output += wtf.CenterText(fmt.Sprintf("[green]Shuffle:[white] off\n"), w.Width())
|
|
}
|
|
return output
|
|
}
|