Skip to content

Commit

Permalink
Update documentation for 1.5
Browse files Browse the repository at this point in the history
  • Loading branch information
nielsvanvelzen committed Jun 2, 2024
1 parent 72d54e6 commit 5d1bb62
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 136 deletions.
21 changes: 10 additions & 11 deletions docs/guide/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ val api = jellyfin.createApi(
Change the token in an existing API client instance like so:

```kotlin
api.accessToken = "02a7174a4d1843448b6d177d8288efd0"
api.update(accessToken = "02a7174a4d1843448b6d177d8288efd0")
```

## Password login
Expand All @@ -43,7 +43,7 @@ try {
)

// Use access token in api instance
api.accessToken = authenticationResult.accessToken
api.update(accessToken = authenticationResult.accessToken)

// Print session information
println(authenticationResult.sessionInfo)
Expand All @@ -70,19 +70,19 @@ the new one.
The Quick Connect functionality may be disabled by a server. In those cases the server responds with an HTTP 401
response. You can check if QuickConnect is enabled first or deal with it when trying to use it.

To start a Quick Connect session you need to request a Quick Connect code with the initiate function. This returns a
state object which can be updated by calling the connect method.
To start a Quick Connect session you need to request a Quick Connect code with the initiateQuickConnect function. This
returns a state object which can be updated by calling the getQuickConnectState method.

```kotlin
val api = jellyfin.createApi(/* .. */)

// Check if Quick Connect is enabled (this is optional)
val enabled by api.quickConnectApi.getEnabled()
val enabled by api.quickConnectApi.getQuickConnectEnabled()
if (!enabled) println("QuickConnect is disabled in the server!")

// Create a Quick Connect session and store the state
try {
var quickConnectState by api.quickConnectApi.initiate()
val quickConnectState by api.quickConnectApi.initiateQuickConnect()
} catch (err: InvalidStatusException) {
if (err.status == 401) {
// Quick Connect is disabled
Expand All @@ -99,13 +99,12 @@ The Quick Connect state contains a few values that are of interest:
- The `code` is for the user to input in a different app

The next step is to show the code to the user. Depending on your app you might want to either show a "next" button to
press
when the user authorized the app or automatically update the Quick Connect state. In the latter example we recommend
updating every 5 seconds.
press when the user authorized the app or automatically update the Quick Connect state. In the latter example we
recommend updating every 5 seconds.

```kotlin
// Update the Quick Connect session state
quickConnectState = api.quickConnectApi.connect(
quickConnectState = api.quickConnectApi.getQuickConnectState(
secret = quickConnectState.secret,
)
```
Expand All @@ -119,7 +118,7 @@ val authenticationResult by api.userApi.authenticateWithQuickConnect(
)

// Use access token in api instance
api.accessToken = authenticationResult.accessToken
api.update(accessToken = authenticationResult.accessToken)

// Print session information
println(authenticationResult.sessionInfo)
Expand Down
8 changes: 3 additions & 5 deletions docs/guide/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ try {
)

// Use access token in api instance
api.accessToken = authenticationResult.accessToken
api.update(accessToken = authenticationResult.accessToken)

// Print session information
println(authenticationResult.sessionInfo)
Expand All @@ -122,9 +122,7 @@ Jellyfin uses WebSockets to communicate events like library changes and activiti
used with the SocketApi.

```kotlin
val instance = api.ws()

instance.addGlobalListener { message ->
api.webSocket.subscribeAll().collect { message ->
println(message)
}
```
Expand Down Expand Up @@ -162,4 +160,4 @@ val recommended = jellyfin.discovery.getRecommendedServers(candidates, Recommend
and series.
- [Homedia](https://github.com/valmnt/homedia) provides a native user interface for Android TV using Jetpack Compose.

_Want to add your project? We'd love to know about it, open an issue or create pull request!_
_Want to add your project? We'd love to know about it, please open a pull request to add it!_
156 changes: 36 additions & 120 deletions docs/guide/websockets.md
Original file line number Diff line number Diff line change
@@ -1,172 +1,88 @@
# Using WebSockets

Added in v1.2.0, the new WebSocket implementation can be used to interact with the Jellyfin WebSocket server. This API
is not supported with the Java language because it heavily relies on coroutines and inline functions.
The WebSocket API can be used to interact with the Jellyfin WebSocket server. Get started by creating a new
authenticated API instance using the `createApi` function in the Jellyfin class.

Get started by creating a new authenticated API instance using the `createApi` function in the Jellyfin class.
Listening the WebSocket messages is done using subscriptions. Whenever one or multiple subscriptions are active the
WebSocket will automatically connect to the server. Credentials changes via the `update()` function will cause the
WebSocket to reconnect. There is a single WebSocket API for each ApiClient.

```kotlin
// Create an API instance
val api = jellyfin.createApi(baseUrl = "https://demo.jellyfin.org/stable/")
```

## Connecting

The socket connection is managed by an "instance". You can have multiple of these instances at the same time. However,
it is recommended to use a single instance during the lifecycle of your application. Use the `ws()`
function from the ApiClient class to create a new instance.

```kotlin
val instance = api.ws()
```

You can close an instance when it's no longer in use with the `stop()` function.

```kotlin
instance.stop()
// Create a subscription for WebSocket all messages
api.webSocket.subscribeAll().collect { message ->
println(message)
}
```

## Updating credentials

An instance does not automatically refresh credentials. You'll need to manually refresh the instance when the access
token, server or device info change. Use the `updateCredentials()` function to apply these changes. The instance
automatically reconnects when required.

```kotlin
instance.updateCredentials()
```
The WebSocket connection will reconnect when the server URL, access token, client information of device information
changes. All existing subscriptions stay active and automatically switch to the new credentials.

## Listen for messages
## Subscribe to messages

Listeners are used to receive the various types of websocket messages. A connection is automatically started and/or
closed depending on the active listeners. Multiple helper functions can be used to register a listener. They all return
a `SocketListener` object that can be used to remove the listener later with the `removeListener()` function on the
instance or the `stop()` on the listener.
Subscriptions are used to receive the various types of websocket messages. A connection is automatically started and/or
closed depending on the availability of subscriptions. Multiple extension functions can be used to create a
subscription. They all return a flow that emits each message as soon as it is received.

## Listen for specific messages
## Subscribe to specific messages

Use the `addListener()` function to create a listener that receives a single type.
Use the `subscribe<T>()` function to create a listener that receives a single message type.

```kotlin
instance.addListener<UserDataChangedMessage> { message ->
api.webSocket.subscribe<UserDataChangedMessage>().collect { message ->
// type of message is UserDataChangedMessage
println("Received a message: $message")
}
```

## Listen for all messages
## Subscribe to all messages

If you want to listen for all types of messages instead. Use the `addGlobalListener` function.
Use the `subscribeAll` function if you want to subscribe to all types of messages instead. This is not recommended if
you only want to receive a subset of message types.

```kotlin
instance.addGlobalListener { message ->
// type of message is IncomingSocketMessage
api.webSocket.subscribeAll().collect { message ->
// type of message is OutboundWebSocketMessage
println("Received a message: $message")
}
```

## Listen for grouped message types
## Subscribe to grouped message types

Some incoming messages are used for multiple kinds of information. These are the general, play state and SyncPlay
commands. To filter the types of commands there are a few helper functions available. All of them support a "commands"
parameter to define the message types to receive. All types will be sent when the commands parameter is omitted. This is
the same behavior as using `addListener`.
Some incoming messages are used for multiple kinds of information. These are the general commands, play state and
SyncPlay command messages. To filter the types of commands there are extensions functions available. All of them support
filtering for one or multiple commands to receive. All types will be sent when the commands parameter is omitted. This is
the same behavior as using the `subscribe<T>` function.

```kotlin
instance.addGeneralCommandsListener(
commands = setOf(GeneralCommandType.DISPLAY_MESSAGE)
) { message ->
api.webSocket.subscribeGeneralCommand(GeneralCommandType.DISPLAY_MESSAGE).collect { message ->
// type of message is GeneralCommandMessage
println("Received a message: $message")
}

instance.addPlayStateCommandsListener(
api.webSocket.subscribePlayStateCommands(
commands = setOf(PlaystateCommand.NEXT_TRACK, PlaystateCommand.PREVIOUS_TRACK)
) { message ->
// type of message is PlayStateMessage
).collect { message ->
// type of message is PlaystateMessage
println("Received a message: $message")
}

instance.addSyncPlayCommandsListener(
api.webSocket.subscribeSyncPlayCommands(
commands = setOf(SendCommandType.PAUSE, SendCommandType.UNPAUSE)
) { message ->
// type of message is SyncPlayCommandMessage
println("Received a message: $message")
}
```

## Advanced listeners

All previously mentioned functions to add listeners use the `addListenerDefinition` function under the hood. This
function is not recommended being used directly. Use the other functions instead. The function receives a listener
definition.

An example for listening to both LibraryChangedMessage and UserDataChangedMessage messages:

```kotlin
instance.addListenerDefinition(
SocketListenerDefinition(
subscribesTo = emptySet(),
filterTypes = setOf(LibraryChangedMessage::class, UserDataChangedMessage::class),
stopOnCredentialsChange = false,
listener = { message ->
// type of message is IncomingSocketMessage
println("Received a message: $message")
}
)
)
```

## Sending messages

The Jellyfin server uses HTTP endpoints, mostly in the SessionApi, to manipulate state. The only messages send by a
client are to enable subscriptions. These subscriptions are automatically managed by the SDK. The `publish()` function
can still be used if you need to send your own messages. The function receives a `OutgoingSocketMessage` type and sends
it to the server.

```kotlin
instance.publish(SessionsStartMessage())
```

> **Note**: Do not send start and stop messages manually. This can confuse the SDK and cause unknown behavior.
## Message Types

The following messages types are supported in the SDK.

### Incoming

- GeneralCommandMessage
- UserDataChangedMessage
- SessionsMessage
- PlayMessage
- SyncPlayCommandMessage
- SyncPlayGroupUpdateMessage
- PlayStateMessage
- RestartRequiredMessage
- ServerShuttingDownMessage
- ServerRestartingMessage
- LibraryChangedMessage
- UserDeletedMessage
- UserUpdatedMessage
- SeriesTimerCreatedMessage
- TimerCreatedMessage
- SeriesTimerCancelledMessage
- TimerCancelledMessage
- RefreshProgressMessage
- ScheduledTaskEndedMessage
- PackageInstallationCancelledMessage
- PackageInstallationFailedMessage
- PackageInstallationCompletedMessage
- PackageInstallingMessage
- PackageUninstalledMessage
- ActivityLogEntryMessage
- ScheduledTasksInfoMessage

### Outgoing

- ActivityLogEntryStartMessage and ActivityLogEntryStopMessage
- SessionsStartMessage and SessionsStopMessage
- ScheduledTasksInfoStartMessage and ScheduledTasksInfoStopMessage
The SDK does not expose any functionality to send messages. Publishing messages is only used to keep the connection
alive or to request for certain message types to be sent. This is all managed by the SDK.

## Sample usage

Expand Down

0 comments on commit 5d1bb62

Please sign in to comment.