Skip to content

Finally some api!

Henrique Kano edited this page May 31, 2022 · 4 revisions

Basic definitions

Some definitions I will try to uphold went writing

  • screen: the TV/device one is trying to cast to
  • remote: the device used to control what's being casted to the screen
  • device: screen or remote device
  • lounge: the definition of a "room" (like the place) in the API - said room being composed of a screen and remotes.
  • event: a object returned by the API to indicate something changed in lounge (something connected, something is playing, etc, etc)
  • chunk: a collection of events

Auth key

The first step for using the API is an auth key of some sort. In this case, the key is called "lounge token". It's the same lounge token mentioned in the "discovery" page of this wiki. But communicating with the second screen directly isn't the only way to obtain this key, the second way being pairing via a, obviously, pair code.

  • In the youtube app settings there's a "Link with TV code" option that gives a 12 digits code that can be used in a http request to get the lounge token:
POST https://www.youtube.com/api/lounge/pairing/get_screen
content-type: application/x-www-form-urlencoded
pairing_code=793868867097

which results in something like:

{
    "screen": {
        "accessType": "permanent",
        "screenId": "qweqweqwe",
        "dialAdditionalDataSupportLevel": "unknown",
        "loungeTokenRefreshIntervalMs": 1123200000,
        "loungeToken": "qweqweqwe",
        "clientName": "tvhtml5",
        "name": "YouTube on TV",
        "expiration": 1550144489377,
        "deviceId": "rergergergerg"
    }
}

As a last point, when pairing with a pair code, the response should somehow be saved locally on the device, so it's possible to reconnect to the screen. The device id should be usable as a unique identifier.

Refreshing the lounge token

As it's apparent in the pair code response, the lounge token expires. Again, with discoverable devices, to refresh the token it's just a process of rediscovering and querying the screen for the lounge token. In the case of code paired screens, there's also a endpoint for the refresh by using the screens' ids:

POST https://www.youtube.com/api/lounge/pairing/get_lounge_token_batch
content-type:application/x-www-form-urlencoded
screen_ids=abc,123

resulting in:

{
    "screens": [
        {
            "screenId": "qweqwe",
            "refreshIntervalInMillis": 1123200000,
            "remoteRefreshIntervalMs": 79200000,
            "refreshIntervalMs": 1123200000,
            "loungeTokenLifespanMs": 1209600000,
            "loungeToken": "gregreg",
            "remoteRefreshIntervalInMillis": 79200000,
            "expiration": 1654079296252
        }
    ]
}

It's possible to batch request lounge tokens by using multiple screen ids separated by commas

Connections are important!

Now with the lounge token at hand, it's necessary to create a link with the lounge so it know the remote exists and how to address it:

POST https://www.youtube.com/api/lounge/bc/bind?RID=1&VER=8&CVER=1&auth_failure_option=send_error
content-type:application/x-www-form-urlencoded
{
    "app":  "web",
    "mdx-version":  "3",
    "name":  "device_name",
    "id":  "",
    "device":  "REMOTE_CONTROL",
    "capabilities":  "que,dsdtr,atp",
    "method":  "setPlaylist",
    "magnaKey":  "cloudPairedDevice",
    "ui":  "",
    "deviceContext": "user_agent=dunno&window_width_points=&window_height_points=&os_name=android&ms=",
    "theme":  "cl"
}

 * The body should be form data encoded, but I wrote as a JSON for better visualization

By making this request, one should notice a "xxx is now connected" (xxx being the device_name used) on the screen's screen - this signalizes that we're in! I have no idea what the parameters capabilities, magnakey, ui or theme are used for, but since it works, it works.

The request also creates a private playlist that will be used when queuing and playing videos.

Don't lose touch

Now that we're connected to the lounge, it's necessary to keep track about what's happening in said lounge, what's playing, who connected, is the video paused, is an ad playing, etc etc. To achieve that, it's necessary some kind of connection to listen for events. In this api, said connection is a long http pooling, but before that: more state variables! Yay! Make a request to:

POST https://www.youtube.com/api/lounge/bc/bind
content-type:application/x-www-form-urlencoded
name=devicename&app=app_name&loungeIdToken=loungeToken

This will result in some events:

234
[[0,[\"c\",\"FEWWEFWEFWEF\",\"\",8]]
,[1,[\"S\",\"wefwefwef\"]]
,[2,[\"loungeStatus\",{}]
,[3,[\"playlistModified\",{\"videoIds\":\"\"}]]
,[4,[\"onAutoplayModeChanged\",{\"autoplayMode\":\"UNSUPPORTED\"}]]
,[5,[\"onPlaylistModeChanged\",{\"shuffleEnabled\":\"false\",\"loopEnabled\":\"false\"}]]
]

Now it's the time to make a tangent to events format

Events

Events emitted by the api will come in chunks with the format:

number
[[id, ["event_name", event_argument, not sure what]
]
  • The first number means how many characters are in the following chunk
  • the id are auto incrementing starting from 0
  • the event_name identifies the event format
  • event_argument may or may not exist depending of the event
  • I'm not sure what the following values in a event mean - sometimes they exist, sometimes not
  • multiple chunks may be sent by the api at once I'm not sure this is a known format, but it kinda reminds me of jsonp?

/Events

Back to the topic. The first chunk returned is important in that it carries two session variables: SID and gsession (event names "c" and "S", respectively). these two will also be used to identify the remote session.

After this first request, the next events may be queried via:

GET https://www.youtube.com/api/lounge/bc/bind?SID=sid&gsessionid=gsession&loungeIdToken=loungeToken&CI=1&TYPE=xmlhttp&AID=???

Now, if you're still conscious after reading everything, you may have noticed the ??? value to AID and that's because it's another state variable to store (surprised pikachu face). You see, the events aren't one time only - so new devices entering the lounge may build a correct state from what happened in the past - so the api have to be paged. In this case, the api uses the reference strategy - by providing the last known event id, one can get all the events until now in a chunk (?).

So a TL;DR of the state variables to keep in mind:

  • SID: comes from the first events chunk
  • gsession: same as SID
  • AID: last know event id
  • lounge token: auth token to the lounge

Finally, no more states. Right...?

Remote

Congratulations, now it's possible to issue remote commands to the api to control the screen. So, to play a video:

POST https://www.youtube.com/api/lounge/bc/bind?RID=???????????&VER=8&CVER=1&gsessionid=session&SID=sid&auth_failure_option=send_error
content-type:application/x-www-form-urlencoded
req0_prioritizeMobileSenderPlaybackStateOnConnection=true
&req0_currentIndex=-1
&count=1
&req0_videoId=xxx
&req0_listId=
&req0_currentTime=0
&req0__sc=setPlaylist
&req0_audioOnly=false
&req0_params=
&req0_playerParams=

Now, all remote commands have one thing in similar: the req0__sc parameter - it indicates what command is being issued. But, hey what's that RID? You guessed it. Another state variable. RID being a remote command id - an auto incremented id for each remote command. And that req0_ stuff? Yes a state variable - a remote command auto incrementing (req1_, req2_, req3_, etc) id. What's the difference? RID should increment only if the command succeeds, while req0_ increments independent of it.

  • EDIT: while writing this, I started to theorize that reqX isn't an id, but a syntax for query parameters array - that is, it's possible to batch commands in one request, which is pretty reasonable. I didn't test it, but if someone want to...

Done

And as the finally, it's necessary to disconnect from the lounge:

POST https://www.youtube.com/api/lounge/bc/bind?RID=x&VER=8&CVER=1&gsessionid=session&SID=sid&auth_failure_option=send_error
content-type:application/x-www-form-urlencoded
ui=&TYPE=terminate&clientDisconnectReason=MDX_SESSION_DISCONNECT_REASON_DISCONNECTED_BY_USER

One should see a "xxx diconnected" on the screen

Clone this wiki locally