1 use std::fmt;
2 use std::sync::{Arc, RwLock};
3 
4 use chrono::{DateTime, Utc};
5 use rspotify::model::album::FullAlbum;
6 use rspotify::model::track::{FullTrack, SavedTrack, SimplifiedTrack};
7 use rspotify::model::Id;
8 
9 use crate::library::Library;
10 use crate::model::album::Album;
11 use crate::model::artist::Artist;
12 use crate::model::playable::Playable;
13 use crate::queue::Queue;
14 use crate::traits::{IntoBoxedViewExt, ListItem, ViewExt};
15 use crate::ui::listview::ListView;
16 
17 #[derive(Clone, Deserialize, Serialize)]
18 pub struct Track {
19     pub id: Option<String>,
20     pub uri: String,
21     pub title: String,
22     pub track_number: u32,
23     pub disc_number: i32,
24     pub duration: u32,
25     pub artists: Vec<String>,
26     pub artist_ids: Vec<String>,
27     pub album: Option<String>,
28     pub album_id: Option<String>,
29     pub album_artists: Vec<String>,
30     pub cover_url: Option<String>,
31     pub url: String,
32     pub added_at: Option<DateTime<Utc>>,
33     pub list_index: usize,
34 }
35 
36 impl Track {
from_simplified_track(track: &SimplifiedTrack, album: &FullAlbum) -> Track37     pub fn from_simplified_track(track: &SimplifiedTrack, album: &FullAlbum) -> Track {
38         let artists = track
39             .artists
40             .iter()
41             .map(|artist| artist.name.clone())
42             .collect::<Vec<String>>();
43         let artist_ids = track
44             .artists
45             .iter()
46             .filter_map(|a| a.id.as_ref().map(|id| id.id().to_string()))
47             .collect::<Vec<String>>();
48         let album_artists = album
49             .artists
50             .iter()
51             .map(|artist| artist.name.clone())
52             .collect::<Vec<String>>();
53 
54         Self {
55             id: track.id.as_ref().map(|id| id.id().to_string()),
56             uri: track.id.as_ref().map(|id| id.uri()).unwrap_or_default(),
57             title: track.name.clone(),
58             track_number: track.track_number,
59             disc_number: track.disc_number,
60             duration: track.duration.as_millis() as u32,
61             artists,
62             artist_ids,
63             album: Some(album.name.clone()),
64             album_id: Some(album.id.id().to_string()),
65             album_artists,
66             cover_url: album.images.get(0).map(|img| img.url.clone()),
67             url: track.id.as_ref().map(|id| id.url()).unwrap_or_default(),
68             added_at: None,
69             list_index: 0,
70         }
71     }
72 
duration_str(&self) -> String73     pub fn duration_str(&self) -> String {
74         let minutes = self.duration / 60_000;
75         let seconds = (self.duration / 1000) % 60;
76         format!("{:02}:{:02}", minutes, seconds)
77     }
78 }
79 
80 impl From<&SimplifiedTrack> for Track {
from(track: &SimplifiedTrack) -> Self81     fn from(track: &SimplifiedTrack) -> Self {
82         let artists = track
83             .artists
84             .iter()
85             .map(|artist| artist.name.clone())
86             .collect::<Vec<String>>();
87         let artist_ids = track
88             .artists
89             .iter()
90             .filter_map(|a| a.id.as_ref().map(|a| a.id().to_string()))
91             .collect::<Vec<String>>();
92 
93         Self {
94             id: track.id.as_ref().map(|id| id.id().to_string()),
95             uri: track.id.as_ref().map(|id| id.uri()).unwrap_or_default(),
96             title: track.name.clone(),
97             track_number: track.track_number,
98             disc_number: track.disc_number,
99             duration: track.duration.as_millis() as u32,
100             artists,
101             artist_ids,
102             album: None,
103             album_id: None,
104             album_artists: Vec::new(),
105             cover_url: None,
106             url: track.id.as_ref().map(|id| id.url()).unwrap_or_default(),
107             added_at: None,
108             list_index: 0,
109         }
110     }
111 }
112 
113 impl From<&FullTrack> for Track {
from(track: &FullTrack) -> Self114     fn from(track: &FullTrack) -> Self {
115         let artists = track
116             .artists
117             .iter()
118             .map(|artist| artist.name.clone())
119             .collect::<Vec<String>>();
120         let artist_ids = track
121             .artists
122             .iter()
123             .filter_map(|a| a.id.as_ref().map(|a| a.id().to_string()))
124             .collect::<Vec<String>>();
125         let album_artists = track
126             .album
127             .artists
128             .iter()
129             .map(|artist| artist.name.clone())
130             .collect::<Vec<String>>();
131 
132         Self {
133             id: track.id.as_ref().map(|id| id.id().to_string()),
134             uri: track.id.as_ref().map(|id| id.uri()).unwrap_or_default(),
135             title: track.name.clone(),
136             track_number: track.track_number,
137             disc_number: track.disc_number,
138             duration: track.duration.as_millis() as u32,
139             artists,
140             artist_ids,
141             album: Some(track.album.name.clone()),
142             album_id: track.album.id.as_ref().map(|a| a.id().to_string()),
143             album_artists,
144             cover_url: track.album.images.get(0).map(|img| img.url.clone()),
145             url: track.id.as_ref().map(|id| id.url()).unwrap_or_default(),
146             added_at: None,
147             list_index: 0,
148         }
149     }
150 }
151 
152 impl From<&SavedTrack> for Track {
from(st: &SavedTrack) -> Self153     fn from(st: &SavedTrack) -> Self {
154         let mut track: Self = (&st.track).into();
155         track.added_at = Some(st.added_at);
156         track
157     }
158 }
159 
160 impl fmt::Display for Track {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result161     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
162         write!(f, "{} - {}", self.artists.join(", "), self.title)
163     }
164 }
165 
166 impl fmt::Debug for Track {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result167     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
168         write!(
169             f,
170             "({} - {} ({:?}))",
171             self.artists.join(", "),
172             self.title,
173             self.id
174         )
175     }
176 }
177 
178 impl ListItem for Track {
is_playing(&self, queue: Arc<Queue>) -> bool179     fn is_playing(&self, queue: Arc<Queue>) -> bool {
180         let current = queue.get_current();
181         current.map(|t| t.id() == self.id).unwrap_or(false)
182     }
183 
as_listitem(&self) -> Box<dyn ListItem>184     fn as_listitem(&self) -> Box<dyn ListItem> {
185         Box::new(self.clone())
186     }
187 
display_left(&self) -> String188     fn display_left(&self) -> String {
189         format!("{}", self)
190     }
191 
display_center(&self, library: Arc<Library>) -> String192     fn display_center(&self, library: Arc<Library>) -> String {
193         if library.cfg.values().album_column.unwrap_or(true) {
194             self.album.clone().unwrap_or_default()
195         } else {
196             "".to_string()
197         }
198     }
199 
display_right(&self, library: Arc<Library>) -> String200     fn display_right(&self, library: Arc<Library>) -> String {
201         let saved = if library.is_saved_track(&Playable::Track(self.clone())) {
202             if library.cfg.values().use_nerdfont.unwrap_or(false) {
203                 "\u{f62b} "
204             } else {
205                 "✓ "
206             }
207         } else {
208             ""
209         };
210         format!("{}{}", saved, self.duration_str())
211     }
212 
play(&mut self, queue: Arc<Queue>)213     fn play(&mut self, queue: Arc<Queue>) {
214         let index = queue.append_next(&vec![Playable::Track(self.clone())]);
215         queue.play(index, true, false);
216     }
217 
play_next(&mut self, queue: Arc<Queue>)218     fn play_next(&mut self, queue: Arc<Queue>) {
219         queue.insert_after_current(Playable::Track(self.clone()));
220     }
221 
queue(&mut self, queue: Arc<Queue>)222     fn queue(&mut self, queue: Arc<Queue>) {
223         queue.append(Playable::Track(self.clone()));
224     }
225 
save(&mut self, library: Arc<Library>)226     fn save(&mut self, library: Arc<Library>) {
227         library.save_tracks(vec![self], true);
228     }
229 
unsave(&mut self, library: Arc<Library>)230     fn unsave(&mut self, library: Arc<Library>) {
231         library.unsave_tracks(vec![self], true);
232     }
233 
toggle_saved(&mut self, library: Arc<Library>)234     fn toggle_saved(&mut self, library: Arc<Library>) {
235         if library.is_saved_track(&Playable::Track(self.clone())) {
236             library.unsave_tracks(vec![self], true);
237         } else {
238             library.save_tracks(vec![self], true);
239         }
240     }
241 
open(&self, _queue: Arc<Queue>, _library: Arc<Library>) -> Option<Box<dyn ViewExt>>242     fn open(&self, _queue: Arc<Queue>, _library: Arc<Library>) -> Option<Box<dyn ViewExt>> {
243         None
244     }
245 
open_recommendations( &mut self, queue: Arc<Queue>, library: Arc<Library>, ) -> Option<Box<dyn ViewExt>>246     fn open_recommendations(
247         &mut self,
248         queue: Arc<Queue>,
249         library: Arc<Library>,
250     ) -> Option<Box<dyn ViewExt>> {
251         let spotify = queue.get_spotify();
252 
253         let recommendations: Option<Vec<Track>> = if let Some(id) = &self.id {
254             spotify
255                 .api
256                 .recommendations(None, None, Some(vec![id]))
257                 .map(|r| r.tracks)
258                 .map(|tracks| tracks.iter().map(Track::from).collect())
259         } else {
260             None
261         };
262 
263         recommendations.map(|tracks| {
264             ListView::new(
265                 Arc::new(RwLock::new(tracks)),
266                 queue.clone(),
267                 library.clone(),
268             )
269             .set_title(format!(
270                 "Similar to \"{} - {}\"",
271                 self.artists.join(", "),
272                 self.title
273             ))
274             .into_boxed_view_ext()
275         })
276     }
277 
share_url(&self) -> Option<String>278     fn share_url(&self) -> Option<String> {
279         self.id
280             .clone()
281             .map(|id| format!("https://open.spotify.com/track/{}", id))
282     }
283 
album(&self, queue: Arc<Queue>) -> Option<Album>284     fn album(&self, queue: Arc<Queue>) -> Option<Album> {
285         let spotify = queue.get_spotify();
286 
287         match self.album_id {
288             Some(ref album_id) => spotify.api.album(album_id).map(|ref fa| fa.into()),
289             None => None,
290         }
291     }
292 
artists(&self) -> Option<Vec<Artist>>293     fn artists(&self) -> Option<Vec<Artist>> {
294         Some(
295             self.artist_ids
296                 .iter()
297                 .zip(self.artists.iter())
298                 .map(|(id, name)| Artist::new(id.clone(), name.clone()))
299                 .collect(),
300         )
301     }
302 
track(&self) -> Option<Track>303     fn track(&self) -> Option<Track> {
304         Some(self.clone())
305     }
306 }
307