diff --git a/internal/backends/lastfm/lastfm.go b/internal/backends/lastfm/lastfm.go index c65a0b4..e9cdf2d 100644 --- a/internal/backends/lastfm/lastfm.go +++ b/internal/backends/lastfm/lastfm.go @@ -16,6 +16,7 @@ Scotty. If not, see . package lastfm import ( + "errors" "fmt" "net/url" "sort" @@ -29,7 +30,10 @@ import ( "golang.org/x/oauth2" ) -const MaxItemsPerGet = 50 +const ( + MaxItemsPerGet = 1000 + MaxListensPerRequest = 50 +) type LastfmApiBackend struct { client *lastfm.Api @@ -65,6 +69,82 @@ func (b *LastfmApiBackend) OAuth2Setup(token oauth2.TokenSource) error { return nil } +func (b *LastfmApiBackend) ImportListens(export models.ListensResult, importResult models.ImportResult, progress chan models.Progress) (models.ImportResult, error) { + total := len(export.Listens) + for i := 0; i < total; i += MaxListensPerRequest { + listens := export.Listens[i:min(i+MaxListensPerRequest, total)] + count := len(listens) + if count == 0 { + break + } + + artists := make([]string, count) + tracks := make([]string, count) + timestamps := make([]string, count) + albums := make([]string, count) + trackNumbers := make([]string, count) + mbids := make([]string, count) + // albumArtists := make([]string, count) + durations := make([]int64, count) + + for _, l := range listens { + artists = append(artists, l.ArtistName()) + tracks = append(tracks, l.TrackName) + timestamps = append(timestamps, strconv.FormatInt(l.ListenedAt.Unix(), 10)) + if l.ReleaseName != "" { + albums = append(albums, l.ReleaseName) + } + if l.TrackNumber > 0 { + trackNumbers = append(trackNumbers, strconv.Itoa(l.TrackNumber)) + } + if l.RecordingMbid != "" { + mbids = append(mbids, string(l.RecordingMbid)) + } + // if l.ReleaseArtist != "" { + // albumArtists = append(albums, l.ReleaseArtist) + // } + if l.Duration > 0 { + durations = append(durations, int64(l.Duration.Seconds())) + } + } + + result, err := b.client.Track.Scrobble(lastfm.P{ + "artist": artists, + "track": tracks, + "timestamp": timestamps, + "album": albums, + "trackNumber": trackNumbers, + "mbid": mbids, + "duration": durations, + }) + if err != nil { + return importResult, err + } + + accepted, err := strconv.Atoi(result.Accepted) + if err != nil { + return importResult, err + } + + if accepted < count { + for _, s := range result.Scrobbles { + ignoreMsg := s.IgnoredMessage.Body + if ignoreMsg != "" { + importResult.ImportErrors = append(importResult.ImportErrors, ignoreMsg) + } + } + errMsg := fmt.Sprintf("Last.fm import ignored %v scrobbles", count-accepted) + return importResult, errors.New(errMsg) + } + + importResult.UpdateTimestamp(listens[count-1].ListenedAt) + importResult.ImportCount += accepted + progress <- models.Progress{}.FromImportResult(importResult) + } + + return importResult, nil +} + func (b *LastfmApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.Progress) { // Choose a high offset, we attempt to search the loves backwards starting // at the oldest one.