metadata.rs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. use eventual::{Async, Future};
  2. use linear_map::LinearMap;
  3. use protobuf;
  4. use protocol;
  5. use mercury::{MercuryRequest, MercuryMethod};
  6. use util::{SpotifyId, FileId, StrChunksExt};
  7. use session::Session;
  8. pub use protocol::metadata::AudioFile_Format as FileFormat;
  9. fn countrylist_contains(list: &str, country: &str) -> bool {
  10. list.chunks(2).any(|cc| cc == country)
  11. }
  12. fn parse_restrictions<'s, I>(restrictions: I, country: &str, catalogue: &str) -> bool
  13. where I: IntoIterator<Item = &'s protocol::metadata::Restriction>
  14. {
  15. restrictions.into_iter()
  16. .filter(|r| r.get_catalogue_str().contains(&catalogue.to_owned()))
  17. .all(|r| {
  18. !countrylist_contains(r.get_countries_forbidden(), country) &&
  19. (!r.has_countries_allowed() ||
  20. countrylist_contains(r.get_countries_allowed(), country))
  21. })
  22. }
  23. pub trait MetadataTrait : Send + 'static {
  24. type Message: protobuf::MessageStatic;
  25. fn base_url() -> &'static str;
  26. fn parse(msg: &Self::Message, session: &Session) -> Self;
  27. }
  28. #[derive(Debug, Clone)]
  29. pub struct Track {
  30. pub id: SpotifyId,
  31. pub name: String,
  32. pub album: SpotifyId,
  33. pub artists: Vec<SpotifyId>,
  34. pub files: LinearMap<FileFormat, FileId>,
  35. pub alternatives: Vec<SpotifyId>,
  36. pub available: bool,
  37. }
  38. #[derive(Debug, Clone)]
  39. pub struct Album {
  40. pub id: SpotifyId,
  41. pub name: String,
  42. pub artists: Vec<SpotifyId>,
  43. pub tracks: Vec<SpotifyId>,
  44. pub covers: Vec<FileId>,
  45. }
  46. #[derive(Debug, Clone)]
  47. pub struct Artist {
  48. pub id: SpotifyId,
  49. pub name: String,
  50. pub top_tracks: Vec<SpotifyId>,
  51. }
  52. pub type MetadataRef<T> = Future<T, ()>;
  53. pub type TrackRef = MetadataRef<Track>;
  54. pub type AlbumRef = MetadataRef<Album>;
  55. pub type ArtistRef = MetadataRef<Artist>;
  56. impl MetadataTrait for Track {
  57. type Message = protocol::metadata::Track;
  58. fn base_url() -> &'static str {
  59. "hm://metadata/3/track"
  60. }
  61. fn parse(msg: &Self::Message, session: &Session) -> Self {
  62. let country = session.country();
  63. let artists = msg.get_artist()
  64. .iter()
  65. .filter(|artist| artist.has_gid())
  66. .map(|artist| SpotifyId::from_raw(artist.get_gid()))
  67. .collect::<Vec<_>>();
  68. let files = msg.get_file()
  69. .iter()
  70. .filter(|file| file.has_file_id())
  71. .map(|file| {
  72. let mut dst = [0u8; 20];
  73. dst.clone_from_slice(&file.get_file_id());
  74. (file.get_format(), FileId(dst))
  75. })
  76. .collect();
  77. Track {
  78. id: SpotifyId::from_raw(msg.get_gid()),
  79. name: msg.get_name().to_owned(),
  80. album: SpotifyId::from_raw(msg.get_album().get_gid()),
  81. artists: artists,
  82. files: files,
  83. alternatives: msg.get_alternative()
  84. .iter()
  85. .map(|alt| SpotifyId::from_raw(alt.get_gid()))
  86. .collect(),
  87. available: parse_restrictions(msg.get_restriction(),
  88. &country,
  89. "premium"),
  90. }
  91. }
  92. }
  93. impl MetadataTrait for Album {
  94. type Message = protocol::metadata::Album;
  95. fn base_url() -> &'static str {
  96. "hm://metadata/3/album"
  97. }
  98. fn parse(msg: &Self::Message, _: &Session) -> Self {
  99. let artists = msg.get_artist()
  100. .iter()
  101. .filter(|artist| artist.has_gid())
  102. .map(|artist| SpotifyId::from_raw(artist.get_gid()))
  103. .collect::<Vec<_>>();
  104. let tracks = msg.get_disc()
  105. .iter()
  106. .flat_map(|disc| disc.get_track())
  107. .filter(|track| track.has_gid())
  108. .map(|track| SpotifyId::from_raw(track.get_gid()))
  109. .collect::<Vec<_>>();
  110. let covers = msg.get_cover_group()
  111. .get_image()
  112. .iter()
  113. .filter(|image| image.has_file_id())
  114. .map(|image| {
  115. let mut dst = [0u8; 20];
  116. dst.clone_from_slice(&image.get_file_id());
  117. FileId(dst)
  118. })
  119. .collect::<Vec<_>>();
  120. Album {
  121. id: SpotifyId::from_raw(msg.get_gid()),
  122. name: msg.get_name().to_owned(),
  123. artists: artists,
  124. tracks: tracks,
  125. covers: covers,
  126. }
  127. }
  128. }
  129. impl MetadataTrait for Artist {
  130. type Message = protocol::metadata::Artist;
  131. fn base_url() -> &'static str {
  132. "hm://metadata/3/artist"
  133. }
  134. fn parse(msg: &Self::Message, session: &Session) -> Self {
  135. let country = session.country();
  136. let top_tracks = msg.get_top_track()
  137. .iter()
  138. .filter(|tt| !tt.has_country() ||
  139. countrylist_contains(tt.get_country(), &country))
  140. .next()
  141. .unwrap()
  142. .get_track()
  143. .iter()
  144. .filter(|track| track.has_gid())
  145. .map(|track| SpotifyId::from_raw(track.get_gid()))
  146. .collect::<Vec<_>>();
  147. Artist {
  148. id: SpotifyId::from_raw(msg.get_gid()),
  149. name: msg.get_name().to_owned(),
  150. top_tracks: top_tracks
  151. }
  152. }
  153. }
  154. pub struct MetadataManager;
  155. impl MetadataManager {
  156. pub fn new() -> MetadataManager {
  157. MetadataManager
  158. }
  159. pub fn get<T: MetadataTrait>(&mut self, session: &Session, id: SpotifyId) -> MetadataRef<T> {
  160. let session = session.clone();
  161. session.mercury(MercuryRequest {
  162. method: MercuryMethod::GET,
  163. uri: format!("{}/{}", T::base_url(), id.to_base16()),
  164. content_type: None,
  165. payload: Vec::new(),
  166. })
  167. .and_then(move |response| {
  168. let data = response.payload.first().expect("Empty payload");
  169. let msg: T::Message = protobuf::parse_from_bytes(data).unwrap();
  170. Ok(T::parse(&msg, &session))
  171. })
  172. }
  173. }