mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
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:
parent
009d4e9019
commit
5db141066a
4 changed files with 59 additions and 159 deletions
|
@ -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;
|
||||
|
|
203
src/metadata.rs
203
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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue