Skip to content

Apple Music tracker can create duplicate records after stale or reordered history responses #69

Description

@mmattbtw

Summary

A user reported that Teal creates Apple Music records overnight while they are asleep. The new records appear to duplicate the last two songs they were listening to.

Reported in Slack on 2026-07-29:

  • Apple Music history showed Lonely in the Future — The Strokes at 5:39 AM
  • I'm Afraid — Charli xcx at 5:52 AM
  • The user said these were duplicates of songs from before they went to sleep, and was unsure whether the Apple Music API was returning stale data.

Investigation

The current Apple Music ingestion path has several properties that can produce this symptom:

  • GetCurrentAppleMusicTrack requests /v1/me/recent/played/tracks?limit=1 and ProcessUser only considers the first item.
  • The incoming track is compared only with the single newest row from tracks.
  • The comparison uses attributes.url (or a generated name/album/artist hash for uploads), even though Apple supplies a stable resource ID / playParams.id.
  • SaveTrack always inserts a row; there is no uniqueness constraint or provider-event identity stored in the database.
  • toTrack assigns time.Now().UTC() as the listen timestamp because the Apple response does not provide the actual play time.
  • The tracker runs immediately at startup and then every 30 seconds.

This means an A → B → A response sequence, a stale/reordered Apple response, or a newer row from another music provider can cause A to be treated as new again. It also explains the related failure mode in #46, where tracks between polls are dropped.

Apple's endpoint is paginated and supports up to 30 recently played tracks, but Piper currently fetches only one and does not use the pagination/history window: https://developer.apple.com/documentation/applemusicapi/get-v1-me-recent-played-tracks

Likely impact

  • Duplicate Apple Music rows and duplicate ATProto play records.
  • Missed tracks when more than one song changes between 30-second polls.
  • Possible cross-provider interference because de-duplication is based on the newest shared tracks row rather than Apple Music-specific state.

Suggested fix

  • Persist and compare Apple Music's stable track/resource ID (including for uploaded tracks where applicable).
  • Track per-user Apple Music ingestion state or a recent ID window, rather than comparing only against the newest global row.
  • Fetch and process the recent-played page in chronological order so multiple tracks between polls are handled.
  • Add regression tests for stale/reordered responses (A → B → A), repeated API responses, restarts, and Spotify/Apple Music interleaving.
  • Log the Apple resource ID and the reason a track was skipped or accepted.

Related: #46

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions