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/cristalhq/aconfig v0.18.3
|
||||
github.com/cristalhq/aconfig/aconfigyaml v0.17.1
|
||||
github.com/zmb3/spotify v1.3.0
|
||||
github.com/zmb3/spotify/v2 v2.3.1
|
||||
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.27/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/go.mod h1:+LVh9CafHu7SedyqYmEf12Rd01dIVlEL845yNhksW0E=
|
||||
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] {
|
||||
case "play":
|
||||
return commands.Play(ctx, client)
|
||||
case "playurl":
|
||||
return commands.PlayUrl(ctx, client, args)
|
||||
case "pause":
|
||||
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":
|
||||
return commands.Like(ctx, client)
|
||||
case "unlike":
|
||||
return commands.Unlike(ctx, client)
|
||||
case "next":
|
||||
return commands.Skip(ctx, client)
|
||||
case "shuffle":
|
||||
return commands.Shuffle(ctx, client)
|
||||
case "repeat":
|
||||
return commands.Repeat(ctx, client)
|
||||
case "radio":
|
||||
return commands.Radio(ctx, client)
|
||||
case "clearradio":
|
||||
@ -47,7 +53,7 @@ func Run(ctx *gctx.Context, client *spotify.Client, args []string) error {
|
||||
case "devices":
|
||||
return commands.Devices(ctx, client)
|
||||
case "setdevice":
|
||||
return commands.SetDevice(ctx, client, args)
|
||||
return tui.DisplayDevices(ctx, client)
|
||||
default:
|
||||
return fmt.Errorf("Unsupported Command")
|
||||
}
|
||||
|
@ -20,9 +20,17 @@ func Play(ctx *gctx.Context, client *spotify.Client) error {
|
||||
err := client.Play(ctx)
|
||||
if err != nil {
|
||||
if isNoActiveError(err) {
|
||||
return playWithTransfer(ctx, client)
|
||||
err := activateDevice(ctx, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = client.Play(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
ctx.Println("Playing!")
|
||||
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))
|
||||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@ -50,8 +62,9 @@ func PlayUrl(ctx *gctx.Context, client *spotify.Client, args []string) error {
|
||||
}
|
||||
ctx.Println("Playing!")
|
||||
return nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = client.Next(ctx)
|
||||
if err != nil {
|
||||
@ -65,14 +78,19 @@ func QueueSong(ctx *gctx.Context, client *spotify.Client, id spotify.ID) error {
|
||||
err := client.QueueSong(ctx, id)
|
||||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
ctx.Println("Queued!")
|
||||
return nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
ctx.Println("Queued!")
|
||||
return nil
|
||||
@ -255,6 +273,17 @@ func Pause(ctx *gctx.Context, client *spotify.Client) error {
|
||||
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 {
|
||||
playing, err := client.PlayerCurrentlyPlaying(ctx)
|
||||
if err != nil {
|
||||
@ -290,6 +319,15 @@ func Skip(ctx *gctx.Context, client *spotify.Client) error {
|
||||
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 {
|
||||
state, err := client.PlayerState(ctx)
|
||||
if err != nil {
|
||||
@ -311,6 +349,25 @@ func Shuffle(ctx *gctx.Context, client *spotify.Client) error {
|
||||
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) {
|
||||
return client.CurrentUsersTracks(ctx, spotify.Limit(50), spotify.Offset((page-1)*50))
|
||||
}
|
||||
@ -335,31 +392,21 @@ func PrintDevices(devices []spotify.PlayerDevice) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetDevice(ctx *gctx.Context, client *spotify.Client, args []string) error {
|
||||
if len(args) < 2 {
|
||||
return fmt.Errorf("Please provide your device ID")
|
||||
}
|
||||
devices, err := client.PlayerDevices(ctx)
|
||||
func SetDevice(ctx *gctx.Context, client *spotify.Client, device spotify.PlayerDevice) error {
|
||||
out, err := json.MarshalIndent(device, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var set_device spotify.PlayerDevice
|
||||
for _, device := range devices {
|
||||
if device.ID.String() == args[1] {
|
||||
set_device = device
|
||||
break
|
||||
}
|
||||
}
|
||||
out, err := json.MarshalIndent(set_device, "", " ")
|
||||
configDir, _ := os.UserConfigDir()
|
||||
err = ioutil.WriteFile(filepath.Join(configDir, "gospt/device.json"), out, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
homdir, _ := os.UserHomeDir()
|
||||
err = ioutil.WriteFile(filepath.Join(homdir, ".config/gospt/device.json"), out, 0o644)
|
||||
err = activateDevice(ctx, client)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
@ -367,77 +414,37 @@ func isNoActiveError(err error) bool {
|
||||
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 {
|
||||
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()
|
||||
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, false)
|
||||
if err != nil {
|
||||
return err
|
||||
if _, err := os.Stat(filepath.Join(configDir, "gospt/device.json")); err == nil {
|
||||
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, to_play)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
fmt.Println("YOU MUST RUN gospt setdevice FIRST")
|
||||
}
|
||||
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