main.rs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. extern crate getopts;
  2. extern crate librespot;
  3. extern crate rpassword;
  4. use rpassword::read_password;
  5. use std::clone::Clone;
  6. use std::fs::File;
  7. use std::io::{stdout, Read, Write};
  8. use std::path::PathBuf;
  9. use std::thread;
  10. use librespot::audio_sink::DefaultSink;
  11. use librespot::authentication::Credentials;
  12. use librespot::discovery::DiscoveryManager;
  13. use librespot::player::Player;
  14. use librespot::session::{Bitrate, Config, Session};
  15. use librespot::spirc::SpircManager;
  16. use librespot::util::version::version_string;
  17. use librespot::cache::{Cache, DefaultCache, NoCache};
  18. #[cfg(feature = "facebook")]
  19. use librespot::facebook::facebook_login;
  20. #[cfg(not(feature = "facebook"))]
  21. fn facebook_login() -> Result<Credentials, ()> {
  22. Err(())
  23. }
  24. static PASSWORD_ENV_NAME: &'static str = "SPOTIFY_PASSWORD";
  25. fn usage(program: &str, opts: &getopts::Options) -> String {
  26. let brief = format!("Usage: {} [options]", program);
  27. format!("{}", opts.usage(&brief))
  28. }
  29. #[cfg(feature = "static-appkey")]
  30. static APPKEY: Option<&'static [u8]> = Some(include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/spotify_appkey.key")));
  31. #[cfg(not(feature = "static-appkey"))]
  32. static APPKEY: Option<&'static [u8]> = None;
  33. fn main() {
  34. let args: Vec<String> = std::env::args().collect();
  35. let program = args[0].clone();
  36. let mut opts = getopts::Options::new();
  37. opts.optopt("u", "username", "Username to sign in with", "USERNAME")
  38. .optopt("p", "password", "Password", "PASSWORD")
  39. .optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE")
  40. .reqopt("n", "name", "Device name", "NAME")
  41. .optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE");
  42. if APPKEY.is_none() {
  43. opts.reqopt("a", "appkey", "Path to a spotify appkey", "APPKEY");
  44. } else {
  45. opts.optopt("a", "appkey", "Path to a spotify appkey", "APPKEY");
  46. };
  47. if cfg!(feature = "facebook") {
  48. opts.optflag("", "facebook", "Login with a Facebook account");
  49. }
  50. let matches = match opts.parse(&args[1..]) {
  51. Ok(m) => m,
  52. Err(f) => {
  53. print!("Error: {}\n{}", f.to_string(), usage(&*program, &opts));
  54. return;
  55. }
  56. };
  57. let appkey = matches.opt_str("a").map(|appkey_path| {
  58. let mut file = File::open(appkey_path)
  59. .expect("Could not open app key.");
  60. let mut data = Vec::new();
  61. file.read_to_end(&mut data).unwrap();
  62. data
  63. }).or_else(|| APPKEY.map(ToOwned::to_owned)).unwrap();
  64. let username = matches.opt_str("u");
  65. let name = matches.opt_str("n").unwrap();
  66. let cache = matches.opt_str("c").map(|cache_location| {
  67. Box::new(DefaultCache::new(PathBuf::from(cache_location)).unwrap()) as Box<Cache + Send + Sync>
  68. }).unwrap_or_else(|| Box::new(NoCache) as Box<Cache + Send + Sync>);
  69. let bitrate = match matches.opt_str("b").as_ref().map(String::as_ref) {
  70. None => Bitrate::Bitrate160, // default value
  71. Some("96") => Bitrate::Bitrate96,
  72. Some("160") => Bitrate::Bitrate160,
  73. Some("320") => Bitrate::Bitrate320,
  74. Some(b) => panic!("Invalid bitrate {}", b),
  75. };
  76. let config = Config {
  77. application_key: appkey,
  78. user_agent: version_string(),
  79. device_name: name,
  80. bitrate: bitrate,
  81. };
  82. let session = Session::new(config, cache);
  83. let credentials = username.map(|username| {
  84. let password = matches.opt_str("p")
  85. .or_else(|| std::env::var(PASSWORD_ENV_NAME).ok())
  86. .unwrap_or_else(|| {
  87. print!("Password: ");
  88. stdout().flush().unwrap();
  89. read_password().unwrap()
  90. });
  91. Credentials::with_password(username, password)
  92. }).or_else(|| {
  93. if cfg!(feature = "facebook") && matches.opt_present("facebook") {
  94. Some(facebook_login().unwrap())
  95. } else {
  96. None
  97. }
  98. }).or_else(|| session.cache().get_credentials())
  99. .unwrap_or_else(|| {
  100. println!("No username provided and no stored credentials, starting discovery ...");
  101. let mut discovery = DiscoveryManager::new(session.clone());
  102. discovery.run()
  103. });
  104. std::env::remove_var(PASSWORD_ENV_NAME);
  105. let reusable_credentials = session.login(credentials).unwrap();
  106. session.cache().put_credentials(&reusable_credentials);
  107. let player = Player::new(session.clone(), || DefaultSink::open());
  108. let spirc = SpircManager::new(session.clone(), player);
  109. thread::spawn(move || spirc.run());
  110. loop {
  111. session.poll();
  112. }
  113. }