librespot/src/session.rs

247 lines
6.6 KiB
Rust
Raw Normal View History

2015-06-23 14:38:29 +00:00
use crypto::digest::Digest;
use crypto::sha1::Sha1;
2017-01-20 02:37:02 +00:00
use futures::Future;
use futures::sync::mpsc;
use futures::{Stream, BoxFuture, IntoFuture};
2017-01-19 22:45:24 +00:00
use std::io;
use std::result::Result;
2017-01-10 16:31:12 +00:00
use std::str::FromStr;
2017-01-20 02:37:02 +00:00
use std::sync::{RwLock, Arc, Weak};
2017-01-29 17:54:32 +00:00
use tokio_core::io::EasyBuf;
2017-01-19 22:45:24 +00:00
use tokio_core::reactor::{Handle, Remote};
2015-06-23 14:38:29 +00:00
use apresolve::apresolve_or_fallback;
use authentication::Credentials;
use cache::Cache;
2017-01-19 22:45:24 +00:00
use component::Lazy;
2017-01-20 02:37:02 +00:00
use connection;
2017-01-18 21:33:52 +00:00
use audio_key::AudioKeyManager;
2017-01-19 22:45:24 +00:00
use channel::ChannelManager;
2017-01-18 21:33:52 +00:00
use mercury::MercuryManager;
2017-01-19 12:56:49 +00:00
use metadata::MetadataManager;
2017-01-19 22:45:24 +00:00
use audio_file::AudioFileManager;
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
2017-01-31 08:21:30 +00:00
#[derive(Clone)]
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,
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
2017-01-20 02:37:02 +00:00
tx_connection: mpsc::UnboundedSender<(u8, Vec<u8>)>,
2017-01-18 20:39:46 +00:00
audio_key: Lazy<AudioKeyManager>,
2017-01-19 22:45:24 +00:00
audio_file: Lazy<AudioFileManager>,
channel: Lazy<ChannelManager>,
2017-01-18 21:33:52 +00:00
mercury: Lazy<MercuryManager>,
2017-01-19 12:56:49 +00:00
metadata: Lazy<MetadataManager>,
cache: Option<Arc<Cache>>,
2017-01-19 22:45:24 +00:00
handle: Remote,
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 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-29 16:25:09 +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: Option<Cache>, handle: Handle)
2017-01-20 12:56:42 +00:00
-> Box<Future<Item=Session, Error=io::Error>>
2017-01-18 18:41:22 +00:00
{
let access_point = apresolve_or_fallback::<io::Error>(&handle);
2017-01-19 22:45:24 +00:00
let handle_ = handle.clone();
2017-01-18 18:41:22 +00:00
let connection = access_point.and_then(move |addr| {
info!("Connecting to AP \"{}\"", addr);
2017-01-19 22:45:24 +00:00
connection::connect::<&str>(&addr, &handle_)
2017-01-18 18:41:22 +00:00
});
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 !");
if let Some(ref cache) = cache {
cache.save_credentials(&reusable_credentials);
}
2017-01-18 18:41:22 +00:00
2017-01-19 22:45:24 +00:00
let (session, task) = Session::create(
&handle, transport, config, cache, reusable_credentials.username.clone()
);
2017-01-20 12:56:42 +00:00
handle.spawn(task.map_err(|e| panic!(e)));
session
2017-01-18 18:41:22 +00:00
});
Box::new(result)
}
2017-01-19 22:45:24 +00:00
fn create(handle: &Handle, transport: connection::Transport,
config: Config, cache: Option<Cache>, username: String)
2017-01-18 20:39:46 +00:00
-> (Session, BoxFuture<(), io::Error>)
2017-01-18 18:41:22 +00:00
{
2017-01-20 02:37:02 +00:00
let (sink, stream) = transport.split();
let (sender_tx, sender_rx) = mpsc::unbounded();
let sender_task = sender_rx
.map_err(|e| -> io::Error { panic!(e) })
.forward(sink).map(|_| ());
2017-01-18 18:41:22 +00:00
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-20 02:37:02 +00:00
tx_connection: sender_tx,
2016-01-01 23:16:12 +00:00
cache: cache.map(Arc::new),
2017-01-18 20:39:46 +00:00
audio_key: Lazy::new(),
2017-01-19 22:45:24 +00:00
audio_file: Lazy::new(),
channel: Lazy::new(),
2017-01-18 21:33:52 +00:00
mercury: Lazy::new(),
2017-01-19 12:56:49 +00:00
metadata: Lazy::new(),
2017-01-19 22:45:24 +00:00
handle: handle.remote().clone(),
2017-01-18 18:41:22 +00:00
}));
2017-01-20 02:37:02 +00:00
let receiver_task = {
let session = session.clone();
stream.for_each(move |(cmd, data)| {
session.dispatch(cmd, data);
Ok(())
})
};
let task = (receiver_task, sender_task).into_future()
.map(|((), ())| ()).boxed();
2017-01-18 18:41:22 +00:00
(session, task)
2015-06-23 14:38:29 +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
2017-01-19 22:45:24 +00:00
pub fn audio_file(&self) -> &AudioFileManager {
self.0.audio_file.get(|| AudioFileManager::new(self.weak()))
}
pub fn channel(&self) -> &ChannelManager {
self.0.channel.get(|| ChannelManager::new(self.weak()))
}
2017-01-18 21:33:52 +00:00
pub fn mercury(&self) -> &MercuryManager {
self.0.mercury.get(|| MercuryManager::new(self.weak()))
}
2017-01-19 12:56:49 +00:00
pub fn metadata(&self) -> &MetadataManager {
self.0.metadata.get(|| MetadataManager::new(self.weak()))
}
2017-01-19 22:45:24 +00:00
pub fn spawn<F, R>(&self, f: F)
where F: FnOnce(&Handle) -> R + Send + 'static,
R: IntoFuture<Item=(), Error=()>,
R::Future: 'static
{
self.0.handle.spawn(f)
}
2017-01-29 16:25:09 +00:00
#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
2017-01-29 17:54:32 +00:00
fn dispatch(&self, cmd: u8, data: EasyBuf) {
match cmd {
2017-01-29 17:54:32 +00:00
0x4 => self.send_packet(0x49, data.as_ref().to_owned()),
0x4a => (),
0x1b => {
2017-01-29 17:54:32 +00:00
let country = String::from_utf8(data.as_ref().to_owned()).unwrap();
self.0.data.write().unwrap().country = country;
2016-01-02 15:19:39 +00:00
}
2017-01-18 21:33:52 +00:00
2017-01-19 22:45:24 +00:00
0x9 | 0xa => self.channel().dispatch(cmd, data),
2017-01-18 21:33:52 +00:00
0xd | 0xe => self.audio_key().dispatch(cmd, data),
0xb2...0xb6 => self.mercury().dispatch(cmd, data),
2016-01-02 15:19:39 +00:00
_ => (),
}
}
2017-01-18 05:57:36 +00:00
pub fn send_packet(&self, cmd: u8, data: Vec<u8>) {
2017-01-20 02:37:02 +00:00
self.0.tx_connection.send((cmd, data)).unwrap();
}
pub fn cache(&self) -> Option<&Arc<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
}
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
}