librespot_metadata/
album.rs

1use std::{
2    fmt::Debug,
3    ops::{Deref, DerefMut},
4};
5
6use crate::{
7    Metadata,
8    artist::Artists,
9    availability::Availabilities,
10    copyright::Copyrights,
11    external_id::ExternalIds,
12    image::Images,
13    request::RequestResult,
14    restriction::Restrictions,
15    sale_period::SalePeriods,
16    track::Tracks,
17    util::{impl_deref_wrapped, impl_try_from_repeated},
18};
19
20use librespot_core::{Error, Session, SpotifyUri, date::Date};
21
22use librespot_protocol as protocol;
23use protocol::metadata::Disc as DiscMessage;
24pub use protocol::metadata::album::Type as AlbumType;
25
26#[derive(Debug, Clone)]
27pub struct Album {
28    pub id: SpotifyUri,
29    pub name: String,
30    pub artists: Artists,
31    pub album_type: AlbumType,
32    pub label: String,
33    pub date: Date,
34    pub popularity: i32,
35    pub covers: Images,
36    pub external_ids: ExternalIds,
37    pub discs: Discs,
38    pub reviews: Vec<String>,
39    pub copyrights: Copyrights,
40    pub restrictions: Restrictions,
41    pub related: Albums,
42    pub sale_periods: SalePeriods,
43    pub cover_group: Images,
44    pub original_title: String,
45    pub version_title: String,
46    pub type_str: String,
47    pub availability: Availabilities,
48}
49
50#[derive(Debug, Clone, Default)]
51pub struct Albums(pub Vec<SpotifyUri>);
52
53impl_deref_wrapped!(Albums, Vec<SpotifyUri>);
54
55#[derive(Debug, Clone)]
56pub struct Disc {
57    pub number: i32,
58    pub name: String,
59    pub tracks: Tracks,
60}
61
62#[derive(Debug, Clone, Default)]
63pub struct Discs(pub Vec<Disc>);
64
65impl_deref_wrapped!(Discs, Vec<Disc>);
66
67impl Album {
68    pub fn tracks(&self) -> impl Iterator<Item = &SpotifyUri> {
69        self.discs.iter().flat_map(|disc| disc.tracks.iter())
70    }
71}
72
73#[async_trait]
74impl Metadata for Album {
75    type Message = protocol::metadata::Album;
76
77    async fn request(session: &Session, album_uri: &SpotifyUri) -> RequestResult {
78        let SpotifyUri::Album { .. } = album_uri else {
79            return Err(Error::invalid_argument("album_uri"));
80        };
81
82        session.spclient().get_album_metadata(album_uri).await
83    }
84
85    fn parse(msg: &Self::Message, _: &SpotifyUri) -> Result<Self, Error> {
86        Self::try_from(msg)
87    }
88}
89
90impl TryFrom<&<Self as Metadata>::Message> for Album {
91    type Error = librespot_core::Error;
92    fn try_from(album: &<Self as Metadata>::Message) -> Result<Self, Self::Error> {
93        Ok(Self {
94            id: album.try_into()?,
95            name: album.name().to_owned(),
96            artists: album.artist.as_slice().try_into()?,
97            album_type: album.type_(),
98            label: album.label().to_owned(),
99            date: album.date.get_or_default().try_into()?,
100            popularity: album.popularity(),
101            covers: album.cover_group.get_or_default().into(),
102            external_ids: album.external_id.as_slice().into(),
103            discs: album.disc.as_slice().try_into()?,
104            reviews: album.review.to_vec(),
105            copyrights: album.copyright.as_slice().into(),
106            restrictions: album.restriction.as_slice().into(),
107            related: album.related.as_slice().try_into()?,
108            sale_periods: album.sale_period.as_slice().try_into()?,
109            cover_group: album.cover_group.image.as_slice().into(),
110            original_title: album.original_title().to_owned(),
111            version_title: album.version_title().to_owned(),
112            type_str: album.type_str().to_owned(),
113            availability: album.availability.as_slice().try_into()?,
114        })
115    }
116}
117
118impl_try_from_repeated!(<Album as Metadata>::Message, Albums);
119
120impl TryFrom<&DiscMessage> for Disc {
121    type Error = librespot_core::Error;
122    fn try_from(disc: &DiscMessage) -> Result<Self, Self::Error> {
123        Ok(Self {
124            number: disc.number(),
125            name: disc.name().to_owned(),
126            tracks: disc.track.as_slice().try_into()?,
127        })
128    }
129}
130
131impl_try_from_repeated!(DiscMessage, Discs);