use std::fmt; use std::sync::Arc; use chrono::{DateTime, Utc}; use rspotify::model::album::{FullAlbum, SavedAlbum, SimplifiedAlbum}; use crate::artist::Artist; use crate::library::Library; use crate::playable::Playable; use crate::queue::Queue; use crate::spotify::Spotify; use crate::track::Track; use crate::traits::{IntoBoxedViewExt, ListItem, ViewExt}; use crate::ui::album::AlbumView; #[derive(Clone, Deserialize, Serialize)] pub struct Album { pub id: Option, pub title: String, pub artists: Vec, pub artist_ids: Vec, pub year: String, pub cover_url: Option, pub url: Option, pub tracks: Option>, pub added_at: Option>, } impl Album { pub fn load_tracks(&mut self, spotify: Arc) { if self.tracks.is_some() { return; } if let Some(ref album_id) = self.id { let mut collected_tracks = Vec::new(); if let Some(full_album) = spotify.full_album(album_id) { let mut tracks_result = Some(full_album.tracks.clone()); while let Some(ref tracks) = tracks_result { for t in &tracks.items { collected_tracks.push(Track::from_simplified_track(t, &full_album)); } debug!("got {} tracks", tracks.items.len()); // load next batch if necessary tracks_result = match tracks.next { Some(_) => { debug!("requesting tracks again.."); spotify.album_tracks( album_id, 50, tracks.offset + tracks.items.len() as u32, ) } None => None, } } } self.tracks = Some(collected_tracks) } } } impl From<&SimplifiedAlbum> for Album { fn from(sa: &SimplifiedAlbum) -> Self { Self { id: sa.id.clone(), title: sa.name.clone(), artists: sa.artists.iter().map(|sa| sa.name.clone()).collect(), artist_ids: sa .artists .iter() .filter(|a| a.id.is_some()) .map(|sa| sa.id.clone().unwrap()) .collect(), year: sa .release_date .clone() .unwrap_or_default() .split('-') .next() .unwrap() .into(), cover_url: sa.images.get(0).map(|i| i.url.clone()), url: sa.uri.clone(), tracks: None, added_at: None, } } } impl From<&FullAlbum> for Album { fn from(fa: &FullAlbum) -> Self { let tracks = Some( fa.tracks .items .iter() .map(|st| Track::from_simplified_track(&st, &fa)) .collect(), ); Self { id: Some(fa.id.clone()), title: fa.name.clone(), artists: fa.artists.iter().map(|sa| sa.name.clone()).collect(), artist_ids: fa .artists .iter() .filter(|a| a.id.is_some()) .map(|sa| sa.id.clone().unwrap()) .collect(), year: fa.release_date.split('-').next().unwrap().into(), cover_url: fa.images.get(0).map(|i| i.url.clone()), url: Some(fa.uri.clone()), tracks, added_at: None, } } } impl From<&SavedAlbum> for Album { fn from(sa: &SavedAlbum) -> Self { let mut album: Self = (&sa.album).into(); album.added_at = Some(sa.added_at); album } } impl fmt::Display for Album { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} - {}", self.artists.join(", "), self.title) } } impl fmt::Debug for Album { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "({} - {} ({:?}))", self.artists.join(", "), self.title, self.id ) } } impl ListItem for Album { fn is_playing(&self, queue: Arc) -> bool { if let Some(tracks) = self.tracks.as_ref() { let playing: Vec = queue .queue .read() .unwrap() .iter() .filter(|t| t.id().is_some()) .map(|t| t.id().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 as_listitem(&self) -> Box { Box::new(self.clone()) } fn display_left(&self) -> String { format!("{}", self) } fn display_right(&self, library: Arc) -> String { let saved = if library.is_saved_album(self) { if library.cfg.values().use_nerdfont.unwrap_or(false) { "\u{f62b} " } else { "✓ " } } else { "" }; format!("{}{}", saved, self.year) } fn play(&mut self, queue: Arc) { self.load_tracks(queue.get_spotify()); if let Some(tracks) = self.tracks.as_ref() { let tracks: Vec = tracks .iter() .map(|track| Playable::Track(track.clone())) .collect(); let index = queue.append_next(tracks); queue.play(index, true, true); } } fn play_next(&mut self, queue: Arc) { self.load_tracks(queue.get_spotify()); if let Some(tracks) = self.tracks.as_ref() { for t in tracks.iter().rev() { queue.insert_after_current(Playable::Track(t.clone())); } } } fn queue(&mut self, queue: Arc) { self.load_tracks(queue.get_spotify()); if let Some(tracks) = self.tracks.as_ref() { for t in tracks { queue.append(Playable::Track(t.clone())); } } } fn save(&mut self, library: Arc) { library.save_album(self); } fn unsave(&mut self, library: Arc) { library.unsave_album(self); } fn toggle_saved(&mut self, library: Arc) { if library.is_saved_album(self) { library.unsave_album(self); } else { library.save_album(self); } } fn open(&self, queue: Arc, library: Arc) -> Option> { Some(AlbumView::new(queue, library, self).as_boxed_view_ext()) } fn share_url(&self) -> Option { self.id .clone() .map(|id| format!("https://open.spotify.com/album/{}", id)) } fn artist(&self) -> Option { Some(Artist::new( self.artist_ids[0].clone(), self.artists[0].clone(), )) } }