added scene request states
This commit is contained in:
parent
a20b3fff6c
commit
984f4d2bad
6 changed files with 194 additions and 22 deletions
16
notes.md
Normal file
16
notes.md
Normal 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
|
|
@ -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
100
src/external_state/mod.rs
Normal 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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
src/external_state/timer.rs
Normal file
42
src/external_state/timer.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/main.rs
22
src/main.rs
|
@ -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);
|
||||||
|
|
|
@ -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))]
|
||||||
|
|
Loading…
Reference in a new issue