[WIP] music platform user data scraper
teal-fm atproto

Moved the parsing of the time stamp for lastFM to the struct

Changed files
+41 -18
service
lastfm
spotify
+4 -15
service/lastfm/lastfm.go
··· 316 return nil 317 } 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) 325 326 // print both 327 fmt.Printf("latestTrackTime: %s\n", latestTrackTime) 328 fmt.Printf("lastKnownTimestamp: %s\n", lastKnownTimestamp) 329 330 - if lastKnownTimestamp != nil && lastKnownTimestamp.Equal(latestTrackTime) { 331 log.Printf("no new tracks to process for user %s.", username) 332 return nil 333 } 334 335 for _, track := range tracks { 336 - if track.Date == nil || track.Date.UTS == "" { 337 log.Printf("skipping track without timestamp for %s: %s - %s", username, track.Artist.Text, track.Name) 338 continue 339 } 340 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 - 348 // before or at last known 349 if lastKnownTimestamp != nil && (trackTime.Before(*lastKnownTimestamp) || trackTime.Equal(*lastKnownTimestamp)) { 350 if processedCount == 0 {
··· 316 return nil 317 } 318 319 + latestTrackTime := lastNonNowPlaying.Date 320 321 // print both 322 fmt.Printf("latestTrackTime: %s\n", latestTrackTime) 323 fmt.Printf("lastKnownTimestamp: %s\n", lastKnownTimestamp) 324 325 + if lastKnownTimestamp != nil && lastKnownTimestamp.Equal(latestTrackTime.Time) { 326 log.Printf("no new tracks to process for user %s.", username) 327 return nil 328 } 329 330 for _, track := range tracks { 331 + if track.Date == nil { 332 log.Printf("skipping track without timestamp for %s: %s - %s", username, track.Artist.Text, track.Name) 333 continue 334 } 335 336 + trackTime := track.Date.Time 337 // before or at last known 338 if lastKnownTimestamp != nil && (trackTime.Before(*lastKnownTimestamp) || trackTime.Equal(*lastKnownTimestamp)) { 339 if processedCount == 0 {
+36 -2
service/lastfm/model.go
··· 1 package lastfm 2 3 // Structs to represent the Last.fm API response for user.getrecenttracks 4 type RecentTracksResponse struct { 5 RecentTracks RecentTracks `json:"recenttracks"` ··· 19 Name string `json:"name"` 20 URL string `json:"url"` 21 Date *TrackDate `json:"date,omitempty"` // Use pointer for optional fields 22 - Attr *struct { // Custom handling for @attr.nowplaying 23 NowPlaying string `json:"nowplaying"` // Field name corrected to match struct tag 24 } `json:"@attr,omitempty"` // This captures the @attr object within the track 25 } ··· 39 Text string `json:"#text"` // Album name 40 } 41 42 - type TrackDate struct { 43 UTS string `json:"uts"` // Unix timestamp string 44 Text string `json:"#text"` // Human-readable date string 45 } 46 47 type TrackXMLAttr struct {
··· 1 package lastfm 2 3 + import ( 4 + "encoding/json" 5 + "strconv" 6 + "time" 7 + ) 8 + 9 // Structs to represent the Last.fm API response for user.getrecenttracks 10 type RecentTracksResponse struct { 11 RecentTracks RecentTracks `json:"recenttracks"` ··· 25 Name string `json:"name"` 26 URL string `json:"url"` 27 Date *TrackDate `json:"date,omitempty"` // Use pointer for optional fields 28 + Attr *struct { // Custom handling for @attr.nowplaying 29 NowPlaying string `json:"nowplaying"` // Field name corrected to match struct tag 30 } `json:"@attr,omitempty"` // This captures the @attr object within the track 31 } ··· 45 Text string `json:"#text"` // Album name 46 } 47 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 { 53 UTS string `json:"uts"` // Unix timestamp string 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 79 } 80 81 type TrackXMLAttr struct {
+1 -1
service/spotify/spotify.go
··· 526 ServiceBaseUrl: "open.spotify.com", 527 ISRC: response.Item.ExternalIDs.ISRC, 528 HasStamped: false, 529 - Timestamp: time.Now(), 530 } 531 532 return track, nil
··· 526 ServiceBaseUrl: "open.spotify.com", 527 ISRC: response.Item.ExternalIDs.ISRC, 528 HasStamped: false, 529 + Timestamp: time.Now().UTC(), 530 } 531 532 return track, nil