Audio: let `audio-devices <device>` write the config
The audio-devices command only listed. Give it an optional positional
argument: with none it lists as before; with a device name (or
case-insensitive fragment) it writes that into [audio] device in
crabidy-server.toml and then lists, so the same command both configures
and confirms (the chosen device is marked with *). If the fragment
matches no current output device it still writes but warns, mirroring
the server's startup fallback -- so a typo is caught here, not as silent
output.
Plumbing: AudioDevices carries an AudioDevicesArgs { device: Option }
on both `crabidy-server` and `cbd`; cli::audio_devices takes the option
and, when set, loads/updates/stores the settings via the existing
ServerSettings writer. README shows the set form.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4c0c5f1401
commit
28a4c155d0
13
README.md
13
README.md
|
|
@ -171,17 +171,24 @@ Audio output devices (* = selected by the current config):
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
Then pin one in `crabidy-server.toml` — the value is matched
|
Then pin one by passing it to the same command — the value is matched
|
||||||
case-insensitively as a substring of the name, so a memorable fragment is
|
case-insensitively as a substring of the name, so a memorable fragment is
|
||||||
enough — and restart the server:
|
enough — and restart the server:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ crabidy-server audio-devices Headphones
|
||||||
|
Set [audio] device = "Headphones" in .../crabidy-server.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
That writes `[audio] device` for you; you can also edit it by hand:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[audio]
|
[audio]
|
||||||
device = "Headphones"
|
device = "Headphones"
|
||||||
```
|
```
|
||||||
|
|
||||||
If the name matches nothing, the server logs a warning and falls back to
|
If the name matches nothing, both the command and the server warn, and the
|
||||||
the system default.
|
server falls back to the system default.
|
||||||
|
|
||||||
## Command line
|
## Command line
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,15 @@ pub struct CompletionsArgs {
|
||||||
pub shell: clap_complete::Shell,
|
pub shell: clap_complete::Shell,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `audio-devices [device]`: list the output devices, or — with `device` — set
|
||||||
|
/// `[audio] device` in `crabidy-server.toml`.
|
||||||
|
#[derive(Debug, Args)]
|
||||||
|
pub struct AudioDevicesArgs {
|
||||||
|
/// A name (or case-insensitive fragment) of an output device to write into
|
||||||
|
/// `[audio] device`. Omit to just list the available devices.
|
||||||
|
pub device: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Subcommands of `crabidy-server`.
|
/// Subcommands of `crabidy-server`.
|
||||||
#[derive(Debug, Subcommand)]
|
#[derive(Debug, Subcommand)]
|
||||||
pub enum ServerCommand {
|
pub enum ServerCommand {
|
||||||
|
|
@ -195,8 +204,8 @@ pub enum ServerCommand {
|
||||||
/// Playback / global operations against a running server.
|
/// Playback / global operations against a running server.
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Global(GlobalCmd),
|
Global(GlobalCmd),
|
||||||
/// List the audio output devices (to pick `[audio] device`).
|
/// List audio output devices, or set `[audio] device` if one is given.
|
||||||
AudioDevices,
|
AudioDevices(AudioDevicesArgs),
|
||||||
/// Print a shell completion script.
|
/// Print a shell completion script.
|
||||||
Completions(CompletionsArgs),
|
Completions(CompletionsArgs),
|
||||||
}
|
}
|
||||||
|
|
@ -251,8 +260,8 @@ pub enum CbdCommand {
|
||||||
Queue(QueueCmd),
|
Queue(QueueCmd),
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Global(GlobalCmd),
|
Global(GlobalCmd),
|
||||||
/// List the audio output devices (to pick `[audio] device`).
|
/// List audio output devices, or set `[audio] device` if one is given.
|
||||||
AudioDevices,
|
AudioDevices(AudioDevicesArgs),
|
||||||
/// Print a shell completion script.
|
/// Print a shell completion script.
|
||||||
Completions(CompletionsArgs),
|
Completions(CompletionsArgs),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ async fn run_command(
|
||||||
match command {
|
match command {
|
||||||
CbdCommand::Guard(args) => server_cli::guard(args).await,
|
CbdCommand::Guard(args) => server_cli::guard(args).await,
|
||||||
CbdCommand::Scan(args) => server_cli::scan(args).await,
|
CbdCommand::Scan(args) => server_cli::scan(args).await,
|
||||||
CbdCommand::AudioDevices => server_cli::audio_devices(),
|
CbdCommand::AudioDevices(args) => server_cli::audio_devices(args.device),
|
||||||
CbdCommand::Auth(args) => {
|
CbdCommand::Auth(args) => {
|
||||||
let password = args
|
let password = args
|
||||||
.password
|
.password
|
||||||
|
|
|
||||||
|
|
@ -102,12 +102,43 @@ pub async fn guard(args: GuardArgs) -> Result<(), Box<dyn Error>> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `audio-devices`: list the available audio output devices so the user can
|
/// `audio-devices [device]`: with no argument, list the available audio output
|
||||||
/// choose one for `[audio] device` in `crabidy-server.toml`. Marks the device
|
/// devices so the user can choose one for `[audio] device` in
|
||||||
/// the current config selects, using the same case-insensitive substring match
|
/// `crabidy-server.toml`, marking the device the current config selects (same
|
||||||
/// the server applies at startup. Fixes the common Raspberry Pi case where the
|
/// case-insensitive substring match the server applies at startup). With a
|
||||||
/// default device is HDMI and audio plays but is silent on the jack/DAC.
|
/// `device` argument, write it into `[audio] device` and then list — so one
|
||||||
pub fn audio_devices() -> Result<(), Box<dyn Error>> {
|
/// command both configures and confirms. Fixes the common Raspberry Pi case
|
||||||
|
/// where the default device is HDMI and audio plays but is silent on the
|
||||||
|
/// jack/DAC.
|
||||||
|
pub fn audio_devices(select: Option<String>) -> Result<(), Box<dyn Error>> {
|
||||||
|
// Setting a device needs a writable config dir; listing tolerates its
|
||||||
|
// absence.
|
||||||
|
if let Some(device) = select {
|
||||||
|
let dir = config_dir()?;
|
||||||
|
let mut settings = ServerSettings::load(&dir)?;
|
||||||
|
settings.audio.device = Some(device.clone());
|
||||||
|
settings.store(&dir)?;
|
||||||
|
println!(
|
||||||
|
"Set [audio] device = \"{device}\" in {}",
|
||||||
|
dir.join(crate::settings::SETTINGS_FILE).display()
|
||||||
|
);
|
||||||
|
// The server matches the same way; warn on a fragment that currently
|
||||||
|
// resolves to nothing so a typo is caught here, not as silent output.
|
||||||
|
let needle = device.to_lowercase();
|
||||||
|
let names = audio_player::output_device_names();
|
||||||
|
if !names.is_empty()
|
||||||
|
&& !names
|
||||||
|
.iter()
|
||||||
|
.any(|name| name.to_lowercase().contains(&needle))
|
||||||
|
{
|
||||||
|
eprintln!(
|
||||||
|
"warning: no current output device name contains \"{device}\"; the server \
|
||||||
|
will fall back to the system default until one matches"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
println!("Restart the server for it to take effect.\n");
|
||||||
|
}
|
||||||
|
|
||||||
let dir = config_dir().ok();
|
let dir = config_dir().ok();
|
||||||
let configured = dir
|
let configured = dir
|
||||||
.as_deref()
|
.as_deref()
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ async fn run_command(
|
||||||
match command {
|
match command {
|
||||||
ServerCommand::Guard(args) => cli::guard(args).await,
|
ServerCommand::Guard(args) => cli::guard(args).await,
|
||||||
ServerCommand::Scan(args) => cli::scan(args).await,
|
ServerCommand::Scan(args) => cli::scan(args).await,
|
||||||
ServerCommand::AudioDevices => cli::audio_devices(),
|
ServerCommand::AudioDevices(args) => cli::audio_devices(args.device),
|
||||||
ServerCommand::Library(cmd) => {
|
ServerCommand::Library(cmd) => {
|
||||||
cbd_cli::run_remote(&cli::connection(remote), RemoteCmd::Library(cmd)).await
|
cbd_cli::run_remote(&cli::connection(remote), RemoteCmd::Library(cmd)).await
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue