Skip to content

Commit

Permalink
Use eventual’s futures in metadata instead of rolling our own.
Browse files Browse the repository at this point in the history
For now the object cache has been removed. It may be added back later.
  • Loading branch information
plietar committed Dec 28, 2015
1 parent 009d4e9 commit 5db1410
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 159 deletions.
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
203 changes: 51 additions & 152 deletions src/metadata.rs
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -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()))
Expand All @@ -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))
})
}
}

11 changes: 6 additions & 5 deletions src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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();
Expand All @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion src/spirc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 5db1410

Please sign in to comment.