diff --git a/src/components/cli/cli.go b/src/components/cli/cli.go index 2ffc616..fb5d0e0 100644 --- a/src/components/cli/cli.go +++ b/src/components/cli/cli.go @@ -6,6 +6,7 @@ import ( "strconv" "github.com/urfave/cli/v2" + "github.com/zmb3/spotify/v2" "go.uber.org/fx" "git.asdf.cafe/abs3nt/gospt-ng/src/components/commands" @@ -199,7 +200,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) { Name: "clearradio", Usage: "Clears the radio queue", Aliases: []string{"cr"}, - Action: func(ctx *cli.Context) error { + Action: func(cCtx *cli.Context) error { return c.ClearRadio() }, }, @@ -207,10 +208,54 @@ func Run(c *commands.Commander, s fx.Shutdowner) { Name: "devices", Usage: "Lists available devices", Aliases: []string{"d"}, - Action: func(ctx *cli.Context) error { + Action: func(cCtx *cli.Context) error { return c.ListDevices() }, }, + { + Name: "setdevice", + Usage: "Set the active device", + Action: func(cCtx *cli.Context) error { + return c.SetDevice(spotify.ID(cCtx.Args().First())) + }, + }, + { + Name: "repeat", + Usage: "Toggle repeat mode", + Action: func(cCtx *cli.Context) error { + return c.Repeat() + }, + }, + { + Name: "seek", + Usage: "Seek to a position in the song", + Aliases: []string{"sk"}, + Action: func(cCtx *cli.Context) error { + pos, err := strconv.Atoi(cCtx.Args().First()) + if err != nil { + return err + } + return c.SetPosition(pos) + }, + Subcommands: []*cli.Command{ + { + Name: "forward", + Aliases: []string{"f"}, + Usage: "Seek forward", + Action: func(cCtx *cli.Context) error { + return c.Seek(true) + }, + }, + { + Name: "backward", + Aliases: []string{"b"}, + Usage: "Seek backward", + Action: func(cCtx *cli.Context) error { + return c.Seek(false) + }, + }, + }, + }, }, } if err := app.Run(os.Args); err != nil { diff --git a/src/components/commands/devices.go b/src/components/commands/devices.go index 7db729a..03c4461 100644 --- a/src/components/commands/devices.go +++ b/src/components/commands/devices.go @@ -3,6 +3,8 @@ package commands import ( "encoding/json" "fmt" + "os" + "path/filepath" "github.com/zmb3/spotify/v2" ) @@ -23,3 +25,29 @@ func PrintDevices(devices []spotify.PlayerDevice) error { fmt.Println(string(out)) return nil } + +func (c *Commander) SetDevice(device spotify.ID) error { + err := c.Client.TransferPlayback(c.Context, device, true) + if err != nil { + return err + } + devices, err := c.Client.PlayerDevices(c.Context) + if err != nil { + return err + } + for _, d := range devices { + if d.ID == device { + out, err := json.MarshalIndent(d, "", " ") + if err != nil { + return err + } + configDir, _ := os.UserConfigDir() + err = os.WriteFile(filepath.Join(configDir, "gospt/device.json"), out, 0o600) + if err != nil { + return err + } + return nil + } + } + return fmt.Errorf("device not found") +} diff --git a/src/components/commands/repeat.go b/src/components/commands/repeat.go new file mode 100644 index 0000000..e1f203d --- /dev/null +++ b/src/components/commands/repeat.go @@ -0,0 +1,23 @@ +package commands + +import ( + "fmt" +) + +func (c *Commander) Repeat() error { + state, err := c.Client.PlayerState(c.Context) + if err != nil { + return err + } + 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 = c.Client.Repeat(c.Context, newState) + if err != nil { + return err + } + fmt.Println("Repeat set to", newState) + return nil +} diff --git a/src/components/commands/seek.go b/src/components/commands/seek.go new file mode 100644 index 0000000..178d542 --- /dev/null +++ b/src/components/commands/seek.go @@ -0,0 +1,25 @@ +package commands + +func (c *Commander) Seek(fwd bool) error { + current, err := c.Client.PlayerCurrentlyPlaying(c.Context) + if err != nil { + return err + } + newPos := current.Progress + 5000 + if !fwd { + newPos = current.Progress - 5000 + } + err = c.Client.Seek(c.Context, newPos) + if err != nil { + return err + } + return nil +} + +func (c *Commander) SetPosition(pos int) error { + err := c.Client.Seek(c.Context, pos) + if err != nil { + return err + } + return nil +}