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 {
|
||||
let mut opts = getopts::Options::new();
|
||||
opts.optopt(
|
||||
opts.optflag(
|
||||
"h",
|
||||
"help",
|
||||
"Print this help menu.",
|
||||
).optopt(
|
||||
"c",
|
||||
"cache",
|
||||
"Path to a directory where files will be cached.",
|
||||
"CACHE",
|
||||
"PATH",
|
||||
).optopt(
|
||||
"",
|
||||
"system-cache",
|
||||
"Path to a directory where system files (credentials, volume) will be cached. Can be different from cache option value",
|
||||
"SYTEMCACHE",
|
||||
"Path to a directory where system files (credentials, volume) will be cached. Can be different from cache option value.",
|
||||
"PATH",
|
||||
).optopt(
|
||||
"",
|
||||
"cache-size-limit",
|
||||
"Limits the size of the cache for audio files.",
|
||||
"CACHE_SIZE_LIMIT"
|
||||
"SIZE"
|
||||
).optflag("", "disable-audio-cache", "Disable caching of the audio data.")
|
||||
.optopt("n", "name", "Device name", "NAME")
|
||||
.optopt("", "device-type", "Displayed device type", "DEVICE_TYPE")
|
||||
.optopt(
|
||||
"b",
|
||||
"bitrate",
|
||||
"Bitrate (96, 160 or 320). Defaults to 160",
|
||||
"BITRATE",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"onevent",
|
||||
"Run PROGRAM when playback is about to begin.",
|
||||
"PROGRAM",
|
||||
)
|
||||
.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", "version", "Display librespot version string")
|
||||
.optopt("u", "username", "Username to sign in with", "USERNAME")
|
||||
.optopt("p", "password", "Password", "PASSWORD")
|
||||
.optopt("", "proxy", "HTTP proxy to use when connecting", "PROXY")
|
||||
.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")
|
||||
.optflag("", "disable-discovery", "Disable discovery mode")
|
||||
.optopt(
|
||||
"",
|
||||
"backend",
|
||||
"Audio backend to use. Use '?' to list options",
|
||||
"BACKEND",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"device",
|
||||
"Audio device to use. Use '?' to list options if using portaudio or alsa",
|
||||
"DEVICE",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"format",
|
||||
"Output format (F32, S32, S24, S24_3 or S16). Defaults to S16",
|
||||
"FORMAT",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"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.",
|
||||
"DITHER",
|
||||
)
|
||||
.optopt("", "mixer", "Mixer to use (alsa or softvol)", "MIXER")
|
||||
.optopt(
|
||||
"m",
|
||||
"mixer-name",
|
||||
"Alsa mixer control, e.g. 'PCM' or 'Master'. Defaults to 'PCM'.",
|
||||
"MIXER_NAME",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"mixer-card",
|
||||
"Alsa mixer card, e.g 'hw:0' or similar from `aplay -l`. Defaults to DEVICE if specified, 'default' otherwise.",
|
||||
"MIXER_CARD",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"mixer-index",
|
||||
"Alsa mixer index, Index of the cards mixer. Defaults to 0",
|
||||
"MIXER_INDEX",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"initial-volume",
|
||||
"Initial volume (%) once connected {0..100}. Defaults to 50 for softvol and for Alsa mixer the current volume.",
|
||||
"VOLUME",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"zeroconf-port",
|
||||
"The port the internal server advertised over zeroconf uses.",
|
||||
"ZEROCONF_PORT",
|
||||
)
|
||||
.optflag(
|
||||
"",
|
||||
"enable-volume-normalisation",
|
||||
"Play all tracks at the same volume",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"normalisation-method",
|
||||
"Specify the normalisation method to use - [basic, dynamic]. Default is dynamic.",
|
||||
"NORMALISATION_METHOD",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"normalisation-gain-type",
|
||||
"Specify the normalisation gain type to use - [track, album]. Default is album.",
|
||||
"GAIN_TYPE",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"normalisation-pregain",
|
||||
"Pregain (dB) applied by volume normalisation",
|
||||
"PREGAIN",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"normalisation-threshold",
|
||||
"Threshold (dBFS) to prevent clipping. Default is -1.0.",
|
||||
"THRESHOLD",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"normalisation-attack",
|
||||
"Attack time (ms) in which the dynamic limiter is reducing gain. Default is 5.",
|
||||
"ATTACK",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"normalisation-release",
|
||||
"Release or decay time (ms) in which the dynamic limiter is restoring gain. Default is 100.",
|
||||
"RELEASE",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"normalisation-knee",
|
||||
"Knee steepness of the dynamic limiter. Default is 1.0.",
|
||||
"KNEE",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"volume-ctrl",
|
||||
"Volume control type {cubic|fixed|linear|log}. Defaults to log.",
|
||||
"VOLUME_CTRL"
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"volume-range",
|
||||
"Range of the volume control (dB). Defaults to 60 for softvol and for Alsa mixer what the mixer supports.",
|
||||
"RANGE",
|
||||
)
|
||||
.optflag(
|
||||
"",
|
||||
"autoplay",
|
||||
"autoplay similar songs when your music ends.",
|
||||
)
|
||||
.optflag(
|
||||
"",
|
||||
"disable-gapless",
|
||||
"disable gapless playback.",
|
||||
)
|
||||
.optflag(
|
||||
"",
|
||||
"passthrough",
|
||||
"Pass raw stream to output, only works for \"pipe\"."
|
||||
);
|
||||
.optopt("n", "name", "Device name.", "NAME")
|
||||
.optopt("", "device-type", "Displayed device type.", "TYPE")
|
||||
.optopt(
|
||||
"b",
|
||||
"bitrate",
|
||||
"Bitrate (kbps) {96|160|320}. Defaults to 160.",
|
||||
"BITRATE",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"onevent",
|
||||
"Run PROGRAM when a playback event occurs.",
|
||||
"PROGRAM",
|
||||
)
|
||||
.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", "version", "Display librespot version string.")
|
||||
.optopt("u", "username", "Username to sign in with.", "USERNAME")
|
||||
.optopt("p", "password", "Password", "PASSWORD")
|
||||
.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.", "PORT")
|
||||
.optflag("", "disable-discovery", "Disable discovery mode.")
|
||||
.optopt(
|
||||
"",
|
||||
"backend",
|
||||
"Audio backend to use. Use '?' to list options.",
|
||||
"NAME",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"device",
|
||||
"Audio device to use. Use '?' to list options if using alsa, portaudio or rodio.",
|
||||
"NAME",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"format",
|
||||
"Output format {F32|S32|S24|S24_3|S16}. Defaults to S16.",
|
||||
"FORMAT",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"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.",
|
||||
"DITHER",
|
||||
)
|
||||
.optopt("", "mixer", "Mixer to use {alsa|softvol}.", "MIXER")
|
||||
.optopt(
|
||||
"m",
|
||||
"mixer-name",
|
||||
"Alsa mixer control, e.g. 'PCM' or 'Master'. Defaults to 'PCM'.",
|
||||
"NAME",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"mixer-card",
|
||||
"Alsa mixer card, e.g 'hw:0' or similar from `aplay -l`. Defaults to DEVICE if specified, 'default' otherwise.",
|
||||
"MIXER_CARD",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"mixer-index",
|
||||
"Alsa index of the cards mixer. Defaults to 0.",
|
||||
"INDEX",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"initial-volume",
|
||||
"Initial volume in % from 0-100. Default for softvol: '50'. For the Alsa mixer: the current volume.",
|
||||
"VOLUME",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"zeroconf-port",
|
||||
"The port the internal server advertised over zeroconf uses.",
|
||||
"PORT",
|
||||
)
|
||||
.optflag(
|
||||
"",
|
||||
"enable-volume-normalisation",
|
||||
"Play all tracks at the same volume.",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"normalisation-method",
|
||||
"Specify the normalisation method to use {basic|dynamic}. Defaults to dynamic.",
|
||||
"METHOD",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"normalisation-gain-type",
|
||||
"Specify the normalisation gain type to use {track|album}. Defaults to album.",
|
||||
"TYPE",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"normalisation-pregain",
|
||||
"Pregain (dB) applied by volume normalisation. Defaults to 0.",
|
||||
"PREGAIN",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"normalisation-threshold",
|
||||
"Threshold (dBFS) to prevent clipping. Defaults to -1.0.",
|
||||
"THRESHOLD",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"normalisation-attack",
|
||||
"Attack time (ms) in which the dynamic limiter is reducing gain. Defaults to 5.",
|
||||
"TIME",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"normalisation-release",
|
||||
"Release or decay time (ms) in which the dynamic limiter is restoring gain. Defaults to 100.",
|
||||
"TIME",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"normalisation-knee",
|
||||
"Knee steepness of the dynamic limiter. Defaults to 1.0.",
|
||||
"KNEE",
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"volume-ctrl",
|
||||
"Volume control type {cubic|fixed|linear|log}. Defaults to log.",
|
||||
"VOLUME_CTRL"
|
||||
)
|
||||
.optopt(
|
||||
"",
|
||||
"volume-range",
|
||||
"Range of the volume control (dB). Default for softvol: 60. For the Alsa mixer: what the control supports.",
|
||||
"RANGE",
|
||||
)
|
||||
.optflag(
|
||||
"",
|
||||
"autoplay",
|
||||
"Automatically play similar songs when your music ends.",
|
||||
)
|
||||
.optflag(
|
||||
"",
|
||||
"disable-gapless",
|
||||
"Disable gapless playback.",
|
||||
)
|
||||
.optflag(
|
||||
"",
|
||||
"passthrough",
|
||||
"Pass raw stream to output, only works for pipe and subprocess.",
|
||||
);
|
||||
|
||||
let matches = match opts.parse(&args[1..]) {
|
||||
Ok(m) => m,
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
if matches.opt_present("h") {
|
||||
println!("{}", usage(&args[0], &opts));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if matches.opt_present("version") {
|
||||
print_version();
|
||||
exit(0);
|
||||
|
@ -552,7 +565,7 @@ fn get_setup(args: &[String]) -> Setup {
|
|||
match Url::parse(&s) {
|
||||
Ok(url) => {
|
||||
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" {
|
||||
|
@ -560,7 +573,7 @@ fn get_setup(args: &[String]) -> Setup {
|
|||
}
|
||||
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 let Some(code) = e.code() {
|
||||
warn!("Sink event prog returned exit code {}", code);
|
||||
warn!("Sink event program returned exit code {}", code);
|
||||
} else {
|
||||
warn!("Sink event prog returned failure");
|
||||
warn!("Sink event program returned failure");
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
warn!("Emitting sink event failed: {}", e);
|
||||
}
|
||||
},
|
||||
}
|
||||
})));
|
||||
}
|
||||
|
@ -849,13 +862,21 @@ async fn main() {
|
|||
|
||||
tokio::spawn(async move {
|
||||
match child.wait().await {
|
||||
Ok(status) if !status.success() => error!("child exited with status {:?}", status.code()),
|
||||
Err(e) => error!("failed to wait on child process: {}", e),
|
||||
_ => {}
|
||||
Ok(e) if e.success() => (),
|
||||
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 {
|
||||
error!("program failed to start");
|
||||
warn!("On event program failed to start");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue