mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Create event loop in main
This commit is contained in:
parent
2a0ccc0d1d
commit
d27063d5da
9 changed files with 129 additions and 132 deletions
|
@ -5,10 +5,10 @@ use alsa::{PCM, Stream, Mode, Format, Access};
|
||||||
pub struct AlsaSink(Option<PCM>, String);
|
pub struct AlsaSink(Option<PCM>, String);
|
||||||
|
|
||||||
impl Open for AlsaSink {
|
impl Open for AlsaSink {
|
||||||
fn open(device: Option<&str>) -> AlsaSink {
|
fn open(device: Option<String>) -> AlsaSink {
|
||||||
info!("Using alsa sink");
|
info!("Using alsa sink");
|
||||||
|
|
||||||
let name = device.unwrap_or("default").to_string();
|
let name = device.unwrap_or("default".to_string());
|
||||||
|
|
||||||
AlsaSink(None, name)
|
AlsaSink(None, name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
pub trait Open {
|
pub trait Open {
|
||||||
fn open(Option<&str>) -> Self;
|
fn open(Option<String>) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Sink {
|
pub trait Sink {
|
||||||
|
@ -49,8 +49,7 @@ macro_rules! _declare_backends {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
fn mk_sink<S: Sink + Open + 'static>(device: Option<String>) -> Box<Sink> {
|
||||||
fn mk_sink<S: Sink + Open + 'static>(device: Option<&str>) -> Box<Sink> {
|
|
||||||
Box::new(S::open(device))
|
Box::new(S::open(device))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +74,7 @@ use self::pipe::StdoutSink;
|
||||||
declare_backends! {
|
declare_backends! {
|
||||||
pub const BACKENDS : &'static [
|
pub const BACKENDS : &'static [
|
||||||
(&'static str,
|
(&'static str,
|
||||||
&'static (Fn(Option<&str>) -> Box<Sink> + Sync + Send + 'static))
|
&'static (Fn(Option<String>) -> Box<Sink> + Sync + Send + 'static))
|
||||||
] = &[
|
] = &[
|
||||||
#[cfg(feature = "alsa-backend")]
|
#[cfg(feature = "alsa-backend")]
|
||||||
("alsa", &mk_sink::<AlsaSink>),
|
("alsa", &mk_sink::<AlsaSink>),
|
||||||
|
@ -87,7 +86,7 @@ declare_backends! {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find<T: AsRef<str>>(name: Option<T>) -> Option<&'static (Fn(Option<&str>) -> Box<Sink> + Send + Sync)> {
|
pub fn find<T: AsRef<str>>(name: Option<T>) -> Option<&'static (Fn(Option<String>) -> Box<Sink> + Send + Sync)> {
|
||||||
if let Some(name) = name.as_ref().map(AsRef::as_ref) {
|
if let Some(name) = name.as_ref().map(AsRef::as_ref) {
|
||||||
BACKENDS.iter().find(|backend| name == backend.0).map(|backend| backend.1)
|
BACKENDS.iter().find(|backend| name == backend.0).map(|backend| backend.1)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::slice;
|
||||||
pub struct StdoutSink(Box<Write>);
|
pub struct StdoutSink(Box<Write>);
|
||||||
|
|
||||||
impl Open for StdoutSink {
|
impl Open for StdoutSink {
|
||||||
fn open(path: Option<&str>) -> StdoutSink {
|
fn open(path: Option<String>) -> StdoutSink {
|
||||||
if let Some(path) = path {
|
if let Some(path) = path {
|
||||||
let file = OpenOptions::new().write(true).open(path).unwrap();
|
let file = OpenOptions::new().write(true).open(path).unwrap();
|
||||||
StdoutSink(Box::new(file))
|
StdoutSink(Box::new(file))
|
||||||
|
|
|
@ -39,13 +39,13 @@ fn find_output(device: &str) -> Option<DeviceIndex> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a> Open for PortAudioSink<'a> {
|
impl <'a> Open for PortAudioSink<'a> {
|
||||||
fn open(device: Option<&str>) -> PortAudioSink<'a> {
|
fn open(device: Option<String>) -> PortAudioSink<'a> {
|
||||||
|
|
||||||
debug!("Using PortAudio sink");
|
debug!("Using PortAudio sink");
|
||||||
|
|
||||||
portaudio::initialize().unwrap();
|
portaudio::initialize().unwrap();
|
||||||
|
|
||||||
let device_idx = match device {
|
let device_idx = match device.as_ref().map(AsRef::as_ref) {
|
||||||
Some("?") => {
|
Some("?") => {
|
||||||
list_outputs();
|
list_outputs();
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::ffi::CString;
|
||||||
pub struct PulseAudioSink(*mut pa_simple);
|
pub struct PulseAudioSink(*mut pa_simple);
|
||||||
|
|
||||||
impl Open for PulseAudioSink {
|
impl Open for PulseAudioSink {
|
||||||
fn open(device: Option<&str>) -> PulseAudioSink {
|
fn open(device: Option<String>) -> PulseAudioSink {
|
||||||
debug!("Using PulseAudio sink");
|
debug!("Using PulseAudio sink");
|
||||||
|
|
||||||
if device.is_some() {
|
if device.is_some() {
|
||||||
|
|
|
@ -6,8 +6,8 @@ use std::thread;
|
||||||
use tokio_core::reactor::Core;
|
use tokio_core::reactor::Core;
|
||||||
use tokio_core::reactor::Handle;
|
use tokio_core::reactor::Handle;
|
||||||
|
|
||||||
pub struct SinkAdaptor<T>(Option<mpsc::Sender<T>>);
|
pub struct SinkAdaptor<T>(pub Option<mpsc::Sender<T>>);
|
||||||
pub struct StreamAdaptor<T, E>(Option<mpsc::Receiver<Result<T, E>>>);
|
pub struct StreamAdaptor<T, E>(pub Option<mpsc::Receiver<Result<T, E>>>);
|
||||||
|
|
||||||
impl <T> SinkAdaptor<T> {
|
impl <T> SinkAdaptor<T> {
|
||||||
pub fn send(&mut self, item: T) {
|
pub fn send(&mut self, item: T) {
|
||||||
|
@ -30,59 +30,33 @@ impl <T, E> StreamAdaptor<T, E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn adapt_sink<S>(sink: S, rx: mpsc::Receiver<S::SinkItem>) -> BoxFuture<(), ()>
|
pub fn adapt<S, E>(transport: S) -> (SinkAdaptor<S::SinkItem>,
|
||||||
where S: Sink + Send + 'static,
|
StreamAdaptor<S::Item, E>,
|
||||||
S::SinkItem: Send,
|
BoxFuture<(), E>)
|
||||||
S::SinkError: Send,
|
where S: Sink<SinkError=E> + Stream<Error=E> + Send + 'static,
|
||||||
{
|
|
||||||
rx.map_err(|_| -> S::SinkError { panic!("") })
|
|
||||||
.forward(sink)
|
|
||||||
.map(|_| ()).map_err(|_| ())
|
|
||||||
.boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn adapt_stream<S>(stream: S, tx: mpsc::Sender<Result<S::Item, S::Error>>) -> BoxFuture<(), ()>
|
|
||||||
where S: Stream + Send + 'static,
|
|
||||||
S::Item: Send,
|
|
||||||
S::Error: Send,
|
|
||||||
{
|
|
||||||
stream.then(ok::<_, mpsc::SendError<_>>)
|
|
||||||
.forward(tx)
|
|
||||||
.map(|_| ()).map_err(|_| ())
|
|
||||||
.boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn adapt<F, U, S>(f: F) -> (SinkAdaptor<S::SinkItem>, StreamAdaptor<S::Item, S::Error>)
|
|
||||||
where F: FnOnce(Handle) -> U + Send + 'static,
|
|
||||||
U: IntoFuture<Item=S>,
|
|
||||||
S: Sink + Stream + Send + 'static,
|
|
||||||
S::Item: Send + 'static,
|
S::Item: Send + 'static,
|
||||||
S::Error: Send + 'static,
|
|
||||||
S::SinkItem: Send + 'static,
|
S::SinkItem: Send + 'static,
|
||||||
S::SinkError: Send + 'static,
|
E: Send + 'static,
|
||||||
{
|
{
|
||||||
|
|
||||||
let (receiver_tx, receiver_rx) = mpsc::channel(0);
|
let (receiver_tx, receiver_rx) = mpsc::channel(0);
|
||||||
let (sender_tx, sender_rx) = mpsc::channel(0);
|
let (sender_tx, sender_rx) = mpsc::channel(0);
|
||||||
|
|
||||||
|
let (sink, stream) = transport.split();
|
||||||
|
|
||||||
thread::spawn(move || {
|
let receiver_task = stream
|
||||||
let mut core = Core::new().unwrap();
|
.then(ok::<_, mpsc::SendError<_>>)
|
||||||
let handle = core.handle();
|
.forward(receiver_tx).map(|_| ())
|
||||||
let task =
|
.map_err(|e| -> E { panic!(e) });
|
||||||
f(handle).into_future()
|
|
||||||
.map(|connection| connection.split())
|
|
||||||
.map_err(|_| ())
|
|
||||||
.and_then(|(sink, stream)| {
|
|
||||||
(adapt_sink(sink, sender_rx),
|
|
||||||
adapt_stream(stream, receiver_tx))
|
|
||||||
});
|
|
||||||
|
|
||||||
core.run(task).unwrap();
|
let sender_task = sender_rx
|
||||||
});
|
.map_err(|e| -> E { panic!(e) })
|
||||||
|
.forward(sink).map(|_| ());
|
||||||
|
|
||||||
|
let task = (receiver_task, sender_task).into_future()
|
||||||
|
.map(|((), ())| ()).boxed();
|
||||||
|
|
||||||
(SinkAdaptor(Some(sender_tx)),
|
(SinkAdaptor(Some(sender_tx)),
|
||||||
StreamAdaptor(Some(receiver_rx)))
|
StreamAdaptor(Some(receiver_rx)), task)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn adapt_future<F, U>(f: F) -> oneshot::Receiver<Result<U::Item, U::Error>>
|
pub fn adapt_future<F, U>(f: F) -> oneshot::Receiver<Result<U::Item, U::Error>>
|
||||||
|
|
67
src/main.rs
67
src/main.rs
|
@ -3,6 +3,8 @@ extern crate getopts;
|
||||||
extern crate librespot;
|
extern crate librespot;
|
||||||
extern crate ctrlc;
|
extern crate ctrlc;
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
|
extern crate futures;
|
||||||
|
extern crate tokio_core;
|
||||||
|
|
||||||
use env_logger::LogBuilder;
|
use env_logger::LogBuilder;
|
||||||
use std::io::{stderr, Write};
|
use std::io::{stderr, Write};
|
||||||
|
@ -11,10 +13,12 @@ use std::thread;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use futures::Future;
|
||||||
|
use tokio_core::reactor::Core;
|
||||||
|
|
||||||
use librespot::spirc::SpircManager;
|
use librespot::spirc::SpircManager;
|
||||||
use librespot::authentication::get_credentials;
|
use librespot::authentication::{get_credentials, Credentials};
|
||||||
use librespot::audio_backend::{self, BACKENDS};
|
use librespot::audio_backend::{self, Sink, BACKENDS};
|
||||||
use librespot::cache::{Cache, DefaultCache, NoCache};
|
use librespot::cache::{Cache, DefaultCache, NoCache};
|
||||||
use librespot::player::Player;
|
use librespot::player::Player;
|
||||||
use librespot::session::{Bitrate, Config, Session};
|
use librespot::session::{Bitrate, Config, Session};
|
||||||
|
@ -59,7 +63,15 @@ fn list_backends() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(args: &[String]) -> (Session, Player) {
|
struct Setup {
|
||||||
|
backend: &'static (Fn(Option<String>) -> Box<Sink> + Send + Sync),
|
||||||
|
cache: Box<Cache + Send + Sync>,
|
||||||
|
config: Config,
|
||||||
|
credentials: Credentials,
|
||||||
|
device: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(args: &[String]) -> Setup {
|
||||||
let mut opts = getopts::Options::new();
|
let mut opts = getopts::Options::new();
|
||||||
opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE")
|
opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE")
|
||||||
.reqopt("n", "name", "Device name", "NAME")
|
.reqopt("n", "name", "Device name", "NAME")
|
||||||
|
@ -101,8 +113,8 @@ fn setup(args: &[String]) -> (Session, Player) {
|
||||||
.map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
|
.map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
|
||||||
.unwrap_or(Bitrate::Bitrate160);
|
.unwrap_or(Bitrate::Bitrate160);
|
||||||
|
|
||||||
let device_name = matches.opt_str("name").unwrap();
|
let name = matches.opt_str("name").unwrap();
|
||||||
let device_id = librespot::session::device_id(&device_name);
|
let device_id = librespot::session::device_id(&name);
|
||||||
|
|
||||||
let cache = matches.opt_str("c").map(|cache_location| {
|
let cache = matches.opt_str("c").map(|cache_location| {
|
||||||
Box::new(DefaultCache::new(PathBuf::from(cache_location)).unwrap())
|
Box::new(DefaultCache::new(PathBuf::from(cache_location)).unwrap())
|
||||||
|
@ -111,46 +123,59 @@ fn setup(args: &[String]) -> (Session, Player) {
|
||||||
|
|
||||||
let cached_credentials = cache.get_credentials();
|
let cached_credentials = cache.get_credentials();
|
||||||
|
|
||||||
let credentials = get_credentials(&device_name, &device_id,
|
let credentials = get_credentials(&name, &device_id,
|
||||||
matches.opt_str("username"),
|
matches.opt_str("username"),
|
||||||
matches.opt_str("password"),
|
matches.opt_str("password"),
|
||||||
cached_credentials);
|
cached_credentials);
|
||||||
|
|
||||||
let config = Config {
|
let config = Config {
|
||||||
user_agent: version::version_string(),
|
user_agent: version::version_string(),
|
||||||
device_name: device_name,
|
name: name,
|
||||||
device_id: device_id,
|
device_id: device_id,
|
||||||
bitrate: bitrate,
|
bitrate: bitrate,
|
||||||
onstart: matches.opt_str("onstart"),
|
onstart: matches.opt_str("onstart"),
|
||||||
onstop: matches.opt_str("onstop"),
|
onstop: matches.opt_str("onstop"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let session = Session::new(config, cache);
|
let device = matches.opt_str("device");
|
||||||
|
|
||||||
session.login(credentials).unwrap();
|
Setup {
|
||||||
|
backend: backend,
|
||||||
let device_name = matches.opt_str("device");
|
cache: cache,
|
||||||
let player = Player::new(session.clone(), move || {
|
config: config,
|
||||||
(backend)(device_name.as_ref().map(AsRef::as_ref))
|
credentials: credentials,
|
||||||
});
|
device: device,
|
||||||
|
}
|
||||||
(session, player)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let mut core = Core::new().unwrap();
|
||||||
|
let handle = core.handle();
|
||||||
|
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
let (session, player) = setup(&args);
|
|
||||||
|
let Setup { backend, cache, config, credentials, device } = setup(&args);
|
||||||
|
|
||||||
|
let connection = Session::connect(config, credentials, cache, handle);
|
||||||
|
|
||||||
|
let task = connection.and_then(move |(session, task)| {
|
||||||
|
let player = Player::new(session.clone(), move || {
|
||||||
|
(backend)(device)
|
||||||
|
});
|
||||||
|
|
||||||
let spirc = SpircManager::new(session.clone(), player);
|
let spirc = SpircManager::new(session.clone(), player);
|
||||||
let spirc_signal = spirc.clone();
|
let spirc_signal = spirc.clone();
|
||||||
thread::spawn(move || spirc.run());
|
|
||||||
|
|
||||||
ctrlc::set_handler(move || {
|
ctrlc::set_handler(move || {
|
||||||
spirc_signal.send_goodbye();
|
spirc_signal.send_goodbye();
|
||||||
exit(0);
|
exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
loop {
|
thread::spawn(move || spirc.run());
|
||||||
session.poll();
|
thread::spawn(move || loop { session.poll() });
|
||||||
}
|
|
||||||
|
task
|
||||||
|
});
|
||||||
|
|
||||||
|
core.run(task).unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use std::sync::{Mutex, RwLock, Arc, mpsc};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use futures::Future as Future_;
|
use futures::Future as Future_;
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use futures::sync::oneshot;
|
use tokio_core::reactor::Handle;
|
||||||
|
|
||||||
use album_cover::AlbumCover;
|
use album_cover::AlbumCover;
|
||||||
use apresolve::apresolve_or_fallback;
|
use apresolve::apresolve_or_fallback;
|
||||||
|
@ -45,7 +45,7 @@ impl FromStr for Bitrate {
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub user_agent: String,
|
pub user_agent: String,
|
||||||
pub device_name: String,
|
pub name: String,
|
||||||
pub device_id: String,
|
pub device_id: String,
|
||||||
pub bitrate: Bitrate,
|
pub bitrate: Bitrate,
|
||||||
pub onstart: Option<String>,
|
pub onstart: Option<String>,
|
||||||
|
@ -66,45 +66,24 @@ pub struct SessionInternal {
|
||||||
metadata: Mutex<MetadataManager>,
|
metadata: Mutex<MetadataManager>,
|
||||||
stream: Mutex<StreamManager>,
|
stream: Mutex<StreamManager>,
|
||||||
audio_key: Mutex<AudioKeyManager>,
|
audio_key: Mutex<AudioKeyManager>,
|
||||||
rx_connection: Mutex<Option<adaptor::StreamAdaptor<(u8, Vec<u8>), io::Error>>>,
|
rx_connection: Mutex<adaptor::StreamAdaptor<(u8, Vec<u8>), io::Error>>,
|
||||||
tx_connection: Mutex<Option<adaptor::SinkAdaptor<(u8, Vec<u8>)>>>,
|
tx_connection: Mutex<adaptor::SinkAdaptor<(u8, Vec<u8>)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Session(pub Arc<SessionInternal>);
|
pub struct Session(pub Arc<SessionInternal>);
|
||||||
|
|
||||||
pub fn device_id(device_name: &str) -> String {
|
pub fn device_id(name: &str) -> String {
|
||||||
let mut h = Sha1::new();
|
let mut h = Sha1::new();
|
||||||
h.input_str(&device_name);
|
h.input_str(&name);
|
||||||
h.result_str()
|
h.result_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
pub fn new(config: Config, cache: Box<Cache + Send + Sync>) -> Session {
|
pub fn connect(config: Config, credentials: Credentials,
|
||||||
Session(Arc::new(SessionInternal {
|
cache: Box<Cache + Send + Sync>, handle: Handle)
|
||||||
config: config,
|
-> Box<Future_<Item=(Session, Box<Future_<Item=(), Error=io::Error>>), Error=io::Error>>
|
||||||
data: RwLock::new(SessionData {
|
{
|
||||||
country: String::new(),
|
|
||||||
canonical_username: String::new(),
|
|
||||||
}),
|
|
||||||
|
|
||||||
rx_connection: Mutex::new(None),
|
|
||||||
tx_connection: Mutex::new(None),
|
|
||||||
|
|
||||||
cache: cache,
|
|
||||||
mercury: Mutex::new(MercuryManager::new()),
|
|
||||||
metadata: Mutex::new(MetadataManager::new()),
|
|
||||||
stream: Mutex::new(StreamManager::new()),
|
|
||||||
audio_key: Mutex::new(AudioKeyManager::new()),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn login(&self, credentials: Credentials) -> Result<Credentials, ()> {
|
|
||||||
let device_id = self.device_id().to_owned();
|
|
||||||
|
|
||||||
let (creds_tx, creds_rx) = oneshot::channel();
|
|
||||||
|
|
||||||
let (tx, rx) = adaptor::adapt(move |handle| {
|
|
||||||
let access_point = apresolve_or_fallback::<io::Error>(&handle);
|
let access_point = apresolve_or_fallback::<io::Error>(&handle);
|
||||||
|
|
||||||
let connection = access_point.and_then(move |addr| {
|
let connection = access_point.and_then(move |addr| {
|
||||||
|
@ -112,29 +91,49 @@ impl Session {
|
||||||
connection::connect::<&str>(&addr, &handle)
|
connection::connect::<&str>(&addr, &handle)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let device_id = config.device_id.clone();
|
||||||
let authentication = connection.and_then(move |connection| {
|
let authentication = connection.and_then(move |connection| {
|
||||||
connection::authenticate(connection, credentials, device_id)
|
connection::authenticate(connection, credentials, device_id)
|
||||||
});
|
});
|
||||||
|
|
||||||
authentication.map(|(transport, creds)| {
|
let result = authentication.map(move |(transport, reusable_credentials)| {
|
||||||
creds_tx.complete(creds);
|
info!("Authenticated !");
|
||||||
transport.map(|(cmd, data)| (cmd, data.as_ref().to_owned()))
|
cache.put_credentials(&reusable_credentials);
|
||||||
})
|
|
||||||
|
let (session, task) = Session::create(transport, config, cache, reusable_credentials.username.clone());
|
||||||
|
(session, task)
|
||||||
});
|
});
|
||||||
|
|
||||||
let reusable_credentials: Credentials = creds_rx.wait().unwrap();
|
Box::new(result)
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
info!("Authenticated !");
|
|
||||||
|
|
||||||
self.0.cache.put_credentials(&reusable_credentials);
|
|
||||||
|
|
||||||
Ok(reusable_credentials)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create(transport: connection::Transport, config: Config,
|
||||||
|
cache: Box<Cache + Send + Sync>, username: String) -> (Session, Box<Future_<Item=(), Error=io::Error>>)
|
||||||
|
{
|
||||||
|
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 {
|
||||||
|
config: config,
|
||||||
|
data: RwLock::new(SessionData {
|
||||||
|
country: String::new(),
|
||||||
|
canonical_username: username,
|
||||||
|
}),
|
||||||
|
|
||||||
|
rx_connection: Mutex::new(rx),
|
||||||
|
tx_connection: Mutex::new(tx),
|
||||||
|
|
||||||
|
cache: cache,
|
||||||
|
mercury: Mutex::new(MercuryManager::new()),
|
||||||
|
metadata: Mutex::new(MetadataManager::new()),
|
||||||
|
stream: Mutex::new(StreamManager::new()),
|
||||||
|
audio_key: Mutex::new(AudioKeyManager::new()),
|
||||||
|
}));
|
||||||
|
|
||||||
|
(session, task)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn poll(&self) {
|
pub fn poll(&self) {
|
||||||
let (cmd, data) = self.recv();
|
let (cmd, data) = self.recv();
|
||||||
|
|
||||||
|
@ -152,11 +151,11 @@ impl Session {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recv(&self) -> (u8, Vec<u8>) {
|
pub fn recv(&self) -> (u8, Vec<u8>) {
|
||||||
self.0.rx_connection.lock().unwrap().as_mut().unwrap().recv().unwrap()
|
self.0.rx_connection.lock().unwrap().recv().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_packet(&self, cmd: u8, data: Vec<u8>) {
|
pub fn send_packet(&self, cmd: u8, data: Vec<u8>) {
|
||||||
self.0.tx_connection.lock().unwrap().as_mut().unwrap().send((cmd, data))
|
self.0.tx_connection.lock().unwrap().send((cmd, data))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn audio_key(&self, track: SpotifyId, file_id: FileId) -> Future<AudioKey, AudioKeyError> {
|
pub fn audio_key(&self, track: SpotifyId, file_id: FileId) -> Future<AudioKey, AudioKeyError> {
|
||||||
|
|
|
@ -46,7 +46,7 @@ struct SpircInternal {
|
||||||
impl SpircManager {
|
impl SpircManager {
|
||||||
pub fn new(session: Session, player: Player) -> SpircManager {
|
pub fn new(session: Session, player: Player) -> SpircManager {
|
||||||
let ident = session.device_id().to_owned();
|
let ident = session.device_id().to_owned();
|
||||||
let name = session.config().device_name.clone();
|
let name = session.config().name.clone();
|
||||||
|
|
||||||
SpircManager(Arc::new(Mutex::new(SpircInternal {
|
SpircManager(Arc::new(Mutex::new(SpircInternal {
|
||||||
player: player,
|
player: player,
|
||||||
|
|
Loading…
Reference in a new issue