Compare commits

..

No commits in common. "shtuff" and "master" have entirely different histories.

12 changed files with 71 additions and 443 deletions

View File

@ -1,9 +1,5 @@
build: build:
go build -ldflags="-X 'git.asdf.cafe/abs3nt/gspot/src/components/cli.Version=$(shell git show -s --date=short --pretty='format:%h (%ad)' HEAD)'" -o dist/ ./cmd/gspot go build -ldflags="-X 'git.asdf.cafe/abs3nt/gspot/src/components/cli.Version=$(shell git show -s --date=short --pretty='format:%h (%ad)' HEAD)'" -o dist/ .
go build -o dist/ ./cmd/gspot-daemon
rundaemon: build
./dist/gspot-daemon
run: build run: build
./dist/gspot ./dist/gspot
@ -16,12 +12,10 @@ clean:
uninstall: uninstall:
rm -f /usr/bin/gspot rm -f /usr/bin/gspot
rm -f /usr/bin/gspot-daemon
rm -f /usr/share/zsh/site-functions/_gspot rm -f /usr/share/zsh/site-functions/_gspot
rm -f /usr/share/bash-completion/completions/gspot rm -f /usr/share/bash-completion/completions/gspot
install: install:
cp ./dist/gspot /usr/bin cp ./dist/gspot /usr/bin
cp ./dist/gspot-daemon /usr/bin
cp ./completions/_gspot /usr/share/zsh/site-functions/_gspot cp ./completions/_gspot /usr/share/zsh/site-functions/_gspot
cp ./completions/gspot /usr/share/bash-completion/completionsgspotg cp ./completions/gspot /usr/share/bash-completion/completionsgspotg

View File

@ -10,8 +10,8 @@ import (
"go.uber.org/fx/fxevent" "go.uber.org/fx/fxevent"
"git.asdf.cafe/abs3nt/gspot/src/components/cache" "git.asdf.cafe/abs3nt/gspot/src/components/cache"
"git.asdf.cafe/abs3nt/gspot/src/components/cli"
"git.asdf.cafe/abs3nt/gspot/src/components/commands" "git.asdf.cafe/abs3nt/gspot/src/components/commands"
"git.asdf.cafe/abs3nt/gspot/src/components/daemon"
"git.asdf.cafe/abs3nt/gspot/src/components/logger" "git.asdf.cafe/abs3nt/gspot/src/components/logger"
"git.asdf.cafe/abs3nt/gspot/src/services" "git.asdf.cafe/abs3nt/gspot/src/services"
) )
@ -33,7 +33,7 @@ func main() {
logger.NewLogger, logger.NewLogger,
), ),
fx.Invoke( fx.Invoke(
daemon.Run, cli.Run,
), ),
) )
app.Run() app.Run()

View File

@ -3,7 +3,6 @@ package cli
import ( import (
"context" "context"
"fmt" "fmt"
"net/rpc"
"os" "os"
"strconv" "strconv"
"strings" "strings"
@ -13,7 +12,6 @@ import (
"go.uber.org/fx" "go.uber.org/fx"
"git.asdf.cafe/abs3nt/gspot/src/components/commands" "git.asdf.cafe/abs3nt/gspot/src/components/commands"
"git.asdf.cafe/abs3nt/gspot/src/components/daemon"
"git.asdf.cafe/abs3nt/gspot/src/components/tui" "git.asdf.cafe/abs3nt/gspot/src/components/tui"
"git.asdf.cafe/abs3nt/gspot/src/components/tuitview" "git.asdf.cafe/abs3nt/gspot/src/components/tuitview"
) )
@ -40,7 +38,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("Play", daemon.PlayArgs{}) return c.Play()
}, },
Category: "Playback", Category: "Playback",
}, },
@ -56,7 +54,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.NArg() > 1 { if cmd.NArg() > 1 {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("PlayURL", daemon.PlayURLArgs{URL: cmd.Args().First()}) return c.PlayURL(cmd.Args().First())
}, },
Category: "Playback", Category: "Playback",
}, },
@ -68,7 +66,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("Pause", daemon.PauseArgs{}) return c.Pause()
}, },
Category: "Playback", Category: "Playback",
}, },
@ -80,7 +78,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("TogglePlay", daemon.TogglePlayArgs{}) return c.TogglePlay()
}, },
Category: "Playback", Category: "Playback",
}, },
@ -92,7 +90,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("Link", daemon.LinkArgs{}) return c.PrintLink()
}, },
Category: "Sharing", Category: "Sharing",
}, },
@ -104,7 +102,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("LinkContext", daemon.LinkContextArgs{}) return c.PrintLinkContext()
}, },
Category: "Sharing", Category: "Sharing",
}, },
@ -116,7 +114,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("YoutubeLink", daemon.YoutubeLinkArgs{}) return c.PrintYoutubeLink()
}, },
Category: "Sharing", Category: "Sharing",
}, },
@ -129,15 +127,14 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.NArg() > 1 { if cmd.NArg() > 1 {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
amount := 1
if cmd.NArg() > 0 { if cmd.NArg() > 0 {
amt, err := strconv.Atoi(cmd.Args().First()) amt, err := strconv.Atoi(cmd.Args().First())
if err != nil { if err != nil {
return err return err
} }
amount = amt return c.Next(amt, false)
} }
return sendCommandRPC("Next", daemon.NextArgs{Amount: amount}) return c.Next(1, false)
}, },
Category: "Playback", Category: "Playback",
}, },
@ -149,7 +146,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("Previous", daemon.PreviousArgs{}) return c.Previous()
}, },
Category: "Playback", Category: "Playback",
}, },
@ -161,7 +158,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("Like", daemon.LikeArgs{}) return c.Like()
}, },
Category: "Library Management", Category: "Library Management",
}, },
@ -173,7 +170,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("UnLike", daemon.UnlikeArgs{}) return c.UnLike()
}, },
Category: "Library Management", Category: "Library Management",
}, },
@ -193,7 +190,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("NowPlaying", daemon.NowPlayingArgs{Force: cmd.Bool("force")}) return c.NowPlaying(cmd.Bool("force"))
}, },
Category: "Info", Category: "Info",
}, },
@ -215,7 +212,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if err != nil { if err != nil {
return err return err
} }
return sendCommandRPC("ChangeVolume", daemon.VolumeArgs{Amount: amt}) return c.ChangeVolume(amt)
}, },
}, },
{ {
@ -231,7 +228,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if err != nil { if err != nil {
return err return err
} }
return sendCommandRPC("ChangeVolume", daemon.VolumeArgs{Amount: -amt}) return c.ChangeVolume(-amt)
}, },
}, },
{ {
@ -242,7 +239,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("Mute", daemon.MuteArgs{}) return c.Mute()
}, },
}, },
{ {
@ -253,7 +250,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("UnMute", daemon.UnmuteArgs{}) return c.UnMute()
}, },
}, },
{ {
@ -264,7 +261,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("ToggleMute", daemon.ToggleMuteArgs{}) return c.ToggleMute()
}, },
}, },
}, },
@ -283,7 +280,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.NArg() > 1 { if cmd.NArg() > 1 {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("DownloadCover", daemon.DownloadCoverArgs{Path: cmd.Args().First()}) return c.DownloadCover(cmd.Args().First())
}, },
Category: "Info", Category: "Info",
}, },
@ -295,7 +292,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("Radio", daemon.RadioArgs{}) return c.Radio()
}, },
Category: "Radio", Category: "Radio",
}, },
@ -307,7 +304,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("ClearRadio", daemon.ClearRadioArgs{}) return c.ClearRadio()
}, },
Category: "Radio", Category: "Radio",
}, },
@ -319,7 +316,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("RefillRadio", daemon.RefillRadioArgs{}) return c.RefillRadio()
}, },
Category: "Radio", Category: "Radio",
}, },
@ -330,7 +327,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("Status", daemon.StatusArgs{}) return c.Status()
}, },
Category: "Info", Category: "Info",
}, },
@ -342,7 +339,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("Devices", daemon.ListDevicesArgs{}) return c.ListDevices()
}, },
Category: "Info", Category: "Info",
}, },
@ -357,7 +354,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.NArg() > 1 { if cmd.NArg() > 1 {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("SetDevice", daemon.SetDeviceArgs{DeviceID: spotify.ID(cmd.Args().First())}) return c.SetDevice(spotify.ID(cmd.Args().First()))
}, },
Category: "Playback", Category: "Playback",
}, },
@ -368,7 +365,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("Repeat", daemon.RepeatArgs{}) return c.Repeat()
}, },
Category: "Playback", Category: "Playback",
}, },
@ -379,7 +376,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("Shuffle", daemon.ShuffleArgs{}) return c.Shuffle()
}, },
Category: "Playback", Category: "Playback",
}, },
@ -417,7 +414,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if err != nil { if err != nil {
return err return err
} }
return sendCommandRPC("SetPosition", daemon.SetPositionArgs{Position: pos}) return c.SetPosition(pos)
}, },
Commands: []*cli.Command{ Commands: []*cli.Command{
{ {
@ -428,7 +425,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("Seek", daemon.SeekArgs{Fwd: true}) return c.Seek(true)
}, },
}, },
{ {
@ -439,7 +436,7 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
if cmd.Args().Present() { if cmd.Args().Present() {
return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " ")) return fmt.Errorf("unexpected arguments: %s", strings.Join(cmd.Args().Slice(), " "))
} }
return sendCommandRPC("Seek", daemon.SeekArgs{Fwd: false}) return c.Seek(false)
}, },
}, },
}, },
@ -452,23 +449,3 @@ func Run(c *commands.Commander, s fx.Shutdowner) {
} }
s.Shutdown() s.Shutdown()
} }
func sendCommandRPC(method string, args interface{}) error {
client, err := rpc.Dial("unix", "/tmp/gspot.sock")
if err != nil {
return fmt.Errorf("could not connect to daemon: %v", err)
}
defer client.Close()
var reply string
err = client.Call("Handler."+method, args, &reply)
if err != nil {
return fmt.Errorf("error calling %s: %v", method, err)
}
if reply != "" {
fmt.Println(reply)
}
return nil
}

View File

@ -9,16 +9,21 @@ import (
"github.com/zmb3/spotify/v2" "github.com/zmb3/spotify/v2"
) )
func (c *Commander) ListDevices() (string, error) { func (c *Commander) ListDevices() error {
devices, err := c.Client().PlayerDevices(c.Context) devices, err := c.Client().PlayerDevices(c.Context)
if err != nil { if err != nil {
return "", err return err
} }
return PrintDevices(devices)
}
func PrintDevices(devices []spotify.PlayerDevice) error {
out, err := json.MarshalIndent(devices, "", " ") out, err := json.MarshalIndent(devices, "", " ")
if err != nil { if err != nil {
return "", err return err
} }
return string(out), nil fmt.Println(string(out))
return nil
} }
func (c *Commander) SetDevice(device spotify.ID) error { func (c *Commander) SetDevice(device spotify.ID) error {

View File

@ -1,17 +1,21 @@
package commands package commands
func (c *Commander) PrintLink() (string, error) { import "fmt"
func (c *Commander) PrintLink() error {
state, err := c.Client().PlayerState(c.Context) state, err := c.Client().PlayerState(c.Context)
if err != nil { if err != nil {
return "", err return err
} }
return state.Item.ExternalURLs["spotify"], nil fmt.Println(state.Item.ExternalURLs["spotify"])
return nil
} }
func (c *Commander) PrintLinkContext() (string, error) { func (c *Commander) PrintLinkContext() error {
state, err := c.Client().PlayerState(c.Context) state, err := c.Client().PlayerState(c.Context)
if err != nil { if err != nil {
return "", err return err
} }
return state.PlaybackContext.ExternalURLs["spotify"], nil fmt.Println(state.PlaybackContext.ExternalURLs["spotify"])
return nil
} }

View File

@ -7,15 +7,16 @@ import (
"github.com/zmb3/spotify/v2" "github.com/zmb3/spotify/v2"
) )
func (c *Commander) NowPlaying(force bool) (string, error) { func (c *Commander) NowPlaying(force bool) error {
if force { if force {
current, err := c.Client().PlayerCurrentlyPlaying(c.Context) current, err := c.Client().PlayerCurrentlyPlaying(c.Context)
if err != nil { if err != nil {
return "", err return err
} }
str := FormatSong(current) str := FormatSong(current)
go c.Cache.Put("now_playing", str, 5*time.Second) fmt.Println(str)
return str, nil _, err = c.Cache.Put("now_playing", str, 5*time.Second)
return err
} }
song, err := c.Cache.GetOrDo("now_playing", func() (string, error) { song, err := c.Cache.GetOrDo("now_playing", func() (string, error) {
current, err := c.Client().PlayerCurrentlyPlaying(c.Context) current, err := c.Client().PlayerCurrentlyPlaying(c.Context)
@ -26,9 +27,10 @@ func (c *Commander) NowPlaying(force bool) (string, error) {
return str, nil return str, nil
}, 5*time.Second) }, 5*time.Second)
if err != nil { if err != nil {
return "", err return err
} }
return song, nil fmt.Println(song)
return nil
} }
func FormatSong(current *spotify.CurrentlyPlaying) string { func FormatSong(current *spotify.CurrentlyPlaying) string {

View File

@ -2,12 +2,13 @@ package commands
import ( import (
"encoding/json" "encoding/json"
"fmt"
"time" "time"
"github.com/zmb3/spotify/v2" "github.com/zmb3/spotify/v2"
) )
func (c *Commander) Status() (string, error) { func (c *Commander) Status() error {
state, err := c.Cache.GetOrDo("state", func() (string, error) { state, err := c.Cache.GetOrDo("state", func() (string, error) {
state, err := c.Client().PlayerState(c.Context) state, err := c.Client().PlayerState(c.Context)
if err != nil { if err != nil {
@ -20,9 +21,10 @@ func (c *Commander) Status() (string, error) {
return str, nil return str, nil
}, 5*time.Second) }, 5*time.Second)
if err != nil { if err != nil {
return "", err return err
} }
return state, nil fmt.Println(state)
return nil
} }
func (c *Commander) FormatState(state *spotify.PlayerState) (string, error) { func (c *Commander) FormatState(state *spotify.PlayerState) (string, error) {

View File

@ -1,14 +1,17 @@
package commands package commands
import ( import (
"fmt"
"git.asdf.cafe/abs3nt/gspot/src/components/youtube" "git.asdf.cafe/abs3nt/gspot/src/components/youtube"
) )
func (c *Commander) PrintYoutubeLink() (string, error) { func (c *Commander) PrintYoutubeLink() error {
state, err := c.Client().PlayerState(c.Context) state, err := c.Client().PlayerState(c.Context)
if err != nil { if err != nil {
return "", err return err
} }
link := youtube.Search(state.Item.Artists[0].Name + state.Item.Name) link := youtube.Search(state.Item.Artists[0].Name + state.Item.Name)
return link, nil fmt.Println(link)
return nil
} }

View File

@ -1,74 +0,0 @@
package daemon
import (
"fmt"
"log"
"net"
"net/rpc"
"os"
"time"
"go.uber.org/fx"
"git.asdf.cafe/abs3nt/gspot/src/components/commands"
"git.asdf.cafe/abs3nt/gspot/src/config"
)
func Run(c *commands.Commander, conf *config.Config, s fx.Shutdowner) {
for {
err := startServer(c, conf)
if err != nil {
log.Printf("Server error: %v", err)
time.Sleep(time.Second)
continue
}
break
}
}
func startServer(c *commands.Commander, conf *config.Config) error {
socketPath := conf.SocketPath
if _, err := os.Stat(socketPath); err == nil {
if err := os.Remove(socketPath); err != nil {
return fmt.Errorf("failed to remove existing socket: %w", err)
}
}
commandHandler := &Handler{Commander: c}
server := rpc.NewServer()
if err := server.Register(commandHandler); err != nil {
return fmt.Errorf("failed to register RPC handler: %w", err)
}
listener, err := net.Listen("unix", socketPath)
if err != nil {
return fmt.Errorf("listen error: %w", err)
}
defer listener.Close()
if err := os.Chmod(socketPath, 0o666); err != nil {
return fmt.Errorf("failed to set socket permissions: %w", err)
}
log.Println("Daemon is listening on", socketPath)
for {
conn, err := listener.Accept()
if err != nil {
log.Println("Accept error:", err)
continue
}
codec := NewLoggingServerCodec(conn)
go handleConnection(server, codec)
}
}
func handleConnection(server *rpc.Server, codec rpc.ServerCodec) {
defer func() {
if r := recover(); r != nil {
log.Printf("Recovered in handleConnection: %v", r)
}
}()
server.ServeCodec(codec)
}

View File

@ -1,200 +0,0 @@
package daemon
import (
"git.asdf.cafe/abs3nt/gspot/src/components/commands"
"github.com/zmb3/spotify/v2"
)
type Handler struct {
Commander *commands.Commander
}
type PlayArgs struct{}
func (h *Handler) Play(args *PlayArgs, reply *string) error {
return h.Commander.Play()
}
type PlayURLArgs struct {
URL string
}
func (h *Handler) PlayURL(args *PlayURLArgs, reply *string) error {
return h.Commander.PlayURL(args.URL)
}
type PauseArgs struct{}
func (h *Handler) Pause(args *PauseArgs, reply *string) error {
return h.Commander.Pause()
}
type TogglePlayArgs struct{}
func (h *Handler) TogglePlay(args *TogglePlayArgs, reply *string) error {
return h.Commander.TogglePlay()
}
type LinkArgs struct{}
func (h *Handler) Link(args *LinkArgs, reply *string) error {
link, err := h.Commander.PrintLink()
*reply = link
return err
}
type LinkContextArgs struct{}
func (h *Handler) LinkContext(args *LinkContextArgs, reply *string) error {
link, err := h.Commander.PrintLinkContext()
*reply = link
return err
}
type YoutubeLinkArgs struct{}
func (h *Handler) YoutubeLink(args *YoutubeLinkArgs, reply *string) error {
link, err := h.Commander.PrintYoutubeLink()
*reply = link
return err
}
type NextArgs struct {
Amount int
}
func (h *Handler) Next(args *NextArgs, reply *string) error {
return h.Commander.Next(args.Amount, false)
}
type PreviousArgs struct{}
func (h *Handler) Previous(args *PreviousArgs, reply *string) error {
return h.Commander.Previous()
}
type LikeArgs struct{}
func (h *Handler) Like(args *LikeArgs, reply *string) error {
return h.Commander.Like()
}
type UnlikeArgs struct{}
func (h *Handler) Unlike(args *UnlikeArgs, reply *string) error {
return h.Commander.UnLike()
}
type NowPlayingArgs struct {
Force bool
}
func (h *Handler) NowPlaying(args *NowPlayingArgs, reply *string) error {
resp, err := h.Commander.NowPlaying(args.Force)
*reply = resp
return err
}
type VolumeArgs struct {
Amount int
}
func (h *Handler) ChangeVolume(args *VolumeArgs, reply *string) error {
return h.Commander.ChangeVolume(args.Amount)
}
type MuteArgs struct{}
func (h *Handler) Mute(args *MuteArgs, reply *string) error {
return h.Commander.Mute()
}
type UnmuteArgs struct{}
func (h *Handler) Unmute(args *UnmuteArgs, reply *string) error {
return h.Commander.UnMute()
}
type ToggleMuteArgs struct{}
func (h *Handler) ToggleMute(args *ToggleMuteArgs, reply *string) error {
return h.Commander.ToggleMute()
}
type DownloadCoverArgs struct {
Path string
}
func (h *Handler) DownloadCover(args *DownloadCoverArgs, reply *string) error {
return h.Commander.DownloadCover(args.Path)
}
type RadioArgs struct{}
func (h *Handler) Radio(args *RadioArgs, reply *string) error {
return h.Commander.Radio()
}
type ClearRadioArgs struct{}
func (h *Handler) ClearRadio(args *ClearRadioArgs, reply *string) error {
return h.Commander.ClearRadio()
}
type RefillRadioArgs struct{}
func (h *Handler) RefillRadio(args *RefillRadioArgs, reply *string) error {
return h.Commander.RefillRadio()
}
type StatusArgs struct{}
func (h *Handler) Status(args *StatusArgs, reply *string) error {
status, err := h.Commander.Status()
*reply = status
return err
}
type ListDevicesArgs struct{}
func (h *Handler) Devices(args *ListDevicesArgs, reply *string) error {
devices, err := h.Commander.ListDevices()
*reply = devices
return err
}
type SetDeviceArgs struct {
DeviceID spotify.ID
}
func (h *Handler) SetDevice(args *SetDeviceArgs, reply *string) error {
return h.Commander.SetDevice(args.DeviceID)
}
type RepeatArgs struct{}
func (h *Handler) Repeat(args *RepeatArgs, reply *string) error {
return h.Commander.Repeat()
}
type ShuffleArgs struct{}
func (h *Handler) Shuffle(args *ShuffleArgs, reply *string) error {
return h.Commander.Shuffle()
}
type SetPositionArgs struct {
Position int
}
func (h *Handler) SetPosition(args *SetPositionArgs, reply *string) error {
return h.Commander.SetPosition(args.Position)
}
type SeekArgs struct {
Fwd bool
}
func (h *Handler) Seek(args *SeekArgs, reply *string) error {
return h.Commander.Seek(args.Fwd)
}

View File

@ -1,84 +0,0 @@
package daemon
import (
"encoding/gob"
"io"
"log/slog"
"net/rpc"
"os"
"reflect"
)
var logger *slog.Logger
func init() {
handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
})
logger = slog.New(handler)
}
type LoggingServerCodec struct {
dec *gob.Decoder
enc *gob.Encoder
c io.Closer
}
func NewLoggingServerCodec(conn io.ReadWriteCloser) *LoggingServerCodec {
return &LoggingServerCodec{
dec: gob.NewDecoder(conn),
enc: gob.NewEncoder(conn),
c: conn,
}
}
func formatBody(body interface{}) slog.Value {
if body == nil {
return slog.StringValue("null")
}
v := reflect.ValueOf(body)
if v.Kind() == reflect.Ptr && !v.IsNil() {
v = v.Elem()
body = v.Interface()
}
return slog.AnyValue(body)
}
func (c *LoggingServerCodec) ReadRequestHeader(r *rpc.Request) error {
err := c.dec.Decode(r)
if err == nil {
logger.Info("Received Request",
"ServiceMethod", r.ServiceMethod,
)
}
return err
}
func (c *LoggingServerCodec) ReadRequestBody(body interface{}) error {
err := c.dec.Decode(body)
if err == nil {
logger.Info("Request Body",
"Body", formatBody(body),
)
}
return err
}
func (c *LoggingServerCodec) WriteResponse(r *rpc.Response, body interface{}) error {
if err := c.enc.Encode(r); err != nil {
return err
}
if err := c.enc.Encode(body); err != nil {
return err
}
logger.Info("Sent Response",
"ServiceMethod", r.ServiceMethod,
"Error", r.Error,
"Body", formatBody(body),
)
return nil
}
func (c *LoggingServerCodec) Close() error {
return c.c.Close()
}

View File

@ -7,5 +7,4 @@ type Config struct {
Port string `yaml:"port"` Port string `yaml:"port"`
LogLevel string `yaml:"log_level" default:"info"` LogLevel string `yaml:"log_level" default:"info"`
LogOutput string `yaml:"log_output" default:"stdout"` LogOutput string `yaml:"log_output" default:"stdout"`
SocketPath string `yaml:"socket_path" default:"/tmp/gspot.sock"`
} }