org.theta4j.osc.OSCClient.kt Maven / Gradle / Ivy
Show all versions of theta-web-api Show documentation
/*
* Copyright (C) 2022 theta4j project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.theta4j.osc
import com.burgstaller.okhttp.AuthenticationCacheInterceptor
import com.burgstaller.okhttp.CachingAuthenticatorDecorator
import com.burgstaller.okhttp.digest.CachingAuthenticator
import com.burgstaller.okhttp.digest.Credentials
import com.burgstaller.okhttp.digest.DigestAuthenticator
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.logging.HttpLoggingInterceptor
import java.io.IOException
import java.util.concurrent.ConcurrentHashMap
class OSCClient private constructor(
private val endpoint: String,
private val httpClient: OkHttpClient,
private val streamHttpClient: OkHttpClient,
) {
companion object {
private val GET_OPTIONS_COMMAND = Command.create("camera.getOptions", GetOptions.Parameter.serializer(), GetOptions.Result.serializer())
private val SET_OPTIONS_COMMAND = Command.create("camera.setOptions", SetOptions.Parameter.serializer(), Unit.serializer())
private val format = Json { ignoreUnknownKeys = true }
fun create(endpoint: String): OSCClient {
val httpClient = OkHttpClient.Builder().apply {
addInterceptor(HttpLoggingInterceptor(logger::debug).setLevel(HttpLoggingInterceptor.Level.BODY))
}.build()
val streamHttpClient = OkHttpClient.Builder().apply {
addInterceptor(HttpLoggingInterceptor(logger::debug).setLevel(HttpLoggingInterceptor.Level.HEADERS))
}.build()
return OSCClient(endpoint, httpClient, streamHttpClient)
}
fun createWithDigestAuthentication(endpoint: String, username: String, password: String): OSCClient {
val credentials = Credentials(username, password)
val authenticator = DigestAuthenticator(credentials)
val authCache = ConcurrentHashMap()
val httpClient = OkHttpClient.Builder().apply {
authenticator(CachingAuthenticatorDecorator(authenticator, authCache))
addInterceptor(AuthenticationCacheInterceptor(authCache))
addInterceptor(HttpLoggingInterceptor(logger::debug).setLevel(HttpLoggingInterceptor.Level.BODY))
}.build()
val streamHttpClient = OkHttpClient.Builder().apply {
authenticator(CachingAuthenticatorDecorator(authenticator, authCache))
addInterceptor(AuthenticationCacheInterceptor(authCache))
addInterceptor(HttpLoggingInterceptor(logger::debug).setLevel(HttpLoggingInterceptor.Level.HEADERS))
}.build()
return OSCClient(endpoint, httpClient, streamHttpClient)
}
}
suspend fun info(serializer: KSerializer): T = format.decodeFromJsonElement(serializer, httpGet("/osc/info"))
suspend fun state(serializer: KSerializer): OSCState = OSCState.decode(serializer, httpPost("/osc/state"))
suspend fun checkForUpdates(fingerprint: String): String {
val reqBody = buildJsonObject { put("stateFingerprint", fingerprint) }
val response = httpPost("/osc/checkForUpdates", reqBody)
response["error"]?.let { throw format.decodeFromJsonElement(it) }
return response["stateFingerprint"]!!.jsonPrimitive.content
}
suspend fun commandExecute(command: Command
, parameters: P? = null): CommandResponse {
val reqBody = buildJsonObject {
put("name", command.name)
if (parameters != null) {
put("parameters", Json.encodeToJsonElement(command.parameterSerializer, parameters))
}
}
val resBody = httpPost("/osc/commands/execute", reqBody)
val response = CommandResponse.decode(command.resultSerializer, resBody)
response.error?.let { throw it }
return response
}
suspend fun commandStatus(response: CommandResponse): CommandResponse {
val reqBody = buildJsonObject {
put("id", response.id)
}
val resBody = httpPost("/osc/commands/status", reqBody)
val newResponse = CommandResponse.decode(response.resultSerializer, resBody)
newResponse.error?.let { throw it }
return newResponse
}
suspend fun await(response: CommandResponse, interval: Long = 100): CommandResponse {
var res = response
while (res.state == CommandState.IN_PROGRESS) {
res = commandStatus(res)
delay(interval)
}
return res
}
suspend fun getOption(option: Option): T = getOptions(option)[option]!!
suspend fun getOption(option: ArrayOption): List = getOptions(option)[option]!!
suspend fun getOptions(vararg options: Option<*>): OptionSet = getOptions(listOf(*options))
suspend fun getOptions(options: Collection