mirror of
https://github.com/jellyfin/jellyfin-sdk-kotlin.git
synced 2025-02-17 06:37:29 +00:00
Add kotlin-console example using kotlinx.cli and the library
This commit is contained in:
parent
4faeaed126
commit
d4925d43b0
17
samples/kotlin-console/README.md
Normal file
17
samples/kotlin-console/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Kotlin Console Example
|
||||
|
||||
This sample project uses the kotlinx-cli library to build a command line tool that uses the Jellyfin
|
||||
library. It's used as a showcase of the libraries abilities and is not meant for general use.
|
||||
|
||||
Features include:
|
||||
- Server discovery
|
||||
- Authenticate
|
||||
- List libraries
|
||||
|
||||
## Basic usage
|
||||
Assuming the binary is called `jellyfin` the following sample will list all libraries in the demo instance:
|
||||
```sh
|
||||
jellyfin libraries --server https://demo.jellyfin.org/stable --token $(jellyfin login --server https://demo.jellyfin.org/stable --username demo)
|
||||
```
|
||||
|
||||
This command is also provided in the `test.sh` file. It requires a local install first using `./gradlew installDist`.
|
36
samples/kotlin-console/build.gradle.kts
Normal file
36
samples/kotlin-console/build.gradle.kts
Normal file
@ -0,0 +1,36 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("application")
|
||||
}
|
||||
|
||||
application {
|
||||
mainClassName = "org.jellyfin.sample.console.MainKt"
|
||||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
|
||||
// Repository needed for kotlinx-cli
|
||||
maven("https://kotlin.bintray.com/kotlinx")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Depend on the library project
|
||||
implementation(project(":library"))
|
||||
|
||||
// Use Kotlin stdlib
|
||||
implementation(kotlin("stdlib"))
|
||||
|
||||
// Use Kotlin coroutines to interact with the library
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5")
|
||||
|
||||
// The CLI library
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-cli:0.2.1")
|
||||
|
||||
// Use JSON
|
||||
implementation("com.google.code.gson:gson:2.8.6")
|
||||
}
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().all {
|
||||
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlinx.cli.ExperimentalCli"
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package org.jellyfin.sample.console
|
||||
|
||||
import kotlinx.cli.ArgParser
|
||||
import org.jellyfin.apiclient.AppInfo
|
||||
import org.jellyfin.apiclient.Jellyfin
|
||||
import org.jellyfin.apiclient.interaction.device.IDevice
|
||||
import org.jellyfin.sample.console.cli.Discover
|
||||
import org.jellyfin.sample.console.cli.Libraries
|
||||
import org.jellyfin.sample.console.cli.Login
|
||||
import org.jellyfin.sample.console.utils.GarbageHttpClient
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val jellyfin = Jellyfin {
|
||||
appInfo = AppInfo("Jellyfin Sample: Kotlin Console", "DEV")
|
||||
httpClient = GarbageHttpClient()
|
||||
}
|
||||
|
||||
val device = object : IDevice {
|
||||
override val deviceName: String = "cli"
|
||||
override val deviceId: String = "cli"
|
||||
}
|
||||
|
||||
ArgParser("jellyfin").apply {
|
||||
subcommands(Discover(jellyfin))
|
||||
subcommands(Login(jellyfin, device))
|
||||
subcommands(Libraries(jellyfin, device))
|
||||
|
||||
parse(args)
|
||||
// parse(arrayOf("discover"))
|
||||
// parse("login --server https://demo.jellyfin.org/stable --username demo --password ".split(" ").toTypedArray())
|
||||
// parse("libraries --server https://demo.jellyfin.org/stable --token 1d1e113ab39e4804bb42580b4323810b".split(" ").toTypedArray())
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package org.jellyfin.sample.console.cli
|
||||
|
||||
import kotlinx.cli.Subcommand
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jellyfin.apiclient.Jellyfin
|
||||
|
||||
class Discover(
|
||||
private val jellyfin: Jellyfin
|
||||
) : Subcommand("discover", "Discover servers on the local network") {
|
||||
override fun execute() = runBlocking {
|
||||
println("Starting discovery")
|
||||
|
||||
jellyfin.discovery.discover().onEach {
|
||||
println("Server ${it.name} was found at address ${it.address}:")
|
||||
println(" $it")
|
||||
}.collect()
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package org.jellyfin.sample.console.cli
|
||||
|
||||
import kotlinx.cli.ArgType
|
||||
import kotlinx.cli.Subcommand
|
||||
import kotlinx.cli.required
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jellyfin.apiclient.Jellyfin
|
||||
import org.jellyfin.apiclient.interaction.device.IDevice
|
||||
import org.jellyfin.apiclient.model.querying.ItemsResult
|
||||
import org.jellyfin.apiclient.model.session.SessionInfoDto
|
||||
import org.jellyfin.sample.console.utils.callApi
|
||||
|
||||
class Libraries(
|
||||
private val jellyfin: Jellyfin,
|
||||
private val device: IDevice
|
||||
) : Subcommand("libraries", "List all libraries") {
|
||||
val server by option(ArgType.String, description = "Url of the server", shortName = "s").required()
|
||||
val token by option(ArgType.String, description = "Access token", shortName = "t").required()
|
||||
|
||||
override fun execute() = runBlocking {
|
||||
val api = jellyfin.createApi(serverAddress = server, accessToken = token, device = device)
|
||||
|
||||
val sessionInfo = callApi<Array<SessionInfoDto>> { callback ->
|
||||
api.GetCurrentSessionAsync(callback)
|
||||
}.firstOrNull()
|
||||
|
||||
if (sessionInfo == null) println("Unknown session")
|
||||
|
||||
val libraries = callApi<ItemsResult> { callback ->
|
||||
api.GetUserViews(sessionInfo!!.userId, callback)
|
||||
}
|
||||
|
||||
libraries.items.forEach {
|
||||
println(it.name)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package org.jellyfin.sample.console.cli
|
||||
|
||||
import kotlinx.cli.ArgType
|
||||
import kotlinx.cli.Subcommand
|
||||
import kotlinx.cli.required
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jellyfin.apiclient.Jellyfin
|
||||
import org.jellyfin.apiclient.interaction.device.IDevice
|
||||
import org.jellyfin.apiclient.model.users.AuthenticationResult
|
||||
import org.jellyfin.sample.console.utils.callApi
|
||||
|
||||
class Login(
|
||||
private val jellyfin: Jellyfin,
|
||||
private val device: IDevice
|
||||
) : Subcommand("login", "Login to a given server and retrieve an access token") {
|
||||
val server by option(ArgType.String, description = "Url of the server", shortName = "s").required()
|
||||
val username by option(ArgType.String, description = "Username", shortName = "u").required()
|
||||
val password by option(ArgType.String, description = "Password", shortName = "p")
|
||||
|
||||
override fun execute() = runBlocking {
|
||||
val api = jellyfin.createApi(serverAddress = server, device = device)
|
||||
|
||||
val result = callApi<AuthenticationResult> { callback ->
|
||||
api.AuthenticateUserAsync(username, password ?: "", callback)
|
||||
}
|
||||
|
||||
if (result.accessToken != null) println(result.accessToken)
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package org.jellyfin.sample.console.utils
|
||||
|
||||
import org.jellyfin.apiclient.interaction.Response
|
||||
import org.jellyfin.apiclient.interaction.http.HttpRequest
|
||||
import org.jellyfin.apiclient.interaction.http.IAsyncHttpClient
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
|
||||
class GarbageHttpClient : IAsyncHttpClient {
|
||||
override fun Send(request: HttpRequest?, response: Response<String>?) {
|
||||
requireNotNull(request)
|
||||
requireNotNull(response)
|
||||
|
||||
val headers = request.requestHeaders.toMap().toMutableMap()
|
||||
|
||||
// Magic
|
||||
if (request.requestContentType != null && !headers.containsKey("Content-Type"))
|
||||
headers["Content-Type"] = request.requestContentType
|
||||
else if (!request.postData.isNullOrEmpty() && !headers.containsKey("Content-Type"))
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
if (request.requestHeaders.authorizationParameter != null)
|
||||
headers["X-Emby-Authorization"] = "${request.requestHeaders.authorizationScheme} ${request.requestHeaders.authorizationParameter}"
|
||||
|
||||
try {
|
||||
val connection = URL(request.url).openConnection() as HttpURLConnection
|
||||
connection.apply {
|
||||
useCaches = request.enableCaching
|
||||
|
||||
requestMethod = request.method
|
||||
|
||||
headers.forEach { header ->
|
||||
setRequestProperty(header.key, header.value)
|
||||
}
|
||||
|
||||
if (!request.postData.isNullOrEmpty()) {
|
||||
doOutput = true
|
||||
outputStream.write(request.postData.GetQueryString().toByteArray())
|
||||
}
|
||||
}
|
||||
|
||||
val res = connection.inputStream.readBytes().toString(Charsets.UTF_8)
|
||||
response.onResponse(res)
|
||||
} catch (err: Exception) {
|
||||
response.onError(err)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package org.jellyfin.sample.console.utils
|
||||
|
||||
import org.jellyfin.apiclient.interaction.Response
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
suspend fun <T : Any> callApi(init: (callback: Response<T>) -> Unit): T = suspendCoroutine { continuation ->
|
||||
init(object : Response<T>() {
|
||||
override fun onResponse(response: T) = continuation.resumeWith(Result.success(response))
|
||||
override fun onError(exception: Exception) = continuation.resumeWith(Result.failure(exception))
|
||||
})
|
||||
}
|
9
samples/kotlin-console/test.sh
Normal file
9
samples/kotlin-console/test.sh
Normal file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
function jellyfin() {
|
||||
"$(pwd)/build/install/kotlin-console/bin/kotlin-console" "$@"
|
||||
}
|
||||
|
||||
server=https://demo.jellyfin.org/stable
|
||||
|
||||
jellyfin libraries --server $server --token "$(jellyfin login --server $server --username demo)"
|
@ -4,3 +4,6 @@ include(":model")
|
||||
|
||||
// Platforms
|
||||
include(":android")
|
||||
|
||||
// Samples
|
||||
include(":samples:kotlin-console")
|
||||
|
Loading…
x
Reference in New Issue
Block a user