From 54bc1ec5a762da6ec57ef12990bf9835b862c67a Mon Sep 17 00:00:00 2001 From: "Brian C. Arnold" Date: Tue, 30 Jan 2024 11:56:05 -0500 Subject: [PATCH 1/3] Updated how local works, failover lyric search. --- cmd/root.go | 25 ++++++++++++++----- lyrics/lyrics.go | 5 +++- player/player.go | 6 ++++- pool/pool.go | 8 +++--- services/browser/browser.go | 3 ++- services/combo/combo.go | 32 ++++++++++++++++++++++++ services/hosted/hosted.go | 17 ++++++++++--- services/local/local.go | 48 ++++++++++++++++++++++++++---------- services/mopidy/mopidy.go | 5 ++-- services/mpd/mpd.go | 10 ++------ services/mpris/mpris_unix.go | 36 ++++++++++++++++++--------- services/spotify/spotify.go | 19 ++++++++++---- ui/ui.go | 1 + 13 files changed, 159 insertions(+), 56 deletions(-) create mode 100644 services/combo/combo.go diff --git a/cmd/root.go b/cmd/root.go index 2b6e302..de1c817 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -8,6 +8,7 @@ import ( "sptlrx/lyrics" "sptlrx/player" "sptlrx/pool" + "sptlrx/services/combo" "sptlrx/services/hosted" "sptlrx/services/local" "sptlrx/services/spotify" @@ -140,18 +141,30 @@ func loadPlayer(conf *config.Config) (player.Player, error) { } func loadProvider(conf *config.Config, player player.Player) (lyrics.Provider, error) { - if conf.Local.Folder != "" { - return local.New(conf.Local.Folder) - } + providers := []lyrics.Provider{} if conf.Cookie == "" { - return hosted.New(conf.Host), nil + + coovieProv := hosted.New(conf.Host) + providers = append(providers, coovieProv) } if spt, ok := player.(*spotify.Client); ok { + // spt, _ := spotify.New(conf.Cookie) // use existing spotify client - return spt, nil + providers = append(providers, spt) + } + if conf.Local.Folder != "" { + localProv, err := local.New(conf.Local.Folder) + if err == nil { + providers = append(providers, localProv) + } + // return local.New(conf.Local.Folder) } // create new spotify client - return spotify.New(conf.Cookie) + if len(providers) == 0 { + var s, _ = spotify.New(conf.Cookie) + providers = append(providers, s) + } + return combo.New(providers) } func parseStyleFlag(value string) config.Style { diff --git a/lyrics/lyrics.go b/lyrics/lyrics.go index a1507fe..fad6f31 100644 --- a/lyrics/lyrics.go +++ b/lyrics/lyrics.go @@ -1,7 +1,10 @@ package lyrics +import "sptlrx/player" + type Provider interface { - Lyrics(id, query string) ([]Line, error) + Lyrics(state player.State) ([]Line, error) + Name() string } type Line struct { diff --git a/player/player.go b/player/player.go index a6ae10d..569d909 100644 --- a/player/player.go +++ b/player/player.go @@ -8,7 +8,11 @@ type State struct { // ID of the current track. ID string // Query is a string that can be used to find lyrics. - Query string + TrackNumber int + Artist string + Album string + Title string + SongPath string // Position of the current track in ms. Position int // Playing means whether the track is playing at the moment. diff --git a/pool/pool.go b/pool/pool.go index ecaea9c..f98722c 100644 --- a/pool/pool.go +++ b/pool/pool.go @@ -47,7 +47,7 @@ func Listen( if newState.ID != state.ID { changed = true if newState.ID != "" { - newLines, err := provider.Lyrics(newState.ID, newState.Query) + newLines, err := provider.Lyrics(newState.State) if err != nil { state.Err = err } @@ -100,12 +100,14 @@ func listenPlayer(player player.Player, ch chan playerState, interval int) { st := playerState{Err: err} if state != nil { st.ID = state.ID - st.Query = state.Query + st.Album = state.Album + st.Artist = state.Artist + st.Title = state.Title + st.TrackNumber = state.TrackNumber st.Playing = state.Playing st.Position = state.Position } ch <- st - time.Sleep(time.Millisecond * time.Duration(interval)) } } diff --git a/services/browser/browser.go b/services/browser/browser.go index 693b0fd..b64dee1 100644 --- a/services/browser/browser.go +++ b/services/browser/browser.go @@ -154,7 +154,8 @@ func (c *Client) State() (*player.State, error) { } return &player.State{ ID: query, - Query: query, + Artist: c.artist, + Title: c.title, Position: position, Playing: c.state == playing, }, nil diff --git a/services/combo/combo.go b/services/combo/combo.go new file mode 100644 index 0000000..df522a8 --- /dev/null +++ b/services/combo/combo.go @@ -0,0 +1,32 @@ +package combo + +import ( + "sptlrx/lyrics" + "sptlrx/player" +) + +func New(providers []lyrics.Provider) (*ComboClient, error) { + + return &ComboClient{providers: providers}, nil +} + +type ComboClient struct { + providers []lyrics.Provider +} + +func (c *ComboClient) Lyrics(state player.State) ([]lyrics.Line, error) { + for _, p := range c.providers { + provider := p + newLines, err := provider.Lyrics(state) + if err != nil || newLines == nil { + continue + } else { + return newLines, nil + } + } + return nil, nil +} + +func (c *ComboClient) Name() string { + return "COMBO" +} diff --git a/services/hosted/hosted.go b/services/hosted/hosted.go index 3d17b8b..966c4c6 100644 --- a/services/hosted/hosted.go +++ b/services/hosted/hosted.go @@ -6,10 +6,11 @@ import ( "net/http" "net/url" "sptlrx/lyrics" + "sptlrx/player" ) // Host your own: https://github.com/raitonoberu/lyricsapi -func New(host string) *Client { +func New(host string) lyrics.Provider { return &Client{ host: host, } @@ -20,7 +21,9 @@ type Client struct { host string } -func (c *Client) Lyrics(id, query string) ([]lyrics.Line, error) { +func (c *Client) Lyrics(state player.State) ([]lyrics.Line, error) { + + query := state.Artist + " " + state.Title var url = fmt.Sprintf("https://%s/api/lyrics?name=%s", c.host, url.QueryEscape(query)) req, _ := http.NewRequest("GET", url, nil) @@ -32,5 +35,13 @@ func (c *Client) Lyrics(id, query string) ([]lyrics.Line, error) { var result []lyrics.Line err = json.NewDecoder(resp.Body).Decode(&result) - return result, err + if len(result) > 0 { + return result, err + } else { + return nil, err + } +} + +func (c *Client) Name() string { + return "HOSTD" } diff --git a/services/local/local.go b/services/local/local.go index 163028c..4b5de6a 100644 --- a/services/local/local.go +++ b/services/local/local.go @@ -8,16 +8,18 @@ import ( "os" "path/filepath" "sptlrx/lyrics" + "sptlrx/player" "strconv" "strings" ) var replacer = strings.NewReplacer( "_", " ", "-", " ", - ",", "", ".", "", - "!", "", "?", "", - "(", "", ")", "", - "[", "", "]", "", + ",", " ", ".", " ", + "!", " ", "?", " ", + "(", " ", ")", " ", + "[", " ", "]", " ", + "/", " ", ) type file struct { @@ -25,7 +27,7 @@ type file struct { NameParts []string } -func New(folder string) (*Client, error) { +func New(folder string) (lyrics.Provider, error) { index, err := createIndex(folder) if err != nil { return nil, err @@ -38,8 +40,9 @@ type Client struct { index []*file } -func (c *Client) Lyrics(id, query string) ([]lyrics.Line, error) { - f := c.findFile(query) +func (c *Client) Lyrics(state player.State) ([]lyrics.Line, error) { + f := c.findFile(state) + if f == nil { return nil, nil } @@ -53,12 +56,24 @@ func (c *Client) Lyrics(id, query string) ([]lyrics.Line, error) { return parseLrcFile(reader), nil } -func (c *Client) findFile(query string) *file { - parts := splitString(query) - +func (c *Client) findFile(state player.State) *file { + possiblePath := strings.Replace(strings.Replace(state.SongPath, ".mp3", ".lrc", 1), "file://", "", 1) var best *file + parts := splitString(state.Artist + " " + state.Album + " " + strconv.Itoa(state.TrackNumber) + " " + state.Title) + + existsFile, existsErr := os.Stat(possiblePath) + + if existsErr == nil && existsFile != nil { + best = &file{ + Path: possiblePath, + NameParts: parts, + } + return best + } + var maxScore int for _, f := range c.index { + var score int for _, part := range parts { for _, namePart := range f.NameParts { @@ -76,7 +91,10 @@ func (c *Client) findFile(query string) *file { } } } - return best + if strings.Contains(best.Path, state.Artist) && strings.Contains(best.Path, state.Album) && strings.Contains(best.Path, state.Title) && strings.Contains(best.Path, strconv.Itoa(state.TrackNumber)) { + return best + } + return nil } func createIndex(folder string) ([]*file, error) { @@ -93,8 +111,8 @@ func createIndex(folder string) ([]*file, error) { if d.IsDir() || !strings.HasSuffix(d.Name(), ".lrc") { return nil } - name := strings.TrimSuffix(d.Name(), ".lrc") - parts := splitString(name) + + parts := splitString(path) index = append(index, &file{ Path: path, @@ -134,3 +152,7 @@ func parseLrcLine(line string) lyrics.Line { Words: line[10:], } } + +func (c *Client) Name() string { + return "LOCAL" +} diff --git a/services/mopidy/mopidy.go b/services/mopidy/mopidy.go index 8ef50c1..9c5a67c 100644 --- a/services/mopidy/mopidy.go +++ b/services/mopidy/mopidy.go @@ -71,12 +71,11 @@ func (c *Client) State() (*player.State, error) { artist += a.Name } - query := artist + " " + current.Result.Name - return &player.State{ ID: current.Result.URI, - Query: query, + Title: current.Result.Name, Position: position.Result, + Artist: artist, Playing: state.Result == "playing", }, err } diff --git a/services/mpd/mpd.go b/services/mpd/mpd.go index daa5dac..598b0df 100644 --- a/services/mpd/mpd.go +++ b/services/mpd/mpd.go @@ -66,16 +66,10 @@ func (c *Client) State() (*player.State, error) { artist = a } - var query string - if artist != "" { - query = artist + " " + title - } else { - query = title - } - return &player.State{ ID: status["songid"], - Query: query, + Artist: artist, + Title: title, Playing: status["state"] == "play", Position: int(elapsed) * 1000, }, nil diff --git a/services/mpris/mpris_unix.go b/services/mpris/mpris_unix.go index d8622c1..608709f 100644 --- a/services/mpris/mpris_unix.go +++ b/services/mpris/mpris_unix.go @@ -74,11 +74,26 @@ func (c *Client) State() (*player.State, error) { return nil, err } - var title string + var songPath string = "" + if p, ok := meta["xesam:url"].Value().(string); ok { + songPath = p + } + + var trackNumber int = 0 + if n, ok := meta["xesam:trackNumber"].Value().(int); ok { + trackNumber = n + } + + var title string = "" if t, ok := meta["xesam:title"].Value().(string); ok { title = t } + var album string = "" + if al, ok := meta["xesam:album"].Value().(string); ok { + album = al + } + var artist string switch a := meta["xesam:artist"].Value(); a.(type) { case string: @@ -87,17 +102,14 @@ func (c *Client) State() (*player.State, error) { artist = strings.Join(a.([]string), " ") } - var query string - if artist != "" { - query = artist + " " + title - } else { - query = title - } - return &player.State{ - ID: query, // use query as id since mpris:trackid is broken - Query: query, - Position: int(position * 1000), // secs to ms - Playing: status == mpris.PlaybackPlaying, + ID: songPath, // use query as id since mpris:trackid is broken + TrackNumber: trackNumber, + Artist: artist, + Album: album, + Title: title, + SongPath: songPath, + Position: int(position * 1000), // secs to ms + Playing: status == mpris.PlaybackPlaying, }, err } diff --git a/services/spotify/spotify.go b/services/spotify/spotify.go index c4edc2b..5b6af36 100644 --- a/services/spotify/spotify.go +++ b/services/spotify/spotify.go @@ -77,15 +77,20 @@ func (c *Client) State() (*player.State, error) { }, nil } -func (c *Client) Lyrics(id, query string) ([]lyrics.Line, error) { - if strings.HasPrefix(id, "spotify:") { - return c.lyrics(id[8:]) +func (c *Client) Lyrics(state player.State) ([]lyrics.Line, error) { + if strings.HasPrefix(state.ID, "spotify:") { + return c.lyrics(state.ID[8:]) } - id, err := c.search(query) + id, err := c.search(state.Artist + " " + state.Title) if err != nil { return nil, err } - return c.lyrics(id) + lys, err := c.lyrics(id) + if len(lys) > 0 && err != nil { + return lys, err + } else { + return nil, err + } } func (c *Client) search(query string) (string, error) { @@ -242,3 +247,7 @@ type searchBody struct { Total int `json:"total"` } `json:"tracks"` } + +func (c *Client) Name() string { + return "SPOTI" +} diff --git a/ui/ui.go b/ui/ui.go index 806a8e7..68404c4 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -104,6 +104,7 @@ func (m *Model) View() string { return "" } if m.state.Err != nil && !m.Config.IgnoreErrors { + os.Stderr.WriteString(m.state.Err.Error()) return gloss.PlaceVertical( m.h, gloss.Center, m.styleCurrent. From fb7756e04d1833abc1417f081a58aedf3f917557 Mon Sep 17 00:00:00 2001 From: "Brian C. Arnold" Date: Tue, 30 Jan 2024 12:18:42 -0500 Subject: [PATCH 2/3] Got hosted, local, and spotify sources failing over. Right now, local is the last option. Also, local now checks if the mp3 path from xesam has a lrc at that exact path. --- services/hosted/hosted.go | 10 +++++++++- services/local/local.go | 11 ++++++++++- services/spotify/spotify.go | 8 ++++++++ ui/ui.go | 1 - 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/services/hosted/hosted.go b/services/hosted/hosted.go index 966c4c6..c1098fa 100644 --- a/services/hosted/hosted.go +++ b/services/hosted/hosted.go @@ -36,7 +36,15 @@ func (c *Client) Lyrics(state player.State) ([]lyrics.Line, error) { var result []lyrics.Line err = json.NewDecoder(resp.Body).Decode(&result) if len(result) > 0 { - return result, err + // var header []lyrics.Line + // header = append(header, lyrics.Line{ + // Time: 0, + // Words: "Loading from Hosted...", + // }) + // if result[0].Time < 10 { + // result[0].Time = 10 + // } + return result, nil } else { return nil, err } diff --git a/services/local/local.go b/services/local/local.go index 4b5de6a..3579f6a 100644 --- a/services/local/local.go +++ b/services/local/local.go @@ -53,7 +53,16 @@ func (c *Client) Lyrics(state player.State) ([]lyrics.Line, error) { } defer reader.Close() - return parseLrcFile(reader), nil + lys := parseLrcFile(reader) + // var header []lyrics.Line + // header = append(header, lyrics.Line{ + // Time: 0, + // Words: "Loading from Local...", + // }) + // if lys[0].Time < 10 { + // lys[0].Time = 10 + // } + return lys, nil } func (c *Client) findFile(state player.State) *file { diff --git a/services/spotify/spotify.go b/services/spotify/spotify.go index 5b6af36..d0a0767 100644 --- a/services/spotify/spotify.go +++ b/services/spotify/spotify.go @@ -87,6 +87,14 @@ func (c *Client) Lyrics(state player.State) ([]lyrics.Line, error) { } lys, err := c.lyrics(id) if len(lys) > 0 && err != nil { + // var header []lyrics.Line + // header = append(header, lyrics.Line{ + // Time: 0, + // Words: "Loading from Spotify...", + // }) + // if lys[0].Time < 10 { + // lys[0].Time = 10 + // } return lys, err } else { return nil, err diff --git a/ui/ui.go b/ui/ui.go index 68404c4..806a8e7 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -104,7 +104,6 @@ func (m *Model) View() string { return "" } if m.state.Err != nil && !m.Config.IgnoreErrors { - os.Stderr.WriteString(m.state.Err.Error()) return gloss.PlaceVertical( m.h, gloss.Center, m.styleCurrent. From 81b3cc405b8104ee7aa4e038d29d9ca597714e40 Mon Sep 17 00:00:00 2001 From: "Brian C. Arnold" Date: Wed, 31 Jan 2024 02:27:04 -0500 Subject: [PATCH 3/3] Temporarily added STDERR logging information, need to check the config to decide whether to log. Also, loads multiple players now (mpris, browser, spotify, ...) but I don't think it's pushing all the events to the same channel? Need to write something so that it handles the first item in the player list when its playing and sends data, that way we can say 'get something from the browser if they browser is playing, but if a local app is sending MPRIS data, then use that data, etc., etc.,' This will allow users to specify that they want Spotify as an app, Strawberry locally, and Jellyfin in a browser to all provide information, and to use the top listed one first if they all send data. Also, it can now provide lyrics from multiple sources, local, hosted, spotify, etc. --- cmd/pipe.go | 8 +++--- cmd/root.go | 33 ++++++++-------------- config/config.go | 55 ++++++++++++++++++++++-------------- pool/pool.go | 6 ++-- services/browser/browser.go | 20 +++++++++---- services/hosted/hosted.go | 4 +++ services/local/local.go | 3 ++ services/mopidy/mopidy.go | 2 +- services/mpd/mpd.go | 2 +- services/mpris/mpris_unix.go | 11 +++++++- services/spotify/spotify.go | 13 ++++++++- 11 files changed, 99 insertions(+), 58 deletions(-) diff --git a/cmd/pipe.go b/cmd/pipe.go index cb65aae..78ffbba 100644 --- a/cmd/pipe.go +++ b/cmd/pipe.go @@ -21,17 +21,17 @@ var pipeCmd = &cobra.Command{ if err != nil { return fmt.Errorf("couldn't load config: %w", err) } - player, err := loadPlayer(conf) + players, err := loadPlayers(conf) if err != nil { - return fmt.Errorf("couldn't load player: %w", err) + return fmt.Errorf("couldn't load players: %w", err) } - provider, err := loadProvider(conf, player) + provider, err := loadProvider(conf) if err != nil { return fmt.Errorf("couldn't load provider: %w", err) } ch := make(chan pool.Update) - go pool.Listen(player, provider, conf, ch) + go pool.Listen(players, provider, conf, ch) for update := range ch { printUpdate(update, conf) diff --git a/cmd/root.go b/cmd/root.go index de1c817..a1f2f5d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -36,7 +36,6 @@ const help = ` 1. Open your browser. var ( FlagCookie string - FlagPlayer string FlagConfig string FlagStyleBefore string @@ -59,17 +58,17 @@ var rootCmd = &cobra.Command{ if err != nil { return fmt.Errorf("couldn't load config: %w", err) } - player, err := loadPlayer(conf) + players, err := loadPlayers(conf) if err != nil { return fmt.Errorf("couldn't load player: %w", err) } - provider, err := loadProvider(conf, player) + provider, err := loadProvider(conf) if err != nil { return fmt.Errorf("couldn't load provider: %w", err) } ch := make(chan pool.Update) - go pool.Listen(player, provider, conf, ch) + go pool.Listen(players, provider, conf, ch) _, err = tea.NewProgram( &ui.Model{ @@ -110,9 +109,6 @@ func loadConfig(cmd *cobra.Command) (*config.Config, error) { conf.IgnoreErrors = false } - if cmd.Flags().Changed("player") { - conf.Player = FlagPlayer - } if cmd.Flags().Changed("before") { conf.Style.Before = parseStyleFlag(FlagStyleBefore) } @@ -128,8 +124,8 @@ func loadConfig(cmd *cobra.Command) (*config.Config, error) { return conf, nil } -func loadPlayer(conf *config.Config) (player.Player, error) { - player, err := config.GetPlayer(conf) +func loadPlayers(conf *config.Config) ([]*player.Player, error) { + players, err := config.GetPlayers(conf) if err != nil { if errors.Is(err, spotify.ErrInvalidCookie) { fmt.Println("If you want to use Spotify as your player, you need to set up your cookie.") @@ -137,20 +133,19 @@ func loadPlayer(conf *config.Config) (player.Player, error) { } return nil, err } - return player, nil + return players, nil } -func loadProvider(conf *config.Config, player player.Player) (lyrics.Provider, error) { +func loadProvider(conf *config.Config) (lyrics.Provider, error) { providers := []lyrics.Provider{} + if conf.Cookie == "" { coovieProv := hosted.New(conf.Host) providers = append(providers, coovieProv) - } - if spt, ok := player.(*spotify.Client); ok { - // spt, _ := spotify.New(conf.Cookie) - // use existing spotify client - providers = append(providers, spt) + } else { + var s, _ = spotify.NewProvider(conf.Cookie) + providers = append(providers, s) } if conf.Local.Folder != "" { localProv, err := local.New(conf.Local.Folder) @@ -159,11 +154,6 @@ func loadProvider(conf *config.Config, player player.Player) (lyrics.Provider, e } // return local.New(conf.Local.Folder) } - // create new spotify client - if len(providers) == 0 { - var s, _ = spotify.New(conf.Cookie) - providers = append(providers, s) - } return combo.New(providers) } @@ -196,7 +186,6 @@ func parseStyleFlag(value string) config.Style { func init() { rootCmd.PersistentFlags().StringVarP(&FlagCookie, "cookie", "c", "", "your cookie") - rootCmd.PersistentFlags().StringVarP(&FlagPlayer, "player", "p", "spotify", "what player to use") rootCmd.PersistentFlags().StringVar(&FlagConfig, "config", config.Path, "path to config file") rootCmd.Flags().StringVar(&FlagStyleBefore, "before", "bold", "style of the lines before the current one") diff --git a/config/config.go b/config/config.go index 7febae9..dec7efb 100644 --- a/config/config.go +++ b/config/config.go @@ -31,12 +31,12 @@ func init() { } type Config struct { - Cookie string `yaml:"cookie"` - Player string `default:"spotify" yaml:"player"` - Host string `default:"lyricsapi.vercel.app" yaml:"host"` - IgnoreErrors bool `default:"true" yaml:"ignoreErrors"` - TimerInterval int `default:"200" yaml:"timerInterval"` - UpdateInterval int `default:"2000" yaml:"updateInterval"` + Cookie string `yaml:"cookie"` + Players []string `default:"[spotify]" yaml:"players"` + Host string `default:"lyricsapi.vercel.app" yaml:"host"` + IgnoreErrors bool `default:"true" yaml:"ignoreErrors"` + TimerInterval int `default:"200" yaml:"timerInterval"` + UpdateInterval int `default:"2000" yaml:"updateInterval"` Style struct { HAlignment string `default:"center" yaml:"hAlignment"` @@ -172,19 +172,32 @@ func validateColor(color string) bool { return false } -// GetPlayer returns a player based on config values -func GetPlayer(conf *Config) (player.Player, error) { - switch conf.Player { - case "spotify": - return spotify.New(conf.Cookie) - case "mpd": - return mpd.New(conf.Mpd.Address, conf.Mpd.Password), nil - case "mopidy": - return mopidy.New(conf.Mopidy.Address), nil - case "mpris": - return mpris.New(conf.Mpris.Players) - case "browser": - return browser.New(conf.Browser.Port) - } - return nil, fmt.Errorf("unknown player: \"%s\"", conf.Player) +// GetPlayers returns a player based on config values +func GetPlayers(conf *Config) ([]*player.Player, error) { + var players []*player.Player + for _, p := range conf.Players { + os.Stderr.WriteString("LOADR: Processing " + p) + switch p { + case "spotify": + spotifyPlayer, _ := spotify.NewPlayer(conf.Cookie) + players = append(players, &spotifyPlayer) + case "mpd": + mpdPlayer := mpd.New(conf.Mpd.Address, conf.Mpd.Password) + players = append(players, &mpdPlayer) + case "mopidy": + mopidyPlayer := mopidy.New(conf.Mopidy.Address) + players = append(players, &mopidyPlayer) + case "mpris": + mprisPlayer, _ := mpris.New(conf.Mpris.Players) + players = append(players, &mprisPlayer) + case "browser": + browserPlayer, _ := browser.New(conf.Browser.Port) + players = append(players, &browserPlayer) + } + } + if len(players) > 0 { + return players, nil + } + + return nil, fmt.Errorf("unknown players: \"%s\"", conf.Players) } diff --git a/pool/pool.go b/pool/pool.go index f98722c..fa0e4c9 100644 --- a/pool/pool.go +++ b/pool/pool.go @@ -18,13 +18,15 @@ type Update struct { // Listen polls for lyrics updates and writes them to the channel. func Listen( - player player.Player, + players []*player.Player, provider lyrics.Provider, conf *config.Config, ch chan Update, ) { stateCh := make(chan playerState) - go listenPlayer(player, stateCh, conf.UpdateInterval) + for _, p := range players { + go listenPlayer(*p, stateCh, conf.UpdateInterval) + } ticker := time.NewTicker( time.Millisecond * time.Duration(conf.TimerInterval), diff --git a/services/browser/browser.go b/services/browser/browser.go index b64dee1..e03a4a2 100644 --- a/services/browser/browser.go +++ b/services/browser/browser.go @@ -5,6 +5,7 @@ import ( "io" "net" "net/http" + "os" "sptlrx/player" "strconv" "strings" @@ -24,7 +25,7 @@ const ( playing ) -func New(port int) (*Client, error) { +func New(port int) (player.Player, error) { c := &Client{} return c, c.start(port) } @@ -73,6 +74,8 @@ func (c *Client) handler(w http.ResponseWriter, r *http.Request) { if err != nil { return } + + os.Stderr.WriteString("BRWSR: msg: " + string(msg) + "\n") if t != websocket.MessageText || len(msg) == 0 { continue } @@ -81,6 +84,8 @@ func (c *Client) handler(w http.ResponseWriter, r *http.Request) { } func (c *Client) processMessage(msg string) { + + os.Stderr.WriteString("BRWSR: Recieved message" + "\n") spaceIndex := strings.IndexByte(msg, ' ') if spaceIndex == -1 { return @@ -140,20 +145,25 @@ func (c *Client) State() (*player.State, error) { if c.state == stopped { return nil, nil } + os.Stderr.WriteString("BRWSR: Found Song" + "\n") - var query string + var id string if c.artist != "" { - query = c.artist + " " + c.title + id = c.artist + " " + c.title } else { - query = c.title + id = c.title } + os.Stderr.WriteString("BRWSR: Artist" + c.artist + "\n") + os.Stderr.WriteString("BRWSR: Title" + c.title + "\n") + os.Stderr.WriteString("BRWSR: Position" + strconv.Itoa(c.position) + "\n") + position := c.position if c.state != paused { position += int(time.Since(c.updateTime).Milliseconds()) } return &player.State{ - ID: query, + ID: id, Artist: c.artist, Title: c.title, Position: position, diff --git a/services/hosted/hosted.go b/services/hosted/hosted.go index c1098fa..5dde553 100644 --- a/services/hosted/hosted.go +++ b/services/hosted/hosted.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "net/url" + "os" "sptlrx/lyrics" "sptlrx/player" ) @@ -29,6 +30,7 @@ func (c *Client) Lyrics(state player.State) ([]lyrics.Line, error) { req, _ := http.NewRequest("GET", url, nil) resp, err := http.DefaultClient.Do(req) if err != nil { + os.Stderr.WriteString("HOSTD: Could not find lyrics" + "\n") return nil, err } defer resp.Body.Close() @@ -44,8 +46,10 @@ func (c *Client) Lyrics(state player.State) ([]lyrics.Line, error) { // if result[0].Time < 10 { // result[0].Time = 10 // } + os.Stderr.WriteString("HOSTD: Found Lyrics" + "\n") return result, nil } else { + os.Stderr.WriteString("HOSTD: Empty Lyrics" + "\n") return nil, err } } diff --git a/services/local/local.go b/services/local/local.go index 3579f6a..52b4774 100644 --- a/services/local/local.go +++ b/services/local/local.go @@ -44,9 +44,12 @@ func (c *Client) Lyrics(state player.State) ([]lyrics.Line, error) { f := c.findFile(state) if f == nil { + os.Stderr.WriteString("LOCAL: Could not find local lyrics" + "\n") return nil, nil } + os.Stderr.WriteString("LOCAL: Found lyrics at:" + f.Path + "\n") + reader, err := os.Open(f.Path) if err != nil { return nil, err diff --git a/services/mopidy/mopidy.go b/services/mopidy/mopidy.go index 9c5a67c..bde3774 100644 --- a/services/mopidy/mopidy.go +++ b/services/mopidy/mopidy.go @@ -8,7 +8,7 @@ import ( "sptlrx/player" ) -func New(address string) *Client { +func New(address string) player.Player { return &Client{address: address} } diff --git a/services/mpd/mpd.go b/services/mpd/mpd.go index 598b0df..6243970 100644 --- a/services/mpd/mpd.go +++ b/services/mpd/mpd.go @@ -7,7 +7,7 @@ import ( "github.com/fhs/gompd/mpd" ) -func New(address, password string) *Client { +func New(address, password string) player.Player { return &Client{ address: address, password: password, diff --git a/services/mpris/mpris_unix.go b/services/mpris/mpris_unix.go index 608709f..f36c425 100644 --- a/services/mpris/mpris_unix.go +++ b/services/mpris/mpris_unix.go @@ -3,14 +3,16 @@ package mpris import ( + "os" "sptlrx/player" + "strconv" "strings" "github.com/Pauloo27/go-mpris" "github.com/godbus/dbus/v5" ) -func New(players []string) (*Client, error) { +func New(players []string) (player.Player, error) { return &Client{players}, nil } @@ -52,6 +54,8 @@ func (c *Client) getPlayer() (*mpris.Player, error) { } func (c *Client) State() (*player.State, error) { + + os.Stderr.WriteString("MPRIS: Found Song" + "\n") p, err := c.getPlayer() if err != nil { return nil, err @@ -77,21 +81,25 @@ func (c *Client) State() (*player.State, error) { var songPath string = "" if p, ok := meta["xesam:url"].Value().(string); ok { songPath = p + os.Stderr.WriteString("MPRIS: Path: " + songPath + "\n") } var trackNumber int = 0 if n, ok := meta["xesam:trackNumber"].Value().(int); ok { trackNumber = n + os.Stderr.WriteString("MPRIS: TrackNum: " + strconv.Itoa(trackNumber) + "\n") } var title string = "" if t, ok := meta["xesam:title"].Value().(string); ok { title = t + os.Stderr.WriteString("MPRIS: Title: " + title + "\n") } var album string = "" if al, ok := meta["xesam:album"].Value().(string); ok { album = al + os.Stderr.WriteString("MPRIS: Album: " + album + "\n") } var artist string @@ -101,6 +109,7 @@ func (c *Client) State() (*player.State, error) { case []string: artist = strings.Join(a.([]string), " ") } + os.Stderr.WriteString("MPRIS: Artist: " + artist + "\n") return &player.State{ ID: songPath, // use query as id since mpris:trackid is broken diff --git a/services/spotify/spotify.go b/services/spotify/spotify.go index d0a0767..4bd61b9 100644 --- a/services/spotify/spotify.go +++ b/services/spotify/spotify.go @@ -6,6 +6,7 @@ import ( "io" "net/http" "net/url" + "os" "sptlrx/lyrics" "sptlrx/player" "strings" @@ -21,7 +22,13 @@ const lyricsUrl = "https://spclient.wg.spotify.com/color-lyrics/v2/track/" const stateUrl = "https://api.spotify.com/v1/me/player/currently-playing" const searchUrl = "https://api.spotify.com/v1/search?" -func New(cookie string) (*Client, error) { +func NewProvider(cookie string) (lyrics.Provider, error) { + if cookie == "" { + return nil, ErrInvalidCookie + } + return &Client{cookie: cookie}, nil +} +func NewPlayer(cookie string) (player.Player, error) { if cookie == "" { return nil, ErrInvalidCookie } @@ -79,10 +86,12 @@ func (c *Client) State() (*player.State, error) { func (c *Client) Lyrics(state player.State) ([]lyrics.Line, error) { if strings.HasPrefix(state.ID, "spotify:") { + os.Stderr.WriteString("SPTFY: Found Lyrics" + "\n") return c.lyrics(state.ID[8:]) } id, err := c.search(state.Artist + " " + state.Title) if err != nil { + os.Stderr.WriteString("SPTFY: Error Finding Lyrics" + "\n") return nil, err } lys, err := c.lyrics(id) @@ -95,8 +104,10 @@ func (c *Client) Lyrics(state player.State) ([]lyrics.Line, error) { // if lys[0].Time < 10 { // lys[0].Time = 10 // } + os.Stderr.WriteString("SPTFY: Found Lyrics" + "\n") return lys, err } else { + os.Stderr.WriteString("SPTFY: Empty Lyrics" + "\n") return nil, err } }