Skip to content

Telling the backend what the TV is

Until this doc’s change the app reported a cast session exactly like local playback. The only difference on the wire was Matomo’s ma_pn (ChromecastPlayer / AirPlayPlayer). Nothing said which receiver it was.

Chromecast mDNS TXT record (CastDevice.extras) md model fn name ve firmware
+ RECEIVER_STATUS ca capabilities id uuid
applications[0].displayName
AirPlay AVAudioSession current route (public) portName, uid
(model / IP / manufacturer are private API: not used)
DLNA not implemented in this app

One value object, PlaybackOutput (lib/domain/entities/playback_output.dart):

type chromecast | airplay (local sessions send nothing)
model md Chromecast only
firmware ve Chromecast only
receiver_app receiver app displayName Chromecast only
name friendly name both
device_key first 12 hex of SHA-256(salt | id or uid) both

Never sent: IP/host, MAC, raw Cast id, raw AirPlay uid, bs/ic/other TXT keys. lib/core/utils/device_key.dart does the hashing; playback_output_mapper.dart builds the object.

CastController / AirPlayController.playbackOutput
│
RemotePlaybackProgressReporter (the one place that sees session start/end)
├─ watch-history POST → "playback_output": {...} (optional field)
├─ Matomo ping + events → dimension4 = type, dimension5 = model
└─ Marktag Play → metadata.playback_output

The Hive progress queue stores the payload as built, so an offline retry keeps the output that was true when the minute was recorded.

  • The backend must accept the optional playback_output object on POST /v1/user/watch-history and ignore it where unknown. The Postman collection is deliberately not updated until it does.
  • Matomo needs custom dimensions 4 and 5 configured for the site (action scope).
  • ca (capabilities bitmask) is parsed from TXT but not sent yet.