lastfm: listens export

This commit is contained in:
Philipp Wolfer 2023-11-28 08:55:53 +01:00
parent 267018901b
commit f9d25e3b6f
No known key found for this signature in database
GPG key ID: 8FDF744D4919943B
2 changed files with 96 additions and 5 deletions

View file

@ -11,6 +11,7 @@ Scotty transfers your listens/scrobbles and favorite tracks between various musi
- Transfer loved tracks from Funkwhale to ListenBrainz
- Submit listens stored in a Rockbox `.scrobbler.log` file to ListenBrainz, Last.fm or Maloja
- Store your favorite tracks from Deezer as a JSPF playlist
- Backup your listening history from ListenBrainz or Last.fm
## Installation
@ -44,7 +45,7 @@ deezer | ✓ | | ✓ | -
dump | | ✓ | | ✓
funkwhale | ✓ | | ✓ | -
jspf | - | ✓ | - | ✓
lastfm | - | - | ✓ | ✓
lastfm | ✓ | ✓ | ✓ | ✓
listenbrainz | ✓ | ✓ | ✓ | ✓
maloja | ✓ | ✓ | |
scrobbler-log | ✓ | ✓ | |

View file

@ -31,8 +31,10 @@ import (
)
const (
MaxItemsPerGet = 1000
MaxListensPerRequest = 50
MaxItemsPerGet = 1000
MaxListensPerGet = 200
MaxListensPerSubmission = 50
MaxPage = 1000000
)
type LastfmApiBackend struct {
@ -69,10 +71,98 @@ func (b *LastfmApiBackend) OAuth2Setup(token oauth2.TokenSource) error {
return nil
}
func (b *LastfmApiBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.Progress) {
page := MaxPage
minTime := oldestTimestamp
perPage := MaxItemsPerGet
defer close(results)
// We need to gather the full list of listens in order to sort them
p := models.Progress{Total: int64(page)}
out:
for page > 0 {
args := lastfm.P{
"user": b.username,
"limit": MaxListensPerGet,
// last.fm includes the listen with the exact timestamp in the result
"from": oldestTimestamp.Add(time.Second).Unix(),
"page": page,
}
result, err := b.client.User.GetRecentTracks(args)
if err != nil {
results <- models.ListensResult{Error: err}
progress <- p.Complete()
return
}
count := len(result.Tracks)
if count == 0 {
// The page was outside of the result range, adjust and request again
if page > result.TotalPages {
page = result.TotalPages
continue
}
break
}
listens := make(models.ListensList, 0, 2*perPage)
for _, scrobble := range result.Tracks {
timestamp, err := strconv.ParseInt(scrobble.Date.Uts, 10, 64)
if err != nil {
results <- models.ListensResult{Error: err}
progress <- p.Complete()
break out
}
if timestamp > oldestTimestamp.Unix() {
p.Elapsed += 1
listen := models.Listen{
ListenedAt: time.Unix(timestamp, 0),
UserName: b.username,
Track: models.Track{
TrackName: scrobble.Name,
ArtistNames: []string{},
ReleaseName: scrobble.Album.Name,
RecordingMbid: models.MBID(scrobble.Mbid),
ArtistMbids: []models.MBID{},
ReleaseMbid: models.MBID(scrobble.Album.Mbid),
},
}
if scrobble.Artist.Name != "" {
listen.Track.ArtistNames = []string{scrobble.Artist.Name}
}
if scrobble.Artist.Mbid != "" {
listen.Track.ArtistMbids = []models.MBID{models.MBID(scrobble.Artist.Mbid)}
}
listens = append(listens, listen)
} else {
break out
}
}
sort.Sort(listens)
minTime = listens[len(listens)-1].ListenedAt
page -= 1
results <- models.ListensResult{
Listens: listens,
Total: result.Total,
OldestTimestamp: minTime,
}
p.Total = int64(result.TotalPages)
p.Elapsed = int64(result.TotalPages - page)
progress <- p
}
results <- models.ListensResult{OldestTimestamp: minTime}
progress <- p.Complete()
}
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)]
for i := 0; i < total; i += MaxListensPerSubmission {
listens := export.Listens[i:min(i+MaxListensPerSubmission, total)]
count := len(listens)
if count == 0 {
break