浏览代码

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.
Paul Lietar 10 年之前
父节点
当前提交
5db141066a
共有 4 个文件被更改,包括 59 次插入159 次删除
  1. 1 1
      src/lib.rs
  2. 51 152
      src/metadata.rs
  3. 6 5
      src/player.rs
  4. 1 1
      src/spirc.rs

+ 1 - 1
src/lib.rs

@@ -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;

+ 51 - 152
src/metadata.rs

@@ -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))
+        })
     }
 }
 

+ 6 - 5
src/player.rs

@@ -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(

+ 1 - 1
src/spirc.rs

@@ -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 {