Use eventual’s futures in metadata instead of rolling our own.

For now the object cache has been removed. It may be added back later.
This commit is contained in:
Paul Lietar 2015-12-28 16:53:54 +01:00
parent 009d4e9019
commit 5db141066a
4 changed files with 59 additions and 159 deletions

View file

@ -1,6 +1,6 @@
#![crate_name = "librespot"]
#![feature(plugin,zero_one,iter_arith,slice_bytes,mpsc_select,clone_from_slice)]
#![feature(plugin,zero_one,iter_arith,mpsc_select,clone_from_slice)]
#![plugin(protobuf_macros)]
#[macro_use] extern crate lazy_static;

View file

@ -1,34 +1,55 @@
use eventual::Async;
use eventual::{Async, Future};
use protobuf;
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt;
use std::sync::{Arc, Condvar, Mutex, MutexGuard, Weak};
use std::thread;
use librespot_protocol as protocol;
use mercury::{MercuryRequest, MercuryMethod};
use util::{SpotifyId, FileId};
use session::Session;
pub trait MetadataTrait : Send + Sized + Any + 'static {
pub trait MetadataTrait : Send + 'static {
type Message: protobuf::MessageStatic;
fn from_msg(msg: &Self::Message) -> Self;
fn base_url() -> &'static str;
fn request(r: MetadataRef<Self>) -> MetadataRequest;
fn parse(msg: &Self::Message) -> Self;
}
#[derive(Debug)]
pub struct Track {
pub id: SpotifyId,
pub name: String,
pub album: SpotifyId,
pub files: Vec<FileId>
pub files: Vec<FileId>,
}
#[derive(Debug)]
pub struct Album {
pub id: SpotifyId,
pub name: String,
pub artists: Vec<SpotifyId>,
pub covers: Vec<FileId>
}
#[derive(Debug)]
pub struct Artist {
pub id: SpotifyId,
pub name: String,
}
pub type MetadataRef<T> = Future<T, ()>;
pub type TrackRef = MetadataRef<Track>;
pub type AlbumRef = MetadataRef<Album>;
pub type ArtistRef = MetadataRef<Artist>;
impl MetadataTrait for Track {
type Message = protocol::metadata::Track;
fn from_msg(msg: &Self::Message) -> Self {
fn base_url() -> &'static str {
"hm://metadata/3/track"
}
fn parse(msg: &Self::Message) -> Self {
Track {
id: SpotifyId::from_raw(msg.get_gid()),
name: msg.get_name().to_owned(),
album: SpotifyId::from_raw(msg.get_album().get_gid()),
files: msg.get_file().iter()
@ -40,25 +61,18 @@ impl MetadataTrait for Track {
.collect(),
}
}
fn base_url() -> &'static str {
"hm://metadata/3/track"
}
fn request(r: MetadataRef<Self>) -> MetadataRequest {
MetadataRequest::Track(r)
}
}
#[derive(Debug)]
pub struct Album {
pub name: String,
pub artists: Vec<SpotifyId>,
pub covers: Vec<FileId>
}
impl MetadataTrait for Album {
type Message = protocol::metadata::Album;
fn from_msg(msg: &Self::Message) -> Self {
fn base_url() -> &'static str {
"hm://metadata/3/album"
}
fn parse(msg: &Self::Message) -> Self {
Album {
id: SpotifyId::from_raw(msg.get_gid()),
name: msg.get_name().to_owned(),
artists: msg.get_artist().iter()
.map(|a| SpotifyId::from_raw(a.get_gid()))
@ -72,160 +86,45 @@ impl MetadataTrait for Album {
.collect(),
}
}
fn base_url() -> &'static str {
"hm://metadata/3/album"
}
fn request(r: MetadataRef<Self>) -> MetadataRequest {
MetadataRequest::Album(r)
}
}
#[derive(Debug)]
pub struct Artist {
pub name: String,
}
impl MetadataTrait for Artist {
type Message = protocol::metadata::Artist;
fn from_msg(msg: &Self::Message) -> Self {
Artist {
name: msg.get_name().to_owned(),
}
}
fn base_url() -> &'static str {
"hm://metadata/3/artist"
}
fn request(r: MetadataRef<Self>) -> MetadataRequest {
MetadataRequest::Artist(r)
}
}
#[derive(Debug)]
pub enum MetadataState<T> {
Loading,
Loaded(T),
Error,
}
pub struct Metadata<T: MetadataTrait> {
id: SpotifyId,
state: Mutex<MetadataState<T>>,
cond: Condvar
}
pub type MetadataRef<T> = Arc<Metadata<T>>;
pub type TrackRef = MetadataRef<Track>;
pub type AlbumRef = MetadataRef<Album>;
pub type ArtistRef = MetadataRef<Artist>;
impl <T: MetadataTrait> Metadata<T> {
pub fn id(&self) -> SpotifyId {
self.id
}
pub fn lock(&self) -> MutexGuard<MetadataState<T>> {
self.state.lock().unwrap()
}
pub fn wait(&self) -> MutexGuard<MetadataState<T>> {
let mut handle = self.lock();
while handle.is_loading() {
handle = self.cond.wait(handle).unwrap();
}
handle
}
pub fn set(&self, state: MetadataState<T>) {
let mut handle = self.lock();
*handle = state;
self.cond.notify_all();
}
}
impl <T: MetadataTrait + fmt::Debug> fmt::Debug for Metadata<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "Metadata<>({:?}, {:?})", self.id, *self.lock())
}
}
impl <T: MetadataTrait> MetadataState<T> {
pub fn is_loading(&self) -> bool {
match *self {
MetadataState::Loading => true,
_ => false
}
}
pub fn is_loaded(&self) -> bool {
match *self {
MetadataState::Loaded(_) => true,
_ => false
}
}
pub fn unwrap(&self) -> &T {
match *self {
MetadataState::Loaded(ref data) => data,
_ => panic!("Not loaded")
fn parse(msg: &Self::Message) -> Self {
Artist {
id: SpotifyId::from_raw(msg.get_gid()),
name: msg.get_name().to_owned(),
}
}
}
#[derive(Debug)]
pub enum MetadataRequest {
Artist(ArtistRef),
Album(AlbumRef),
Track(TrackRef)
}
pub struct MetadataManager {
cache: HashMap<(SpotifyId, TypeId), Box<Any + Send + 'static>>
}
pub struct MetadataManager;
impl MetadataManager {
pub fn new() -> MetadataManager {
MetadataManager {
cache: HashMap::new()
}
MetadataManager
}
pub fn get<T: MetadataTrait>(&mut self, session: &Session, id: SpotifyId)
-> MetadataRef<T> {
let key = (id, TypeId::of::<T>());
self.cache.get(&key)
.and_then(|x| x.downcast_ref::<Weak<Metadata<T>>>())
.and_then(|x| x.upgrade())
.unwrap_or_else(|| {
let x : MetadataRef<T> = Arc::new(Metadata{
id: id,
state: Mutex::new(MetadataState::Loading),
cond: Condvar::new()
});
self.cache.insert(key, Box::new(Arc::downgrade(&x)));
self.load(session, x.clone());
x
})
}
fn load<T: MetadataTrait> (&self, session: &Session, object: MetadataRef<T>) {
let rx = session.mercury(MercuryRequest {
session.mercury(MercuryRequest {
method: MercuryMethod::GET,
uri: format!("{}/{}", T::base_url(), object.id.to_base16()),
uri: format!("{}/{}", T::base_url(), id.to_base16()),
content_type: None,
payload: Vec::new()
});
thread::spawn(move || {
let response = rx.await().unwrap();
}).and_then(|response| {
let msg : T::Message = protobuf::parse_from_bytes(
response.payload.first().unwrap()).unwrap();
object.set(MetadataState::Loaded(T::from_msg(&msg)));
});
Ok(T::parse(&msg))
})
}
}

View file

@ -4,7 +4,7 @@ use std::sync::{mpsc, Mutex, Arc, Condvar, MutexGuard};
use std::thread;
use vorbis;
use metadata::TrackRef;
use metadata::Track;
use session::Session;
use audio_decrypt::AudioDecrypt;
use util::{self, SpotifyId, Subfile};
@ -86,7 +86,7 @@ impl PlayerInternal {
loop {
match self.commands.try_recv() {
Ok(PlayerCommand::Load(id, play, position)) => {
Ok(PlayerCommand::Load(track_id, play, position)) => {
self.update(|state| {
if state.status == PlayStatus::kPlayStatusPlay {
stream.stop().unwrap();
@ -98,10 +98,11 @@ impl PlayerInternal {
return true;
});
let track : TrackRef = self.session.metadata(id);
let file_id = *track.wait().unwrap().files.first().unwrap();
let track = self.session.metadata::<Track>(track_id).await().unwrap();
let key = self.session.audio_key(track.id(), file_id).await().unwrap();
let file_id = track.files[0];
let key = self.session.audio_key(track.id, file_id).await().unwrap();
decoder = Some(
vorbis::Decoder::new(
Subfile::new(

View file

@ -91,7 +91,7 @@ impl <'s, D: SpircDelegate> SpircManager<'s, D> {
}
pub fn run(&mut self) {
let rx = self.session.mercury_sub(format!("hm://remote/user/{}/", self.username));
let rx = self.session.mercury_sub(format!("hm://remote/3/user/{}/", self.username));
let updates = self.delegate.updates();
loop {