nikolai.one
● SYS_CONSOLE // type 'help' for commands
Nikolai Systems Terminal v1.0.4 [x86_64-linux]
Type 'help' to see all available commands. Press '~' or ESC to toggle console.
Try: 'whoami', 'articles' or especially 'showoff'
>
MY ARCHIVE Click the book on the shelf to view bio • Move mouse to look • Press ESC to exit
πŸ“– Inspect Book
← All writing

Casting YouTube Music to KEF LSX via DLNA: Solving the Missing Cast Target

#audio #dlna #cast #reverse-engineering #nodejs

The first-generation KEF LSX speakers sound fantastic, but their network streaming software is showing its age. They support Spotify Connect, AirPlay 2, and DLNA/UPnP, but there is no native Google Cast (Chromecast built-in) receiver.

If you use YouTube Music as your primary streaming service on Android or desktop Chrome, you are stuck: YouTube Music has no option to output to raw DLNA renderers. You either settle for lossy Bluetooth compression or juggle third-party casting utilities that constantly drop connections.

I decided to solve this properly by building kef-yt-streamer, a lightweight bridge service that runs on my local server (or a Raspberry Pi) and acts as an official Cast receiver target.

The Core Problem

When you hit the "Cast" button in YouTube Music, the app advertises discovery via mDNS / SSDP and expects to talk to a Cast V2 device. Once connected, YouTube Music spins up a web receiver session on the target device, passing stream tokens and media metadata over TLS.

The KEF LSX, meanwhile, exposes a standard UPnP/AVTransport endpoint on port 8080:

  • SetAVTransportURI to pass the media stream URL.
  • Play, Pause, Stop, Seek.
  • RenderingControl for hardware volume adjustment.

How the Bridge Works

  1. Cast V2 Receiver Emulation: The bridge runs an mDNS advertiser declaring itself as a Cast-compatible audio receiver named "KEF LSX".
  2. Session Handshake: When you tap "KEF LSX" in YouTube Music, the bridge handles the initial TLS handshake and receiver launch request.
  3. Stream Resolution: When a track starts playing, the bridge intercepts the video/audio ID, resolves the direct audio stream URL (via internal stream extraction), and initializes the audio pipeline.
  4. DLNA Pipe & Metadata: The resolved audio stream is immediately pushed to the KEF LSX over UPnP AVTransport using raw PCM or high-bitrate AAC, accompanied by DIDL-Lite XML metadata (track title, artist, album art).
  5. Two-Way Control Synchronization: Volume changes made inside the YouTube Music app map directly to the speaker hardware volume, and physical remote commands (pause/play) map back to the cast session.
// Simplified AVTransport URI assignment
async function playTrackOnKef(speakerIp, streamUrl, meta) {
  const didlXml = buildDidlMetadata(meta);
  const action = 'SetAVTransportURI';
  const body = `
    <u:SetAVTransportURI xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
      <InstanceID>0</InstanceID>
      <CurrentURI>${streamUrl}</CurrentURI>
      <CurrentURIMetaData>${escapeXml(didlXml)}</CurrentURIMetaData>
    </u:SetAVTransportURI>
  `;
  await sendSoapRequest(speakerIp, 8080, 'AVTransport', action, body);
  await sendSoapRequest(speakerIp, 8080, 'AVTransport', 'Play', '<u:Play xmlns:u="urn:schemas-upnp-org:service:AVTransport:1"><InstanceID>0</InstanceID><Speed>1</Speed></u:Play>');
}

Running as a Persistent Service

The entire bridge runs in Docker on my home server with host networking enabled so mDNS packets flow freely:

services:
  kef-yt-streamer:
    image: ghcr.io/kennycoder/kef-yt-streamer:latest
    network_mode: host
    environment:
      - SPEAKER_IP=192.168.1.140
      - DEVICE_NAME=KEF LSX
    restart: unless-stopped

Now, whenever anyone on my home Wi-Fi opens YouTube Music, "KEF LSX" shows up instantly in the cast list, latency is under 200ms, and audio streams bit-perfect without Bluetooth compression.