| 
					
				 | 
			
			
				@@ -1,166 +1,47 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 extern crate getopts; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 extern crate librespot; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-extern crate rpassword; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 extern crate env_logger; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #[macro_use] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 extern crate log; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-use rpassword::read_password; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-use std::clone::Clone; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-use std::fs::File; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-use std::io::{stdout, Read, Write}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-use std::path::PathBuf; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use std::process::exit; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use std::thread; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use std::env; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-use librespot::audio_backend::BACKENDS; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-use librespot::authentication::{Credentials, facebook_login, discovery_login}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-use librespot::cache::{Cache, DefaultCache, NoCache}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-use librespot::player::Player; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-use librespot::session::{Bitrate, Config, Session}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use librespot::spirc::SpircManager; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-use librespot::version; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use librespot::main_helper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 fn usage(program: &str, opts: &getopts::Options) -> String { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     let brief = format!("Usage: {} [options]", program); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     format!("{}", opts.usage(&brief)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-#[cfg(feature = "static-appkey")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static APPKEY: Option<&'static [u8]> = Some(include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/spotify_appkey.key"))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-#[cfg(not(feature = "static-appkey"))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static APPKEY: Option<&'static [u8]> = None; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 fn main() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     if env::var("RUST_LOG").is_err() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         env::set_var("RUST_LOG", "info,librespot=trace") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     env_logger::init().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    info!("librespot {} ({}). Built on {}.", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             version::short_sha(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             version::commit_date(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             version::short_now()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let args: Vec<String> = std::env::args().collect(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let program = args[0].clone(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     let mut opts = getopts::Options::new(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    opts.optopt("u", "username", "Username to sign in with", "USERNAME") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .optopt("p", "password", "Password", "PASSWORD") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .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("", "backend", "Audio backend to use. Use '?' to list options", "BACKEND") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        .optflag("", "facebook", "Login with a Facebook account"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    main_helper::add_session_arguments(&mut opts); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    main_helper::add_authentication_arguments(&mut opts); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    main_helper::add_player_arguments(&mut opts); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    if APPKEY.is_none() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        opts.reqopt("a", "appkey", "Path to a spotify appkey", "APPKEY"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        opts.optopt("a", "appkey", "Path to a spotify appkey", "APPKEY"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let args: Vec<String> = std::env::args().collect(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     let matches = match opts.parse(&args[1..]) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Ok(m) => m, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Err(f) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            error!("Error: {}\n{}", f.to_string(), usage(&*program, &opts)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let make_backend = match matches.opt_str("backend").as_ref().map(AsRef::as_ref) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        Some("?") => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            println!("Available Backends : "); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            for (&(name, _), idx) in BACKENDS.iter().zip(0..) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if idx == 0 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    println!("- {} (default)", name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    println!("- {}", name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        Some(name) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            BACKENDS.iter().find(|backend| name == backend.0).expect("Unknown backend").1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        None => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            BACKENDS.first().expect("No backends were enabled at build time").1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let appkey = matches.opt_str("a").map(|appkey_path| { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let mut file = File::open(appkey_path) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            .expect("Could not open app key."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let mut data = Vec::new(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        file.read_to_end(&mut data).unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    }).or_else(|| APPKEY.map(ToOwned::to_owned)).unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let username = matches.opt_str("u"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let name = matches.opt_str("n").unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    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) as Box<Cache + Send + Sync>); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let bitrate = match matches.opt_str("b").as_ref().map(String::as_ref) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        None => Bitrate::Bitrate160, // default value 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        Some("96") => Bitrate::Bitrate96, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        Some("160") => Bitrate::Bitrate160, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        Some("320") => Bitrate::Bitrate320, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        Some(b) => panic!("Invalid bitrate {}", b), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let config = Config { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        application_key: appkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        user_agent: version::version_string(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        device_name: name, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        bitrate: bitrate, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let stored_credentials = cache.get_credentials(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let session = Session::new(config, cache); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let credentials = match (username, matches.opt_str("p"), stored_credentials) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        (Some(username), Some(password), _) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            => Credentials::with_password(username, password), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        (Some(ref username), _, Some(ref credentials)) if *username == credentials.username 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            => credentials.clone(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        (Some(username), None, _) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            print!("Password for {}: ", username); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            stdout().flush().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            let password = read_password().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            Credentials::with_password(username.clone(), password) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        (None, _, _) if matches.opt_present("facebook") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            => facebook_login().unwrap(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        (None, _, Some(credentials)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            => credentials, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        (None, _, None) if cfg!(feature = "discovery") => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            info!("No username provided and no stored credentials, starting discovery ..."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            discovery_login(&session.config().device_name, session.device_id()).unwrap() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        (None, _, None) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            error!("No credentials provided"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            error!("Error: {}\n{}", f.to_string(), usage(&args[0], &opts)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            exit(1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let reusable_credentials = session.login(credentials).unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    session.cache().put_credentials(&reusable_credentials); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let session = main_helper::create_session(&matches); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let credentials = main_helper::get_credentials(&session, &matches); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    session.login(credentials).unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    let player = Player::new(session.clone(), move || make_backend()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let player = main_helper::create_player(&session, &matches); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     let spirc = SpircManager::new(session.clone(), player); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     thread::spawn(move || spirc.run()); 
			 |