use std::time::Duration; use rspotify::{ClientCredsSpotify, Credentials}; use rspotify::model::{AlbumId, FullTrack, PlaylistId, TrackId}; use rspotify::model::PlayableItem::{Episode, Track}; use rspotify::prelude::*; pub enum SpotifyKind { Track(String), Album(String), Playlist(String), Podcast(String), Episode(String) } pub enum PlayableKind { Track(TrackInfo), Podcast(EpisodeInfo) } pub struct TrackInfo { pub(crate) name: String, pub(crate) artists: Vec, pub(crate) duration: Duration, } pub struct EpisodeInfo { pub(crate) name: String, pub(crate) show: String, pub(crate) duration: Duration, pub(crate) description: String, pub(crate) languages: Vec, pub(crate) release_date: String } pub struct AlbumInfo { pub(crate) name: String, pub(crate) artists: Vec, pub(crate) genres: Vec, pub(crate) tracks: Vec, } pub struct PlaylistInfo { pub(crate) name: String, pub(crate) artists: Vec, pub(crate) tracks: Vec, } fn get_id_in_url(url: &str) -> Option<&str> { url.rsplit('/') .next() .and_then(|x| x.split(' ').next()) .and_then(|x| x.split('?').next()) } fn extract_artists_from_tracks(tracks: Vec) -> Vec { tracks .iter() .flat_map(|t| t.artists.iter().map(|a| a.name.clone())) .collect() } pub fn get_entry_kind(url: &str) -> Option { if url.contains("https://open.spotify.com/track/") { let track_id = get_id_in_url(url); return match track_id { Some(id) => Some(SpotifyKind::Track(id.to_string())), None => None, }; } if url.contains("https://open.spotify.com/album/") { let album_id = get_id_in_url(url); return match album_id { Some(id) => Some(SpotifyKind::Album(id.to_string())), None => None, }; } if url.contains("https://open.spotify.com/playlist/") { let playlist_id = get_id_in_url(url); return match playlist_id { Some(id) => Some(SpotifyKind::Playlist(id.to_string())), None => None, }; } if url.contains("https://open.spotify.com/show/") { let playlist_id = get_id_in_url(url); return match playlist_id { Some(id) => Some(SpotifyKind::Podcast(id.to_string())), None => None, }; } if url.contains("https://open.spotify.com/episode/") { let playlist_id = get_id_in_url(url); return match playlist_id { Some(id) => Some(SpotifyKind::Episode(id.to_string())), None => None, }; } return None; } pub async fn get_client() -> Box { let spotify_creds = Credentials::from_env().expect("RSPOTIFY_CLIENT_ID and RSPOTIFY_CLIENT_SECRET not found."); let mut spotify = ClientCredsSpotify::new(spotify_creds); spotify.request_token().await.unwrap(); Box::new(spotify) } pub async fn get_track(spotify: Box, id: &String) -> Option { let track_id = match TrackId::from_id(id.as_str()) { Ok(track) => track, Err(_e) => return None }; match spotify.track(&track_id).await { Ok(track) => Some(TrackInfo{ name: track.name, artists: track.artists.iter().map(|x| x.name.clone()).collect(), duration: track.duration, }), Err(_e) => None } } pub async fn get_album(spotify: Box, id: &String) -> Option { let album_id = match AlbumId::from_id(id.as_str()) { Ok(album) => album, Err(_e) => return None }; match spotify.album(&album_id).await { Ok(album) => Some(AlbumInfo { name: album.name, artists: album.artists.iter().map(|x| x.name.clone()).collect(), genres: album.genres, tracks: album .tracks .items .iter() .map(|t| TrackInfo { name: t.name.clone(), artists: t.artists.iter().map(|x| x.name.clone()).collect(), duration: t.duration, }) .collect(), }), Err(_e) => None, } } pub async fn get_playlist(spotify: Box, id: &String) -> Option { let playlist_id = match PlaylistId::from_id(id.as_str()) { Ok(playlist) => playlist, Err(_e) => return None }; match spotify.playlist(&playlist_id, None, None).await { Ok(playlist) => Some(PlaylistInfo { name: playlist.name, artists: playlist .tracks .items .iter() .flat_map(|p| { match &p.track { Some(t) => match t { Track(t) => t.artists.iter().map(|a| { a.name.clone() }).collect(), Episode(e) => vec![e.show.publisher.clone()] } None => Vec::new(), }.into_iter() }).collect(), tracks: playlist .tracks .items .iter() .map(|p| { match &p.track { Some(t) => match t { Track(t) => Some(PlayableKind::Track(TrackInfo{ name: t.name.clone(), artists: t.artists.iter().map(|a| a.name.clone()).collect(), duration: t.duration })), Episode(e) => Some(PlayableKind::Podcast(EpisodeInfo{ name: e.name.clone(), show: e.show.name.clone(), duration: e.duration, description: e.description.clone(), languages: e.languages.clone(), release_date: e.release_date.clone() })) }, None => None } }) .filter(|i| i.is_some()) .map(|i| i.unwrap()) .collect(), }), Err(_e) => None, } }