mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Implement additional metadata for Artist (#1036)
- Added `*-current()` functions to `Artist` to get the list of current versions / releases of each album - This is useful since the `AlbumGroups` can contain multiple versions / releases of the same album
This commit is contained in:
parent
8ffaf7cb8d
commit
e5092c84fd
1 changed files with 196 additions and 2 deletions
|
@ -4,20 +4,51 @@ use std::{
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{request::RequestResult, track::Tracks, util::try_from_repeated_message, Metadata};
|
use crate::{
|
||||||
|
album::Albums,
|
||||||
|
availability::Availabilities,
|
||||||
|
external_id::ExternalIds,
|
||||||
|
image::Images,
|
||||||
|
request::RequestResult,
|
||||||
|
restriction::Restrictions,
|
||||||
|
sale_period::SalePeriods,
|
||||||
|
track::Tracks,
|
||||||
|
util::{from_repeated_message, try_from_repeated_message},
|
||||||
|
Metadata,
|
||||||
|
};
|
||||||
|
|
||||||
use librespot_core::{Error, Session, SpotifyId};
|
use librespot_core::{Error, Session, SpotifyId};
|
||||||
|
|
||||||
use librespot_protocol as protocol;
|
use librespot_protocol as protocol;
|
||||||
use protocol::metadata::ArtistWithRole as ArtistWithRoleMessage;
|
|
||||||
pub use protocol::metadata::ArtistWithRole_ArtistRole as ArtistRole;
|
pub use protocol::metadata::ArtistWithRole_ArtistRole as ArtistRole;
|
||||||
|
|
||||||
|
use protocol::metadata::ActivityPeriod as ActivityPeriodMessage;
|
||||||
|
use protocol::metadata::AlbumGroup as AlbumGroupMessage;
|
||||||
|
use protocol::metadata::ArtistWithRole as ArtistWithRoleMessage;
|
||||||
|
use protocol::metadata::Biography as BiographyMessage;
|
||||||
use protocol::metadata::TopTracks as TopTracksMessage;
|
use protocol::metadata::TopTracks as TopTracksMessage;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Artist {
|
pub struct Artist {
|
||||||
pub id: SpotifyId,
|
pub id: SpotifyId,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub popularity: i32,
|
||||||
pub top_tracks: CountryTopTracks,
|
pub top_tracks: CountryTopTracks,
|
||||||
|
pub albums: AlbumGroups,
|
||||||
|
pub singles: AlbumGroups,
|
||||||
|
pub compilations: AlbumGroups,
|
||||||
|
pub appears_on_albums: AlbumGroups,
|
||||||
|
pub genre: Vec<String>,
|
||||||
|
pub external_ids: ExternalIds,
|
||||||
|
pub portraits: Images,
|
||||||
|
pub biographies: Biographies,
|
||||||
|
pub activity_periods: ActivityPeriods,
|
||||||
|
pub restrictions: Restrictions,
|
||||||
|
pub related: Artists,
|
||||||
|
pub is_portrait_album_cover: bool,
|
||||||
|
pub portrait_group: Images,
|
||||||
|
pub sales_periods: SalePeriods,
|
||||||
|
pub availabilities: Availabilities,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
@ -63,6 +94,69 @@ impl Deref for CountryTopTracks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct AlbumGroup(pub Albums);
|
||||||
|
|
||||||
|
impl Deref for AlbumGroup {
|
||||||
|
type Target = Albums;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `AlbumGroups` contains collections of album variants (different releases of the same album).
|
||||||
|
/// Ignoring the wrapping types it is structured roughly like this:
|
||||||
|
/// ```text
|
||||||
|
/// AlbumGroups [
|
||||||
|
/// [Album1], [Album2-relelease, Album2-older-release], [Album3]
|
||||||
|
/// ]
|
||||||
|
/// ```
|
||||||
|
/// In most cases only the current variant of each album is needed. A list of every album in it's
|
||||||
|
/// current release variant can be obtained by using [`AlbumGroups::current_releases`]
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct AlbumGroups(pub Vec<AlbumGroup>);
|
||||||
|
|
||||||
|
impl Deref for AlbumGroups {
|
||||||
|
type Target = Vec<AlbumGroup>;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Biography {
|
||||||
|
pub text: String,
|
||||||
|
pub portraits: Images,
|
||||||
|
pub portrait_group: Vec<Images>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct Biographies(pub Vec<Biography>);
|
||||||
|
|
||||||
|
impl Deref for Biographies {
|
||||||
|
type Target = Vec<Biography>;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ActivityPeriod {
|
||||||
|
pub start_year: i32,
|
||||||
|
pub end_year: i32,
|
||||||
|
pub decade: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct ActivityPeriods(pub Vec<ActivityPeriod>);
|
||||||
|
|
||||||
|
impl Deref for ActivityPeriods {
|
||||||
|
type Target = Vec<ActivityPeriod>;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CountryTopTracks {
|
impl CountryTopTracks {
|
||||||
pub fn for_country(&self, country: &str) -> Tracks {
|
pub fn for_country(&self, country: &str) -> Tracks {
|
||||||
if let Some(country) = self.0.iter().find(|top_track| top_track.country == country) {
|
if let Some(country) = self.0.iter().find(|top_track| top_track.country == country) {
|
||||||
|
@ -77,6 +171,37 @@ impl CountryTopTracks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Artist {
|
||||||
|
/// Get the full list of albums, not containing duplicate variants of the same albums.
|
||||||
|
///
|
||||||
|
/// See also [`AlbumGroups`](struct@AlbumGroups) and [`AlbumGroups::current_releases`]
|
||||||
|
pub fn albums_current(&self) -> Albums {
|
||||||
|
self.albums.current_releases()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the full list of singles, not containing duplicate variants of the same singles.
|
||||||
|
///
|
||||||
|
/// See also [`AlbumGroups`](struct@AlbumGroups) and [`AlbumGroups::current_releases`]
|
||||||
|
pub fn singles_current(&self) -> Albums {
|
||||||
|
self.singles.current_releases()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the full list of compilations, not containing duplicate variants of the same
|
||||||
|
/// compilations.
|
||||||
|
///
|
||||||
|
/// See also [`AlbumGroups`](struct@AlbumGroups) and [`AlbumGroups::current_releases`]
|
||||||
|
pub fn compilations_current(&self) -> Albums {
|
||||||
|
self.compilations.current_releases()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the full list of albums, not containing duplicate variants of the same albums.
|
||||||
|
///
|
||||||
|
/// See also [`AlbumGroups`](struct@AlbumGroups) and [`AlbumGroups::current_releases`]
|
||||||
|
pub fn appears_on_albums_current(&self) -> Albums {
|
||||||
|
self.appears_on_albums.current_releases()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Metadata for Artist {
|
impl Metadata for Artist {
|
||||||
type Message = protocol::metadata::Artist;
|
type Message = protocol::metadata::Artist;
|
||||||
|
@ -96,7 +221,23 @@ impl TryFrom<&<Self as Metadata>::Message> for Artist {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
id: artist.try_into()?,
|
id: artist.try_into()?,
|
||||||
name: artist.get_name().to_owned(),
|
name: artist.get_name().to_owned(),
|
||||||
|
popularity: artist.get_popularity(),
|
||||||
top_tracks: artist.get_top_track().try_into()?,
|
top_tracks: artist.get_top_track().try_into()?,
|
||||||
|
albums: artist.get_album_group().try_into()?,
|
||||||
|
singles: artist.get_single_group().try_into()?,
|
||||||
|
compilations: artist.get_compilation_group().try_into()?,
|
||||||
|
appears_on_albums: artist.get_appears_on_group().try_into()?,
|
||||||
|
genre: artist.get_genre().to_vec(),
|
||||||
|
external_ids: artist.get_external_id().into(),
|
||||||
|
portraits: artist.get_portrait().into(),
|
||||||
|
biographies: artist.get_biography().into(),
|
||||||
|
activity_periods: artist.get_activity_period().into(),
|
||||||
|
restrictions: artist.get_restriction().into(),
|
||||||
|
related: artist.get_related().try_into()?,
|
||||||
|
is_portrait_album_cover: artist.get_is_portrait_album_cover(),
|
||||||
|
portrait_group: artist.get_portrait_group().get_image().into(),
|
||||||
|
sales_periods: artist.get_sale_period().try_into()?,
|
||||||
|
availabilities: artist.get_availability().try_into()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,3 +268,56 @@ impl TryFrom<&TopTracksMessage> for TopTracks {
|
||||||
}
|
}
|
||||||
|
|
||||||
try_from_repeated_message!(TopTracksMessage, CountryTopTracks);
|
try_from_repeated_message!(TopTracksMessage, CountryTopTracks);
|
||||||
|
|
||||||
|
impl TryFrom<&AlbumGroupMessage> for AlbumGroup {
|
||||||
|
type Error = librespot_core::Error;
|
||||||
|
fn try_from(album_groups: &AlbumGroupMessage) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self(album_groups.get_album().try_into()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AlbumGroups {
|
||||||
|
/// Get the contained albums. This will only use the latest release / variant of an album if
|
||||||
|
/// multiple variants are available. This should be used if multiple variants of the same album
|
||||||
|
/// are not explicitely desired.
|
||||||
|
pub fn current_releases(&self) -> Albums {
|
||||||
|
let albums = self
|
||||||
|
.iter()
|
||||||
|
.filter_map(|agrp| agrp.first())
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
Albums(albums)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try_from_repeated_message!(AlbumGroupMessage, AlbumGroups);
|
||||||
|
|
||||||
|
impl From<&BiographyMessage> for Biography {
|
||||||
|
fn from(biography: &BiographyMessage) -> Self {
|
||||||
|
let portrait_group = biography
|
||||||
|
.get_portrait_group()
|
||||||
|
.iter()
|
||||||
|
.map(|it| it.get_image().into())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
text: biography.get_text().to_owned(),
|
||||||
|
portraits: biography.get_portrait().into(),
|
||||||
|
portrait_group,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from_repeated_message!(BiographyMessage, Biographies);
|
||||||
|
|
||||||
|
impl From<&ActivityPeriodMessage> for ActivityPeriod {
|
||||||
|
fn from(activity_periode: &ActivityPeriodMessage) -> Self {
|
||||||
|
Self {
|
||||||
|
start_year: activity_periode.get_start_year(),
|
||||||
|
end_year: activity_periode.get_end_year(),
|
||||||
|
decade: activity_periode.get_decade(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from_repeated_message!(ActivityPeriodMessage, ActivityPeriods);
|
||||||
|
|
Loading…
Reference in a new issue