mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Improve getopts and stderr message consistency in main (#757)
This commit is contained in:
parent
8abc0becaf
commit
87743394d9
1 changed files with 188 additions and 167 deletions
355
src/main.rs
355
src/main.rs
|
@ -190,182 +190,195 @@ struct Setup {
|
||||||
|
|
||||||
fn get_setup(args: &[String]) -> Setup {
|
fn get_setup(args: &[String]) -> Setup {
|
||||||
let mut opts = getopts::Options::new();
|
let mut opts = getopts::Options::new();
|
||||||
opts.optopt(
|
opts.optflag(
|
||||||
|
"h",
|
||||||
|
"help",
|
||||||
|
"Print this help menu.",
|
||||||
|
).optopt(
|
||||||
"c",
|
"c",
|
||||||
"cache",
|
"cache",
|
||||||
"Path to a directory where files will be cached.",
|
"Path to a directory where files will be cached.",
|
||||||
"CACHE",
|
"PATH",
|
||||||
).optopt(
|
).optopt(
|
||||||
"",
|
"",
|
||||||
"system-cache",
|
"system-cache",
|
||||||
"Path to a directory where system files (credentials, volume) will be cached. Can be different from cache option value",
|
"Path to a directory where system files (credentials, volume) will be cached. Can be different from cache option value.",
|
||||||
"SYTEMCACHE",
|
"PATH",
|
||||||
).optopt(
|
).optopt(
|
||||||
"",
|
"",
|
||||||
"cache-size-limit",
|
"cache-size-limit",
|
||||||
"Limits the size of the cache for audio files.",
|
"Limits the size of the cache for audio files.",
|
||||||
"CACHE_SIZE_LIMIT"
|
"SIZE"
|
||||||
).optflag("", "disable-audio-cache", "Disable caching of the audio data.")
|
).optflag("", "disable-audio-cache", "Disable caching of the audio data.")
|
||||||
.optopt("n", "name", "Device name", "NAME")
|
.optopt("n", "name", "Device name.", "NAME")
|
||||||
.optopt("", "device-type", "Displayed device type", "DEVICE_TYPE")
|
.optopt("", "device-type", "Displayed device type.", "TYPE")
|
||||||
.optopt(
|
.optopt(
|
||||||
"b",
|
"b",
|
||||||
"bitrate",
|
"bitrate",
|
||||||
"Bitrate (96, 160 or 320). Defaults to 160",
|
"Bitrate (kbps) {96|160|320}. Defaults to 160.",
|
||||||
"BITRATE",
|
"BITRATE",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"onevent",
|
"onevent",
|
||||||
"Run PROGRAM when playback is about to begin.",
|
"Run PROGRAM when a playback event occurs.",
|
||||||
"PROGRAM",
|
"PROGRAM",
|
||||||
)
|
)
|
||||||
.optflag("", "emit-sink-events", "Run program set by --onevent before sink is opened and after it is closed.")
|
.optflag("", "emit-sink-events", "Run program set by --onevent before sink is opened and after it is closed.")
|
||||||
.optflag("v", "verbose", "Enable verbose output")
|
.optflag("v", "verbose", "Enable verbose output.")
|
||||||
.optflag("V", "version", "Display librespot version string")
|
.optflag("V", "version", "Display librespot version string.")
|
||||||
.optopt("u", "username", "Username to sign in with", "USERNAME")
|
.optopt("u", "username", "Username to sign in with.", "USERNAME")
|
||||||
.optopt("p", "password", "Password", "PASSWORD")
|
.optopt("p", "password", "Password", "PASSWORD")
|
||||||
.optopt("", "proxy", "HTTP proxy to use when connecting", "PROXY")
|
.optopt("", "proxy", "HTTP proxy to use when connecting.", "URL")
|
||||||
.optopt("", "ap-port", "Connect to AP with specified port. If no AP with that port are present fallback AP will be used. Available ports are usually 80, 443 and 4070", "AP_PORT")
|
.optopt("", "ap-port", "Connect to AP with specified port. If no AP with that port are present fallback AP will be used. Available ports are usually 80, 443 and 4070.", "PORT")
|
||||||
.optflag("", "disable-discovery", "Disable discovery mode")
|
.optflag("", "disable-discovery", "Disable discovery mode.")
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"backend",
|
"backend",
|
||||||
"Audio backend to use. Use '?' to list options",
|
"Audio backend to use. Use '?' to list options.",
|
||||||
"BACKEND",
|
"NAME",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"device",
|
"device",
|
||||||
"Audio device to use. Use '?' to list options if using portaudio or alsa",
|
"Audio device to use. Use '?' to list options if using alsa, portaudio or rodio.",
|
||||||
"DEVICE",
|
"NAME",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"format",
|
"format",
|
||||||
"Output format (F32, S32, S24, S24_3 or S16). Defaults to S16",
|
"Output format {F32|S32|S24|S24_3|S16}. Defaults to S16.",
|
||||||
"FORMAT",
|
"FORMAT",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"dither",
|
"dither",
|
||||||
"Specify the dither algorithm to use - [none, gpdf, tpdf, tpdf_hp]. Defaults to 'tpdf' for formats S16, S24, S24_3 and 'none' for other formats.",
|
"Specify the dither algorithm to use - [none, gpdf, tpdf, tpdf_hp]. Defaults to 'tpdf' for formats S16, S24, S24_3 and 'none' for other formats.",
|
||||||
"DITHER",
|
"DITHER",
|
||||||
)
|
)
|
||||||
.optopt("", "mixer", "Mixer to use (alsa or softvol)", "MIXER")
|
.optopt("", "mixer", "Mixer to use {alsa|softvol}.", "MIXER")
|
||||||
.optopt(
|
.optopt(
|
||||||
"m",
|
"m",
|
||||||
"mixer-name",
|
"mixer-name",
|
||||||
"Alsa mixer control, e.g. 'PCM' or 'Master'. Defaults to 'PCM'.",
|
"Alsa mixer control, e.g. 'PCM' or 'Master'. Defaults to 'PCM'.",
|
||||||
"MIXER_NAME",
|
"NAME",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"mixer-card",
|
"mixer-card",
|
||||||
"Alsa mixer card, e.g 'hw:0' or similar from `aplay -l`. Defaults to DEVICE if specified, 'default' otherwise.",
|
"Alsa mixer card, e.g 'hw:0' or similar from `aplay -l`. Defaults to DEVICE if specified, 'default' otherwise.",
|
||||||
"MIXER_CARD",
|
"MIXER_CARD",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"mixer-index",
|
"mixer-index",
|
||||||
"Alsa mixer index, Index of the cards mixer. Defaults to 0",
|
"Alsa index of the cards mixer. Defaults to 0.",
|
||||||
"MIXER_INDEX",
|
"INDEX",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"initial-volume",
|
"initial-volume",
|
||||||
"Initial volume (%) once connected {0..100}. Defaults to 50 for softvol and for Alsa mixer the current volume.",
|
"Initial volume in % from 0-100. Default for softvol: '50'. For the Alsa mixer: the current volume.",
|
||||||
"VOLUME",
|
"VOLUME",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"zeroconf-port",
|
"zeroconf-port",
|
||||||
"The port the internal server advertised over zeroconf uses.",
|
"The port the internal server advertised over zeroconf uses.",
|
||||||
"ZEROCONF_PORT",
|
"PORT",
|
||||||
)
|
)
|
||||||
.optflag(
|
.optflag(
|
||||||
"",
|
"",
|
||||||
"enable-volume-normalisation",
|
"enable-volume-normalisation",
|
||||||
"Play all tracks at the same volume",
|
"Play all tracks at the same volume.",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"normalisation-method",
|
"normalisation-method",
|
||||||
"Specify the normalisation method to use - [basic, dynamic]. Default is dynamic.",
|
"Specify the normalisation method to use {basic|dynamic}. Defaults to dynamic.",
|
||||||
"NORMALISATION_METHOD",
|
"METHOD",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"normalisation-gain-type",
|
"normalisation-gain-type",
|
||||||
"Specify the normalisation gain type to use - [track, album]. Default is album.",
|
"Specify the normalisation gain type to use {track|album}. Defaults to album.",
|
||||||
"GAIN_TYPE",
|
"TYPE",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"normalisation-pregain",
|
"normalisation-pregain",
|
||||||
"Pregain (dB) applied by volume normalisation",
|
"Pregain (dB) applied by volume normalisation. Defaults to 0.",
|
||||||
"PREGAIN",
|
"PREGAIN",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"normalisation-threshold",
|
"normalisation-threshold",
|
||||||
"Threshold (dBFS) to prevent clipping. Default is -1.0.",
|
"Threshold (dBFS) to prevent clipping. Defaults to -1.0.",
|
||||||
"THRESHOLD",
|
"THRESHOLD",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"normalisation-attack",
|
"normalisation-attack",
|
||||||
"Attack time (ms) in which the dynamic limiter is reducing gain. Default is 5.",
|
"Attack time (ms) in which the dynamic limiter is reducing gain. Defaults to 5.",
|
||||||
"ATTACK",
|
"TIME",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"normalisation-release",
|
"normalisation-release",
|
||||||
"Release or decay time (ms) in which the dynamic limiter is restoring gain. Default is 100.",
|
"Release or decay time (ms) in which the dynamic limiter is restoring gain. Defaults to 100.",
|
||||||
"RELEASE",
|
"TIME",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"normalisation-knee",
|
"normalisation-knee",
|
||||||
"Knee steepness of the dynamic limiter. Default is 1.0.",
|
"Knee steepness of the dynamic limiter. Defaults to 1.0.",
|
||||||
"KNEE",
|
"KNEE",
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"volume-ctrl",
|
"volume-ctrl",
|
||||||
"Volume control type {cubic|fixed|linear|log}. Defaults to log.",
|
"Volume control type {cubic|fixed|linear|log}. Defaults to log.",
|
||||||
"VOLUME_CTRL"
|
"VOLUME_CTRL"
|
||||||
)
|
)
|
||||||
.optopt(
|
.optopt(
|
||||||
"",
|
"",
|
||||||
"volume-range",
|
"volume-range",
|
||||||
"Range of the volume control (dB). Defaults to 60 for softvol and for Alsa mixer what the mixer supports.",
|
"Range of the volume control (dB). Default for softvol: 60. For the Alsa mixer: what the control supports.",
|
||||||
"RANGE",
|
"RANGE",
|
||||||
)
|
)
|
||||||
.optflag(
|
.optflag(
|
||||||
"",
|
"",
|
||||||
"autoplay",
|
"autoplay",
|
||||||
"autoplay similar songs when your music ends.",
|
"Automatically play similar songs when your music ends.",
|
||||||
)
|
)
|
||||||
.optflag(
|
.optflag(
|
||||||
"",
|
"",
|
||||||
"disable-gapless",
|
"disable-gapless",
|
||||||
"disable gapless playback.",
|
"Disable gapless playback.",
|
||||||
)
|
)
|
||||||
.optflag(
|
.optflag(
|
||||||
"",
|
"",
|
||||||
"passthrough",
|
"passthrough",
|
||||||
"Pass raw stream to output, only works for \"pipe\"."
|
"Pass raw stream to output, only works for pipe and subprocess.",
|
||||||
);
|
);
|
||||||
|
|
||||||
let matches = match opts.parse(&args[1..]) {
|
let matches = match opts.parse(&args[1..]) {
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
Err(f) => {
|
Err(f) => {
|
||||||
eprintln!("error: {}\n{}", f.to_string(), usage(&args[0], &opts));
|
eprintln!(
|
||||||
|
"Error parsing command line options: {}\n{}",
|
||||||
|
f,
|
||||||
|
usage(&args[0], &opts)
|
||||||
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if matches.opt_present("h") {
|
||||||
|
println!("{}", usage(&args[0], &opts));
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
if matches.opt_present("version") {
|
if matches.opt_present("version") {
|
||||||
print_version();
|
print_version();
|
||||||
exit(0);
|
exit(0);
|
||||||
|
@ -552,7 +565,7 @@ fn get_setup(args: &[String]) -> Setup {
|
||||||
match Url::parse(&s) {
|
match Url::parse(&s) {
|
||||||
Ok(url) => {
|
Ok(url) => {
|
||||||
if url.host().is_none() || url.port_or_known_default().is_none() {
|
if url.host().is_none() || url.port_or_known_default().is_none() {
|
||||||
panic!("Invalid proxy url, only urls on the format \"http://host:port\" are allowed");
|
panic!("Invalid proxy url, only URLs on the format \"http://host:port\" are allowed");
|
||||||
}
|
}
|
||||||
|
|
||||||
if url.scheme() != "http" {
|
if url.scheme() != "http" {
|
||||||
|
@ -560,7 +573,7 @@ fn get_setup(args: &[String]) -> Setup {
|
||||||
}
|
}
|
||||||
url
|
url
|
||||||
},
|
},
|
||||||
Err(err) => panic!("Invalid proxy url: {}, only urls on the format \"http://host:port\" are allowed", err)
|
Err(err) => panic!("Invalid proxy URL: {}, only URLs in the format \"http://host:port\" are allowed", err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -792,14 +805,14 @@ async fn main() {
|
||||||
Ok(e) if e.success() => (),
|
Ok(e) if e.success() => (),
|
||||||
Ok(e) => {
|
Ok(e) => {
|
||||||
if let Some(code) = e.code() {
|
if let Some(code) = e.code() {
|
||||||
warn!("Sink event prog returned exit code {}", code);
|
warn!("Sink event program returned exit code {}", code);
|
||||||
} else {
|
} else {
|
||||||
warn!("Sink event prog returned failure");
|
warn!("Sink event program returned failure");
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Emitting sink event failed: {}", e);
|
warn!("Emitting sink event failed: {}", e);
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
@ -849,13 +862,21 @@ async fn main() {
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
match child.wait().await {
|
match child.wait().await {
|
||||||
Ok(status) if !status.success() => error!("child exited with status {:?}", status.code()),
|
Ok(e) if e.success() => (),
|
||||||
Err(e) => error!("failed to wait on child process: {}", e),
|
Ok(e) => {
|
||||||
_ => {}
|
if let Some(code) = e.code() {
|
||||||
|
warn!("On event program returned exit code {}", code);
|
||||||
|
} else {
|
||||||
|
warn!("On event program returned failure");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
warn!("On event program failed: {}", e);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
error!("program failed to start");
|
warn!("On event program failed to start");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue