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,
|
||||
};
|
||||
|
||||
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_protocol as protocol;
|
||||
use protocol::metadata::ArtistWithRole as ArtistWithRoleMessage;
|
||||
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;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Artist {
|
||||
pub id: SpotifyId,
|
||||
pub name: String,
|
||||
pub popularity: i32,
|
||||
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)]
|
||||
|
@ -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 {
|
||||
pub fn for_country(&self, country: &str) -> Tracks {
|
||||
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]
|
||||
impl Metadata for Artist {
|
||||
type Message = protocol::metadata::Artist;
|
||||
|
@ -96,7 +221,23 @@ impl TryFrom<&<Self as Metadata>::Message> for Artist {
|
|||
Ok(Self {
|
||||
id: artist.try_into()?,
|
||||
name: artist.get_name().to_owned(),
|
||||
popularity: artist.get_popularity(),
|
||||
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);
|
||||
|
||||
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