From 984f4d2badea699c8c25a49aa029f4cfb1e30eb3 Mon Sep 17 00:00:00 2001 From: Nickiel12 Date: Sun, 21 Jul 2024 00:40:09 +0000 Subject: [PATCH] added scene request states --- notes.md | 16 ++++++ src/core/mod.rs | 3 +- src/external_state/mod.rs | 100 ++++++++++++++++++++++++++++++++++++ src/external_state/timer.rs | 42 +++++++++++++++ src/main.rs | 32 ++++++------ src/satellite_state.rs | 23 +++++++-- 6 files changed, 194 insertions(+), 22 deletions(-) create mode 100644 notes.md create mode 100644 src/external_state/mod.rs create mode 100644 src/external_state/timer.rs diff --git a/notes.md b/notes.md new file mode 100644 index 0000000..3191de7 --- /dev/null +++ b/notes.md @@ -0,0 +1,16 @@ + + +Active camera and screen scene, +augmented scene + +timer state +timer length +clicker can change scene +time can run +start/stop stream/recording + +override RPC for: +- volume up and down +- next/prev slide + +If I can make all the scenes dynamic, it gets rid of a lot of the states and state management, so if I can declare the types and names of scenes, and save those to config files, then that diff --git a/src/core/mod.rs b/src/core/mod.rs index 7a942d3..ffe6fd9 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,4 +1,3 @@ - use futures_util::{ stream::{SplitSink, SplitStream}, SinkExt, StreamExt, @@ -20,7 +19,7 @@ pub struct ConnectionCoreParam { pub shutdown_rx: watch::Receiver, } -#[instrument] +#[instrument(skip(params, on_connection_oneshot))] pub async fn connect_to_core( params: ConnectionCoreParam, on_connection_oneshot: oneshot::Sender>, diff --git a/src/external_state/mod.rs b/src/external_state/mod.rs new file mode 100644 index 0000000..bf9eacc --- /dev/null +++ b/src/external_state/mod.rs @@ -0,0 +1,100 @@ +mod timer; +use obws::{requests::scenes::SceneId, responses::scenes::Scene, Client}; +use timer::Timer; + +#[derive(Default, Debug)] +pub struct ExternalState { + pub scene_timer: Timer, + + pub is_recording: bool, + pub is_streaming: bool, + + pub scenes: Vec, +} + + +impl ExternalState { + pub async fn update_obs(&mut self, client: &Client) { + self.is_recording = client + .recording() + .status() + .await + .map(|res| res.active) + .unwrap_or(false); + + self.is_streaming = client + .streaming() + .status() + .await + .map(|res| res.active) + .unwrap_or(false); + + let src_scenes = client + .scenes() + .list() + .await + .unwrap_or(obws::responses::scenes::Scenes::default()) + .scenes; + + let mut scenes = Vec::::with_capacity(src_scenes.len()); + for scene in src_scenes { + scenes.push(SceneState::new(scene, &client).await); + } + self.scenes = scenes; + } +} + +#[derive(Default, Debug)] +pub struct SceneState { + pub scene: Scene, + pub scene_items: Vec, +} + +impl SceneState { + async fn new(source: Scene, client: &Client) -> Self { + let scene_id = obws::requests::scenes::SceneId::Name(&source.name); + let items = client.scene_items().list(scene_id).await; + + + if let Ok(items) = items { + let mut scene_items = Vec::::with_capacity(items.len()); + for item in items { + scene_items.push(SceneItem::new(item, scene_id, client).await); + } + + SceneState { + scene: source, + scene_items + } + } else { + SceneState { + scene: source, + scene_items: vec![] + } + } + } +} + +#[derive(Debug)] +pub struct SceneItem { + pub scene_item: obws::responses::scene_items::SceneItem, + pub transform: obws::responses::scene_items::SceneItemTransform, +} + +impl SceneItem { + async fn new<'a>(source: obws::responses::scene_items::SceneItem, id: SceneId<'a>, client: &Client) -> Self { + let transform = client.scene_items().transform(id, source.id).await; + + if let Ok(transform) = transform { + SceneItem { + scene_item: source, + transform, + } + } else { + SceneItem { + scene_item: source, + transform: obws::responses::scene_items::SceneItemTransform::default(), + } + } + } +} diff --git a/src/external_state/timer.rs b/src/external_state/timer.rs new file mode 100644 index 0000000..d65c3e5 --- /dev/null +++ b/src/external_state/timer.rs @@ -0,0 +1,42 @@ +use std::time::{Duration, Instant}; + +#[derive(Default, Debug)] +pub struct Timer { + timer_start: Option, + timer_len: Duration, +} + +impl Timer { + pub fn set_len(&mut self, new_len: Duration) { + self.timer_len = new_len; + } + pub fn start(&mut self) { + self.timer_start = Some(Instant::now()); + } + pub fn stop(&mut self) { + self.timer_start = None; + } + + /// Returns true if the time_start + timer_len > Instant::now + /// or if the timer is stopped + pub fn is_complete(&mut self) -> bool { + if let Some(start) = self.timer_start { + start + self.timer_len > Instant::now() + } else { + false + } + } + + /// Returns the time left, or None if the timer is complete + pub fn time_left(&mut self) -> Option { + if self.is_complete() { + None + } else { + if let Some(start) = self.timer_start { + Some((start + self.timer_len) - Instant::now()) + } else { + None + } + } + } +} diff --git a/src/main.rs b/src/main.rs index 2b66c5e..60f46a1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,17 @@ - use std::time::Duration; -use tokio::sync::{mpsc::{self, error::TryRecvError}, watch}; +use tokio::sync::{ + mpsc::{self, error::TryRecvError}, + watch, +}; use tracing::{error, info, warn}; mod config; mod core; +mod external_state; mod satellite_state; +use external_state::ExternalState; use satellite_state::SatelliteState; pub enum AppEvent { @@ -38,6 +42,7 @@ async fn main() { let (tx, rx) = mpsc::channel::(10); let mut state = SatelliteState { + ext_state: ExternalState::default(), config, shutdown_tx, shutdown_rx, @@ -53,22 +58,17 @@ async fn main() { state.check_sockets().await; match state.events_rx.try_recv() { - Err(e) => { - match e { - TryRecvError::Empty => { - tokio::time::sleep(Duration::from_millis(500)).await; - }, - TryRecvError::Disconnected => { - error!("events mpsc was closed! Exiting main thread"); - break; - } + Err(e) => match e { + TryRecvError::Empty => { + tokio::time::sleep(Duration::from_millis(500)).await; } - } - Ok(res) => { - - } + TryRecvError::Disconnected => { + error!("events mpsc was closed! Exiting main thread"); + break; + } + }, + Ok(res) => {} } - } // let version = client.general().version().await.unwrap(); diff --git a/src/satellite_state.rs b/src/satellite_state.rs index d1f54fc..a12800c 100644 --- a/src/satellite_state.rs +++ b/src/satellite_state.rs @@ -4,9 +4,16 @@ use tokio::sync::{mpsc, oneshot, watch}; use tokio_tungstenite::tungstenite::Message; use tracing::{info, instrument, warn}; -use crate::{config::AppConfig, core::{connect_to_core, ConnectionCoreParam}, AppEvent}; +use crate::{ + config::AppConfig, + core::{connect_to_core, ConnectionCoreParam}, + external_state::ExternalState, + AppEvent, +}; pub struct SatelliteState { + pub ext_state: ExternalState, + pub config: AppConfig, pub obs_client: Option, @@ -22,6 +29,7 @@ pub struct SatelliteState { impl SatelliteState { pub async fn check_sockets(&mut self) { + if self.obs_client.is_none() { self.obs_client = match Client::connect( self.config.obs_ip.clone(), @@ -34,7 +42,11 @@ impl SatelliteState { info!("Failed to connect to obs: {e}"); None } - Ok(client) => Some(client), + Ok(client) => { + self.ext_state.update_obs(&client).await; + info!("{:#?}", self.ext_state); + Some(client) + } } } @@ -55,7 +67,11 @@ impl SatelliteState { let param = ConnectionCoreParam { shutdown_rx: self.shutdown_rx.clone(), event_loop_tx: self.events_tx.clone(), - connection_string: format!("ws://{}:{}", self.config.core_ip, self.config.core_port.to_string()), + connection_string: format!( + "ws://{}:{}", + self.config.core_ip, + self.config.core_port.to_string() + ), }; let (tx, rx) = oneshot::channel::>(); @@ -63,7 +79,6 @@ impl SatelliteState { self.to_core_oneshot = Some(rx); tokio::spawn(connect_to_core(param, tx)); - } #[instrument(skip(obs_client, event_tx))]