From 458294c8de2af24313d5d3e430482fb187128434 Mon Sep 17 00:00:00 2001 From: Thibaut Horel Date: Mon, 19 Jun 2017 19:21:58 -0400 Subject: Make lastfm scrobble imports cleaner and more efficient --- lfmclient.go | 131 ++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 85 insertions(+), 46 deletions(-) diff --git a/lfmclient.go b/lfmclient.go index c845b7d..bbc0492 100644 --- a/lfmclient.go +++ b/lfmclient.go @@ -28,6 +28,11 @@ type Date struct { Uts string } +func (d *Date) ToTime() time.Time { + ts, _ := strconv.Atoi(d.Uts) + return time.Unix(int64(ts), 0) +} + type PageAttrs struct { TotalPages string Total string @@ -55,6 +60,19 @@ type TrackInfo struct { Listeners string } +type LovedTrack struct { + Name string + Mbid string + Url string + Date Date + Artist ArtistInfo + Images []Image +} + +type Playing struct { + NowPlaying string +} + type RecentTrack struct { Name string Mbid string @@ -67,8 +85,9 @@ type RecentTrack struct { Name string `json:"#text"` Mbid string } - Images []Image `json:"image"` - Date Date + Images []Image `json:"image"` + Date Date + Playing `json:"@attr"` } func (app *App) LfmQuery(payload map[string]string) []byte { @@ -118,6 +137,58 @@ func (app *App) TrackInfo(artist, name string) TrackInfo { return dst["track"] } +type RecentTracks struct { + dst map[string]struct { + Attrs PageAttrs `json:"@attr"` + Tracks []RecentTrack `json:"track"` + } + payload map[string]string + *App +} + +func RecentTrackToScrobble(r *RecentTrack, s *Session) Scrobble { + scrobble := Scrobble{ + Artist: NewCorrectable(r.Artist.Name), + Album: NewCorrectable(r.Album.Name), + TrackName: NewCorrectable(r.Name), + Mbid: r.Mbid, + Time: r.Date.ToTime(), + UserId: s.UserId, + SessionKey: s.Key, + Image: GetImage(r.Images, "medium"), + } + return scrobble +} + +func NewRecentTracks(name string, from, to time.Time, app *App) *RecentTracks { + rt := &RecentTracks{App: app} + rt.payload = map[string]string{ + "method": "user.getRecentTracks", + "limit": "200", + "user": name, + "to": strconv.Itoa(int(to.Unix())), + } + if from.IsZero() { + rt.payload["from"] = "0" + } else { + rt.payload["from"] = strconv.Itoa(int(from.Unix())) + } + return rt +} + +func (rt *RecentTracks) Next() []RecentTrack { + r := rt.App.LfmQuery(rt.payload) + json.Unmarshal(r, &rt.dst) + tracks := rt.dst["recenttracks"].Tracks + if len(tracks) > 0 { + d := tracks[len(tracks)-1].Date + if d.Uts != "" && d.Uts != "0" { + rt.payload["to"] = d.Uts + } + } + return tracks +} + func (app *App) ImportRecentTracks(user *User) { s := &Session{ UserId: user.Id, @@ -128,77 +199,45 @@ func (app *App) ImportRecentTracks(user *User) { } app.PutSession(s) i := app.NewImport(user.LfmName) - payload := map[string]string{ - "method": "user.getRecentTracks", - "limit": "200", - "user": user.LfmName, - "to": strconv.Itoa(int(i.To.Unix())), - } - if i.From.IsZero() { - payload["from"] = "0" - } else { - payload["from"] = strconv.Itoa(int(i.From.Unix())) - } - var dst map[string]struct { - Attrs PageAttrs `json:"@attr"` - Tracks []RecentTrack `json:"track"` - } + rt := NewRecentTracks(user.LfmName, i.From, i.To, app) scrobbles := make([]Scrobble, 0, 200) - var st time.Time - for { + for tracks := rt.Next(); len(tracks) > 0; tracks = rt.Next() { scrobbles = scrobbles[:0] - if !i.LastFetch.IsZero() { - payload["to"] = strconv.Itoa(int(i.LastFetch.Unix())) - } - r := app.LfmQuery(payload) - json.Unmarshal(r, &dst) - tracks := dst["recenttracks"].Tracks - if len(tracks) == 0 { - i.Done = true - break - } for _, t := range tracks { - ts, _ := strconv.Atoi(t.Date.Uts) - if ts == 0 { + if t.NowPlaying == "true" { continue } - st = time.Unix(int64(ts), 0) - scrobble := Scrobble{ - Artist: NewCorrectable(t.Artist.Name), - Album: NewCorrectable(t.Album.Name), - TrackName: NewCorrectable(t.Name), - Mbid: t.Mbid, - Time: st, - UserId: user.Id, - SessionKey: s.Key, - Image: GetImage(t.Images, "medium"), - } + scrobble := RecentTrackToScrobble(&t, s) app.GetScrobbleSong(&scrobble) scrobbles = append(scrobbles, scrobble) } + if len(scrobbles) == 0 { + break + } if err := app.PutScrobbles(scrobbles); err != nil { log.Println(err) } - i.LastFetch = st + i.LastFetch = scrobbles[len(scrobbles)-1].Time i.Count += len(scrobbles) if err := app.SaveImport(i); err != nil { log.Println(err) } } + i.Done = true if err := app.SaveImport(i); err != nil { log.Println(err) } } -func (app *App) LovedTracks(user string) []TrackInfo { +func (app *App) LovedTracks(user string) []LovedTrack { r := app.LfmQuery(map[string]string{ "method": "user.getLovedTracks", "limit": "100", "user": user, }) var dst map[string]struct { - Attrs PageAttrs `json:"@attr"` - Tracks []TrackInfo `json:"track"` + Attrs PageAttrs `json:"@attr"` + Tracks []LovedTrack `json:"track"` } json.Unmarshal(r, &dst) root := dst["lovedtracks"] -- cgit v1.2.3-70-g09d2