Homepage: https://jman012.github.io/FloatplaneAPIDocs
This document describes the asynchronous/event-driven API layer of https://www.floatplane.com, a content creation and video streaming website created by Floatplane Media Inc. and Linus Media Group, where users can support their favorite creates via paid subscriptions in order to watch their video and livestream content in higher quality and other perks.
This API is specific to the chat/livestream activities of the Floatplane website, which is responsible for sending and receiving chat message, user lists, emote lists, etc., and is meant for a connection to chat.floatplane.com. If you are looking for the frontend API layer of Floatplane (www.floatplane.com), please visit this document instead.
This document is an AsyncAPI implementation on top of a Socket.IO connection. AsyncAPI does not have any specifics for Socket.IO, and so any automatic code-generation from this document may not work 100% out-of-the-box on a WebSocket connection. At best, it will provide all of the proper models needed for the different events and messages. But it may require some glue code to get it working properly with Sails. See the below section on the difference between Socket.IO, Sails, and Floatplane for this socket connection.
It is recommended for any client implementation to use a Socket.IO reference library implementation in your language of choice with this document. A version of this document for Sails would be preferred, but there is only a single client reference implementation (JavaScript), and the Sails layer on top of Socket.IO is fairly straightforward.
Floatplane's backend primarily uses Sails and the website UI uses an Angular frontend along with Sails' socket connection for low-latency request/response and event-driven architecture. Sails is an MVC framework for making Node.js websites and APIs. Sails' socket connection is built on three tiers of technology that should be understood when implementing this API:
0
prefix, pings/pongs as a single byte (2
/3
), or messages with the 4
prefix. If you are analyzing the WebSocket frames directly, it would be beneficial to familiarize yourself with the Engine.IO protocol.2
, acknowledgements are prefixed with 3
(both are after the Engine.IO prefixes/headers), and the data of the event is stored as a JSON-encoded array of items, where the first item is always a string identifying the event name, and optional subsequent items are the arguments to the event.GET
, POST
, etc. onto the socket connection, and uses Socket.IO's acknowledgements in order to send back a response body."get"
, "post"
, etc.), and the first and only argument to the Socket.IO event is a data structure with Sails-specific fields: method
, headers
, url
, and data
. The data
field is where the application-specific data is stored in the event.body
, headers
, and statusCode
fields as the first and only argument in the ack.The following list shows some examples of what a raw WebSocket connection might entail, and splits up the data between the various layers. This section is mostly to help with debugging raw connections, and understanding the technical stack of Floatplane.
GET wss://www.floatplane.com/socket.io/?__sails_io_sdk_version=0.13.8&__sails_io_sdk_platform=browser&__sails_io_sdk_language=javascript&EIO=3&transport=websocket
HTTP 101 Switching Protocols
.0{"sid":"b1VcCLtZ1SUXiYEGAB49","upgrades":[],"pingInterval":25000,"pingTimeout":60000}
0
: Engine.IO open header.{"sid":"b1VcCLtZ1SUXiYEGAB49","upgrades":[],"pingInterval":25000,"pingTimeout":60000}
: Engine.IO open payload.40
/
).4
: Engine.IO message header.0
: Socket.IO connect header./
is implied but not included in this message due to the Socket.IO protocol marking is as optional. The non-encoded message for this in Socket.IO would look like {"type": 0,"nsp": "/admin","data": {}}
.420["post",{"method":"post","headers":{},"data":{},"url":"/api/v3/socket/connect"}]
4
: Engine.IO message header.20
: Socket.IO header.2
: Marks that this message is an Event.0
: Id parameter. Needed for the following acknowledgement.["post",{"method":"post","headers":{},"data":{},"url":"/api/v3/socket/connect"}]
: Socket.IO event data."post"
: Socket.IO event name.{"method":"post","headers":{},"data":{},"url":"/api/v3/socket/connect"}
: Socket.IO event argument for the Sails "post"
event."method":"post,"url":"/api/v3/socket/connect"
: This Sails POST request is destined for the /api/v3/socket/connect
endpoint."data":{}
: Sails request body.{}
: No Floatplane data in the request.430[{"body":{"message":"User sync setup."},"headers":{},"statusCode":200}]
4
: Engine.IO message header.30
: Socket.IO header.3
: Marks that this message is an Acknowledgement0
: Id parameter. Marks that it is acknowledging event number 0.[{"body":{"message":"User sync setup."},"headers":{},"statusCode":200}]
: Socket.IO acknowledgement data.{"body":{"message":"User sync setup."},"headers":{},"statusCode":200}
: The only argument in the Socket.IO ack"statusCode":200
: Sails HTTP response status code."body":{"message":"User sync setup."}
: Sails HTTP response body.{"message":"User sync setup."}
: Floatplane connection response.421["syncEvent",{"event": "creatorNotification","data": {"id": "CONTENT_POST_RELEASE:yQDi4v3EMc","eventType": "CONTENT_POST_RELEASE",...}}]
4
: Engine.IO message header.2
: Socket.IO event header.["syncEvent",{"event": "creatorNotification","data": {"id": "CONTENT_POST_RELEASE:yQDi4v3EMc","eventType": "CONTENT_POST_RELEASE",...}}]
: Socket.IO event data."syncEvent"
: Socket.IO event name.{"event": "creatorNotification","data": {"id": "CONTENT_POST_RELEASE:yQDi4v3EMc","eventType": "CONTENT_POST_RELEASE",...}}
: Socket.IO event argument for the "syncEvent"
event.This document is primarily organized around the AsyncAPI blog post on Socket.IO (Part 1, and Part 2). The extension x-ack
is used in order to model Socket.IO's acknowledgement feature that emulates the request/response paradigm. The Socket.IO acknowledgement does not have an event name that goes over the wire, and instead directly references the initiating event via its unique identifier. As such, acknowledgements are not specified in the AsyncAPI's channel's publisher or subscriber operators.
This document also manually incorporates features of Sails on top of Socket.IO. It does so manually because the Sails abstraction is rather light, by basically specifying event names and models for data to pass through for requests and responses to virtual endpoints. Sails introduces the event names "get"
, "post"
, "put"
, and other HTTP verbs, where the event argument (request) is JSON in the form of:
{
"method": "get",
"headers": {},
"body": {
...
},
"url": "/api/v3/..."
}
And the acknowledgement/response argument is JSON in the form of:
{
"body": {
...
},
"headers": {},
"statusCode: 200
}
Where body
in each is specific to Floatplane. The rest of the data structure emulates HTTP requests and responses. As such, this AsyncAPI document explicitly models these structures around the Floatplane-specific requests and responses.
Finally, because Sails uses generic "get"
, "post"
, etc. event names for multiple types of actualized events on Socket.IO, a single AsyncAPI Operator is defined for each of "get"
and "post"
, and uses JSON Schema's oneOf
feature for multiple kinds of body
models, one for each request/response.
Useful links for AsyncAPI and WebSockets/Socket.IO:
When configuring a Socket.IO connection for use with Floatplane, there are some particular configurations to perform in order to prevent issues.
/socket.io
/socket.io
./engine.io
and may result in an HTTP 404 if used.true
websocket
/ Secure: true
__sails_io_sdk_version
: 0.13.8
__sails_io_sdk_platform
: {your platform}__sails_io_sdk_language
: {your language}0.9.0
which will throw an error when performing an Sails-related events, because it thinks your code will be too old to handle it.Origin: https://www.floatplane.com"
extraHeaders
configuration.sails.sid
sails.sid
cookie is not required to make a raw Socket.IO connection with Floatplane, but will be required for making most Sails get/post requests.A client connects to Floatplane's asynchronous API via WebSockets with TLS (wss). The socket it connects to is goeverned by Sails, which requires specifying which version of Sails to use, as well as other parameters like the platform and language. The purpose of the EIO
query parameter is unknown. If using a proper Sails client library, the parameters should be auto-filled for you, and simply connecting to www.floatplane.com/socket.io/
should suffice.
The value 0.13.8
is the current value at the time of writing (2022-05-08). This gets updated every so often with updates to the Floatplane frontend. There may be compatibility issues if too old of a value is supplied (the Sails backend may reject the connection). It is important to keep this value as up-to-date as possible, in order to prevent rejected connection issues.
The value browser
is the current value used by the Sails JS client library. It is not known what effect values other than those defined by Sails may have on the socket connection.
The value javascript
is the current value used by the Sails JS client library. It is not known what effect values other than javascript
may have on the socket connection.
Socket.IO and Sails groups messages and events into different namespaces, with /
being the default namespace. Multiple kinds of messages can be sent in a single namespace, with Socket.IO having its own mechanisms to differentiate message types. Floatplane only uses the root Socket.IO namespace (/
).
Accepts one of the following messages:
HTTP GET via Sails socket connection
This one asynchronous Sails/Socket.IO event may contain different Floatplane events in the payload, and contain different Floatplane events in the response/acknowledgement (depending on the request).
Connecting to a Floatplane livestream via a Sails HTTP GET.
{
"method": "get",
"headers": {},
"data": {
"channel": "/live/5c13f3c006f1be15e08e05c0"
},
"url": "/RadioMessage/joinLivestreamRadioFrequency"
}
Retrieve the user list in a livestream chat via a Sails HTTP GET.
{
"method": "get",
"headers": {},
"data": {
"channel": "/live/5c13f3c006f1be15e08e05c0"
},
"url": "/RadioMessage/getChatUserList/"
}
HTTP POST via Sails socket connection
This one asynchronous Sails/Socket.IO event may contain different Floatplane events in the payload, and contain different Floatplane events in the response/acknowledgement (depending on the request).
Send a livestream chat message via a Sails HTTP POST.
{
"method": "post",
"headers": {},
"data": {
"channel": "/live/5c13f3c006f1be15e08e05c0",
"message": "test"
},
"url": "/RadioMessage/sendLivestreamRadioChatter/"
}
Leave a Floatplane livestream via a Sails HTTP POST.
{
"method": "post",
"headers": {},
"data": {
"channel": "/live/5c13f3c006f1be15e08e05c0",
"message": "bye!"
},
"url": "/RadioMessage/leaveLivestreamRadioFrequency"
}
Socket.IO and Sails groups messages and events into different namespaces, with /
being the default namespace. Multiple kinds of messages can be sent in a single namespace, with Socket.IO having its own mechanisms to differentiate message types. Floatplane only uses the root Socket.IO namespace (/
).
Accepts the following message:
A radio chatter event arrives from Floatplane after joining a livestream via `/RadioMessage/joinLivestreamRadioFrequency`.
These events are primarily incoming chats from others in the livestream chat room, specifying who sent the chat, the message of the chat, and other meta data.
{
"id": "cl30pgqfr01x301eh212rdr8q",
"userGUID": "5fb69b4f8573b6cd8cc7f3b0",
"username": "jamamp",
"channel": "/live/5c13f3c006f1be15e08e05c0",
"message": "test",
"userType": "Normal",
"emotes": [],
"success": true
}
HTTP GET via Sails socket connection
This one asynchronous Sails/Socket.IO event may contain different Floatplane events in the payload, and contain different Floatplane events in the response/acknowledgement (depending on the request).
HTTP POST via Sails socket connection
This one asynchronous Sails/Socket.IO event may contain different Floatplane events in the payload, and contain different Floatplane events in the response/acknowledgement (depending on the request).
A radio chatter event arrives from Floatplane after joining a livestream via `/RadioMessage/joinLivestreamRadioFrequency`.
These events are primarily incoming chats from others in the livestream chat room, specifying who sent the chat, the message of the chat, and other meta data.