set device, repeat, toggleplay etc'
This commit is contained in:
parent
8f5c4bffb8
commit
6e542a030f
1
go.mod
1
go.mod
@ -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
2
go.sum
@ -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=
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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
95
internal/tui/device.go
Normal 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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user