Skip to content

Commit

Permalink
Fetch album and artist content (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ovenoboyo committed Dec 20, 2024
1 parent 47e14a4 commit fd16925
Show file tree
Hide file tree
Showing 15 changed files with 524 additions and 56 deletions.
2 changes: 1 addition & 1 deletion .zed/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"Rust": {
"formatter": {
"external": {
"command": "leptosfmt",
"command": "/home/ovenoboyo/.cargo/bin/leptosfmt",
"arguments": ["--stdin", "--rustfmt"]
}
}
Expand Down
10 changes: 6 additions & 4 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ use extensions::{
install_extension, remove_extension, send_extra_event,
};
use providers::handler::{
fetch_playback_url, fetch_playlist_content, fetch_user_playlists, get_all_status,
get_provider_key_by_id, get_provider_keys, get_suggestions, initialize_all_providers,
match_url, playlist_from_url, provider_authorize, provider_login, provider_search,
provider_signout, song_from_url,
fetch_playback_url, fetch_playlist_content, fetch_user_playlists, get_album_content,
get_all_status, get_artist_content, get_provider_key_by_id, get_provider_keys, get_suggestions,
initialize_all_providers, match_url, playlist_from_url, provider_authorize, provider_login,
provider_search, provider_signout, song_from_url,
};
use scanner::{get_scanner_state, start_scan, ScanTask};
use tauri::{Listener, Manager, State};
Expand Down Expand Up @@ -204,6 +204,8 @@ pub fn run() {
playlist_from_url,
song_from_url,
get_suggestions,
get_album_content,
get_artist_content,
// Rodio player
rodio_get_volume,
rodio_load,
Expand Down
18 changes: 17 additions & 1 deletion src-tauri/src/providers/extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use futures::{channel::mpsc::UnboundedSender, SinkExt};
use serde_json::Value;
use tauri::AppHandle;
use types::{
entities::{QueryablePlaylist, SearchResult},
entities::{QueryableAlbum, QueryableArtist, QueryablePlaylist, SearchResult},
errors::Result,
extensions::{
AccountLoginArgs, CustomRequestReturnType, ExtensionDetail, ExtensionExtraEvent,
Expand Down Expand Up @@ -322,4 +322,20 @@ impl GenericProvider for ExtensionProvider {

Ok(res.songs)
}

async fn get_album_content(
&self,
album: QueryableAlbum,
pagination: Pagination,
) -> Result<(Vec<Song>, Pagination)> {
todo!()
}

async fn get_artist_content(
&self,
artist: QueryableArtist,
pagination: Pagination,
) -> Result<(Vec<Song>, Pagination)> {
todo!()
}
}
20 changes: 19 additions & 1 deletion src-tauri/src/providers/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use tauri::{
AppHandle, Emitter, State,
};
use types::{
entities::{QueryablePlaylist, SearchResult},
entities::{QueryableAlbum, QueryableArtist, QueryablePlaylist, SearchResult},
errors::{MoosyncError, Result},
providers::generic::{GenericProvider, Pagination, ProviderStatus},
songs::Song,
Expand Down Expand Up @@ -347,6 +347,22 @@ impl ProviderHandler {
result_type: Vec<Song>,
method_name: get_suggestions,
},
get_album_content {
args: {
album: QueryableAlbum,
pagination: Pagination
},
result_type: (Vec<Song>, Pagination),
method_name: get_album_content,
},
get_artist_content {
args: {
artist: QueryableArtist,
pagination: Pagination
},
result_type: (Vec<Song>, Pagination),
method_name: get_artist_content,
},
);
}

Expand All @@ -370,3 +386,5 @@ generate_command_async_cached!(playlist_from_url, ProviderHandler, QueryablePlay
generate_command_async_cached!(song_from_url, ProviderHandler, Song, key: String, url: String);
generate_command_async_cached!(match_url, ProviderHandler, bool, key: String, url: String);
generate_command_async_cached!(get_suggestions, ProviderHandler, Vec<Song>, key: String);
generate_command_async_cached!(get_artist_content, ProviderHandler, (Vec<Song>, Pagination), key: String, artist: QueryableArtist, pagination: Pagination);
generate_command_async_cached!(get_album_content, ProviderHandler, (Vec<Song>, Pagination), key: String, album: QueryableAlbum, pagination: Pagination);
169 changes: 166 additions & 3 deletions src-tauri/src/providers/spotify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use regex::Regex;
use rspotify::{
clients::{BaseClient, OAuthClient},
model::{
FullArtist, FullTrack, Id, PlaylistId, PlaylistTracksRef, SearchType, SimplifiedAlbum,
SimplifiedArtist, SimplifiedPlaylist, TrackId,
AlbumId, AlbumType, ArtistId, FullAlbum, FullArtist, FullTrack, Id, PlaylistId,
PlaylistTracksRef, SearchType, SimplifiedAlbum, SimplifiedArtist, SimplifiedPlaylist,
SimplifiedTrack, TrackId,
},
AuthCodePkceSpotify, Token,
};
Expand Down Expand Up @@ -213,7 +214,11 @@ impl SpotifyProvider {
provider_extension: Some(self.key()),
..Default::default()
},
album: Some(self.parse_album(item.album)),
album: if item.album.id.is_some() {
Some(self.parse_album(item.album))
} else {
None
},
artists: Some(
item.artists
.into_iter()
Expand Down Expand Up @@ -244,6 +249,47 @@ impl SpotifyProvider {

Err("API client not initialized".into())
}

fn get_full_track(&self, track: SimplifiedTrack) -> FullTrack {
FullTrack {
album: track.album.unwrap_or_default(),
artists: track.artists,
available_markets: track.available_markets.unwrap_or_default(),
disc_number: track.disc_number,
duration: track.duration,
explicit: track.explicit,
external_ids: HashMap::new(),
external_urls: track.external_urls,
href: track.href,
id: track.id,
is_local: track.is_local,
is_playable: track.is_playable,
linked_from: track.linked_from,
restrictions: track.restrictions,
name: track.name,
popularity: 0,
preview_url: track.preview_url,
track_number: track.track_number,
}
}

fn get_simple_album(&self, album: FullAlbum) -> SimplifiedAlbum {
let album_type: &'static str = album.album_type.into();
SimplifiedAlbum {
album_group: None,
album_type: Some(album_type.to_string()),
artists: album.artists,
available_markets: album.available_markets.unwrap_or_default(),
external_urls: album.external_urls,
href: Some(album.href),
id: Some(album.id),
images: album.images,
name: album.name,
release_date: Some(album.release_date),
release_date_precision: None,
restrictions: None,
}
}
}

#[async_trait]
Expand Down Expand Up @@ -672,4 +718,121 @@ impl GenericProvider for SpotifyProvider {
}
Err("API Client not initialized".into())
}

async fn get_album_content(
&self,
album: QueryableAlbum,
pagination: Pagination,
) -> Result<(Vec<Song>, Pagination)> {
if let Some(api_client) = &self.api_client {
let mut raw_id = album.album_id;
if let Some(id) = &raw_id {
if !self.match_id(id.clone()) {
if let Some(album_name) = album.album_name {
let res = self.search(album_name).await?;
if let Some(album) = res.albums.first() {
raw_id = album.album_id.clone();
} else {
raw_id = None;
}
} else {
raw_id = None;
}
}
}

if let Some(id) = &raw_id {
tracing::debug!("Got album id: {}", id);
let id = id.replace("spotify-album:", "");
let id = AlbumId::from_id_or_uri(id.as_str())?;
let album = api_client.album(id.clone(), None).await?;
let album_tracks = api_client
.album_track_manual(id, None, Some(pagination.limit), Some(pagination.offset))
.await?;
let mut items = album_tracks.items.clone();
let songs = items
.iter_mut()
.map(|t| {
t.album = Some(self.get_simple_album(album.clone()));
self.parse_playlist_item(self.get_full_track(t.clone()))
})
.collect::<Vec<_>>();

return Ok((songs, pagination.next_page()));
}
}
Err("API Client not initialized".into())
}

async fn get_artist_content(
&self,
artist: QueryableArtist,
pagination: Pagination,
) -> Result<(Vec<Song>, Pagination)> {
if let Some(api_client) = &self.api_client {
if let Some(next_page_token) = &pagination.token {
// TODO: Fetch next pages
let tokens = next_page_token.split(";").collect::<Vec<_>>();
return Ok((vec![], pagination.next_page_wtoken(None)));
}

let mut raw_id = artist.artist_id;
if let Some(id) = &raw_id {
if !self.match_id(id.clone()) {
if let Some(artist_name) = artist.artist_name {
let res = self.search(artist_name).await?;
if let Some(artist) = res.artists.first() {
raw_id = artist.artist_id.clone();
} else {
raw_id = None;
}
} else {
raw_id = None;
}
}
}

if let Some(id) = &raw_id {
tracing::debug!("Got artist id: {}", id);
let mut songs = vec![];
let mut next_page_tokens = vec![];
let id = id.replace("spotify-artist:", "");
let albums =
api_client.artist_albums(ArtistId::from_id_or_uri(id.as_str())?, [], None);

let album_ids = albums.filter_map(|a| async {
if let Ok(a) = a {
if let Some(id) = a.id {
return Some(id);
}
}
None
});

let album_ids = album_ids.collect::<Vec<_>>().await;

for chunk in album_ids.chunks(20) {
let albums = api_client.albums(chunk.to_vec(), None).await?;
tracing::debug!("Got albums {:?}", albums);
for a in albums {
let mut tracks = a.tracks.items.clone();
let parsed = tracks.iter_mut().map(|t| {
t.album = Some(self.get_simple_album(a.clone()));
self.parse_playlist_item(self.get_full_track(t.clone()))
});

songs.extend(parsed);

if let Some(next) = a.tracks.next {
next_page_tokens.push(next);
}
}
}

let next_page_token = next_page_tokens.join(";");
return Ok((songs, pagination.next_page_wtoken(Some(next_page_token))));
}
}
Err("API Client not initialized".into())
}
}
Loading

0 comments on commit fd16925

Please sign in to comment.