mirror of
https://github.com/arabianq/pipewire-soundpad.git
synced 2026-09-20 22:23:31 +00:00
Each of the three groups listed its subcommands by hand, and all three lists had drifted: `get` advertised a `current-file-path` that does not exist while omitting tracks, outputs, hotkeys and both new volumes; `set` omitted loop, output and hotkeys; `action` omitted kill, toggle-loop and the hotkey commands. Replaced the hand-kept lists with a short description of what each group is for. Clap already prints the real subcommands underneath, so duplicating them in the parent was guaranteed to rot. Also `save-daemon-config`, an action, was described as "Daemon configuration", which says nothing about what it does. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
246 lines
7.8 KiB
Rust
246 lines
7.8 KiB
Rust
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<u32>,
|
|
},
|
|
/// Resume audio playback
|
|
Resume {
|
|
#[clap(short, long)]
|
|
id: Option<u32>,
|
|
},
|
|
/// Toggle pause
|
|
TogglePause {
|
|
#[clap(short, long)]
|
|
id: Option<u32>,
|
|
},
|
|
/// Stop audio playback and clear the queue
|
|
Stop {
|
|
#[clap(short, long)]
|
|
id: Option<u32>,
|
|
},
|
|
/// Play a file
|
|
Play {
|
|
file_path: PathBuf,
|
|
#[clap(short, long)]
|
|
concurrent: bool,
|
|
},
|
|
/// Toggle loop
|
|
ToggleLoop {
|
|
#[clap(short, long)]
|
|
id: Option<u32>,
|
|
},
|
|
/// 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<u32>,
|
|
},
|
|
/// 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<u32>,
|
|
},
|
|
/// Duration of the current file
|
|
Duration {
|
|
#[clap(short, long)]
|
|
id: Option<u32>,
|
|
},
|
|
/// 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<u32>,
|
|
},
|
|
/// 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<u32>,
|
|
},
|
|
/// 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<u32>,
|
|
},
|
|
/// 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::<Request>(&action)?,
|
|
&key_chord,
|
|
),
|
|
},
|
|
};
|
|
|
|
let response = make_request(request).await.map_err(|e| anyhow!(e))?;
|
|
println!("{} : {}", response.status, response.message);
|
|
|
|
Ok(())
|
|
}
|