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"]
|
#![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)]
|
#![plugin(protobuf_macros)]
|
||||||
#[macro_use] extern crate lazy_static;
|
#[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 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 librespot_protocol as protocol;
|
||||||
use mercury::{MercuryRequest, MercuryMethod};
|
use mercury::{MercuryRequest, MercuryMethod};
|
||||||
use util::{SpotifyId, FileId};
|
use util::{SpotifyId, FileId};
|
||||||
use session::Session;
|
use session::Session;
|
||||||
|
|
||||||
pub trait MetadataTrait : Send + Sized + Any + 'static {
|
pub trait MetadataTrait : Send + 'static {
|
||||||
type Message: protobuf::MessageStatic;
|
type Message: protobuf::MessageStatic;
|
||||||
fn from_msg(msg: &Self::Message) -> Self;
|
|
||||||
fn base_url() -> &'static str;
|
fn base_url() -> &'static str;
|
||||||
fn request(r: MetadataRef<Self>) -> MetadataRequest;
|
fn parse(msg: &Self::Message) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Track {
|
pub struct Track {
|
||||||
|
pub id: SpotifyId,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub album: SpotifyId,
|
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 {
|
impl MetadataTrait for Track {
|
||||||
type Message = protocol::metadata::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 {
|
Track {
|
||||||
|
id: SpotifyId::from_raw(msg.get_gid()),
|
||||||
name: msg.get_name().to_owned(),
|
name: msg.get_name().to_owned(),
|
||||||
album: SpotifyId::from_raw(msg.get_album().get_gid()),
|
album: SpotifyId::from_raw(msg.get_album().get_gid()),
|
||||||
files: msg.get_file().iter()
|
files: msg.get_file().iter()
|
||||||
|
@ -40,25 +61,18 @@ impl MetadataTrait for Track {
|
||||||
.collect(),
|
.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 {
|
impl MetadataTrait for Album {
|
||||||
type Message = protocol::metadata::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 {
|
Album {
|
||||||
|
id: SpotifyId::from_raw(msg.get_gid()),
|
||||||
name: msg.get_name().to_owned(),
|
name: msg.get_name().to_owned(),
|
||||||
artists: msg.get_artist().iter()
|
artists: msg.get_artist().iter()
|
||||||
.map(|a| SpotifyId::from_raw(a.get_gid()))
|
.map(|a| SpotifyId::from_raw(a.get_gid()))
|
||||||
|
@ -72,160 +86,45 @@ impl MetadataTrait for Album {
|
||||||
.collect(),
|
.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 {
|
impl MetadataTrait for Artist {
|
||||||
type Message = protocol::metadata::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 {
|
fn base_url() -> &'static str {
|
||||||
"hm://metadata/3/artist"
|
"hm://metadata/3/artist"
|
||||||
}
|
}
|
||||||
fn request(r: MetadataRef<Self>) -> MetadataRequest {
|
|
||||||
MetadataRequest::Artist(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
fn parse(msg: &Self::Message) -> Self {
|
||||||
pub enum MetadataState<T> {
|
Artist {
|
||||||
Loading,
|
id: SpotifyId::from_raw(msg.get_gid()),
|
||||||
Loaded(T),
|
name: msg.get_name().to_owned(),
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub struct MetadataManager;
|
||||||
pub enum MetadataRequest {
|
|
||||||
Artist(ArtistRef),
|
|
||||||
Album(AlbumRef),
|
|
||||||
Track(TrackRef)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MetadataManager {
|
|
||||||
cache: HashMap<(SpotifyId, TypeId), Box<Any + Send + 'static>>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MetadataManager {
|
impl MetadataManager {
|
||||||
pub fn new() -> MetadataManager {
|
pub fn new() -> MetadataManager {
|
||||||
MetadataManager {
|
MetadataManager
|
||||||
cache: HashMap::new()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get<T: MetadataTrait>(&mut self, session: &Session, id: SpotifyId)
|
pub fn get<T: MetadataTrait>(&mut self, session: &Session, id: SpotifyId)
|
||||||
-> MetadataRef<T> {
|
-> MetadataRef<T> {
|
||||||
let key = (id, TypeId::of::<T>());
|
|
||||||
|
|
||||||
self.cache.get(&key)
|
session.mercury(MercuryRequest {
|
||||||
.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 {
|
|
||||||
method: MercuryMethod::GET,
|
method: MercuryMethod::GET,
|
||||||
uri: format!("{}/{}", T::base_url(), object.id.to_base16()),
|
uri: format!("{}/{}", T::base_url(), id.to_base16()),
|
||||||
content_type: None,
|
content_type: None,
|
||||||
payload: Vec::new()
|
payload: Vec::new()
|
||||||
});
|
}).and_then(|response| {
|
||||||
|
|
||||||
thread::spawn(move || {
|
|
||||||
let response = rx.await().unwrap();
|
|
||||||
|
|
||||||
let msg : T::Message = protobuf::parse_from_bytes(
|
let msg : T::Message = protobuf::parse_from_bytes(
|
||||||
response.payload.first().unwrap()).unwrap();
|
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 std::thread;
|
||||||
use vorbis;
|
use vorbis;
|
||||||
|
|
||||||
use metadata::TrackRef;
|
use metadata::Track;
|
||||||
use session::Session;
|
use session::Session;
|
||||||
use audio_decrypt::AudioDecrypt;
|
use audio_decrypt::AudioDecrypt;
|
||||||
use util::{self, SpotifyId, Subfile};
|
use util::{self, SpotifyId, Subfile};
|
||||||
|
@ -86,7 +86,7 @@ impl PlayerInternal {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match self.commands.try_recv() {
|
match self.commands.try_recv() {
|
||||||
Ok(PlayerCommand::Load(id, play, position)) => {
|
Ok(PlayerCommand::Load(track_id, play, position)) => {
|
||||||
self.update(|state| {
|
self.update(|state| {
|
||||||
if state.status == PlayStatus::kPlayStatusPlay {
|
if state.status == PlayStatus::kPlayStatusPlay {
|
||||||
stream.stop().unwrap();
|
stream.stop().unwrap();
|
||||||
|
@ -98,10 +98,11 @@ impl PlayerInternal {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
let track : TrackRef = self.session.metadata(id);
|
let track = self.session.metadata::<Track>(track_id).await().unwrap();
|
||||||
let file_id = *track.wait().unwrap().files.first().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(
|
decoder = Some(
|
||||||
vorbis::Decoder::new(
|
vorbis::Decoder::new(
|
||||||
Subfile::new(
|
Subfile::new(
|
||||||
|
|
|
@ -91,7 +91,7 @@ impl <'s, D: SpircDelegate> SpircManager<'s, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) {
|
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();
|
let updates = self.delegate.updates();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|
Loading…
Reference in a new issue