2015-06-23 14:38:29 +00:00
|
|
|
use crypto::digest::Digest;
|
|
|
|
use crypto::sha1::Sha1;
|
2016-03-13 20:03:40 +00:00
|
|
|
use eventual;
|
2015-09-01 11:20:37 +00:00
|
|
|
use eventual::Future;
|
2016-03-16 04:07:04 +00:00
|
|
|
use eventual::Async;
|
2017-01-18 05:57:36 +00:00
|
|
|
use std::io::{self, Read, Cursor};
|
2016-03-13 20:45:31 +00:00
|
|
|
use std::result::Result;
|
2017-01-18 20:39:46 +00:00
|
|
|
use std::sync::{Mutex, RwLock, Arc, mpsc, Weak};
|
2017-01-10 16:31:12 +00:00
|
|
|
use std::str::FromStr;
|
2017-01-18 05:57:36 +00:00
|
|
|
use futures::Future as Future_;
|
2017-01-18 20:39:46 +00:00
|
|
|
use futures::{Stream, BoxFuture};
|
2017-01-18 18:41:22 +00:00
|
|
|
use tokio_core::reactor::Handle;
|
2015-06-23 14:38:29 +00:00
|
|
|
|
2016-05-09 11:22:51 +00:00
|
|
|
use album_cover::AlbumCover;
|
2017-01-18 15:17:10 +00:00
|
|
|
use apresolve::apresolve_or_fallback;
|
2016-03-16 04:07:04 +00:00
|
|
|
use audio_file::AudioFile;
|
2017-01-18 20:39:46 +00:00
|
|
|
use audio_key::AudioKeyManager;
|
2016-03-13 20:45:31 +00:00
|
|
|
use authentication::Credentials;
|
2016-03-16 04:07:04 +00:00
|
|
|
use cache::Cache;
|
2017-01-18 05:57:36 +00:00
|
|
|
use connection::{self, adaptor};
|
2015-07-02 17:24:25 +00:00
|
|
|
use mercury::{MercuryManager, MercuryRequest, MercuryResponse};
|
|
|
|
use metadata::{MetadataManager, MetadataRef, MetadataTrait};
|
2016-05-09 11:22:51 +00:00
|
|
|
use stream::StreamManager;
|
2017-01-18 20:39:46 +00:00
|
|
|
use util::{SpotifyId, FileId, ReadSeek, Lazy};
|
2016-03-13 20:45:31 +00:00
|
|
|
|
2016-05-09 11:22:51 +00:00
|
|
|
use stream;
|
|
|
|
|
2017-01-06 16:09:57 +00:00
|
|
|
#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq)]
|
2016-01-02 02:30:24 +00:00
|
|
|
pub enum Bitrate {
|
|
|
|
Bitrate96,
|
|
|
|
Bitrate160,
|
2016-01-02 15:19:39 +00:00
|
|
|
Bitrate320,
|
2016-01-02 02:30:24 +00:00
|
|
|
}
|
2017-01-10 16:31:12 +00:00
|
|
|
impl FromStr for Bitrate {
|
|
|
|
type Err = String;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
match s {
|
|
|
|
"96" => Ok(Bitrate::Bitrate96),
|
|
|
|
"160" => Ok(Bitrate::Bitrate160),
|
|
|
|
"320" => Ok(Bitrate::Bitrate320),
|
|
|
|
_ => Err(s.into()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-02 02:30:24 +00:00
|
|
|
|
2015-05-09 10:07:24 +00:00
|
|
|
pub struct Config {
|
|
|
|
pub user_agent: String,
|
2017-01-18 18:41:22 +00:00
|
|
|
pub name: String,
|
2017-01-18 17:07:20 +00:00
|
|
|
pub device_id: String,
|
2016-01-02 02:30:24 +00:00
|
|
|
pub bitrate: Bitrate,
|
2016-09-08 18:49:17 +00:00
|
|
|
pub onstart: Option<String>,
|
|
|
|
pub onstop: Option<String>,
|
2015-05-09 10:07:24 +00:00
|
|
|
}
|
|
|
|
|
2015-09-01 11:20:37 +00:00
|
|
|
pub struct SessionData {
|
2016-01-26 23:01:45 +00:00
|
|
|
country: String,
|
|
|
|
canonical_username: String,
|
2015-12-29 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct SessionInternal {
|
2016-01-26 22:34:57 +00:00
|
|
|
config: Config,
|
2016-01-26 23:01:45 +00:00
|
|
|
data: RwLock<SessionData>,
|
2015-06-23 14:38:29 +00:00
|
|
|
|
2016-03-16 04:07:04 +00:00
|
|
|
cache: Box<Cache + Send + Sync>,
|
2015-07-02 17:24:25 +00:00
|
|
|
mercury: Mutex<MercuryManager>,
|
|
|
|
metadata: Mutex<MetadataManager>,
|
|
|
|
stream: Mutex<StreamManager>,
|
2017-01-18 18:41:22 +00:00
|
|
|
rx_connection: Mutex<adaptor::StreamAdaptor<(u8, Vec<u8>), io::Error>>,
|
|
|
|
tx_connection: Mutex<adaptor::SinkAdaptor<(u8, Vec<u8>)>>,
|
2017-01-18 20:39:46 +00:00
|
|
|
|
|
|
|
audio_key: Lazy<AudioKeyManager>,
|
2015-04-25 20:32:07 +00:00
|
|
|
}
|
|
|
|
|
2015-09-01 11:20:37 +00:00
|
|
|
#[derive(Clone)]
|
2015-12-29 12:13:26 +00:00
|
|
|
pub struct Session(pub Arc<SessionInternal>);
|
2015-07-02 17:24:25 +00:00
|
|
|
|
2017-01-18 20:39:46 +00:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct SessionWeak(pub Weak<SessionInternal>);
|
|
|
|
|
2017-01-18 18:41:22 +00:00
|
|
|
pub fn device_id(name: &str) -> String {
|
2017-01-18 17:07:20 +00:00
|
|
|
let mut h = Sha1::new();
|
2017-01-18 18:41:22 +00:00
|
|
|
h.input_str(&name);
|
2017-01-18 17:07:20 +00:00
|
|
|
h.result_str()
|
|
|
|
}
|
|
|
|
|
2015-04-25 20:32:07 +00:00
|
|
|
impl Session {
|
2017-01-18 18:41:22 +00:00
|
|
|
pub fn connect(config: Config, credentials: Credentials,
|
|
|
|
cache: Box<Cache + Send + Sync>, handle: Handle)
|
2017-01-18 20:39:46 +00:00
|
|
|
-> Box<Future_<Item=(Session, BoxFuture<(), io::Error>), Error=io::Error>>
|
2017-01-18 18:41:22 +00:00
|
|
|
{
|
|
|
|
let access_point = apresolve_or_fallback::<io::Error>(&handle);
|
|
|
|
|
|
|
|
let connection = access_point.and_then(move |addr| {
|
|
|
|
info!("Connecting to AP \"{}\"", addr);
|
|
|
|
connection::connect::<&str>(&addr, &handle)
|
|
|
|
});
|
|
|
|
|
|
|
|
let device_id = config.device_id.clone();
|
|
|
|
let authentication = connection.and_then(move |connection| {
|
|
|
|
connection::authenticate(connection, credentials, device_id)
|
|
|
|
});
|
|
|
|
|
|
|
|
let result = authentication.map(move |(transport, reusable_credentials)| {
|
|
|
|
info!("Authenticated !");
|
|
|
|
cache.put_credentials(&reusable_credentials);
|
|
|
|
|
|
|
|
let (session, task) = Session::create(transport, config, cache, reusable_credentials.username.clone());
|
|
|
|
(session, task)
|
|
|
|
});
|
|
|
|
|
|
|
|
Box::new(result)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create(transport: connection::Transport, config: Config,
|
2017-01-18 20:39:46 +00:00
|
|
|
cache: Box<Cache + Send + Sync>, username: String)
|
|
|
|
-> (Session, BoxFuture<(), io::Error>)
|
2017-01-18 18:41:22 +00:00
|
|
|
{
|
|
|
|
let transport = transport.map(|(cmd, data)| (cmd, data.as_ref().to_owned()));
|
|
|
|
let (tx, rx, task) = adaptor::adapt(transport);
|
|
|
|
|
|
|
|
let session = Session(Arc::new(SessionInternal {
|
2016-01-01 23:16:12 +00:00
|
|
|
config: config,
|
|
|
|
data: RwLock::new(SessionData {
|
|
|
|
country: String::new(),
|
2017-01-18 18:41:22 +00:00
|
|
|
canonical_username: username,
|
2016-01-01 23:16:12 +00:00
|
|
|
}),
|
|
|
|
|
2017-01-18 18:41:22 +00:00
|
|
|
rx_connection: Mutex::new(rx),
|
|
|
|
tx_connection: Mutex::new(tx),
|
2016-01-01 23:16:12 +00:00
|
|
|
|
2016-03-16 04:07:04 +00:00
|
|
|
cache: cache,
|
2016-01-01 23:16:12 +00:00
|
|
|
mercury: Mutex::new(MercuryManager::new()),
|
|
|
|
metadata: Mutex::new(MetadataManager::new()),
|
|
|
|
stream: Mutex::new(StreamManager::new()),
|
2017-01-18 20:39:46 +00:00
|
|
|
|
|
|
|
audio_key: Lazy::new(),
|
2017-01-18 18:41:22 +00:00
|
|
|
}));
|
2016-03-13 20:45:31 +00:00
|
|
|
|
2017-01-18 18:41:22 +00:00
|
|
|
(session, task)
|
2015-06-23 14:38:29 +00:00
|
|
|
}
|
2015-05-09 10:07:24 +00:00
|
|
|
|
2017-01-18 20:39:46 +00:00
|
|
|
pub fn audio_key(&self) -> &AudioKeyManager {
|
|
|
|
self.0.audio_key.get(|| AudioKeyManager::new(self.weak()))
|
|
|
|
}
|
2017-01-18 18:41:22 +00:00
|
|
|
|
2015-06-23 14:38:29 +00:00
|
|
|
pub fn poll(&self) {
|
2016-01-01 23:16:12 +00:00
|
|
|
let (cmd, data) = self.recv();
|
2015-07-02 17:24:25 +00:00
|
|
|
|
|
|
|
match cmd {
|
2017-01-18 05:57:36 +00:00
|
|
|
0x4 => self.send_packet(0x49, data),
|
2015-07-02 17:24:25 +00:00
|
|
|
0x4a => (),
|
2016-05-09 11:22:51 +00:00
|
|
|
0x9 | 0xa => self.0.stream.lock().unwrap().handle(cmd, data, self),
|
2017-01-18 20:39:46 +00:00
|
|
|
0xd | 0xe => self.audio_key().dispatch(cmd, data),
|
2015-12-29 12:13:26 +00:00
|
|
|
0x1b => {
|
2016-01-02 15:19:39 +00:00
|
|
|
self.0.data.write().unwrap().country = String::from_utf8(data).unwrap();
|
|
|
|
}
|
2016-05-09 11:22:51 +00:00
|
|
|
0xb2...0xb6 => self.0.mercury.lock().unwrap().handle(cmd, data, self),
|
2016-01-02 15:19:39 +00:00
|
|
|
_ => (),
|
2015-07-02 17:24:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-01 23:16:12 +00:00
|
|
|
pub fn recv(&self) -> (u8, Vec<u8>) {
|
2017-01-18 18:41:22 +00:00
|
|
|
self.0.rx_connection.lock().unwrap().recv().unwrap()
|
2016-01-01 23:16:12 +00:00
|
|
|
}
|
|
|
|
|
2017-01-18 05:57:36 +00:00
|
|
|
pub fn send_packet(&self, cmd: u8, data: Vec<u8>) {
|
2017-01-18 18:41:22 +00:00
|
|
|
self.0.tx_connection.lock().unwrap().send((cmd, data))
|
2015-07-02 17:24:25 +00:00
|
|
|
}
|
2015-12-29 12:13:26 +00:00
|
|
|
|
2017-01-18 20:39:46 +00:00
|
|
|
/*
|
2016-03-16 04:07:04 +00:00
|
|
|
pub fn audio_key(&self, track: SpotifyId, file_id: FileId) -> Future<AudioKey, AudioKeyError> {
|
|
|
|
self.0.cache
|
|
|
|
.get_audio_key(track, file_id)
|
|
|
|
.map(Future::of)
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
let self_ = self.clone();
|
|
|
|
self.0.audio_key.lock().unwrap()
|
|
|
|
.request(self, track, file_id)
|
|
|
|
.map(move |key| {
|
|
|
|
self_.0.cache.put_audio_key(track, file_id, key);
|
|
|
|
key
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
2017-01-18 20:39:46 +00:00
|
|
|
*/
|
2016-03-16 04:07:04 +00:00
|
|
|
|
|
|
|
pub fn audio_file(&self, file_id: FileId) -> Box<ReadSeek> {
|
|
|
|
self.0.cache
|
|
|
|
.get_file(file_id)
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
let (audio_file, complete_rx) = AudioFile::new(self, file_id);
|
|
|
|
|
|
|
|
let self_ = self.clone();
|
|
|
|
complete_rx.map(move |mut complete_file| {
|
|
|
|
self_.0.cache.put_file(file_id, &mut complete_file)
|
|
|
|
}).fire();
|
|
|
|
|
2016-05-09 11:22:51 +00:00
|
|
|
Box::new(audio_file.await().unwrap())
|
2016-03-16 04:07:04 +00:00
|
|
|
})
|
2015-07-02 17:24:25 +00:00
|
|
|
}
|
|
|
|
|
2016-03-16 04:07:04 +00:00
|
|
|
pub fn album_cover(&self, file_id: FileId) -> eventual::Future<Vec<u8>, ()> {
|
|
|
|
self.0.cache
|
|
|
|
.get_file(file_id)
|
|
|
|
.map(|mut f| {
|
|
|
|
let mut data = Vec::new();
|
|
|
|
f.read_to_end(&mut data).unwrap();
|
|
|
|
Future::of(data)
|
|
|
|
})
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
let self_ = self.clone();
|
2016-05-09 11:22:51 +00:00
|
|
|
AlbumCover::get(file_id, self)
|
2016-03-16 04:07:04 +00:00
|
|
|
.map(move |data| {
|
|
|
|
self_.0.cache.put_file(file_id, &mut Cursor::new(&data));
|
|
|
|
data
|
|
|
|
})
|
|
|
|
})
|
2015-07-03 00:23:49 +00:00
|
|
|
}
|
|
|
|
|
2016-05-09 11:22:51 +00:00
|
|
|
pub fn stream(&self, handler: Box<stream::Handler>) {
|
|
|
|
self.0.stream.lock().unwrap().create(handler, self)
|
2016-03-13 20:03:40 +00:00
|
|
|
}
|
|
|
|
|
2015-07-02 17:24:25 +00:00
|
|
|
pub fn metadata<T: MetadataTrait>(&self, id: SpotifyId) -> MetadataRef<T> {
|
2015-09-01 11:20:37 +00:00
|
|
|
self.0.metadata.lock().unwrap().get(self, id)
|
2015-07-02 17:24:25 +00:00
|
|
|
}
|
|
|
|
|
2015-09-01 11:20:37 +00:00
|
|
|
pub fn mercury(&self, req: MercuryRequest) -> Future<MercuryResponse, ()> {
|
|
|
|
self.0.mercury.lock().unwrap().request(self, req)
|
2015-07-02 17:24:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn mercury_sub(&self, uri: String) -> mpsc::Receiver<MercuryResponse> {
|
2015-09-01 11:20:37 +00:00
|
|
|
self.0.mercury.lock().unwrap().subscribe(self, uri)
|
2015-04-25 20:32:07 +00:00
|
|
|
}
|
2016-01-26 22:34:57 +00:00
|
|
|
|
2016-03-16 04:07:04 +00:00
|
|
|
pub fn cache(&self) -> &Cache {
|
|
|
|
self.0.cache.as_ref()
|
|
|
|
}
|
|
|
|
|
2016-01-26 22:34:57 +00:00
|
|
|
pub fn config(&self) -> &Config {
|
|
|
|
&self.0.config
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn username(&self) -> String {
|
|
|
|
self.0.data.read().unwrap().canonical_username.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn country(&self) -> String {
|
|
|
|
self.0.data.read().unwrap().country.clone()
|
|
|
|
}
|
|
|
|
|
2016-03-17 03:06:56 +00:00
|
|
|
pub fn device_id(&self) -> &str {
|
2017-01-18 17:07:20 +00:00
|
|
|
&self.config().device_id
|
2016-01-26 22:34:57 +00:00
|
|
|
}
|
2017-01-18 20:39:46 +00:00
|
|
|
|
|
|
|
pub fn weak(&self) -> SessionWeak {
|
|
|
|
SessionWeak(Arc::downgrade(&self.0))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SessionWeak {
|
|
|
|
pub fn upgrade(&self) -> Session {
|
|
|
|
Session(self.0.upgrade().expect("Session died"))
|
|
|
|
}
|
2015-04-25 20:32:07 +00:00
|
|
|
}
|
2016-05-09 11:22:51 +00:00
|
|
|
|
|
|
|
pub trait PacketHandler {
|
|
|
|
fn handle(&mut self, cmd: u8, data: Vec<u8>, session: &Session);
|
|
|
|
}
|