librespot/src/session.rs

250 lines
7.5 KiB
Rust
Raw Normal View History

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;
use eventual::Async;
2017-01-18 05:57:36 +00:00
use std::io::{self, Read, Cursor};
use std::result::Result;
2016-01-01 23:16:12 +00:00
use std::sync::{Mutex, RwLock, Arc, mpsc};
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_;
use futures::Stream;
use futures::sync::oneshot;
2015-06-23 14:38:29 +00:00
use album_cover::AlbumCover;
use apresolve::apresolve_or_fallback;
use audio_file::AudioFile;
2017-01-18 05:57:36 +00:00
use audio_key::{AudioKeyManager, AudioKey, AudioKeyError};
use authentication::Credentials;
use cache::Cache;
2017-01-18 05:57:36 +00:00
use connection::{self, adaptor};
use mercury::{MercuryManager, MercuryRequest, MercuryResponse};
use metadata::{MetadataManager, MetadataRef, MetadataTrait};
use stream::StreamManager;
2017-01-18 05:57:36 +00:00
use util::{SpotifyId, FileId, ReadSeek};
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
pub struct Config {
pub user_agent: String,
2016-01-01 23:16:12 +00:00
pub device_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,
pub onstart: Option<String>,
pub onstop: Option<String>,
}
2015-09-01 11:20:37 +00:00
pub struct SessionData {
country: String,
canonical_username: String,
}
pub struct SessionInternal {
config: Config,
data: RwLock<SessionData>,
2015-06-23 14:38:29 +00:00
cache: Box<Cache + Send + Sync>,
mercury: Mutex<MercuryManager>,
metadata: Mutex<MetadataManager>,
stream: Mutex<StreamManager>,
audio_key: Mutex<AudioKeyManager>,
2017-01-18 05:57:36 +00:00
rx_connection: Mutex<Option<adaptor::StreamAdaptor<(u8, Vec<u8>), io::Error>>>,
tx_connection: Mutex<Option<adaptor::SinkAdaptor<(u8, Vec<u8>)>>>,
2015-04-25 20:32:07 +00:00
}
2015-09-01 11:20:37 +00:00
#[derive(Clone)]
pub struct Session(pub Arc<SessionInternal>);
2017-01-18 17:07:20 +00:00
pub fn device_id(device_name: &str) -> String {
let mut h = Sha1::new();
h.input_str(&device_name);
h.result_str()
}
2015-04-25 20:32:07 +00:00
impl Session {
pub fn new(config: Config, cache: Box<Cache + Send + Sync>) -> Session {
2016-01-01 23:16:12 +00:00
Session(Arc::new(SessionInternal {
config: config,
data: RwLock::new(SessionData {
country: String::new(),
canonical_username: String::new(),
}),
rx_connection: Mutex::new(None),
tx_connection: Mutex::new(None),
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()),
audio_key: Mutex::new(AudioKeyManager::new()),
}))
}
2017-01-18 05:57:36 +00:00
pub fn login(&self, credentials: Credentials) -> Result<Credentials, ()> {
let device_id = self.device_id().to_owned();
2017-01-18 05:57:36 +00:00
let (creds_tx, creds_rx) = oneshot::channel();
2017-01-18 05:57:36 +00:00
let (tx, rx) = adaptor::adapt(move |handle| {
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 authentication = connection.and_then(move |connection| {
connection::authenticate(connection, credentials, device_id)
});
authentication.map(|(transport, creds)| {
creds_tx.complete(creds);
transport.map(|(cmd, data)| (cmd, data.as_ref().to_owned()))
})
});
let reusable_credentials: Credentials = creds_rx.wait().unwrap();
2017-01-18 05:57:36 +00:00
self.0.data.write().unwrap().canonical_username = reusable_credentials.username.clone();
*self.0.rx_connection.lock().unwrap() = Some(rx);
*self.0.tx_connection.lock().unwrap() = Some(tx);
2017-01-18 05:57:36 +00:00
info!("Authenticated !");
2017-01-18 05:57:36 +00:00
self.0.cache.put_credentials(&reusable_credentials);
2017-01-18 05:57:36 +00:00
Ok(reusable_credentials)
2015-06-23 14:38:29 +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();
match cmd {
2017-01-18 05:57:36 +00:00
0x4 => self.send_packet(0x49, data),
0x4a => (),
0x9 | 0xa => self.0.stream.lock().unwrap().handle(cmd, data, self),
0xd | 0xe => self.0.audio_key.lock().unwrap().handle(cmd, data, self),
0x1b => {
2016-01-02 15:19:39 +00:00
self.0.data.write().unwrap().country = String::from_utf8(data).unwrap();
}
0xb2...0xb6 => self.0.mercury.lock().unwrap().handle(cmd, data, self),
2016-01-02 15:19:39 +00:00
_ => (),
}
}
2016-01-01 23:16:12 +00:00
pub fn recv(&self) -> (u8, Vec<u8>) {
2017-01-18 05:57:36 +00:00
self.0.rx_connection.lock().unwrap().as_mut().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>) {
self.0.tx_connection.lock().unwrap().as_mut().unwrap().send((cmd, data))
}
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
})
})
}
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();
Box::new(audio_file.await().unwrap())
})
}
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();
AlbumCover::get(file_id, self)
.map(move |data| {
self_.0.cache.put_file(file_id, &mut Cursor::new(&data));
data
})
})
2015-07-03 00:23:49 +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
}
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-09-01 11:20:37 +00:00
pub fn mercury(&self, req: MercuryRequest) -> Future<MercuryResponse, ()> {
self.0.mercury.lock().unwrap().request(self, req)
}
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
}
pub fn cache(&self) -> &Cache {
self.0.cache.as_ref()
}
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()
}
pub fn device_id(&self) -> &str {
2017-01-18 17:07:20 +00:00
&self.config().device_id
}
2015-04-25 20:32:07 +00:00
}
pub trait PacketHandler {
fn handle(&mut self, cmd: u8, data: Vec<u8>, session: &Session);
}