Improve getopts and stderr message consistency in main (#757)

This commit is contained in:
Roderick van Domburg 2021-05-26 22:30:32 +02:00 committed by GitHub
parent 8abc0becaf
commit 87743394d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -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");
} }
} }
} }