diff --git a/Cargo.lock b/Cargo.lock index 3e6818f..1e391b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,17 @@ version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -87,6 +98,43 @@ dependencies = [ "winapi", ] +[[package]] +name = "clap" +version = "4.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335867764ed2de42325fafe6d18b8af74ba97ee0c590fa016f157535b42ab04b" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a1b0f6422af32d5da0c58e2703320f379216ee70198241c84173a8c5ac28f3" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -97,6 +145,18 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "confy" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37668cb35145dcfaa1931a5f37fde375eeae8068b4c0d2f289da28a270b2d2c" +dependencies = [ + "directories", + "serde", + "thiserror", + "toml", +] + [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -211,6 +271,26 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "directories" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "fnv" version = "1.0.7" @@ -297,6 +377,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -490,7 +576,10 @@ name = "obs-cli" version = "0.1.0" dependencies = [ "anyhow", + "clap", + "confy", "obws", + "serde", "tokio", ] @@ -523,6 +612,12 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +[[package]] +name = "os_str_bytes" +version = "6.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" + [[package]] name = "parking_lot" version = "0.12.1" @@ -570,6 +665,30 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.47" @@ -627,6 +746,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + [[package]] name = "rgb" version = "0.8.34" @@ -902,6 +1032,15 @@ dependencies = [ "tungstenite", ] +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + [[package]] name = "tracing" version = "0.1.37" diff --git a/Cargo.toml b/Cargo.toml index c22cfd6..3048d0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,7 @@ edition = "2021" obws = "0.10.0-beta.4" tokio = { version = "1.21.2", features = ["full"] } anyhow = "1.0" +clap = { version = "4.0.18", features = ["derive"] } +confy = "0.5.1" +serde = { version = "1.0", features = ["derive"] } + diff --git a/src/main.rs b/src/main.rs index d426840..2db7a08 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,113 @@ 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", 4455, Some("OBS")).await?; + let client = Client::connect("localhost", cfg.websocket_port, Some(cfg.websocket_password)).await?; - // Get and print out version information of OBS and obs-websocket. - let version = client.general().version().await?; - println!("{:#?}", version); + 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?; + } - // Get a list of available scenes and print them out. - let scene_list = client.scenes().list().await?; - println!("{:#?}", scene_list); 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() + } + } +} +