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
|
||||
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
|
||||
[audio]
|
||||
device = "Headphones"
|
||||
```
|
||||
|
||||
If the name matches nothing, the server logs a warning and falls back to
|
||||
the system default.
|
||||
If the name matches nothing, both the command and the server warn, and the
|
||||
server falls back to the system default.
|
||||
|
||||
## Command line
|
||||
|
||||
|
|
|
|||
|
|
@ -179,6 +179,15 @@ pub struct CompletionsArgs {
|
|||
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`.
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum ServerCommand {
|
||||
|
|
@ -195,8 +204,8 @@ pub enum ServerCommand {
|
|||
/// Playback / global operations against a running server.
|
||||
#[command(subcommand)]
|
||||
Global(GlobalCmd),
|
||||
/// List the audio output devices (to pick `[audio] device`).
|
||||
AudioDevices,
|
||||
/// List audio output devices, or set `[audio] device` if one is given.
|
||||
AudioDevices(AudioDevicesArgs),
|
||||
/// Print a shell completion script.
|
||||
Completions(CompletionsArgs),
|
||||
}
|
||||
|
|
@ -251,8 +260,8 @@ pub enum CbdCommand {
|
|||
Queue(QueueCmd),
|
||||
#[command(subcommand)]
|
||||
Global(GlobalCmd),
|
||||
/// List the audio output devices (to pick `[audio] device`).
|
||||
AudioDevices,
|
||||
/// List audio output devices, or set `[audio] device` if one is given.
|
||||
AudioDevices(AudioDevicesArgs),
|
||||
/// Print a shell completion script.
|
||||
Completions(CompletionsArgs),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ async fn run_command(
|
|||
match command {
|
||||
CbdCommand::Guard(args) => server_cli::guard(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) => {
|
||||
let password = args
|
||||
.password
|
||||
|
|
|
|||
|
|
@ -102,12 +102,43 @@ pub async fn guard(args: GuardArgs) -> Result<(), Box<dyn Error>> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// `audio-devices`: list the available audio output devices so the user can
|
||||
/// choose one for `[audio] device` in `crabidy-server.toml`. Marks the device
|
||||
/// the current config selects, using the same case-insensitive substring match
|
||||
/// the server applies at startup. 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() -> Result<(), Box<dyn Error>> {
|
||||
/// `audio-devices [device]`: with no argument, list the available audio output
|
||||
/// devices so the user can choose one for `[audio] device` in
|
||||
/// `crabidy-server.toml`, marking the device the current config selects (same
|
||||
/// case-insensitive substring match the server applies at startup). With a
|
||||
/// `device` argument, write it into `[audio] device` and then list — so one
|
||||
/// 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 configured = dir
|
||||
.as_deref()
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ async fn run_command(
|
|||
match command {
|
||||
ServerCommand::Guard(args) => cli::guard(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) => {
|
||||
cbd_cli::run_remote(&cli::connection(remote), RemoteCmd::Library(cmd)).await
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue