@@ -23,6 +23,8 @@ import (
2323
2424const (
2525 lastfmAPIBaseURL = "https://ws.audioscrobbler.com/2.0/"
26+ downloadLimit = 200
27+ noTimestampLimit = 5
2628)
2729
2830type Service struct {
@@ -97,17 +99,42 @@ func (l *Service) loadUsernames() error {
9799}
98100
99101// getRecentTracks fetches the most recent tracks for a given Last.fm user.
100- func (l * Service ) getRecentTracks (ctx context.Context , username string , limit int ) (* RecentTracksResponse , error ) {
102+ func (l * Service ) getRecentTracks (ctx context.Context , username string ) (* RecentTracksResponse , error ) {
101103 if username == "" {
102104 return nil , fmt .Errorf ("username cannot be empty" )
103105 }
104106
107+ if l .db == nil {
108+ return nil , fmt .Errorf ("database connection is nil" )
109+ }
110+
111+ user , err := l .db .GetUserByLastFM (username )
112+ if err != nil {
113+ return nil , fmt .Errorf ("failed to get user ID for %s: %w" , username , err )
114+ }
115+
116+ lastKnownTimestamp , err := l .db .GetLastKnownTimestamp (user .ID )
117+ if err != nil {
118+ return nil , fmt .Errorf ("failed to get last scrobble timestamp for %s: %w" , username , err )
119+ }
120+
105121 params := url.Values {}
106122 params .Set ("method" , "user.getrecenttracks" )
107123 params .Set ("user" , username )
108124 params .Set ("api_key" , l .apiKey )
109125 params .Set ("format" , "json" )
110- params .Set ("limit" , strconv .Itoa (limit )) // Fetch a few more to handle duplicates/now playing
126+
127+ if lastKnownTimestamp == nil {
128+ // If no timestamp, then just get the {noTimestampLimit} most recent tracks
129+ l .logger .Printf ("no previous scrobble timestamp found for user %s. retrieving last 5 tracks" , username )
130+ params .Set ("limit" , strconv .Itoa (noTimestampLimit ))
131+ } else {
132+ // As last.fm returns tracks >= from, this will always return at leastthe user's most recent track, but should
133+ // cover the (extremely remote) edge case where a user scrobbles multiple tracks within a single second
134+ l .logger .Printf ("retrieving all tracks for %s since last timestamp %s" , username , lastKnownTimestamp .Format (time .RFC3339 ))
135+ params .Set ("limit" , strconv .Itoa (downloadLimit ))
136+ params .Set ("from" , strconv .FormatInt (lastKnownTimestamp .Unix (), 10 ))
137+ }
111138
112139 apiURL := lastfmAPIBaseURL + "?" + params .Encode ()
113140
@@ -232,8 +259,7 @@ func (l *Service) fetchAllUserTracks(ctx context.Context) {
232259
233260 // Fetch slightly more than 1 track to better handle edge cases
234261 // where the latest is 'now playing' or duplicates exist.
235- const fetchLimit = 5
236- recentTracks , err := l .getRecentTracks (ctx , uname , fetchLimit )
262+ recentTracks , err := l .getRecentTracks (ctx , uname )
237263 if err != nil {
238264 l .logger .Printf ("Error fetching tracks for %s: %v" , uname , err )
239265 fetchErrors <- fmt .Errorf ("fetch failed for %s: %w" , uname , err ) // Report error
@@ -332,7 +358,7 @@ func (l *Service) processTracks(ctx context.Context, username string, tracks []T
332358
333359 // find last non-now-playing track
334360 var lastNonNowPlaying * Track
335- for i := len ( tracks ) - 1 ; i >= 0 ; i -- {
361+ for i := range tracks {
336362 if tracks [i ].Attr == nil || tracks [i ].Attr .NowPlaying != "true" {
337363 lastNonNowPlaying = & tracks [i ]
338364 break
0 commit comments