librespot_metadata/
track.rs

1use std::{
2    fmt::Debug,
3    ops::{Deref, DerefMut},
4};
5
6use uuid::Uuid;
7
8use crate::{
9    Album, Metadata, RequestResult,
10    artist::{Artists, ArtistsWithRole},
11    audio::file::AudioFiles,
12    availability::Availabilities,
13    content_rating::ContentRatings,
14    external_id::ExternalIds,
15    restriction::Restrictions,
16    sale_period::SalePeriods,
17    util::{impl_deref_wrapped, impl_try_from_repeated},
18};
19
20use librespot_core::{Error, Session, SpotifyUri, date::Date};
21use librespot_protocol as protocol;
22
23#[derive(Debug, Clone)]
24pub struct Track {
25    pub id: SpotifyUri,
26    pub name: String,
27    pub album: Album,
28    pub artists: Artists,
29    pub number: i32,
30    pub disc_number: i32,
31    pub duration: i32,
32    pub popularity: i32,
33    pub is_explicit: bool,
34    pub external_ids: ExternalIds,
35    pub restrictions: Restrictions,
36    pub files: AudioFiles,
37    pub alternatives: Tracks,
38    pub sale_periods: SalePeriods,
39    pub previews: AudioFiles,
40    pub tags: Vec<String>,
41    pub earliest_live_timestamp: Date,
42    pub has_lyrics: bool,
43    pub availability: Availabilities,
44    pub licensor: Uuid,
45    pub language_of_performance: Vec<String>,
46    pub content_ratings: ContentRatings,
47    pub original_title: String,
48    pub version_title: String,
49    pub artists_with_role: ArtistsWithRole,
50}
51
52#[derive(Debug, Clone, Default)]
53pub struct Tracks(pub Vec<SpotifyUri>);
54
55impl_deref_wrapped!(Tracks, Vec<SpotifyUri>);
56
57#[async_trait]
58impl Metadata for Track {
59    type Message = protocol::metadata::Track;
60
61    async fn request(session: &Session, track_uri: &SpotifyUri) -> RequestResult {
62        let SpotifyUri::Track { .. } = track_uri else {
63            return Err(Error::invalid_argument("track_uri"));
64        };
65
66        session.spclient().get_track_metadata(track_uri).await
67    }
68
69    fn parse(msg: &Self::Message, _: &SpotifyUri) -> Result<Self, Error> {
70        Self::try_from(msg)
71    }
72}
73
74impl TryFrom<&<Self as Metadata>::Message> for Track {
75    type Error = librespot_core::Error;
76    fn try_from(track: &<Self as Metadata>::Message) -> Result<Self, Self::Error> {
77        Ok(Self {
78            id: track.try_into()?,
79            name: track.name().to_owned(),
80            album: track.album.get_or_default().try_into()?,
81            artists: track.artist.as_slice().try_into()?,
82            number: track.number(),
83            disc_number: track.disc_number(),
84            duration: track.duration(),
85            popularity: track.popularity(),
86            is_explicit: track.explicit(),
87            external_ids: track.external_id.as_slice().into(),
88            restrictions: track.restriction.as_slice().into(),
89            files: track.file.as_slice().into(),
90            alternatives: track.alternative.as_slice().try_into()?,
91            sale_periods: track.sale_period.as_slice().try_into()?,
92            previews: track.preview.as_slice().into(),
93            tags: track.tags.to_vec(),
94            earliest_live_timestamp: Date::from_timestamp_ms(track.earliest_live_timestamp())?,
95            has_lyrics: track.has_lyrics(),
96            availability: track.availability.as_slice().try_into()?,
97            licensor: Uuid::from_slice(track.licensor.uuid()).unwrap_or_else(|_| Uuid::nil()),
98            language_of_performance: track.language_of_performance.to_vec(),
99            content_ratings: track.content_rating.as_slice().into(),
100            original_title: track.original_title().to_owned(),
101            version_title: track.version_title().to_owned(),
102            artists_with_role: track.artist_with_role.as_slice().try_into()?,
103        })
104    }
105}
106
107impl_try_from_repeated!(<Track as Metadata>::Message, Tracks);