use std::fmt; use std::sync::Arc; use rspotify::spotify::model::artist::{FullArtist, SimplifiedArtist}; use album::Album; use library::Library; use queue::Queue; use spotify::Spotify; use track::Track; use traits::{IntoBoxedViewExt, ListItem, ViewExt}; use ui::artist::ArtistView; #[derive(Clone, Deserialize, Serialize)] pub struct Artist { pub id: Option, pub name: String, pub url: Option, pub albums: Option>, pub tracks: Option>, pub is_followed: bool, } impl Artist { pub fn new(id: String, name: String) -> Self { Self { id: Some(id), name, url: None, albums: None, tracks: None, is_followed: false, } } pub fn load_albums(&mut self, spotify: Arc) { if let Some(albums) = self.albums.as_mut() { for album in albums { album.load_tracks(spotify.clone()); } return; } if let Some(ref artist_id) = self.id { if let Some(sas) = spotify.artist_albums(artist_id, 50, 0) { let mut albums: Vec = Vec::new(); for sa in sas.items { if Some("appears_on".into()) == sa.album_group { continue; } if let Some(album_id) = sa.id { if let Some(fa) = spotify.full_album(&album_id).as_ref() { albums.push(fa.into()); } } } self.albums = Some(albums); } } } fn tracks(&self) -> Option> { if let Some(tracks) = self.tracks.as_ref() { Some(tracks.iter().collect()) } else if let Some(albums) = self.albums.as_ref() { Some( albums .iter() .map(|a| a.tracks.as_ref().unwrap()) .flatten() .collect(), ) } else { None } } } impl From<&SimplifiedArtist> for Artist { fn from(sa: &SimplifiedArtist) -> Self { Self { id: sa.id.clone(), name: sa.name.clone(), url: sa.uri.clone(), albums: None, tracks: None, is_followed: false, } } } impl From<&FullArtist> for Artist { fn from(fa: &FullArtist) -> Self { Self { id: Some(fa.id.clone()), name: fa.name.clone(), url: Some(fa.uri.clone()), albums: None, tracks: None, is_followed: false, } } } impl fmt::Display for Artist { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.name) } } impl fmt::Debug for Artist { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} ({:?})", self.name, self.id) } } impl ListItem for Artist { fn is_playing(&self, queue: Arc) -> bool { if let Some(tracks) = self.tracks() { let playing: Vec = queue .queue .read() .unwrap() .iter() .filter(|t| t.id.is_some()) .map(|t| t.id.clone().unwrap()) .collect(); let ids: Vec = tracks .iter() .filter(|t| t.id.is_some()) .map(|t| t.id.clone().unwrap()) .collect(); !ids.is_empty() && playing == ids } else { false } } fn display_left(&self) -> String { format!("{}", self) } fn display_right(&self, library: Arc) -> String { let followed = if library.is_followed_artist(self) { if library.use_nerdfont { "\u{f62b} " } else { "✓ " } } else { "" }; let tracks = if let Some(tracks) = self.tracks.as_ref() { format!("{:>3} saved tracks", tracks.len()) } else { "".into() }; format!("{}{}", followed, tracks) } fn play(&mut self, queue: Arc) { self.load_albums(queue.get_spotify()); if let Some(tracks) = self.tracks() { let index = queue.append_next(tracks); queue.play(index, true); } } fn queue(&mut self, queue: Arc) { self.load_albums(queue.get_spotify()); if let Some(tracks) = self.tracks() { for t in tracks { queue.append(t); } } } fn toggle_saved(&mut self, library: Arc) { if library.is_followed_artist(self) { library.unfollow_artist(self); } else { library.follow_artist(self); } } fn open(&self, queue: Arc, library: Arc) -> Option> { Some(ArtistView::new(queue, library, self).as_boxed_view_ext()) } fn share_url(&self) -> Option { self.id .clone() .map(|id| format!("https://open.spotify.com/artist/{}", id)) } }