librespot/src/main.rs

176 lines
5.2 KiB
Rust
Raw Normal View History

2017-01-10 16:31:12 +00:00
#[macro_use] extern crate log;
extern crate getopts;
2015-07-09 19:08:56 +00:00
extern crate librespot;
extern crate ctrlc;
2017-01-10 16:31:12 +00:00
extern crate env_logger;
2017-01-18 18:41:22 +00:00
extern crate futures;
extern crate tokio_core;
2015-04-25 20:32:07 +00:00
2017-01-10 16:31:12 +00:00
use env_logger::LogBuilder;
use std::io::{stderr, Write};
use std::process::exit;
2017-01-10 16:31:12 +00:00
use std::env;
use std::path::PathBuf;
use std::str::FromStr;
2017-01-18 18:41:22 +00:00
use futures::Future;
use tokio_core::reactor::Core;
2017-01-20 12:56:42 +00:00
use librespot::spirc::Spirc;
2017-01-18 18:41:22 +00:00
use librespot::authentication::{get_credentials, Credentials};
use librespot::audio_backend::{self, Sink, BACKENDS};
use librespot::cache::Cache;
2017-01-10 16:31:12 +00:00
use librespot::player::Player;
use librespot::session::{Bitrate, Config, Session};
use librespot::version;
2016-03-16 00:05:05 +00:00
fn usage(program: &str, opts: &getopts::Options) -> String {
let brief = format!("Usage: {} [options]", program);
format!("{}", opts.usage(&brief))
}
2017-01-10 16:31:12 +00:00
fn setup_logging(verbose: bool) {
let mut builder = LogBuilder::new();
2016-01-02 02:30:24 +00:00
2017-01-10 16:31:12 +00:00
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);
}
}
}
2017-01-18 18:41:22 +00:00
struct Setup {
backend: &'static (Fn(Option<String>) -> Box<Sink> + Send + Sync),
cache: Option<Cache>,
2017-01-18 18:41:22 +00:00
config: Config,
credentials: Credentials,
device: Option<String>,
}
fn setup(args: &[String]) -> Setup {
2017-01-10 16:31:12 +00:00
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..]) {
2016-01-02 15:19:39 +00:00
Ok(m) => m,
Err(f) => {
writeln!(stderr(), "error: {}\n{}", f.to_string(), usage(&args[0], &opts)).unwrap();
exit(1);
2016-04-24 11:15:53 +00:00
}
};
2017-01-10 16:31:12 +00:00
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);
2017-01-18 18:41:22 +00:00
let name = matches.opt_str("name").unwrap();
let device_id = librespot::session::device_id(&name);
2017-01-18 17:07:20 +00:00
let cache = matches.opt_str("c").map(|cache_location| {
Cache::new(PathBuf::from(cache_location))
});
2017-01-18 17:07:20 +00:00
let cached_credentials = cache.as_ref().and_then(Cache::credentials);
2017-01-18 17:07:20 +00:00
2017-01-18 18:41:22 +00:00
let credentials = get_credentials(&name, &device_id,
2017-01-18 17:07:20 +00:00
matches.opt_str("username"),
matches.opt_str("password"),
cached_credentials);
2017-01-10 16:31:12 +00:00
let config = Config {
user_agent: version::version_string(),
2017-01-18 18:41:22 +00:00
name: name,
2017-01-18 17:07:20 +00:00
device_id: device_id,
2017-01-10 16:31:12 +00:00
bitrate: bitrate,
onstart: matches.opt_str("onstart"),
onstop: matches.opt_str("onstop"),
};
2017-01-18 18:41:22 +00:00
let device = matches.opt_str("device");
2017-01-18 18:41:22 +00:00
Setup {
backend: backend,
cache: cache,
config: config,
credentials: credentials,
device: device,
}
2017-01-10 16:31:12 +00:00
}
fn main() {
2017-01-18 18:41:22 +00:00
let mut core = Core::new().unwrap();
let handle = core.handle();
2017-01-10 16:31:12 +00:00
let args: Vec<String> = std::env::args().collect();
2017-01-18 18:41:22 +00:00
let Setup { backend, cache, config, credentials, device } = setup(&args);
2017-01-18 18:41:22 +00:00
let connection = Session::connect(config, credentials, cache, handle);
2017-01-20 12:56:42 +00:00
let task = connection.and_then(move |session| {
2017-01-18 18:41:22 +00:00
let player = Player::new(session.clone(), move || {
(backend)(device)
});
2017-01-20 12:56:42 +00:00
let (spirc, task) = Spirc::new(session.clone(), player);
let spirc = ::std::cell::RefCell::new(spirc);
2017-01-18 18:41:22 +00:00
ctrlc::set_handler(move || {
2017-01-20 12:56:42 +00:00
spirc.borrow_mut().shutdown();
2017-01-18 18:41:22 +00:00
});
2017-01-20 12:56:42 +00:00
task.map_err(|()| panic!("spirc error"))
});
2017-01-18 18:41:22 +00:00
core.run(task).unwrap()
2015-07-01 17:49:03 +00:00
}