From 406e150987e3a3b7f0b4406fc343dd2768a1d4cf Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Sun, 26 Nov 2023 12:56:27 +0100 Subject: [PATCH] lastfm: loves import --- README.md | 2 +- internal/backends/auth.go | 5 ++++- internal/backends/backends_test.go | 20 ++++++++++++++++++-- internal/backends/lastfm/auth.go | 7 ++++++- internal/backends/lastfm/lastfm.go | 29 ++++++++++++++++++++++++++++- 5 files changed, 57 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 862d388..d7d2be9 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ deezer | ✓ | ⨯ | ✓ | - dump | ⨯ | ✓ | ⨯ | ✓ funkwhale | ✓ | ⨯ | ✓ | - jspf | - | ✓ | - | ✓ -lastfm | - | - | - | - +lastfm | - | - | ✓ | ✓ listenbrainz | ✓ | ✓ | ✓ | ✓ maloja | ✓ | ✓ | ⨯ | ⨯ scrobbler-log | ✓ | ✓ | ⨯ | ⨯ diff --git a/internal/backends/auth.go b/internal/backends/auth.go index 49584ec..d27efd6 100644 --- a/internal/backends/auth.go +++ b/internal/backends/auth.go @@ -49,7 +49,10 @@ func Authenticate(service string, backend models.Backend, db storage.Database, c } conf := authenticator.OAuth2Strategy(redirectURL).Config() tokenSource := auth.NewDatabaseTokenSource(db, service, &conf, token) - authenticator.OAuth2Setup(tokenSource) + err = authenticator.OAuth2Setup(tokenSource) + if err != nil { + return needAuth, err + } } return needAuth, nil } diff --git a/internal/backends/backends_test.go b/internal/backends/backends_test.go index 89407a3..1af09a4 100644 --- a/internal/backends/backends_test.go +++ b/internal/backends/backends_test.go @@ -24,12 +24,15 @@ import ( "github.com/spf13/viper" "github.com/stretchr/testify/assert" "go.uploadedlobster.com/scotty/internal/backends" + "go.uploadedlobster.com/scotty/internal/backends/deezer" "go.uploadedlobster.com/scotty/internal/backends/dump" "go.uploadedlobster.com/scotty/internal/backends/funkwhale" "go.uploadedlobster.com/scotty/internal/backends/jspf" + "go.uploadedlobster.com/scotty/internal/backends/lastfm" "go.uploadedlobster.com/scotty/internal/backends/listenbrainz" "go.uploadedlobster.com/scotty/internal/backends/maloja" "go.uploadedlobster.com/scotty/internal/backends/scrobblerlog" + "go.uploadedlobster.com/scotty/internal/backends/spotify" "go.uploadedlobster.com/scotty/internal/backends/subsonic" "go.uploadedlobster.com/scotty/internal/models" ) @@ -73,6 +76,10 @@ func TestGetBackends(t *testing.T) { } func TestImplementsInterfaces(t *testing.T) { + expectInterface[models.ListensExport](t, &deezer.DeezerApiBackend{}) + expectInterface[models.LovesExport](t, &deezer.DeezerApiBackend{}) + // expectInterface[models.LovesImport](t, &deezer.DeezerApiBackend{}) + expectInterface[models.ListensImport](t, &dump.DumpBackend{}) expectInterface[models.LovesImport](t, &dump.DumpBackend{}) @@ -82,18 +89,27 @@ func TestImplementsInterfaces(t *testing.T) { // expectInterface[models.LovesImport](t, &funkwhale.FunkwhaleApiBackend{}) // expectInterface[models.ListensExport](t, &jspf.JSPFBackend{}) - // expectInterface[models.ListensImport](t, &jspf.JSPFBackend{}) + expectInterface[models.ListensImport](t, &jspf.JSPFBackend{}) // expectInterface[models.LovesExport](t, &jspf.JSPFBackend{}) expectInterface[models.LovesImport](t, &jspf.JSPFBackend{}) + // expectInterface[models.ListensExport](t, &lastfm.LastfmApiBackend{}) + // expectInterface[models.ListensImport](t, &lastfm.LastfmApiBackend{}) + expectInterface[models.LovesExport](t, &lastfm.LastfmApiBackend{}) + expectInterface[models.LovesImport](t, &lastfm.LastfmApiBackend{}) + expectInterface[models.ListensExport](t, &listenbrainz.ListenBrainzApiBackend{}) - // expectInterface[models.ListensImport](t, &listenbrainz.ListenBrainzApiBackend{}) + expectInterface[models.ListensImport](t, &listenbrainz.ListenBrainzApiBackend{}) expectInterface[models.LovesExport](t, &listenbrainz.ListenBrainzApiBackend{}) expectInterface[models.LovesImport](t, &listenbrainz.ListenBrainzApiBackend{}) expectInterface[models.ListensExport](t, &maloja.MalojaApiBackend{}) expectInterface[models.ListensImport](t, &maloja.MalojaApiBackend{}) + expectInterface[models.ListensExport](t, &spotify.SpotifyApiBackend{}) + expectInterface[models.LovesExport](t, &spotify.SpotifyApiBackend{}) + // expectInterface[models.LovesImport](t, &spotify.SpotifyApiBackend{}) + expectInterface[models.ListensExport](t, &scrobblerlog.ScrobblerLogBackend{}) expectInterface[models.ListensImport](t, &scrobblerlog.ScrobblerLogBackend{}) diff --git a/internal/backends/lastfm/auth.go b/internal/backends/lastfm/auth.go index c9718d5..bbc65bc 100644 --- a/internal/backends/lastfm/auth.go +++ b/internal/backends/lastfm/auth.go @@ -47,5 +47,10 @@ func (s lastfmStrategy) AuthCodeURL(verifier string, state string) auth.AuthUrl func (s lastfmStrategy) ExchangeToken(code auth.CodeResponse, verifier string) (*oauth2.Token, error) { // The token is directly valid - return &oauth2.Token{AccessToken: code.Code}, nil + err := s.client.LoginWithToken(code.Code) + if err != nil { + return nil, err + } + sk := s.client.GetSessionKey() + return &oauth2.Token{AccessToken: sk}, nil } diff --git a/internal/backends/lastfm/lastfm.go b/internal/backends/lastfm/lastfm.go index f215654..c65a0b4 100644 --- a/internal/backends/lastfm/lastfm.go +++ b/internal/backends/lastfm/lastfm.go @@ -16,6 +16,7 @@ Scotty. If not, see . package lastfm import ( + "fmt" "net/url" "sort" "strconv" @@ -45,6 +46,9 @@ func (b *LastfmApiBackend) FromConfig(config *viper.Viper) models.Backend { return b } +func (b *LastfmApiBackend) StartImport() error { return nil } +func (b *LastfmApiBackend) FinishImport() error { return nil } + func (b *LastfmApiBackend) OAuth2Strategy(redirectUrl *url.URL) auth.OAuth2Strategy { return lastfmStrategy{ client: b.client, @@ -57,7 +61,8 @@ func (b *LastfmApiBackend) OAuth2Setup(token oauth2.TokenSource) error { if err != nil { return err } - return b.client.LoginWithToken(t.AccessToken) + b.client.SetSession(t.AccessToken) + return nil } func (b *LastfmApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.Progress) { @@ -130,3 +135,25 @@ out: results <- models.LovesResult{Loves: loves, Total: totalCount} progress <- p.Complete() } + +func (b *LastfmApiBackend) ImportLoves(export models.LovesResult, importResult models.ImportResult, progress chan models.Progress) (models.ImportResult, error) { + for _, love := range export.Loves { + err := b.client.Track.Love(lastfm.P{ + "track": love.TrackName, + "artist": love.ArtistName(), + }) + + if err == nil { + importResult.UpdateTimestamp(love.Created) + importResult.ImportCount += 1 + } else { + msg := fmt.Sprintf("Failed import of \"%s\" by %s: %v", + love.TrackName, love.ArtistName(), err.Error()) + importResult.ImportErrors = append(importResult.ImportErrors, msg) + } + + progress <- models.Progress{}.FromImportResult(importResult) + } + + return importResult, nil +}