|
@@ -1,27 +1,76 @@
|
|
|
+#[macro_use] extern crate log;
|
|
|
extern crate getopts;
|
|
|
extern crate librespot;
|
|
|
extern crate ctrlc;
|
|
|
+extern crate env_logger;
|
|
|
|
|
|
+use env_logger::LogBuilder;
|
|
|
use std::io::{stderr, Write};
|
|
|
use std::process::exit;
|
|
|
use std::thread;
|
|
|
+use std::env;
|
|
|
+use std::path::PathBuf;
|
|
|
+use std::str::FromStr;
|
|
|
|
|
|
use librespot::spirc::SpircManager;
|
|
|
-use librespot::main_helper;
|
|
|
use librespot::authentication::get_credentials;
|
|
|
+use librespot::audio_backend::{self, BACKENDS};
|
|
|
+use librespot::cache::{Cache, DefaultCache, NoCache};
|
|
|
+use librespot::player::Player;
|
|
|
+use librespot::session::{Bitrate, Config, Session};
|
|
|
+use librespot::version;
|
|
|
|
|
|
fn usage(program: &str, opts: &getopts::Options) -> String {
|
|
|
let brief = format!("Usage: {} [options]", program);
|
|
|
format!("{}", opts.usage(&brief))
|
|
|
}
|
|
|
|
|
|
-fn main() {
|
|
|
- let mut opts = getopts::Options::new();
|
|
|
- main_helper::add_session_arguments(&mut opts);
|
|
|
- main_helper::add_authentication_arguments(&mut opts);
|
|
|
- main_helper::add_player_arguments(&mut opts);
|
|
|
+fn setup_logging(verbose: bool) {
|
|
|
+ let mut builder = LogBuilder::new();
|
|
|
|
|
|
- let args: Vec<String> = std::env::args().collect();
|
|
|
+ match env::var("RUST_LOG") {
|
|
|
+ Ok(config) => {
|
|
|
+ builder.parse(&config);
|
|
|
+ builder.init().unwrap();
|
|
|
+
|
|
|
+ if verbose {
|
|
|
+ warn!("`--verbose` flag overidden by `RUST_LOG` environment variable");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Err(_) => {
|
|
|
+ if verbose {
|
|
|
+ builder.parse("mdns=info,librespot=trace");
|
|
|
+ } else {
|
|
|
+ builder.parse("mdns=info,librespot=info");
|
|
|
+ }
|
|
|
+ builder.init().unwrap();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn list_backends() {
|
|
|
+ println!("Available Backends : ");
|
|
|
+ for (&(name, _), idx) in BACKENDS.iter().zip(0..) {
|
|
|
+ if idx == 0 {
|
|
|
+ println!("- {} (default)", name);
|
|
|
+ } else {
|
|
|
+ println!("- {}", name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn setup(args: &[String]) -> (Session, Player) {
|
|
|
+ let mut opts = getopts::Options::new();
|
|
|
+ opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE")
|
|
|
+ .reqopt("n", "name", "Device name", "NAME")
|
|
|
+ .optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE")
|
|
|
+ .optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM")
|
|
|
+ .optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM")
|
|
|
+ .optflag("v", "verbose", "Enable verbose output")
|
|
|
+ .optopt("u", "username", "Username to sign in with", "USERNAME")
|
|
|
+ .optopt("p", "password", "Password", "PASSWORD")
|
|
|
+ .optopt("", "backend", "Audio backend to use. Use '?' to list options", "BACKEND")
|
|
|
+ .optopt("", "device", "Audio device to use. Use '?' to list options", "DEVICE");
|
|
|
|
|
|
let matches = match opts.parse(&args[1..]) {
|
|
|
Ok(m) => m,
|
|
@@ -31,14 +80,57 @@ fn main() {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- main_helper::setup_logging(&matches);
|
|
|
+ let verbose = matches.opt_present("verbose");
|
|
|
+ setup_logging(verbose);
|
|
|
+
|
|
|
+ info!("librespot {} ({}). Built on {}.",
|
|
|
+ version::short_sha(),
|
|
|
+ version::commit_date(),
|
|
|
+ version::short_now());
|
|
|
+
|
|
|
+ let backend_name = matches.opt_str("backend");
|
|
|
+ if backend_name == Some("?".into()) {
|
|
|
+ list_backends();
|
|
|
+ exit(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ let backend = audio_backend::find(backend_name.as_ref())
|
|
|
+ .expect("Invalid backend");
|
|
|
+
|
|
|
+ let bitrate = matches.opt_str("b").as_ref()
|
|
|
+ .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
|
|
|
+ .unwrap_or(Bitrate::Bitrate160);
|
|
|
+
|
|
|
+ let config = Config {
|
|
|
+ user_agent: version::version_string(),
|
|
|
+ device_name: matches.opt_str("name").unwrap(),
|
|
|
+ bitrate: bitrate,
|
|
|
+ onstart: matches.opt_str("onstart"),
|
|
|
+ onstop: matches.opt_str("onstop"),
|
|
|
+ };
|
|
|
+
|
|
|
+ let cache = matches.opt_str("c").map(|cache_location| {
|
|
|
+ Box::new(DefaultCache::new(PathBuf::from(cache_location)).unwrap())
|
|
|
+ as Box<Cache + Send + Sync>
|
|
|
+ }).unwrap_or_else(|| Box::new(NoCache));
|
|
|
+
|
|
|
+ let session = Session::new(config, cache);
|
|
|
|
|
|
- let session = main_helper::create_session(&matches);
|
|
|
let credentials = get_credentials(&session, matches.opt_str("username"),
|
|
|
- matches.opt_str("password"));
|
|
|
+ matches.opt_str("password"));
|
|
|
session.login(credentials).unwrap();
|
|
|
|
|
|
- let player = main_helper::create_player(&session, &matches);
|
|
|
+ let device_name = matches.opt_str("device");
|
|
|
+ let player = Player::new(session.clone(), move || {
|
|
|
+ (backend)(device_name.as_ref().map(AsRef::as_ref))
|
|
|
+ });
|
|
|
+
|
|
|
+ (session, player)
|
|
|
+}
|
|
|
+
|
|
|
+fn main() {
|
|
|
+ let args: Vec<String> = std::env::args().collect();
|
|
|
+ let (session, player) = setup(&args);
|
|
|
|
|
|
let spirc = SpircManager::new(session.clone(), player);
|
|
|
let spirc_signal = spirc.clone();
|