librespot/src/metadata.rs

195 lines
6 KiB
Rust
Raw Normal View History

2017-01-19 12:56:49 +00:00
use futures::{Future, BoxFuture};
use linear_map::LinearMap;
2015-12-28 00:29:53 +00:00
use protobuf;
2015-06-23 14:38:29 +00:00
2017-01-19 12:56:49 +00:00
use mercury::MercuryError;
2016-01-27 10:40:00 +00:00
use protocol;
use session::Session;
2017-01-19 12:56:49 +00:00
use util::{SpotifyId, FileId, StrChunksExt};
2015-06-23 14:38:29 +00:00
2016-01-27 10:40:00 +00:00
pub use protocol::metadata::AudioFile_Format as FileFormat;
2016-01-02 02:30:24 +00:00
fn countrylist_contains(list: &str, country: &str) -> bool {
list.chunks(2).any(|cc| cc == country)
}
fn parse_restrictions<'s, I>(restrictions: I, country: &str, catalogue: &str) -> bool
2016-01-25 09:44:48 +00:00
where I: IntoIterator<Item = &'s protocol::metadata::Restriction>
2016-01-02 15:19:39 +00:00
{
2016-01-25 09:44:48 +00:00
restrictions.into_iter()
.filter(|r| r.get_catalogue_str().contains(&catalogue.to_owned()))
2016-01-02 15:19:39 +00:00
.all(|r| {
!countrylist_contains(r.get_countries_forbidden(), country) &&
(!r.has_countries_allowed() ||
countrylist_contains(r.get_countries_allowed(), country))
})
}
pub trait MetadataTrait : Send + 'static {
2015-06-23 14:38:29 +00:00
type Message: protobuf::MessageStatic;
2015-06-23 14:38:29 +00:00
fn base_url() -> &'static str;
fn parse(msg: &Self::Message, session: &Session) -> Self;
2015-06-23 14:38:29 +00:00
}
#[derive(Debug, Clone)]
2015-06-23 14:38:29 +00:00
pub struct Track {
pub id: SpotifyId,
2015-06-23 14:38:29 +00:00
pub name: String,
pub album: SpotifyId,
2016-01-25 09:44:48 +00:00
pub artists: Vec<SpotifyId>,
pub files: LinearMap<FileFormat, FileId>,
pub alternatives: Vec<SpotifyId>,
pub available: bool,
}
#[derive(Debug, Clone)]
pub struct Album {
pub id: SpotifyId,
pub name: String,
pub artists: Vec<SpotifyId>,
2016-01-25 09:44:48 +00:00
pub tracks: Vec<SpotifyId>,
2016-01-02 15:19:39 +00:00
pub covers: Vec<FileId>,
2015-06-23 14:38:29 +00:00
}
#[derive(Debug, Clone)]
pub struct Artist {
pub id: SpotifyId,
pub name: String,
2016-01-25 09:44:48 +00:00
pub top_tracks: Vec<SpotifyId>,
}
2015-06-23 14:38:29 +00:00
impl MetadataTrait for Track {
type Message = protocol::metadata::Track;
fn base_url() -> &'static str {
"hm://metadata/3/track"
}
fn parse(msg: &Self::Message, session: &Session) -> Self {
let country = session.country();
2016-01-25 09:44:48 +00:00
let artists = msg.get_artist()
.iter()
.filter(|artist| artist.has_gid())
.map(|artist| SpotifyId::from_raw(artist.get_gid()))
.collect::<Vec<_>>();
let files = msg.get_file()
.iter()
.filter(|file| file.has_file_id())
.map(|file| {
let mut dst = [0u8; 20];
2017-01-29 16:25:09 +00:00
dst.clone_from_slice(file.get_file_id());
(file.get_format(), FileId(dst))
2016-01-25 09:44:48 +00:00
})
.collect();
2015-06-23 14:38:29 +00:00
Track {
id: SpotifyId::from_raw(msg.get_gid()),
2015-09-01 11:20:37 +00:00
name: msg.get_name().to_owned(),
2015-06-23 14:38:29 +00:00
album: SpotifyId::from_raw(msg.get_album().get_gid()),
2016-01-25 09:44:48 +00:00
artists: artists,
files: files,
2016-01-02 15:19:39 +00:00
alternatives: msg.get_alternative()
.iter()
.map(|alt| SpotifyId::from_raw(alt.get_gid()))
.collect(),
2016-01-25 09:44:48 +00:00
available: parse_restrictions(msg.get_restriction(),
&country,
2016-01-02 15:19:39 +00:00
"premium"),
2015-06-23 14:38:29 +00:00
}
}
}
impl MetadataTrait for Album {
type Message = protocol::metadata::Album;
fn base_url() -> &'static str {
"hm://metadata/3/album"
}
fn parse(msg: &Self::Message, _: &Session) -> Self {
2016-01-25 09:44:48 +00:00
let artists = msg.get_artist()
.iter()
.filter(|artist| artist.has_gid())
.map(|artist| SpotifyId::from_raw(artist.get_gid()))
.collect::<Vec<_>>();
let tracks = msg.get_disc()
.iter()
.flat_map(|disc| disc.get_track())
.filter(|track| track.has_gid())
.map(|track| SpotifyId::from_raw(track.get_gid()))
.collect::<Vec<_>>();
let covers = msg.get_cover_group()
.get_image()
.iter()
.filter(|image| image.has_file_id())
.map(|image| {
let mut dst = [0u8; 20];
2017-01-29 16:25:09 +00:00
dst.clone_from_slice(image.get_file_id());
2016-01-25 09:44:48 +00:00
FileId(dst)
})
.collect::<Vec<_>>();
2015-06-23 14:38:29 +00:00
Album {
id: SpotifyId::from_raw(msg.get_gid()),
2015-09-01 11:20:37 +00:00
name: msg.get_name().to_owned(),
2016-01-25 09:44:48 +00:00
artists: artists,
tracks: tracks,
covers: covers,
2015-06-23 14:38:29 +00:00
}
}
}
impl MetadataTrait for Artist {
type Message = protocol::metadata::Artist;
2015-06-23 14:38:29 +00:00
fn base_url() -> &'static str {
"hm://metadata/3/artist"
}
2016-01-25 09:44:48 +00:00
fn parse(msg: &Self::Message, session: &Session) -> Self {
let country = session.country();
2016-01-25 09:44:48 +00:00
let top_tracks = msg.get_top_track()
.iter()
2017-01-29 16:25:09 +00:00
.find(|tt| !tt.has_country() || countrylist_contains(tt.get_country(), &country))
2016-01-25 09:44:48 +00:00
.unwrap()
.get_track()
.iter()
.filter(|track| track.has_gid())
.map(|track| SpotifyId::from_raw(track.get_gid()))
.collect::<Vec<_>>();
Artist {
id: SpotifyId::from_raw(msg.get_gid()),
name: msg.get_name().to_owned(),
2016-01-25 09:44:48 +00:00
top_tracks: top_tracks
2015-06-23 14:38:29 +00:00
}
}
}
2017-01-19 12:56:49 +00:00
component! {
MetadataManager : MetadataManagerInner { }
}
2015-06-23 14:38:29 +00:00
impl MetadataManager {
2017-01-19 12:56:49 +00:00
pub fn get<T: MetadataTrait>(&self, id: SpotifyId) -> BoxFuture<T, MercuryError> {
let session = self.session();
2017-01-18 21:33:52 +00:00
let uri = format!("{}/{}", T::base_url(), id.to_base16());
let request = session.mercury().get(uri);
2017-01-19 12:56:49 +00:00
request.and_then(move |response| {
2017-01-18 21:33:52 +00:00
let data = response.payload.first().expect("Empty payload");
let msg: T::Message = protobuf::parse_from_bytes(data).unwrap();
Ok(T::parse(&msg, &session))
2017-01-19 12:56:49 +00:00
}).boxed()
2015-06-23 14:38:29 +00:00
}
}