use anyhow::Result; use obws::Client; use clap::Parser; use tokio::time::{sleep_until, Instant, Duration}; use tokio::process::Command; use serde::{Serialize, Deserialize}; #[tokio::main] async fn main() -> Result<()> { let args = Args::parse(); // Load run config let cfg: RunConfig = match confy::load("obs-cli", None) { Ok(cfg) => cfg, Err(e) => { println!("Error: {}", e); RunConfig::default() } }; // Print the recording details println!("Recording starts in {}min for {}min", args.start_minutes, args.duration_minutes); // Wait the given amount of time sleep_until(Instant::now() + Duration::from_secs(args.start_minutes * 60)).await; // Connect to the OBS instance through obs-websocket. let client = Client::connect("localhost", cfg.websocket_port, Some(cfg.websocket_password)).await?; if let Ok(active) = is_recording(&client).await { if active { println!("Already recording. Exiting..."); return Ok(()); } } // Start the recording start_recording(&client).await?; println!("Recording started."); // Wait for the recording to finish sleep_until(Instant::now() + Duration::from_secs(args.duration_minutes * 60)).await; // Stop the recording stop_recording(&client).await?; println!("Recording stopped."); // Shutdown the machine if args.poweroff { // Wait a minute to give OBS some time to write data sleep_until(Instant::now() + Duration::from_secs(60)).await; shutdown().await?; } Ok(()) } async fn start_recording(client: &Client) -> Result<(), obws::Error> { client.recording().start().await?; Ok(()) } async fn stop_recording(client: &Client) -> Result<(), obws::Error> { client.recording().stop().await?; Ok(()) } async fn is_recording(client: &Client) -> Result { let status = client.recording().status().await?; Ok(status.active) } async fn shutdown() -> Result<(), anyhow::Error> { Command::new("poweroff").spawn().expect("Could not spawn process").wait().await?; Ok(()) } /// obs-cli is a simple cli tool for planned OBS recordings #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct Args { /// Define the minutes where the recording should start #[arg(short, long, default_value_t = 0)] start_minutes: u64, /// Define the duration of the recording in minutes #[arg(short, long, default_value_t = 1)] duration_minutes: u64, /// Flag to shutdown the machine after recording. #[arg(short, long, default_value_t = false)] poweroff: bool, } #[derive(Serialize, Deserialize, Debug)] struct RunConfig { websocket_port: u16, websocket_password: String, } /// `RunConfig` implements `Default` impl ::std::default::Default for RunConfig { fn default() -> Self { Self { websocket_port: 4455, websocket_password: "Websocket".to_string() } } }