From 6b0f5b99f94912fd9b981a3ea4730ff4258096f2 Mon Sep 17 00:00:00 2001 From: stephen Date: Fri, 20 Oct 2023 11:16:12 -0400 Subject: [PATCH] obs impl. --- build.gradle.kts | 4 + src/main/kotlin/net/eksb/obsdc/DBus.kt | 79 +++++++++++++++++++ src/main/kotlin/net/eksb/obsdc/Handler.kt | 5 ++ src/main/kotlin/net/eksb/obsdc/Main.kt | 67 +--------------- src/main/kotlin/net/eksb/obsdc/Obs.kt | 5 ++ .../kotlin/net/eksb/obsdc/ObsCommunity.kt | 43 ++++++++++ src/main/kotlin/net/eksb/obsdc/Op.kt | 7 ++ 7 files changed, 147 insertions(+), 63 deletions(-) create mode 100644 src/main/kotlin/net/eksb/obsdc/DBus.kt create mode 100644 src/main/kotlin/net/eksb/obsdc/Handler.kt create mode 100644 src/main/kotlin/net/eksb/obsdc/Obs.kt create mode 100644 src/main/kotlin/net/eksb/obsdc/ObsCommunity.kt create mode 100644 src/main/kotlin/net/eksb/obsdc/Op.kt diff --git a/build.gradle.kts b/build.gradle.kts index fa266fa..3db7c2e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,8 +6,12 @@ plugins { dependencies { testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.3") + implementation("com.github.hypfvieh:dbus-java-core:4.3.1") runtimeOnly("com.github.hypfvieh:dbus-java-transport-native-unixsocket:4.3.1") + + implementation("io.obs-websocket.community:client:2.0.0") + implementation("org.slf4j:slf4j-api:2.0.9") runtimeOnly("org.slf4j:slf4j-simple:2.0.9") } diff --git a/src/main/kotlin/net/eksb/obsdc/DBus.kt b/src/main/kotlin/net/eksb/obsdc/DBus.kt new file mode 100644 index 0000000..308f76a --- /dev/null +++ b/src/main/kotlin/net/eksb/obsdc/DBus.kt @@ -0,0 +1,79 @@ +package net.eksb.obsdc + +import org.freedesktop.dbus.annotations.DBusInterfaceName +import org.freedesktop.dbus.connections.impl.DBusConnection +import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder +import org.freedesktop.dbus.interfaces.DBusInterface +import org.freedesktop.dbus.interfaces.DBusSigHandler +import org.freedesktop.dbus.messages.DBusSignal +import org.slf4j.LoggerFactory +import kotlin.concurrent.thread + +class DBus(handler: Handler) { + + private val thread = thread( + start = true, + isDaemon = false, + name = "dbus", + ) { + DBusConnectionBuilder.forSessionBus().build().use { dbus -> + + // These lines are not necessary to handle signals, but are necessary to register methods. + dbus.requestBusName("net.eksb.obsdc") + dbus.exportObject("/", ObsdcDBusInterfaceImpl()) + + dbus.addSigHandler { signal -> + log.info("signal: ${signal.op}") + val op = Op.valueOf(signal.op) + log.info("op: ${op}") + handler.handle(op) + } + while (true) { + try { + Thread.sleep(60_000) + } catch (e:InterruptedException) { + log.info("interrupted") + break + } + } + } + log.info("done") + } + + fun stop() { + thread.interrupt() + } + + private val log = LoggerFactory.getLogger(DBus::class.java) +} + +inline fun DBusConnection.addSigHandler(handler: DBusSigHandler) { + addSigHandler(T::class.java, handler) +} + +@DBusInterfaceName("net.eksb.Obsdc") +interface ObsdcDBusInterface: DBusInterface { + fun echo(message:String): String + class Signal(path:String, val op:String): DBusSignal(path, op) +} +class ObsdcDBusInterfaceImpl: ObsdcDBusInterface { + override fun echo(message: String):String { + return message + } + override fun getObjectPath(): String = "/" +} + +/* +Monitor: +`dbus-monitor` + +See what is registered: +`qdbus net.eksb.obsdc /` + +Send signal: +`dbus-send --session --type=signal --dest=net.eksb.obsdc / net.eksb.Obsdc.Signal string:a` + +Call method: +`dbus-send --session --type=method_call --print-reply --dest=net.eksb.obsdc / net.eksb.Obsdc.echo string:b` +`qdbus net.eksb.obsdc / net.eksb.Obsdc.echo hello` + */ diff --git a/src/main/kotlin/net/eksb/obsdc/Handler.kt b/src/main/kotlin/net/eksb/obsdc/Handler.kt new file mode 100644 index 0000000..aed49cd --- /dev/null +++ b/src/main/kotlin/net/eksb/obsdc/Handler.kt @@ -0,0 +1,5 @@ +package net.eksb.obsdc + +fun interface Handler { + fun handle(op:Op) +} \ No newline at end of file diff --git a/src/main/kotlin/net/eksb/obsdc/Main.kt b/src/main/kotlin/net/eksb/obsdc/Main.kt index fad2dbd..9942b10 100644 --- a/src/main/kotlin/net/eksb/obsdc/Main.kt +++ b/src/main/kotlin/net/eksb/obsdc/Main.kt @@ -1,68 +1,9 @@ package net.eksb.obsdc -import org.freedesktop.dbus.annotations.DBusInterfaceName -import org.freedesktop.dbus.connections.impl.DBusConnection -import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder -import org.freedesktop.dbus.interfaces.DBusInterface -import org.freedesktop.dbus.interfaces.DBusSigHandler -import org.freedesktop.dbus.messages.DBusSignal -import org.slf4j.LoggerFactory - object Main { @JvmStatic - fun main(args:Array) { - - DBusConnectionBuilder.forSessionBus().build().use { dbus -> - - // These lines are not necessary to handle signals, but are necessary to register methods. - dbus.requestBusName("net.eksb.obsdc") - dbus.exportObject("/", ObsdcDBusInterfaceImpl()) - - dbus.addSigHandler { signal -> log.info("signal: ${signal.message}") } - while (true) { - try { - Thread.sleep(60_000) - } catch (e:InterruptedException) { - log.info("interrupted") - break - } - } - } - log.info("done") + fun main(args: Array) { + val obs = ObsCommunity() + DBus(obs.handler) // forks non-daemon thread } - -} - -inline fun DBusConnection.addSigHandler(handler:DBusSigHandler) { - addSigHandler(T::class.java, handler) -} - -private val log = LoggerFactory.getLogger(Main::class.java) - -@DBusInterfaceName("net.eksb.Obsdc") -interface ObsdcDBusInterface: DBusInterface { - fun echo(message:String): String - class Signal(path:String, val message:String): DBusSignal(path, message) -} -class ObsdcDBusInterfaceImpl: ObsdcDBusInterface { - override fun echo(message: String):String { - log.info("echo: ${message}") - return message - } - override fun getObjectPath(): String = "/" -} - -/* -Monitor: -`dbus-monitor` - -See what is registered: -`qdbus net.eksb.obsdc /` - -Send signal: -`dbus-send --session --type=signal --dest=net.eksb.obsdc / net.eksb.Obsdc.Signal string:a` - -Call method: -`dbus-send --session --type=method_call --print-reply --dest=net.eksb.obsdc / net.eksb.Obsdc.echo string:b` -`qdbus net.eksb.obsdc / net.eksb.Obsdc.echo hello` - */ \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/kotlin/net/eksb/obsdc/Obs.kt b/src/main/kotlin/net/eksb/obsdc/Obs.kt new file mode 100644 index 0000000..0a9a455 --- /dev/null +++ b/src/main/kotlin/net/eksb/obsdc/Obs.kt @@ -0,0 +1,5 @@ +package net.eksb.obsdc + +interface Obs { + val handler:Handler +} \ No newline at end of file diff --git a/src/main/kotlin/net/eksb/obsdc/ObsCommunity.kt b/src/main/kotlin/net/eksb/obsdc/ObsCommunity.kt new file mode 100644 index 0000000..d245cdc --- /dev/null +++ b/src/main/kotlin/net/eksb/obsdc/ObsCommunity.kt @@ -0,0 +1,43 @@ +package net.eksb.obsdc + +import io.obswebsocket.community.client.OBSRemoteController +import org.slf4j.LoggerFactory + +class ObsCommunity: Obs, AutoCloseable { + + // TODO: make a single controller, and read ops from a queue. + + override fun close() { + } + + override val handler: Handler = Handler { op -> + var controller:OBSRemoteController? = null + + fun ready() { + controller?.let { controller -> + log.info("ready to send ${op}") + + controller.triggerStudioModeTransition { response -> + log.info("Response successful: ${response.isSuccessful}") + controller.disconnect() + } + } + } + + controller = OBSRemoteController.builder() + .host("localhost") + .port(4455) + .password("R3tRkVXhFofJ2wRF") // TODO put this in a file + .autoConnect(true) + .lifecycle() + .onReady { + ready() + } + .and() + .build() + } + + companion object { + private val log = LoggerFactory.getLogger(ObsCommunity::class.java) + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/eksb/obsdc/Op.kt b/src/main/kotlin/net/eksb/obsdc/Op.kt new file mode 100644 index 0000000..32755be --- /dev/null +++ b/src/main/kotlin/net/eksb/obsdc/Op.kt @@ -0,0 +1,7 @@ +package net.eksb.obsdc + +enum class Op { + A, + B, + ; +} \ No newline at end of file