Listenbrainz scrobbling (#1424)
* Refactor session_keys to its own package * Adjust play_tracker - Don't send external NowPlaying/Scrobble for tracks with unknown artist - Continue to the next agent on error * Implement ListenBrainz Agent and Auth Router * Implement frontend for ListenBrainz linking * Update listenBrainzRequest - Don't marshal Player to json - Rename Track to Title * Return ErrRetryLater on ListenBrainz server errors * Add tests for listenBrainzAgent * Add tests for ListenBrainz Client * Adjust ListenBrainzTokenDialog to handle errors better * Refactor listenbrainz.formatListen and listenBrainzRequest structs * Refactor agent auth_routers * Refactor session_keys to agents package * Add test for listenBrainzResponse * Add tests for ListenBrainz auth_router * Update ListenBrainzTokenDialog and auth_router * Adjust player scrobble toggle
This commit is contained in:
@@ -14,12 +14,13 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
lastFMAgentName = "lastfm"
|
||||
lastFMAgentName = "lastfm"
|
||||
sessionKeyProperty = "LastFMSessionKey"
|
||||
)
|
||||
|
||||
type lastfmAgent struct {
|
||||
ds model.DataStore
|
||||
sessionKeys *sessionKeys
|
||||
sessionKeys *agents.SessionKeys
|
||||
apiKey string
|
||||
secret string
|
||||
lang string
|
||||
@@ -32,7 +33,7 @@ func lastFMConstructor(ds model.DataStore) *lastfmAgent {
|
||||
lang: conf.Server.LastFM.Language,
|
||||
apiKey: conf.Server.LastFM.ApiKey,
|
||||
secret: conf.Server.LastFM.Secret,
|
||||
sessionKeys: &sessionKeys{ds: ds},
|
||||
sessionKeys: &agents.SessionKeys{DataStore: ds, KeyName: sessionKeyProperty},
|
||||
}
|
||||
hc := &http.Client{
|
||||
Timeout: consts.DefaultHttpClientTimeOut,
|
||||
@@ -159,7 +160,7 @@ func (l *lastfmAgent) callArtistGetTopTracks(ctx context.Context, artistName, mb
|
||||
}
|
||||
|
||||
func (l *lastfmAgent) NowPlaying(ctx context.Context, userId string, track *model.MediaFile) error {
|
||||
sk, err := l.sessionKeys.get(ctx, userId)
|
||||
sk, err := l.sessionKeys.Get(ctx, userId)
|
||||
if err != nil || sk == "" {
|
||||
return scrobbler.ErrNotAuthorized
|
||||
}
|
||||
@@ -181,7 +182,7 @@ func (l *lastfmAgent) NowPlaying(ctx context.Context, userId string, track *mode
|
||||
}
|
||||
|
||||
func (l *lastfmAgent) Scrobble(ctx context.Context, userId string, s scrobbler.Scrobble) error {
|
||||
sk, err := l.sessionKeys.get(ctx, userId)
|
||||
sk, err := l.sessionKeys.Get(ctx, userId)
|
||||
if err != nil || sk == "" {
|
||||
return scrobbler.ErrNotAuthorized
|
||||
}
|
||||
@@ -215,7 +216,7 @@ func (l *lastfmAgent) Scrobble(ctx context.Context, userId string, s scrobbler.S
|
||||
}
|
||||
|
||||
func (l *lastfmAgent) IsAuthorized(ctx context.Context, userId string) bool {
|
||||
sk, err := l.sessionKeys.get(ctx, userId)
|
||||
sk, err := l.sessionKeys.Get(ctx, userId)
|
||||
return err == nil && sk != ""
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/navidrome/navidrome/conf"
|
||||
"github.com/navidrome/navidrome/consts"
|
||||
"github.com/navidrome/navidrome/core/agents"
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/navidrome/navidrome/model/request"
|
||||
@@ -25,7 +26,7 @@ var tokenReceivedPage []byte
|
||||
type Router struct {
|
||||
http.Handler
|
||||
ds model.DataStore
|
||||
sessionKeys *sessionKeys
|
||||
sessionKeys *agents.SessionKeys
|
||||
client *Client
|
||||
apiKey string
|
||||
secret string
|
||||
@@ -36,7 +37,7 @@ func NewRouter(ds model.DataStore) *Router {
|
||||
ds: ds,
|
||||
apiKey: conf.Server.LastFM.ApiKey,
|
||||
secret: conf.Server.LastFM.Secret,
|
||||
sessionKeys: &sessionKeys{ds: ds},
|
||||
sessionKeys: &agents.SessionKeys{DataStore: ds, KeyName: sessionKeyProperty},
|
||||
}
|
||||
r.Handler = r.routes()
|
||||
hc := &http.Client{
|
||||
@@ -63,9 +64,9 @@ func (s *Router) routes() http.Handler {
|
||||
}
|
||||
|
||||
func (s *Router) getLinkStatus(w http.ResponseWriter, r *http.Request) {
|
||||
resp := map[string]interface{}{"status": true}
|
||||
resp := map[string]interface{}{}
|
||||
u, _ := request.UserFrom(r.Context())
|
||||
key, err := s.sessionKeys.get(r.Context(), u.ID)
|
||||
key, err := s.sessionKeys.Get(r.Context(), u.ID)
|
||||
if err != nil && err != model.ErrNotFound {
|
||||
resp["error"] = err
|
||||
resp["status"] = false
|
||||
@@ -78,7 +79,7 @@ func (s *Router) getLinkStatus(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (s *Router) unlink(w http.ResponseWriter, r *http.Request) {
|
||||
u, _ := request.UserFrom(r.Context())
|
||||
err := s.sessionKeys.delete(r.Context(), u.ID)
|
||||
err := s.sessionKeys.Delete(r.Context(), u.ID)
|
||||
if err != nil {
|
||||
_ = rest.RespondWithError(w, http.StatusInternalServerError, err.Error())
|
||||
} else {
|
||||
@@ -119,7 +120,7 @@ func (s *Router) fetchSessionKey(ctx context.Context, uid, token string) error {
|
||||
"requestId", middleware.GetReqID(ctx), err)
|
||||
return err
|
||||
}
|
||||
err = s.sessionKeys.put(ctx, uid, sessionKey)
|
||||
err = s.sessionKeys.Put(ctx, uid, sessionKey)
|
||||
if err != nil {
|
||||
log.Error("Could not save LastFM session key", "userId", uid, "requestId", middleware.GetReqID(ctx), err)
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package lastfm
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/navidrome/navidrome/model"
|
||||
)
|
||||
|
||||
const (
|
||||
sessionKeyProperty = "LastFMSessionKey"
|
||||
)
|
||||
|
||||
// sessionKeys is a simple wrapper around the UserPropsRepository
|
||||
type sessionKeys struct {
|
||||
ds model.DataStore
|
||||
}
|
||||
|
||||
func (sk *sessionKeys) put(ctx context.Context, userId, sessionKey string) error {
|
||||
return sk.ds.UserProps(ctx).Put(userId, sessionKeyProperty, sessionKey)
|
||||
}
|
||||
|
||||
func (sk *sessionKeys) get(ctx context.Context, userId string) (string, error) {
|
||||
return sk.ds.UserProps(ctx).Get(userId, sessionKeyProperty)
|
||||
}
|
||||
|
||||
func (sk *sessionKeys) delete(ctx context.Context, userId string) error {
|
||||
return sk.ds.UserProps(ctx).Delete(userId, sessionKeyProperty)
|
||||
}
|
||||
Reference in New Issue
Block a user