set device, repeat, toggleplay etc'

This commit is contained in:
jjohnstondev 2023-01-08 10:00:00 -08:00
parent 8f5c4bffb8
commit 6e542a030f
5 changed files with 207 additions and 96 deletions

1
go.mod
View File

@ -8,6 +8,7 @@ require (
github.com/charmbracelet/lipgloss v0.6.0 github.com/charmbracelet/lipgloss v0.6.0
github.com/cristalhq/aconfig v0.18.3 github.com/cristalhq/aconfig v0.18.3
github.com/cristalhq/aconfig/aconfigyaml v0.17.1 github.com/cristalhq/aconfig/aconfigyaml v0.17.1
github.com/zmb3/spotify v1.3.0
github.com/zmb3/spotify/v2 v2.3.1 github.com/zmb3/spotify/v2 v2.3.1
golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5 golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5
) )

2
go.sum
View File

@ -171,6 +171,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/zmb3/spotify v1.3.0 h1:6Z2F1IMx0Hviq/dpf8nFwvKPppFEMXn8yfReSBVi16k=
github.com/zmb3/spotify v1.3.0/go.mod h1:GD7AAEMUJVYc2Z7p2a2S0E3/5f/KxM/vOnErNr4j+Tw=
github.com/zmb3/spotify/v2 v2.3.1 h1:aEyIPotROM3JJjHMCImFROgnPIUpzVo8wymYSaPSd9w= github.com/zmb3/spotify/v2 v2.3.1 h1:aEyIPotROM3JJjHMCImFROgnPIUpzVo8wymYSaPSd9w=
github.com/zmb3/spotify/v2 v2.3.1/go.mod h1:+LVh9CafHu7SedyqYmEf12Rd01dIVlEL845yNhksW0E= github.com/zmb3/spotify/v2 v2.3.1/go.mod h1:+LVh9CafHu7SedyqYmEf12Rd01dIVlEL845yNhksW0E=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=

View File

@ -22,18 +22,24 @@ func Run(ctx *gctx.Context, client *spotify.Client, args []string) error {
switch args[0] { switch args[0] {
case "play": case "play":
return commands.Play(ctx, client) return commands.Play(ctx, client)
case "playurl":
return commands.PlayUrl(ctx, client, args)
case "pause": case "pause":
return commands.Pause(ctx, client) return commands.Pause(ctx, client)
case "toggleplay":
return commands.TogglePlay(ctx, client)
case "next":
return commands.Skip(ctx, client)
case "previous":
return commands.Previous(ctx, client)
case "playurl":
return commands.PlayUrl(ctx, client, args)
case "like": case "like":
return commands.Like(ctx, client) return commands.Like(ctx, client)
case "unlike": case "unlike":
return commands.Unlike(ctx, client) return commands.Unlike(ctx, client)
case "next":
return commands.Skip(ctx, client)
case "shuffle": case "shuffle":
return commands.Shuffle(ctx, client) return commands.Shuffle(ctx, client)
case "repeat":
return commands.Repeat(ctx, client)
case "radio": case "radio":
return commands.Radio(ctx, client) return commands.Radio(ctx, client)
case "clearradio": case "clearradio":
@ -47,7 +53,7 @@ func Run(ctx *gctx.Context, client *spotify.Client, args []string) error {
case "devices": case "devices":
return commands.Devices(ctx, client) return commands.Devices(ctx, client)
case "setdevice": case "setdevice":
return commands.SetDevice(ctx, client, args) return tui.DisplayDevices(ctx, client)
default: default:
return fmt.Errorf("Unsupported Command") return fmt.Errorf("Unsupported Command")
} }

View File

@ -20,10 +20,18 @@ func Play(ctx *gctx.Context, client *spotify.Client) error {
err := client.Play(ctx) err := client.Play(ctx)
if err != nil { if err != nil {
if isNoActiveError(err) { if isNoActiveError(err) {
return playWithTransfer(ctx, client) err := activateDevice(ctx, client)
} if err != nil {
return err return err
} }
err = client.Play(ctx)
if err != nil {
return err
}
} else {
return err
}
}
ctx.Println("Playing!") ctx.Println("Playing!")
return nil return nil
} }
@ -40,7 +48,11 @@ func PlayUrl(ctx *gctx.Context, client *spotify.Client, args []string) error {
err = client.QueueSong(ctx, spotify.ID(track_id)) err = client.QueueSong(ctx, spotify.ID(track_id))
if err != nil { if err != nil {
if isNoActiveError(err) { if isNoActiveError(err) {
err = queueWithTransfer(ctx, client, spotify.ID(track_id)) err := activateDevice(ctx, client)
if err != nil {
return err
}
err = client.QueueSong(ctx, spotify.ID(track_id))
if err != nil { if err != nil {
return err return err
} }
@ -50,9 +62,10 @@ func PlayUrl(ctx *gctx.Context, client *spotify.Client, args []string) error {
} }
ctx.Println("Playing!") ctx.Println("Playing!")
return nil return nil
} } else {
return err return err
} }
}
err = client.Next(ctx) err = client.Next(ctx)
if err != nil { if err != nil {
return err return err
@ -65,15 +78,20 @@ func QueueSong(ctx *gctx.Context, client *spotify.Client, id spotify.ID) error {
err := client.QueueSong(ctx, id) err := client.QueueSong(ctx, id)
if err != nil { if err != nil {
if isNoActiveError(err) { if isNoActiveError(err) {
err := queueWithTransfer(ctx, client, id) err := activateDevice(ctx, client)
if err != nil {
return err
}
err = client.QueueSong(ctx, id)
if err != nil { if err != nil {
return err return err
} }
ctx.Println("Queued!") ctx.Println("Queued!")
return nil return nil
} } else {
return err return err
} }
}
ctx.Println("Queued!") ctx.Println("Queued!")
return nil return nil
} }
@ -255,6 +273,17 @@ func Pause(ctx *gctx.Context, client *spotify.Client) error {
return nil return nil
} }
func TogglePlay(ctx *gctx.Context, client *spotify.Client) error {
current, err := client.PlayerCurrentlyPlaying(ctx)
if err != nil {
return err
}
if !current.Playing {
return Play(ctx, client)
}
return Pause(ctx, client)
}
func Like(ctx *gctx.Context, client *spotify.Client) error { func Like(ctx *gctx.Context, client *spotify.Client) error {
playing, err := client.PlayerCurrentlyPlaying(ctx) playing, err := client.PlayerCurrentlyPlaying(ctx)
if err != nil { if err != nil {
@ -290,6 +319,15 @@ func Skip(ctx *gctx.Context, client *spotify.Client) error {
return nil return nil
} }
func Previous(ctx *gctx.Context, client *spotify.Client) error {
err := client.Previous(ctx)
if err != nil {
return err
}
ctx.Println("Previous!")
return nil
}
func Status(ctx *gctx.Context, client *spotify.Client) error { func Status(ctx *gctx.Context, client *spotify.Client) error {
state, err := client.PlayerState(ctx) state, err := client.PlayerState(ctx)
if err != nil { if err != nil {
@ -311,6 +349,25 @@ func Shuffle(ctx *gctx.Context, client *spotify.Client) error {
return nil return nil
} }
func Repeat(ctx *gctx.Context, client *spotify.Client) error {
state, err := client.PlayerState(ctx)
if err != nil {
return fmt.Errorf("Failed to get current playstate")
}
fmt.Println(state.RepeatState)
newState := "off"
if state.RepeatState == "off" {
newState = "context"
}
// spotifyd only supports binary value for repeat, context or off, change when/if spotifyd is better
err = client.Repeat(ctx, newState)
if err != nil {
return err
}
ctx.Println("Repeat set to", newState)
return nil
}
func TrackList(ctx *gctx.Context, client *spotify.Client, page int) (*spotify.SavedTrackPage, error) { func TrackList(ctx *gctx.Context, client *spotify.Client, page int) (*spotify.SavedTrackPage, error) {
return client.CurrentUsersTracks(ctx, spotify.Limit(50), spotify.Offset((page-1)*50)) return client.CurrentUsersTracks(ctx, spotify.Limit(50), spotify.Offset((page-1)*50))
} }
@ -335,31 +392,21 @@ func PrintDevices(devices []spotify.PlayerDevice) error {
return nil return nil
} }
func SetDevice(ctx *gctx.Context, client *spotify.Client, args []string) error { func SetDevice(ctx *gctx.Context, client *spotify.Client, device spotify.PlayerDevice) error {
if len(args) < 2 { out, err := json.MarshalIndent(device, "", " ")
return fmt.Errorf("Please provide your device ID")
}
devices, err := client.PlayerDevices(ctx)
if err != nil { if err != nil {
return err return err
} }
var set_device spotify.PlayerDevice configDir, _ := os.UserConfigDir()
for _, device := range devices { err = ioutil.WriteFile(filepath.Join(configDir, "gospt/device.json"), out, 0o644)
if device.ID.String() == args[1] {
set_device = device
break
}
}
out, err := json.MarshalIndent(set_device, "", " ")
if err != nil { if err != nil {
return err return err
} }
homdir, _ := os.UserHomeDir() err = activateDevice(ctx, client)
err = ioutil.WriteFile(filepath.Join(homdir, ".config/gospt/device.json"), out, 0o644)
if err != nil { if err != nil {
return err return err
} }
fmt.Println("Your device has been set to: ", set_device.Name) fmt.Println("Your device has been set to: ", device.Name)
return nil return nil
} }
@ -367,60 +414,17 @@ func isNoActiveError(err error) bool {
return strings.Contains(err.Error(), "No active device found") return strings.Contains(err.Error(), "No active device found")
} }
func playWithTransfer(ctx *gctx.Context, client *spotify.Client) error {
configDir, _ := os.UserConfigDir()
deviceFile, err := os.Open(filepath.Join(configDir, "gospt/device.json"))
if err != nil {
return err
}
defer deviceFile.Close()
deviceValue, err := ioutil.ReadAll(deviceFile)
if err != nil {
return err
}
var device *spotify.PlayerDevice
err = json.Unmarshal(deviceValue, &device)
if err != nil {
return err
}
err = client.TransferPlayback(ctx, device.ID, true)
if err != nil {
return err
}
ctx.Println("Playing!")
return nil
}
func queueWithTransfer(ctx *gctx.Context, client *spotify.Client, track_id spotify.ID) error {
configDir, _ := os.UserConfigDir()
deviceFile, err := os.Open(filepath.Join(configDir, "gospt/device.json"))
if err != nil {
return err
}
defer deviceFile.Close()
deviceValue, err := ioutil.ReadAll(deviceFile)
if err != nil {
return err
}
var device *spotify.PlayerDevice
err = json.Unmarshal(deviceValue, &device)
if err != nil {
return err
}
err = client.TransferPlayback(ctx, device.ID, true)
if err != nil {
return err
}
err = client.QueueSong(ctx, track_id)
if err != nil {
return err
}
ctx.Println("Playing!")
return nil
}
func activateDevice(ctx *gctx.Context, client *spotify.Client) error { func activateDevice(ctx *gctx.Context, client *spotify.Client) error {
to_play := true
current, err := client.PlayerCurrentlyPlaying(ctx)
if err != nil {
return err
}
if current.Item == nil || !current.Playing {
to_play = false
}
configDir, _ := os.UserConfigDir() configDir, _ := os.UserConfigDir()
if _, err := os.Stat(filepath.Join(configDir, "gospt/device.json")); err == nil {
deviceFile, err := os.Open(filepath.Join(configDir, "gospt/device.json")) deviceFile, err := os.Open(filepath.Join(configDir, "gospt/device.json"))
if err != nil { if err != nil {
return err return err
@ -435,10 +439,13 @@ func activateDevice(ctx *gctx.Context, client *spotify.Client) error {
if err != nil { if err != nil {
return err return err
} }
err = client.TransferPlayback(ctx, device.ID, false) err = client.TransferPlayback(ctx, device.ID, to_play)
if err != nil { if err != nil {
return err return err
} }
} else {
fmt.Println("YOU MUST RUN gospt setdevice FIRST")
}
return nil return nil
} }

95
internal/tui/device.go Normal file
View File

@ -0,0 +1,95 @@
package tui
import (
"fmt"
"os"
"gospt/internal/commands"
"gospt/internal/gctx"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/zmb3/spotify/v2"
)
var deviceDocStyle = lipgloss.NewStyle().Margin(1, 2)
type deviceItem struct {
spotify.PlayerDevice
}
func (i deviceItem) Title() string { return i.Name }
func (i deviceItem) Description() string {
return fmt.Sprintf("%s - active: %t", i.ID, i.Active)
}
func (i deviceItem) FilterValue() string { return i.Title() }
type deviceModel struct {
list list.Model
page int
ctx *gctx.Context
client *spotify.Client
}
func (m deviceModel) Init() tea.Cmd {
return nil
}
func (m deviceModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
if msg.String() == "ctrl+c" {
return m, tea.Quit
}
if msg.String() == "enter" {
fmt.Println("SELECTING")
device := m.list.SelectedItem()
var err error
err = commands.SetDevice(m.ctx, m.client, device.(deviceItem).PlayerDevice)
if err != nil {
m.ctx.Printf(err.Error())
}
fmt.Println("DEVICE SET")
return m, tea.Quit
}
case tea.WindowSizeMsg:
h, v := docStyle.GetFrameSize()
m.list.SetSize(msg.Width-h, msg.Height-v)
}
var cmd tea.Cmd
m.list, cmd = m.list.Update(msg)
return m, cmd
}
func (m deviceModel) View() string {
return docStyle.Render(m.list.View())
}
func DisplayDevices(ctx *gctx.Context, client *spotify.Client) error {
items := []list.Item{}
devices, err := client.PlayerDevices(ctx)
if err != nil {
return err
}
for _, device := range devices {
items = append(items, deviceItem{
device,
})
}
if err != nil {
return err
}
m := deviceModel{list: list.New(items, list.NewDefaultDelegate(), 0, 0), page: 1, ctx: ctx, client: client}
m.list.Title = "Saved Tracks"
p := tea.NewProgram(m, tea.WithAltScreen())
if _, err := p.Run(); err != nil {
fmt.Println("Error running program:", err)
os.Exit(1)
}
fmt.Println("DEVICE SET AND SAVED")
return nil
}