From e4640181c3f203d5424d606f655121f11551e583 Mon Sep 17 00:00:00 2001 From: stephen Date: Fri, 20 Oct 2023 17:40:13 -0400 Subject: [PATCH] pan lowest non-locked item --- src/main/kotlin/net/eksb/obsdc/Obs.kt | 68 +++++++++++++++++++++------ 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/net/eksb/obsdc/Obs.kt b/src/main/kotlin/net/eksb/obsdc/Obs.kt index d1d0a7f..fd65ddb 100644 --- a/src/main/kotlin/net/eksb/obsdc/Obs.kt +++ b/src/main/kotlin/net/eksb/obsdc/Obs.kt @@ -2,6 +2,9 @@ package net.eksb.obsdc import io.obswebsocket.community.client.OBSRemoteController import io.obswebsocket.community.client.message.event.ui.StudioModeStateChangedEvent +import io.obswebsocket.community.client.message.request.RequestBatch +import io.obswebsocket.community.client.message.request.sceneitems.GetSceneItemLockedRequest +import io.obswebsocket.community.client.message.response.sceneitems.GetSceneItemLockedResponse import io.obswebsocket.community.client.model.Scene import io.obswebsocket.community.client.model.SceneItem.Transform import io.obswebsocket.community.client.model.SceneItem.Transform.TransformBuilder @@ -15,6 +18,8 @@ class Obs(private val q:BlockingQueue): AutoCloseable { private val panAmount = 50F + private var alive = true + private val controller = OBSRemoteController.builder() .host("localhost") .port(4455) @@ -26,14 +31,16 @@ class Obs(private val q:BlockingQueue): AutoCloseable { .onClose { code -> log.error("closed:${code}")} .onControllerError { e -> log.error("controller error", e ) } .onCommunicatorError { e -> log.error("comm error", e ) } - .onDisconnect { log.info("disconnected") } + .onDisconnect { + log.info("disconnected") + if (alive) reconnect() + } .and() .registerEventListener(StudioModeStateChangedEvent::class.java) { log.info("studio mode state change: ${it}") } .build() - private var alive = true init { Runtime.getRuntime().addShutdownHook(thread(start=false) { @@ -43,6 +50,10 @@ class Obs(private val q:BlockingQueue): AutoCloseable { }) } + private fun reconnect() { + controller.connect() + } + fun run() { controller.connect() } @@ -99,24 +110,53 @@ class Obs(private val q:BlockingQueue): AutoCloseable { } } + // Pan the bottom-most non-locked item. private fun pan(block:TransformBuilder.(Transform)->TransformBuilder) { controller.getCurrentProgramScene { response -> val sceneName = response.currentProgramSceneName log.info("scene name: ${sceneName}") controller.getSceneItemList(sceneName) { response -> - val item = response.sceneItems.last() - val itemId = item.sceneItemId - log.info("last item id: ${itemId}") - controller.getSceneItemTransform(sceneName, itemId) { response -> - val transform = response.sceneItemTransform - log.info("position: ${transform.positionX} x ${transform.positionY}") - val newTransform = block(Transform.builder(), transform).build() - controller.setSceneItemTransform(sceneName, itemId, newTransform) { response -> - log.info("transform successful: ${response.isSuccessful}") - // Have to set the current scene to take effect if in studio mode. - controller.setCurrentProgramScene(sceneName) { response -> - ready() + val items = response.sceneItems + // Even though locked status is in the response from OBS, the library does not parse it. + // So we have to ask for it explicitly: + controller.sendRequestBatch( + RequestBatch.builder() + .requests( + response.sceneItems.map { item -> + GetSceneItemLockedRequest.builder() + .sceneName(sceneName) + .sceneItemId(item.sceneItemId) + .build() + } + ) + .build() + ) { response -> + val item = response.data.results + .map { result -> + (result.responseData as GetSceneItemLockedResponse.SpecificData).sceneItemLocked } + .zip(items) + .asSequence() + .filter { (locked,item) -> ! locked } + .map { (locked,item) -> item } + .sortedBy { item -> item.sceneItemIndex } + .firstOrNull() + log.info("item to pan: ${item?.sceneItemId}") + if (item != null) { + controller.getSceneItemTransform(sceneName, item.sceneItemId) { response -> + val transform = response.sceneItemTransform + log.info("position: ${transform.positionX} x ${transform.positionY}") + val newTransform = block(Transform.builder(), transform).build() + controller.setSceneItemTransform(sceneName, item.sceneItemId, newTransform) { response -> + log.info("transform successful: ${response.isSuccessful}") + // Have to set the current scene to take effect if in studio mode. + controller.setCurrentProgramScene(sceneName) { response -> + ready() + } + } + } + } else { + ready() } } }