main.rs 4.3 KB

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