Skip to content

Add Session.report_playback to report plays to TIDAL - #415

Open
Petru101 wants to merge 1 commit into
EbbLabs:mainfrom
Petru101:feature/playlog
Open

Petru101 wants to merge 1 commit into
EbbLabs:mainfrom
Petru101:feature/playlog

Conversation

@Petru101

@Petru101 Petru101 commented Oct 16, 2026 •

Copy link
Copy Markdown

Fixes #401.

Tracks played through tidalapi never show up in Recently played, and don't count in the listening history TIDAL uses for recommendations. Fetching and playing a stream isn't enough: the official players report every play as a playback_session event to TIDAL's event producer (https://ec.tidal.com/api/event-batch). The attempt in #401 sent the web player's events to tidal.com/api/event-batch, which TIDAL accepts but doesn't show anywhere.

This adds Session.report_playback() and a tidalapi.playlog module that builds the same events as TIDAL's own players. The layout comes from TIDAL's open source SDK, tidal-sdk-android: the eventproducer module (an SQS SendMessageBatch form with a Headers attribute) and the player/events models (PlaybackSession, AudioPlaybackSession, User, Client). The user and client fields come from the access token's claims, as the SDK does.

Apart from the client id of the OAuth client the session logged in with, the events describe the real client: the operating system and version from platform, and an application version the caller passes in (defaults to the tidalapi version). They don't add device details. Reported this way with report_playback, plays showed up in Recently played within 20 seconds in my tests.

from tidalapi import PlaybackSession, SourceType

session.report_playback(
    [
        PlaybackSession(
            track_id=track.id,
            start_timestamp=started_at_ms,
            end_timestamp=ended_at_ms,
            end_position=position_seconds,
            source_type=SourceType.album,
            source_id=track.album.id,
        )
    ],
    app_version="My Player 1.0",
)
  • Each play has a source: the album, playlist, mix or artist it was played from, or ITEM with the track's own id for a track played on its own. Recently played lists the source.
  • Pauses and seeks can be passed as PlaybackActions, as the official players do.
  • TIDAL counts a play after 30 seconds, so the docs suggest reporting plays that lasted that long.
  • Up to 10 plays are sent per request. report_playback returns False if TIDAL rejects them, and raises if they couldn't be sent (offline, server errors), so applications can keep them and try again later. It uses the session's token refresh and backoff.

The tests in tests/test_playlog.py run offline and are added to the test workflow. There are also docs in api.rst and an entry in HISTORY.rst.

High Tide, which uses tidalapi, already reports plays this way in a local build.

🤖 Generated with Claude Code

Fetching and playing a stream doesn't count as a play, so tracks played through tidalapi never show up in Recently played or in the listening history TIDAL uses for recommendations. The official players report each play as a playback_session event to TIDAL's event producer; tidalapi.playlog builds the same events, following TIDAL's open source SDK (tidal-sdk-android), and Session.report_playback sends them. The events describe the real client (operating system and application version) instead of an official TIDAL app.

Fixes EbbLabs#401

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Petru101

Copy link
Copy Markdown
Author

A note on why I think this matters beyond Recently played: TIDAL's own SDK puts the play log in its NECESSARY consent category (streaming metrics are PERFORMANCE), and every official player sends it for every play. Without it, plays from tidalapi-based players may not count the way TIDAL's own players' plays do, possibly including what artists get paid for them. I can't confirm how TIDAL uses it, but it seems worth getting right.

Two things you may want to weigh: the session's OAuth client is TIDAL's Android app, while the events describe the real client (OS, app version), so they don't fully match. And since report_playback takes any timestamps, it could be misused to fake plays. Happy to change it if you'd prefer a different shape, e.g. only reporting tracks the session actually fetched a stream for.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Playback logging endpoint?

1 participant