added scene request states

This commit is contained in:
Nickiel12 2024-07-21 00:40:09 +00:00
parent a20b3fff6c
commit 984f4d2bad
6 changed files with 194 additions and 22 deletions

16
notes.md Normal file
View file

@ -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

View file

@ -1,4 +1,3 @@
use futures_util::{ use futures_util::{
stream::{SplitSink, SplitStream}, stream::{SplitSink, SplitStream},
SinkExt, StreamExt, SinkExt, StreamExt,
@ -20,7 +19,7 @@ pub struct ConnectionCoreParam {
pub shutdown_rx: watch::Receiver<bool>, pub shutdown_rx: watch::Receiver<bool>,
} }
#[instrument] #[instrument(skip(params, on_connection_oneshot))]
pub async fn connect_to_core( pub async fn connect_to_core(
params: ConnectionCoreParam, params: ConnectionCoreParam,
on_connection_oneshot: oneshot::Sender<mpsc::Sender<Message>>, on_connection_oneshot: oneshot::Sender<mpsc::Sender<Message>>,

100
src/external_state/mod.rs Normal file
View file

@ -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<SceneState>,
}
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::<SceneState>::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<SceneItem>,
}
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::<SceneItem>::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(),
}
}
}
}

View file

@ -0,0 +1,42 @@
use std::time::{Duration, Instant};
#[derive(Default, Debug)]
pub struct Timer {
timer_start: Option<Instant>,
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<Duration> {
if self.is_complete() {
None
} else {
if let Some(start) = self.timer_start {
Some((start + self.timer_len) - Instant::now())
} else {
None
}
}
}
}

View file

@ -1,13 +1,17 @@
use std::time::Duration; 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}; use tracing::{error, info, warn};
mod config; mod config;
mod core; mod core;
mod external_state;
mod satellite_state; mod satellite_state;
use external_state::ExternalState;
use satellite_state::SatelliteState; use satellite_state::SatelliteState;
pub enum AppEvent { pub enum AppEvent {
@ -38,6 +42,7 @@ async fn main() {
let (tx, rx) = mpsc::channel::<AppEvent>(10); let (tx, rx) = mpsc::channel::<AppEvent>(10);
let mut state = SatelliteState { let mut state = SatelliteState {
ext_state: ExternalState::default(),
config, config,
shutdown_tx, shutdown_tx,
shutdown_rx, shutdown_rx,
@ -53,23 +58,18 @@ async fn main() {
state.check_sockets().await; state.check_sockets().await;
match state.events_rx.try_recv() { match state.events_rx.try_recv() {
Err(e) => { Err(e) => match e {
match e {
TryRecvError::Empty => { TryRecvError::Empty => {
tokio::time::sleep(Duration::from_millis(500)).await; tokio::time::sleep(Duration::from_millis(500)).await;
}, }
TryRecvError::Disconnected => { TryRecvError::Disconnected => {
error!("events mpsc was closed! Exiting main thread"); error!("events mpsc was closed! Exiting main thread");
break; break;
} }
},
Ok(res) => {}
} }
} }
Ok(res) => {
}
}
}
// let version = client.general().version().await.unwrap(); // let version = client.general().version().await.unwrap();
// info!("Connected to OBS version: {:?}", version); // info!("Connected to OBS version: {:?}", version);

View file

@ -4,9 +4,16 @@ use tokio::sync::{mpsc, oneshot, watch};
use tokio_tungstenite::tungstenite::Message; use tokio_tungstenite::tungstenite::Message;
use tracing::{info, instrument, warn}; 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 struct SatelliteState {
pub ext_state: ExternalState,
pub config: AppConfig, pub config: AppConfig,
pub obs_client: Option<Client>, pub obs_client: Option<Client>,
@ -22,6 +29,7 @@ pub struct SatelliteState {
impl SatelliteState { impl SatelliteState {
pub async fn check_sockets(&mut self) { pub async fn check_sockets(&mut self) {
if self.obs_client.is_none() { if self.obs_client.is_none() {
self.obs_client = match Client::connect( self.obs_client = match Client::connect(
self.config.obs_ip.clone(), self.config.obs_ip.clone(),
@ -34,7 +42,11 @@ impl SatelliteState {
info!("Failed to connect to obs: {e}"); info!("Failed to connect to obs: {e}");
None 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 { let param = ConnectionCoreParam {
shutdown_rx: self.shutdown_rx.clone(), shutdown_rx: self.shutdown_rx.clone(),
event_loop_tx: self.events_tx.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::<mpsc::Sender<Message>>(); let (tx, rx) = oneshot::channel::<mpsc::Sender<Message>>();
@ -63,7 +79,6 @@ impl SatelliteState {
self.to_core_oneshot = Some(rx); self.to_core_oneshot = Some(rx);
tokio::spawn(connect_to_core(param, tx)); tokio::spawn(connect_to_core(param, tx));
} }
#[instrument(skip(obs_client, event_tx))] #[instrument(skip(obs_client, event_tx))]