mirror of
https://github.com/librespot-org/librespot.git
synced 2024-11-08 16:45:43 +00:00
Fix --device
argument to various backends (#938)
Fix `--device` argument to various backends
This commit is contained in:
parent
a605444d18
commit
1e54913523
5 changed files with 42 additions and 57 deletions
|
@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- [contrib] Hardened security of the systemd service units
|
- [contrib] Hardened security of the systemd service units
|
||||||
- [main] Verbose logging mode (`-v`, `--verbose`) now logs all parsed environment variables and command line arguments (credentials are redacted).
|
- [main] Verbose logging mode (`-v`, `--verbose`) now logs all parsed environment variables and command line arguments (credentials are redacted).
|
||||||
- [playback] `Sink`: `write()` now receives ownership of the packet (breaking).
|
- [playback] `Sink`: `write()` now receives ownership of the packet (breaking).
|
||||||
|
- [playback] `pipe`: create file if it doesn't already exist
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- [cache] Add `disable-credential-cache` flag (breaking).
|
- [cache] Add `disable-credential-cache` flag (breaking).
|
||||||
|
|
|
@ -4,19 +4,27 @@ use crate::convert::Converter;
|
||||||
use crate::decoder::AudioPacket;
|
use crate::decoder::AudioPacket;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
pub struct StdoutSink {
|
pub struct StdoutSink {
|
||||||
output: Option<Box<dyn Write>>,
|
output: Option<Box<dyn Write>>,
|
||||||
path: Option<String>,
|
file: Option<String>,
|
||||||
format: AudioFormat,
|
format: AudioFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Open for StdoutSink {
|
impl Open for StdoutSink {
|
||||||
fn open(path: Option<String>, format: AudioFormat) -> Self {
|
fn open(file: Option<String>, format: AudioFormat) -> Self {
|
||||||
|
if let Some("?") = file.as_deref() {
|
||||||
|
info!("Usage:");
|
||||||
|
println!(" Output to stdout: --backend pipe");
|
||||||
|
println!(" Output to file: --backend pipe --device {{filename}}");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
info!("Using pipe sink with format: {:?}", format);
|
info!("Using pipe sink with format: {:?}", format);
|
||||||
Self {
|
Self {
|
||||||
output: None,
|
output: None,
|
||||||
path,
|
file,
|
||||||
format,
|
format,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,11 +33,12 @@ impl Open for StdoutSink {
|
||||||
impl Sink for StdoutSink {
|
impl Sink for StdoutSink {
|
||||||
fn start(&mut self) -> SinkResult<()> {
|
fn start(&mut self) -> SinkResult<()> {
|
||||||
if self.output.is_none() {
|
if self.output.is_none() {
|
||||||
let output: Box<dyn Write> = match self.path.as_deref() {
|
let output: Box<dyn Write> = match self.file.as_deref() {
|
||||||
Some(path) => {
|
Some(file) => {
|
||||||
let open_op = OpenOptions::new()
|
let open_op = OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.open(path)
|
.create(true)
|
||||||
|
.open(file)
|
||||||
.map_err(|e| SinkError::ConnectionRefused(e.to_string()))?;
|
.map_err(|e| SinkError::ConnectionRefused(e.to_string()))?;
|
||||||
Box::new(open_op)
|
Box::new(open_op)
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,21 +135,18 @@ fn create_sink(
|
||||||
host: &cpal::Host,
|
host: &cpal::Host,
|
||||||
device: Option<String>,
|
device: Option<String>,
|
||||||
) -> Result<(rodio::Sink, rodio::OutputStream), RodioError> {
|
) -> Result<(rodio::Sink, rodio::OutputStream), RodioError> {
|
||||||
let rodio_device = match device {
|
let rodio_device = match device.as_deref() {
|
||||||
Some(ask) if &ask == "?" => {
|
Some("?") => match list_outputs(host) {
|
||||||
let exit_code = match list_outputs(host) {
|
Ok(()) => exit(0),
|
||||||
Ok(()) => 0,
|
Err(e) => {
|
||||||
Err(e) => {
|
error!("{}", e);
|
||||||
error!("{}", e);
|
exit(1);
|
||||||
1
|
}
|
||||||
}
|
},
|
||||||
};
|
|
||||||
exit(exit_code)
|
|
||||||
}
|
|
||||||
Some(device_name) => {
|
Some(device_name) => {
|
||||||
host.output_devices()?
|
host.output_devices()?
|
||||||
.find(|d| d.name().ok().map_or(false, |name| name == device_name)) // Ignore devices for which getting name fails
|
.find(|d| d.name().ok().map_or(false, |name| name == device_name)) // Ignore devices for which getting name fails
|
||||||
.ok_or(RodioError::DeviceNotAvailable(device_name))?
|
.ok_or_else(|| RodioError::DeviceNotAvailable(device_name.to_string()))?
|
||||||
}
|
}
|
||||||
None => host
|
None => host
|
||||||
.default_output_device()
|
.default_output_device()
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::decoder::AudioPacket;
|
||||||
use shell_words::split;
|
use shell_words::split;
|
||||||
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::process::{Child, Command, Stdio};
|
use std::process::{exit, Child, Command, Stdio};
|
||||||
|
|
||||||
pub struct SubprocessSink {
|
pub struct SubprocessSink {
|
||||||
shell_command: String,
|
shell_command: String,
|
||||||
|
@ -15,16 +15,24 @@ pub struct SubprocessSink {
|
||||||
|
|
||||||
impl Open for SubprocessSink {
|
impl Open for SubprocessSink {
|
||||||
fn open(shell_command: Option<String>, format: AudioFormat) -> Self {
|
fn open(shell_command: Option<String>, format: AudioFormat) -> Self {
|
||||||
|
let shell_command = match shell_command.as_deref() {
|
||||||
|
Some("?") => {
|
||||||
|
info!("Usage: --backend subprocess --device {{shell_command}}");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
Some(cmd) => cmd.to_owned(),
|
||||||
|
None => {
|
||||||
|
error!("subprocess sink requires specifying a shell command");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
info!("Using subprocess sink with format: {:?}", format);
|
info!("Using subprocess sink with format: {:?}", format);
|
||||||
|
|
||||||
if let Some(shell_command) = shell_command {
|
Self {
|
||||||
SubprocessSink {
|
shell_command,
|
||||||
shell_command,
|
child: None,
|
||||||
child: None,
|
format,
|
||||||
format,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panic!("subprocess sink requires specifying a shell command");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
30
src/main.rs
30
src/main.rs
|
@ -748,18 +748,7 @@ fn get_setup() -> Setup {
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
#[cfg(any(
|
|
||||||
feature = "alsa-backend",
|
|
||||||
feature = "rodio-backend",
|
|
||||||
feature = "portaudio-backend"
|
|
||||||
))]
|
|
||||||
let device = opt_str(DEVICE);
|
let device = opt_str(DEVICE);
|
||||||
|
|
||||||
#[cfg(any(
|
|
||||||
feature = "alsa-backend",
|
|
||||||
feature = "rodio-backend",
|
|
||||||
feature = "portaudio-backend"
|
|
||||||
))]
|
|
||||||
if let Some(ref value) = device {
|
if let Some(ref value) = device {
|
||||||
if value == "?" {
|
if value == "?" {
|
||||||
backend(device, format);
|
backend(device, format);
|
||||||
|
@ -769,25 +758,6 @@ fn get_setup() -> Setup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(
|
|
||||||
feature = "alsa-backend",
|
|
||||||
feature = "rodio-backend",
|
|
||||||
feature = "portaudio-backend"
|
|
||||||
)))]
|
|
||||||
let device: Option<String> = None;
|
|
||||||
|
|
||||||
#[cfg(not(any(
|
|
||||||
feature = "alsa-backend",
|
|
||||||
feature = "rodio-backend",
|
|
||||||
feature = "portaudio-backend"
|
|
||||||
)))]
|
|
||||||
if opt_present(DEVICE) {
|
|
||||||
warn!(
|
|
||||||
"The `--{}` / `-{}` option is not supported by the included audio backend(s), and has no effect.",
|
|
||||||
DEVICE, DEVICE_SHORT,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "alsa-backend")]
|
#[cfg(feature = "alsa-backend")]
|
||||||
let mixer_type = opt_str(MIXER_TYPE);
|
let mixer_type = opt_str(MIXER_TYPE);
|
||||||
#[cfg(not(feature = "alsa-backend"))]
|
#[cfg(not(feature = "alsa-backend"))]
|
||||||
|
|
Loading…
Reference in a new issue