+4
-15
service/lastfm/lastfm.go
+4
-15
service/lastfm/lastfm.go
···
316
316
return nil
317
317
}
318
318
319
-
uts, err := strconv.ParseInt(lastNonNowPlaying.Date.UTS, 10, 64)
320
-
if err != nil {
321
-
log.Printf("error parsing timestamp '%s' for track %s - %s: %v",
322
-
lastNonNowPlaying.Date.UTS, lastNonNowPlaying.Artist.Text, lastNonNowPlaying.Name, err)
323
-
}
324
-
latestTrackTime := time.Unix(uts, 0)
319
+
latestTrackTime := lastNonNowPlaying.Date
325
320
326
321
// print both
327
322
fmt.Printf("latestTrackTime: %s\n", latestTrackTime)
328
323
fmt.Printf("lastKnownTimestamp: %s\n", lastKnownTimestamp)
329
324
330
-
if lastKnownTimestamp != nil && lastKnownTimestamp.Equal(latestTrackTime) {
325
+
if lastKnownTimestamp != nil && lastKnownTimestamp.Equal(latestTrackTime.Time) {
331
326
log.Printf("no new tracks to process for user %s.", username)
332
327
return nil
333
328
}
334
329
335
330
for _, track := range tracks {
336
-
if track.Date == nil || track.Date.UTS == "" {
331
+
if track.Date == nil {
337
332
log.Printf("skipping track without timestamp for %s: %s - %s", username, track.Artist.Text, track.Name)
338
333
continue
339
334
}
340
335
341
-
uts, err := strconv.ParseInt(track.Date.UTS, 10, 64)
342
-
if err != nil {
343
-
log.Printf("error parsing timestamp '%s' for track %s - %s: %v", track.Date.UTS, track.Artist.Text, track.Name, err)
344
-
continue
345
-
}
346
-
trackTime := time.Unix(uts, 0)
347
-
336
+
trackTime := track.Date.Time
348
337
// before or at last known
349
338
if lastKnownTimestamp != nil && (trackTime.Before(*lastKnownTimestamp) || trackTime.Equal(*lastKnownTimestamp)) {
350
339
if processedCount == 0 {
+36
-2
service/lastfm/model.go
+36
-2
service/lastfm/model.go
···
1
1
package lastfm
2
2
3
+
import (
4
+
"encoding/json"
5
+
"strconv"
6
+
"time"
7
+
)
8
+
3
9
// Structs to represent the Last.fm API response for user.getrecenttracks
4
10
type RecentTracksResponse struct {
5
11
RecentTracks RecentTracks `json:"recenttracks"`
···
19
25
Name string `json:"name"`
20
26
URL string `json:"url"`
21
27
Date *TrackDate `json:"date,omitempty"` // Use pointer for optional fields
22
-
Attr *struct { // Custom handling for @attr.nowplaying
28
+
Attr *struct { // Custom handling for @attr.nowplaying
23
29
NowPlaying string `json:"nowplaying"` // Field name corrected to match struct tag
24
30
} `json:"@attr,omitempty"` // This captures the @attr object within the track
25
31
}
···
39
45
Text string `json:"#text"` // Album name
40
46
}
41
47
42
-
type TrackDate struct {
48
+
// ApiTrackDate This is the real structure returned from lastFM.
49
+
// Represents a date associated with a track, including both a Unix timestamp and a human-readable string.
50
+
// UTS is a Unix timestamp stored as a string.
51
+
// Text contains the human-readable date string.
52
+
type ApiTrackDate struct {
43
53
UTS string `json:"uts"` // Unix timestamp string
44
54
Text string `json:"#text"` // Human-readable date string
55
+
}
56
+
57
+
// TrackDate This is the struct we use to represent a date associated with a track.
58
+
// It is a wrapper around time.Time that implements json.Unmarshaler.
59
+
type TrackDate struct {
60
+
time.Time
61
+
}
62
+
63
+
// UnmarshalJSON Implements json.Unmarshaler.
64
+
// Parses the UTS field from the API response and converts it to a time.Time.
65
+
// The time.Time is stored in the Time field.
66
+
// The Text field is ignored since it can be parsed from the Time field if needed.
67
+
func (t *TrackDate) UnmarshalJSON(b []byte) (err error) {
68
+
var apiTrackDate ApiTrackDate
69
+
if err := json.Unmarshal(b, &apiTrackDate); err != nil {
70
+
return err
71
+
}
72
+
uts, err := strconv.ParseInt(apiTrackDate.UTS, 10, 64)
73
+
if err != nil {
74
+
return err
75
+
}
76
+
date := time.Unix(uts, 0).UTC()
77
+
t.Time = date
78
+
return
45
79
}
46
80
47
81
type TrackXMLAttr struct {