···316316 return nil
317317 }
318318319319- uts, err := strconv.ParseInt(lastNonNowPlaying.Date.UTS, 10, 64)
320320- if err != nil {
321321- log.Printf("error parsing timestamp '%s' for track %s - %s: %v",
322322- lastNonNowPlaying.Date.UTS, lastNonNowPlaying.Artist.Text, lastNonNowPlaying.Name, err)
323323- }
324324- latestTrackTime := time.Unix(uts, 0)
319319+ latestTrackTime := lastNonNowPlaying.Date
325320326321 // print both
327322 fmt.Printf("latestTrackTime: %s\n", latestTrackTime)
328323 fmt.Printf("lastKnownTimestamp: %s\n", lastKnownTimestamp)
329324330330- if lastKnownTimestamp != nil && lastKnownTimestamp.Equal(latestTrackTime) {
325325+ if lastKnownTimestamp != nil && lastKnownTimestamp.Equal(latestTrackTime.Time) {
331326 log.Printf("no new tracks to process for user %s.", username)
332327 return nil
333328 }
334329335330 for _, track := range tracks {
336336- if track.Date == nil || track.Date.UTS == "" {
331331+ if track.Date == nil {
337332 log.Printf("skipping track without timestamp for %s: %s - %s", username, track.Artist.Text, track.Name)
338333 continue
339334 }
340335341341- uts, err := strconv.ParseInt(track.Date.UTS, 10, 64)
342342- if err != nil {
343343- log.Printf("error parsing timestamp '%s' for track %s - %s: %v", track.Date.UTS, track.Artist.Text, track.Name, err)
344344- continue
345345- }
346346- trackTime := time.Unix(uts, 0)
347347-336336+ trackTime := track.Date.Time
348337 // before or at last known
349338 if lastKnownTimestamp != nil && (trackTime.Before(*lastKnownTimestamp) || trackTime.Equal(*lastKnownTimestamp)) {
350339 if processedCount == 0 {
+36-2
service/lastfm/model.go
···11package lastfm
2233+import (
44+ "encoding/json"
55+ "strconv"
66+ "time"
77+)
88+39// Structs to represent the Last.fm API response for user.getrecenttracks
410type RecentTracksResponse struct {
511 RecentTracks RecentTracks `json:"recenttracks"`
···1925 Name string `json:"name"`
2026 URL string `json:"url"`
2127 Date *TrackDate `json:"date,omitempty"` // Use pointer for optional fields
2222- Attr *struct { // Custom handling for @attr.nowplaying
2828+ Attr *struct { // Custom handling for @attr.nowplaying
2329 NowPlaying string `json:"nowplaying"` // Field name corrected to match struct tag
2430 } `json:"@attr,omitempty"` // This captures the @attr object within the track
2531}
···3945 Text string `json:"#text"` // Album name
4046}
41474242-type TrackDate struct {
4848+// ApiTrackDate This is the real structure returned from lastFM.
4949+// Represents a date associated with a track, including both a Unix timestamp and a human-readable string.
5050+// UTS is a Unix timestamp stored as a string.
5151+// Text contains the human-readable date string.
5252+type ApiTrackDate struct {
4353 UTS string `json:"uts"` // Unix timestamp string
4454 Text string `json:"#text"` // Human-readable date string
5555+}
5656+5757+// TrackDate This is the struct we use to represent a date associated with a track.
5858+// It is a wrapper around time.Time that implements json.Unmarshaler.
5959+type TrackDate struct {
6060+ time.Time
6161+}
6262+6363+// UnmarshalJSON Implements json.Unmarshaler.
6464+// Parses the UTS field from the API response and converts it to a time.Time.
6565+// The time.Time is stored in the Time field.
6666+// The Text field is ignored since it can be parsed from the Time field if needed.
6767+func (t *TrackDate) UnmarshalJSON(b []byte) (err error) {
6868+ var apiTrackDate ApiTrackDate
6969+ if err := json.Unmarshal(b, &apiTrackDate); err != nil {
7070+ return err
7171+ }
7272+ uts, err := strconv.ParseInt(apiTrackDate.UTS, 10, 64)
7373+ if err != nil {
7474+ return err
7575+ }
7676+ date := time.Unix(uts, 0).UTC()
7777+ t.Time = date
7878+ return
4579}
46804781type TrackXMLAttr struct {