use anyhow::{Result, anyhow}; use clap::{Parser, Subcommand}; use pwsp_lib::{ types::socket::Request, utils::daemon::{make_request, wait_for_daemon}, }; use std::path::PathBuf; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Cli { #[clap(subcommand)] command: Commands, } #[derive(Subcommand, Debug)] enum Commands { /// Tell the player to do something: play, stop, pause, manage hotkeys Action { #[clap(subcommand)] action: Actions, }, /// Read player state: volumes, position, tracks, devices, hotkeys Get { #[clap(subcommand)] parameter: GetCommands, }, /// Change player settings: volumes, position, devices, loop, hotkeys Set { #[clap(subcommand)] parameter: SetCommands, }, } #[derive(Subcommand, Debug)] enum Actions { /// Ping the daemon Ping, /// Kill the daemon Kill, /// Pause audio playback Pause { #[clap(short, long)] id: Option, }, /// Resume audio playback Resume { #[clap(short, long)] id: Option, }, /// Toggle pause TogglePause { #[clap(short, long)] id: Option, }, /// Stop audio playback and clear the queue Stop { #[clap(short, long)] id: Option, }, /// Play a file Play { file_path: PathBuf, #[clap(short, long)] concurrent: bool, }, /// Toggle loop ToggleLoop { #[clap(short, long)] id: Option, }, /// Write the daemon's current settings to disk SaveDaemonConfig, /// Play a sound by hotkey slot name PlayHotkey { slot: String }, /// Remove the hotkey slot ClearHotkey { slot: String }, /// Clear the key chord for a hotkey slot ClearHotkeyKey { slot: String }, } #[derive(Subcommand, Debug)] enum GetCommands { /// Check if the player is paused IsPaused, /// Playback volume Volume { #[clap(short, long)] id: Option, }, /// Volume of what you hear locally MonitoringVolume, /// Volume of what is sent to the virtual microphone MicVolume, /// Volume multiplier for all tracks VolumeMultiplier, /// Playback position (in seconds) Position { #[clap(short, long)] id: Option, }, /// Duration of the current file Duration { #[clap(short, long)] id: Option, }, /// Player state (Playing, Paused or Stopped) State, /// Get all playing tracks Tracks, /// Current audio input Input, /// All audio inputs Inputs, /// Current audio output used for monitoring Output, /// All audio outputs Outputs, /// Version of the daemon DaemonVersion, /// Daemon configuration DaemonConfig, /// Full player state FullState, /// All hotkey slots Hotkeys, } #[derive(Subcommand, Debug)] enum SetCommands { /// Playback volume. Without --id it sets both the monitoring and mic volumes Volume { volume: f32, #[clap(short, long)] id: Option, }, /// Volume of what you hear locally. Values above 1.0 amplify MonitoringVolume { volume: f32 }, /// Volume of what is sent to the virtual microphone. Values above 1.0 amplify MicVolume { volume: f32 }, /// Volume multiplier for all tracks VolumeMultiplier { volume: f32 }, /// Playback position (in seconds) Position { position: f32, #[clap(short, long)] id: Option, }, /// Audio input id (see pwsp-cli get inputs) Input { name: String }, /// Audio output used for monitoring (see pwsp-cli get outputs) Output { name: String }, /// Enable or disable loop (true or false) Loop { enabled: String, #[clap(short, long)] id: Option, }, /// Assign a sound file to a hotkey slot Hotkey { slot: String, file_path: PathBuf }, /// Set the key chord for a hotkey slot (e.g. "Ctrl+Alt+1") HotkeyKey { slot: String, key_chord: String }, /// Atomically set the action and key chord for a hotkey slot HotkeyActionAndKey { slot: String, action: String, key_chord: String, }, } #[tokio::main] async fn main() -> Result<()> { let cli = Cli::parse(); wait_for_daemon().await?; let request = match cli.command { Commands::Action { action } => match action { Actions::Ping => Request::ping(), Actions::Kill => Request::kill(), Actions::Pause { id } => Request::pause(id), Actions::Resume { id } => Request::resume(id), Actions::TogglePause { id } => Request::toggle_pause(id), Actions::Stop { id } => Request::stop(id), Actions::Play { file_path, concurrent, } => Request::play(&file_path.to_string_lossy(), concurrent), Actions::ToggleLoop { id } => Request::toggle_loop(id), Actions::SaveDaemonConfig => Request::save_daemon_config(), Actions::PlayHotkey { slot } => Request::play_hotkey(&slot), Actions::ClearHotkey { slot } => Request::clear_hotkey(&slot), Actions::ClearHotkeyKey { slot } => Request::clear_hotkey_key(&slot), }, Commands::Get { parameter } => match parameter { GetCommands::IsPaused => Request::get_is_paused(), GetCommands::Volume { id } => Request::get_volume(id), GetCommands::MonitoringVolume => Request::get_monitoring_volume(), GetCommands::MicVolume => Request::get_mic_volume(), GetCommands::VolumeMultiplier => Request::get_volume_multiplier(), GetCommands::Position { id } => Request::get_position(id), GetCommands::Duration { id } => Request::get_duration(id), GetCommands::State => Request::get_state(), GetCommands::Tracks => Request::get_tracks(), GetCommands::Input => Request::get_input(), GetCommands::Inputs => Request::get_inputs(), GetCommands::Output => Request::get_output(), GetCommands::Outputs => Request::get_outputs(), GetCommands::DaemonVersion => Request::get_daemon_version(), GetCommands::DaemonConfig => Request::get_daemon_config(), GetCommands::FullState => Request::get_full_state(), GetCommands::Hotkeys => Request::get_hotkeys(), }, Commands::Set { parameter } => match parameter { SetCommands::Volume { volume, id } => Request::set_volume(volume, id), SetCommands::MonitoringVolume { volume } => Request::set_monitoring_volume(volume), SetCommands::MicVolume { volume } => Request::set_mic_volume(volume), SetCommands::VolumeMultiplier { volume } => Request::set_volume_multiplier(volume), SetCommands::Position { position, id } => Request::seek(position, id), SetCommands::Input { name } => Request::set_input(&name), SetCommands::Output { name } => Request::set_output(&name), SetCommands::Loop { enabled, id } => Request::set_loop(&enabled, id), SetCommands::Hotkey { slot, file_path } => { Request::set_hotkey(&slot, &file_path.to_string_lossy()) } SetCommands::HotkeyKey { slot, key_chord } => { Request::set_hotkey_key(&slot, &key_chord) } SetCommands::HotkeyActionAndKey { slot, action, key_chord, } => Request::set_hotkey_action_and_key( &slot, &serde_json::from_str::(&action)?, &key_chord, ), }, }; let response = make_request(request).await.map_err(|e| anyhow!(e))?; println!("{} : {}", response.status, response.message); Ok(()) }