Merge remote-tracking branch 'origin/feature/new_rspotify' into develop

This commit is contained in:
Henrik Friedrichsen
2019-04-24 21:01:15 +02:00
9 changed files with 168 additions and 87 deletions

View File

@@ -35,8 +35,8 @@ rand = "0.6.5"
webbrowser = "0.5" webbrowser = "0.5"
[dependencies.rspotify] [dependencies.rspotify]
git = "https://github.com/KoffeinFlummi/rspotify" git = "https://github.com/samrayleung/rspotify"
rev = "1a30afc" rev = "9d9cf7c"
[dependencies.librespot] [dependencies.librespot]
git = "https://github.com/librespot-org/librespot.git" git = "https://github.com/librespot-org/librespot.git"

View File

@@ -14,13 +14,13 @@ use ui::album::AlbumView;
#[derive(Clone, Deserialize, Serialize)] #[derive(Clone, Deserialize, Serialize)]
pub struct Album { pub struct Album {
pub id: String, pub id: Option<String>,
pub title: String, pub title: String,
pub artists: Vec<String>, pub artists: Vec<String>,
pub artist_ids: Vec<String>, pub artist_ids: Vec<String>,
pub year: String, pub year: String,
pub cover_url: Option<String>, pub cover_url: Option<String>,
pub url: String, pub url: Option<String>,
pub tracks: Option<Vec<Track>>, pub tracks: Option<Vec<Track>>,
pub added_at: Option<DateTime<Utc>>, pub added_at: Option<DateTime<Utc>>,
} }
@@ -31,14 +31,16 @@ impl Album {
return; return;
} }
if let Some(fa) = spotify.full_album(&self.id) { if let Some(ref album_id) = self.id {
self.tracks = Some( if let Some(fa) = spotify.full_album(&album_id) {
fa.tracks self.tracks = Some(
.items fa.tracks
.iter() .items
.map(|st| Track::from_simplified_track(&st, &fa)) .iter()
.collect(), .map(|st| Track::from_simplified_track(&st, &fa))
); .collect(),
);
}
} }
} }
} }
@@ -49,8 +51,20 @@ impl From<&SimplifiedAlbum> for Album {
id: sa.id.clone(), id: sa.id.clone(),
title: sa.name.clone(), title: sa.name.clone(),
artists: sa.artists.iter().map(|sa| sa.name.clone()).collect(), artists: sa.artists.iter().map(|sa| sa.name.clone()).collect(),
artist_ids: sa.artists.iter().map(|sa| sa.id.clone()).collect(), artist_ids: sa
year: sa.release_date.split('-').next().unwrap().into(), .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()), cover_url: sa.images.get(0).map(|i| i.url.clone()),
url: sa.uri.clone(), url: sa.uri.clone(),
tracks: None, tracks: None,
@@ -70,13 +84,18 @@ impl From<&FullAlbum> for Album {
); );
Self { Self {
id: fa.id.clone(), id: Some(fa.id.clone()),
title: fa.name.clone(), title: fa.name.clone(),
artists: fa.artists.iter().map(|sa| sa.name.clone()).collect(), artists: fa.artists.iter().map(|sa| sa.name.clone()).collect(),
artist_ids: fa.artists.iter().map(|sa| sa.id.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(), year: fa.release_date.split('-').next().unwrap().into(),
cover_url: fa.images.get(0).map(|i| i.url.clone()), cover_url: fa.images.get(0).map(|i| i.url.clone()),
url: fa.uri.clone(), url: Some(fa.uri.clone()),
tracks, tracks,
added_at: None, added_at: None,
} }
@@ -101,7 +120,7 @@ impl fmt::Debug for Album {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
f, f,
"({} - {} ({}))", "({} - {} ({:?}))",
self.artists.join(", "), self.artists.join(", "),
self.title, self.title,
self.id self.id
@@ -117,9 +136,14 @@ impl ListItem for Album {
.read() .read()
.unwrap() .unwrap()
.iter() .iter()
.map(|t| t.id.clone()) .filter(|t| t.id.is_some())
.map(|t| t.id.clone().unwrap())
.collect();
let ids: Vec<String> = tracks
.iter()
.filter(|t| t.id.is_some())
.map(|t| t.id.clone().unwrap())
.collect(); .collect();
let ids: Vec<String> = tracks.iter().map(|t| t.id.clone()).collect();
!ids.is_empty() && playing == ids !ids.is_empty() && playing == ids
} else { } else {
false false

View File

@@ -13,9 +13,9 @@ use ui::artist::ArtistView;
#[derive(Clone, Deserialize, Serialize)] #[derive(Clone, Deserialize, Serialize)]
pub struct Artist { pub struct Artist {
pub id: String, pub id: Option<String>,
pub name: String, pub name: String,
pub url: String, pub url: Option<String>,
pub albums: Option<Vec<Album>>, pub albums: Option<Vec<Album>>,
pub tracks: Option<Vec<Track>>, pub tracks: Option<Vec<Track>>,
pub is_followed: bool, pub is_followed: bool,
@@ -24,9 +24,9 @@ pub struct Artist {
impl Artist { impl Artist {
pub fn new(id: String, name: String) -> Self { pub fn new(id: String, name: String) -> Self {
Self { Self {
id, id: Some(id),
name, name,
url: "".into(), url: None,
albums: None, albums: None,
tracks: None, tracks: None,
is_followed: false, is_followed: false,
@@ -41,20 +41,24 @@ impl Artist {
return; return;
} }
if let Some(sas) = spotify.artist_albums(&self.id, 50, 0) { if let Some(ref artist_id) = self.id {
let mut albums: Vec<Album> = Vec::new(); if let Some(sas) = spotify.artist_albums(artist_id, 50, 0) {
let mut albums: Vec<Album> = Vec::new();
for sa in sas.items { for sa in sas.items {
if Some("appears_on".into()) == sa.album_group { if Some("appears_on".into()) == sa.album_group {
continue; continue;
}
if let Some(album_id) = sa.id {
if let Some(fa) = spotify.full_album(&album_id).as_ref() {
albums.push(fa.into());
}
}
} }
if let Some(fa) = spotify.full_album(&sa.id).as_ref() { self.albums = Some(albums);
albums.push(fa.into());
}
} }
self.albums = Some(albums);
} }
} }
@@ -91,9 +95,9 @@ impl From<&SimplifiedArtist> for Artist {
impl From<&FullArtist> for Artist { impl From<&FullArtist> for Artist {
fn from(fa: &FullArtist) -> Self { fn from(fa: &FullArtist) -> Self {
Self { Self {
id: fa.id.clone(), id: Some(fa.id.clone()),
name: fa.name.clone(), name: fa.name.clone(),
url: fa.uri.clone(), url: Some(fa.uri.clone()),
albums: None, albums: None,
tracks: None, tracks: None,
is_followed: false, is_followed: false,
@@ -109,7 +113,7 @@ impl fmt::Display for Artist {
impl fmt::Debug for Artist { impl fmt::Debug for Artist {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} ({})", self.name, self.id) write!(f, "{} ({:?})", self.name, self.id)
} }
} }
@@ -121,9 +125,14 @@ impl ListItem for Artist {
.read() .read()
.unwrap() .unwrap()
.iter() .iter()
.map(|t| t.id.clone()) .filter(|t| t.id.is_some())
.map(|t| t.id.clone().unwrap())
.collect();
let ids: Vec<String> = tracks
.iter()
.filter(|t| t.id.is_some())
.map(|t| t.id.clone().unwrap())
.collect(); .collect();
let ids: Vec<String> = tracks.iter().map(|t| t.id.clone()).collect();
!ids.is_empty() && playing == ids !ids.is_empty() && playing == ids
} else { } else {
false false

View File

@@ -345,7 +345,7 @@ impl Library {
artists.extend(page.items.iter().map(|fa| fa.into())); artists.extend(page.items.iter().map(|fa| fa.into()));
if page.next.is_some() { if page.next.is_some() {
last = Some(artists.last().unwrap().id.clone()); last = artists.last().unwrap().id.clone();
} else { } else {
break; break;
} }
@@ -369,7 +369,10 @@ impl Library {
fn insert_artist(&self, id: &str, name: &str) { fn insert_artist(&self, id: &str, name: &str) {
let mut artists = self.artists.write().unwrap(); let mut artists = self.artists.write().unwrap();
if !artists.iter().any(|a| a.id == id) { if !artists
.iter()
.any(|a| a.id.clone().unwrap_or_default() == id)
{
let mut artist = Artist::new(id.to_string(), name.to_string()); let mut artist = Artist::new(id.to_string(), name.to_string());
artist.tracks = Some(Vec::new()); artist.tracks = Some(Vec::new());
artists.push(artist); artists.push(artist);
@@ -402,7 +405,7 @@ impl Library {
.items .items
.iter() .iter()
.enumerate() .enumerate()
.any(|(i, a)| a.album.id != store[i].id) .any(|(i, a)| a.album.id != store[i].id.clone().unwrap_or_default())
{ {
return; return;
} }
@@ -501,7 +504,9 @@ impl Library {
let index = if let Some(i) = lookup.get(artist_id).cloned() { let index = if let Some(i) = lookup.get(artist_id).cloned() {
i i
} else { } else {
let i = artists.iter().position(|a| &a.id == artist_id); let i = artists
.iter()
.position(|a| &a.id.clone().unwrap_or_default() == artist_id);
lookup.insert(artist_id.clone(), i); lookup.insert(artist_id.clone(), i);
i i
}; };
@@ -542,7 +547,13 @@ impl Library {
if api if api
&& self && self
.spotify .spotify
.current_user_saved_tracks_add(tracks.iter().map(|t| t.id.clone()).collect()) .current_user_saved_tracks_add(
tracks
.iter()
.filter(|t| t.id.is_some())
.map(|t| t.id.clone().unwrap())
.collect(),
)
.is_none() .is_none()
{ {
return; return;
@@ -575,7 +586,13 @@ impl Library {
if api if api
&& self && self
.spotify .spotify
.current_user_saved_tracks_delete(tracks.iter().map(|t| t.id.clone()).collect()) .current_user_saved_tracks_delete(
tracks
.iter()
.filter(|t| t.id.is_some())
.map(|t| t.id.clone().unwrap())
.collect(),
)
.is_none() .is_none()
{ {
return; return;
@@ -610,12 +627,14 @@ impl Library {
return; return;
} }
if self if let Some(ref album_id) = album.id {
.spotify if self
.current_user_saved_albums_add(vec![album.id.clone()]) .spotify
.is_none() .current_user_saved_albums_add(vec![album_id.clone()])
{ .is_none()
return; {
return;
}
} }
album.load_tracks(self.spotify.clone()); album.load_tracks(self.spotify.clone());
@@ -639,12 +658,14 @@ impl Library {
return; return;
} }
if self if let Some(ref album_id) = album.id {
.spotify if self
.current_user_saved_albums_delete(vec![album.id.clone()]) .spotify
.is_none() .current_user_saved_albums_delete(vec![album_id.clone()])
{ .is_none()
return; {
return;
}
} }
album.load_tracks(self.spotify.clone()); album.load_tracks(self.spotify.clone());
@@ -675,12 +696,14 @@ impl Library {
return; return;
} }
if self if let Some(ref artist_id) = artist.id {
.spotify if self
.user_follow_artists(vec![artist.id.clone()]) .spotify
.is_none() .user_follow_artists(vec![artist_id.clone()])
{ .is_none()
return; {
return;
}
} }
{ {
@@ -704,12 +727,14 @@ impl Library {
return; return;
} }
if self if let Some(ref artist_id) = artist.id {
.spotify if self
.user_unfollow_artists(vec![artist.id.clone()]) .spotify
.is_none() .user_unfollow_artists(vec![artist_id.clone()])
{ .is_none()
return; {
return;
}
} }
{ {

View File

@@ -29,7 +29,7 @@ fn get_metadata(queue: Arc<Queue>) -> HashMap<String, Variant<Box<RefArg>>> {
"mpris:trackid".to_string(), "mpris:trackid".to_string(),
Variant(Box::new( Variant(Box::new(
track track
.map(|t| format!("spotify:track:{}", t.id)) .map(|t| format!("spotify:track:{}", t.id.clone().unwrap_or("0".to_string())))
.unwrap_or_default(), .unwrap_or_default(),
)), )),
); );

View File

@@ -23,9 +23,15 @@ impl ListItem for Playlist {
.read() .read()
.unwrap() .unwrap()
.iter() .iter()
.map(|t| t.id.clone()) .filter(|t| t.id.is_some())
.map(|t| t.id.clone().unwrap())
.collect();
let ids: Vec<String> = self
.tracks
.iter()
.filter(|t| t.id.is_some())
.map(|t| t.id.clone().unwrap())
.collect(); .collect();
let ids: Vec<String> = self.tracks.iter().map(|t| t.id.clone()).collect();
!ids.is_empty() && playing == ids !ids.is_empty() && playing == ids
} }

View File

@@ -124,9 +124,13 @@ impl futures::Future for Worker {
debug!("message received!"); debug!("message received!");
match cmd { match cmd {
WorkerCommand::Load(track) => { WorkerCommand::Load(track) => {
let id = SpotifyId::from_base62(&track.id).expect("could not parse id"); if let Some(track_id) = &track.id {
self.play_task = Box::new(self.player.load(id, false, 0)); let id = SpotifyId::from_base62(track_id).expect("could not parse id");
info!("player loading track: {:?}", track); self.play_task = Box::new(self.player.load(id, false, 0));
info!("player loading track: {:?}", track);
} else {
self.events.send(Event::Player(PlayerEvent::FinishedTrack));
}
} }
WorkerCommand::Play => { WorkerCommand::Play => {
self.player.play(); self.player.play();
@@ -408,7 +412,11 @@ impl Spotify {
pub fn overwrite_playlist(&self, id: &str, tracks: &[Track]) { pub fn overwrite_playlist(&self, id: &str, tracks: &[Track]) {
// extract only track IDs // extract only track IDs
let mut tracks: Vec<String> = tracks.iter().map(|track| track.id.clone()).collect(); let mut tracks: Vec<String> = tracks
.iter()
.filter(|track| track.id.is_some())
.map(|track| track.id.clone().unwrap())
.collect();
// we can only send 100 tracks per request // we can only send 100 tracks per request
let mut remainder = if tracks.len() > 100 { let mut remainder = if tracks.len() > 100 {

View File

@@ -13,7 +13,7 @@ use traits::{ListItem, ViewExt};
#[derive(Clone, Deserialize, Serialize)] #[derive(Clone, Deserialize, Serialize)]
pub struct Track { pub struct Track {
pub id: String, pub id: Option<String>,
pub title: String, pub title: String,
pub track_number: u32, pub track_number: u32,
pub disc_number: i32, pub disc_number: i32,
@@ -21,7 +21,7 @@ pub struct Track {
pub artists: Vec<String>, pub artists: Vec<String>,
pub artist_ids: Vec<String>, pub artist_ids: Vec<String>,
pub album: String, pub album: String,
pub album_id: String, pub album_id: Option<String>,
pub album_artists: Vec<String>, pub album_artists: Vec<String>,
pub cover_url: String, pub cover_url: String,
pub url: String, pub url: String,
@@ -38,7 +38,8 @@ impl Track {
let artist_ids = track let artist_ids = track
.artists .artists
.iter() .iter()
.map(|ref artist| artist.id.clone()) .filter(|a| a.id.is_some())
.map(|ref artist| artist.id.clone().unwrap())
.collect::<Vec<String>>(); .collect::<Vec<String>>();
let album_artists = album let album_artists = album
.artists .artists
@@ -60,7 +61,7 @@ impl Track {
artists, artists,
artist_ids, artist_ids,
album: album.name.clone(), album: album.name.clone(),
album_id: album.id.clone(), album_id: Some(album.id.clone()),
album_artists, album_artists,
cover_url, cover_url,
url: track.uri.clone(), url: track.uri.clone(),
@@ -85,7 +86,8 @@ impl From<&FullTrack> for Track {
let artist_ids = track let artist_ids = track
.artists .artists
.iter() .iter()
.map(|ref artist| artist.id.clone()) .filter(|a| a.id.is_some())
.map(|ref artist| artist.id.clone().unwrap())
.collect::<Vec<String>>(); .collect::<Vec<String>>();
let album_artists = track let album_artists = track
.album .album
@@ -135,7 +137,7 @@ impl fmt::Debug for Track {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
f, f,
"({} - {} ({}))", "({} - {} ({:?}))",
self.artists.join(", "), self.artists.join(", "),
self.title, self.title,
self.id self.id
@@ -190,7 +192,10 @@ impl ListItem for Track {
fn album(&self, queue: Arc<Queue>) -> Option<Album> { fn album(&self, queue: Arc<Queue>) -> Option<Album> {
let spotify = queue.get_spotify(); let spotify = queue.get_spotify();
spotify.album(&self.album_id).map(|ref fa| fa.into()) match self.album_id {
Some(ref album_id) => spotify.album(&album_id).map(|ref fa| fa.into()),
None => None,
}
} }
fn artist(&self) -> Option<Artist> { fn artist(&self) -> Option<Artist> {

View File

@@ -37,8 +37,10 @@ impl ArtistView {
let spotify = spotify.clone(); let spotify = spotify.clone();
let id = artist.id.clone(); let id = artist.id.clone();
thread::spawn(move || { thread::spawn(move || {
if let Some(tracks) = spotify.artist_top_tracks(id) { if let Some(id) = id {
top_tracks.write().unwrap().extend(tracks); if let Some(tracks) = spotify.artist_top_tracks(id) {
top_tracks.write().unwrap().extend(tracks);
}
} }
}); });
} }
@@ -49,8 +51,10 @@ impl ArtistView {
let spotify = spotify.clone(); let spotify = spotify.clone();
let id = artist.id.clone(); let id = artist.id.clone();
thread::spawn(move || { thread::spawn(move || {
if let Some(artists) = spotify.artist_related_artists(id) { if let Some(id) = id {
related.write().unwrap().extend(artists); if let Some(artists) = spotify.artist_related_artists(id) {
related.write().unwrap().extend(artists);
}
} }
}); });
} }