Skip to content

Instantly share code, notes, and snippets.

@xdavidhu
Last active April 3, 2021 14:39
Show Gist options
  • Save xdavidhu/cb87f926924bc174e6073b9f0b9d764e to your computer and use it in GitHub Desktop.
Save xdavidhu/cb87f926924bc174e6073b9f0b9d764e to your computer and use it in GitHub Desktop.

YouTube on TV local network pairing

This is how the YouTube Lounge API local-network pairing process looks like on a Samsung TV:

  1. The device discoveres the TV via SSDP/MDNS on the local network
  2. The device sends a request to /ws/app/YouTube to see if the YT app is available/running:
GET /ws/app/YouTube HTTP/1.1
Host: [tv_ip]:8080


HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type
API-Version: v1.00
Content-type: text/xml
Date: Sat, 03 Apr 2021 08:25:03 GMT
Server: WebServer
Content-Length: 348

<?xml version="1.0" encoding="UTF-8"?>
<service xmlns="urn:dial-multiscreen-org:schemas:dial" dialVer="2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<name>YouTube</name>
<options allowStop="true"/>
<state>stopped</state>
<version>2.1.498</version>
<link rel="run" href="run"/>
<additionalData>
  <testYWRkaXR>c0ef1ca</testYWRkaXR>
</additionalData>
</service>
  1. The device starts YouTube on the TV by sending a POST request to /ws/app/YouTube, and in the body specifies a pairingCode, which is a random UUID generated by the device:
POST /ws/app/YouTube HTTP/1.1
Host: [tv_ip]:8080
Content-Length: 22

pairingCode=[random_uuid]&theme=cl
HTTP/1.1 201 Created
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type
API-Version: v1.00
Content-type: text/html
LOCATION: http://[tv_ip]:8080/ws/apps/YouTube/run
Date: Sat, 03 Apr 2021 08:28:24 GMT
Server: WebServer
Content-Length: 45

http://[tv_ip]:8080/ws/apps/YouTube/run
  1. The device gets a lounge_token from the Leanback API using the random-generated pairingCode from Step 3.:
POST /api/lounge/pairing/get_screen HTTP/1.1
Host: www.youtube.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 49

pairing_code=[redacted]&theme=cl
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Date: Sat, 03 Apr 2021 08:31:28 GMT
Expires: Sat, 03 Apr 2021 08:31:28 GMT
Cache-Control: private, max-age=0
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'
X-XSS-Protection: 1; mode=block
Server: GSE
Alt-Svc: h3-29=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
Accept-Ranges: none
Vary: Accept-Encoding
Content-Length: 652

{
    "screen": {
        "accessType": "permanent",
        "screenId": "[readacted]",
        "dialAdditionalDataSupportLevel": "full",
        "loungeTokenRefreshIntervalMs": 1123200000,
        "loungeToken": "[readacted]",
        "clientName": "tvhtml5",
        "shortLivedLoungeToken": {
            "refreshIntervalMs": 1200000,
            "lifespanMs": 3600000,
            "loungeTokenType": "one_hour",
            "value": "[readacted]"
        },
        "name": "YouTube on TV",
        "expiration": 1618561888899,
        "deviceId": "[readacted]"
    }
}
  1. The device can quit the YouTube app on the TV by sending a DELETE requsest to /ws/app/YouTube/run:
DELETE /ws/app/YouTube/run HTTP/1.1
Host: [tv_ip]:8080
Content-Length: 0


HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type
API-Version: v1.00
Content-Length: 0
Date: Sat, 03 Apr 2021 14:23:20 GMT
Server: WebServer


Actually, if the YouTube app is already running, the lounge_token can be simply obtained from /ws/app/YouTube:

GET /ws/app/YouTube HTTP/1.1
Host: [tv_ip]:8080


HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type
API-Version: v1.00
Content-type: text/xml
Date: Sat, 03 Apr 2021 08:25:03 GMT
Server: WebServer
Content-Length: 348

<service xmlns="urn:dial-multiscreen-org:schemas:dial" xmlns:atom="http://www.w3.org/2005/Atom" dialVer="2.2">
<name>YouTube</name>
<options allowStop="true"/>
<state>running</state>
<version>2.1.498</version>
<link rel="run" href="run"/>
<additionalData>
  <testYWRkaXR>c0ef1ca</testYWRkaXR>
  <screenId>[redacted]</screenId>
  <theme>cl</theme>
  <deviceId>[redacted]</deviceId>
  <loungeToken>[redacted]</loungeToken>
  <loungeTokenRefreshIntervalMs>1500000</loungeTokenRefreshIntervalMs>
</additionalData>
</service>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment