diff --git a/src/engine.rs b/src/engine.rs index 08b2174..9348114 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,37 +1,15 @@ use crate::spotify::ContentKind; -use crate::{spotify, youtube}; -use chrono::{Date, Utc}; -use std::time::Duration; - -pub(crate) enum MusicData { - Track(Track), - Album(Album), -} +use crate::youtube::Video; +use crate::{spotify, youtube, TrackInfo}; #[derive(Debug, Clone)] -pub(crate) struct Track { - name: String, - authors: Vec, - duration: Duration, - album: Option, - description: Option, - lyrics: Option, -} - -#[derive(Debug, Clone)] -pub(crate) struct Album { - name: String, - authors: Vec, - description: Option, - year: Option>, -} - -#[derive(Debug, Clone)] -pub(crate) struct Author { - name: String, +pub(crate) struct TrackItem { + spotify_track: Option, + youtube_track: Option>, } // The enum holds all the currently supported type of Id which the engine can search for +#[derive(Debug, Clone)] pub(crate) enum ServiceIdKind { Spotify(String), Youtube(String), @@ -40,6 +18,7 @@ pub(crate) enum ServiceIdKind { // This struct will allow us in the future to search, cache and store data and metadata regarding // tracks, albums and playlists +#[derive(Debug, Clone)] pub(crate) struct MusicEngine { spotify: spotify::Client, youtube: youtube::Client, @@ -63,56 +42,57 @@ impl MusicEngine { } } - pub(crate) async fn search_by_name(&self, name: &str) { + pub(crate) async fn search_song_by_name(&self, name: &str) { todo!("In the future it would be possible to search for all metadata on a record from this call") } - pub(crate) async fn search_by_id(&self, id: ServiceIdKind) -> Option { - match id { - ServiceIdKind::Spotify(id) => { - let entry_kind = spotify::get_entry_kind(id.as_str()); - match entry_kind { - None => None, - Some(entry) => match entry { - ContentKind::Track(id) => { - self.spotify.get_track(id.as_str()).await.map(|track| { - MusicData::Track(Track { - name: track.name.clone(), - authors: track - .artists - .iter() - .map(|artist| Author { - name: artist.clone(), - }) - .collect(), - duration: track.duration.clone(), - album: None, // FIXME missing metadata - description: None, // FIXME missing metadata - lyrics: None, // FIXME missing metadata - }) - }) - } - ContentKind::Album(id) => { - self.spotify.get_album(id.as_str()).await.map(|album| { - MusicData::Album(Album { - name: album.name.clone(), - authors: album - .artists - .iter() - .map(|artist| Author { - name: artist.clone(), - }) - .collect(), - description: None, // FIXME missing metadata - year: None, // FIXME missing metadata - }) - }) - } - _ => None, - }, - } - } - _ => todo!("Search type not present yet"), //TODO implement + pub(crate) async fn search_song_by_id(&self, id: &str) -> Option { + let entry_kind = spotify::get_entry_kind(id); + let track_info = match entry_kind { + Some(entry) => match entry { + ContentKind::Track(id) => self.spotify.get_track(id.as_str()).await, + _ => None, + }, + None => None, + }; + + if track_info.is_some() { + let ti = track_info.unwrap(); + let youtube_search = match self + .youtube + .search_video( + format!( + "{} {}", + ti.artists + .get(0) + .map(|artist| format!("{} -", artist)) + .unwrap_or("".to_string()), + ti.name + ) + .as_str(), + None, + ) + .await + { + Err(_) => None, + Ok(search) => Some(search), + }; + + return Some(TrackItem { + spotify_track: Some(ti), + youtube_track: youtube_search.map(|search| search.items), + }); } + return None; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_search_track_by_spotify_id() { + todo!("Implement me!") } } diff --git a/src/spotify/mod.rs b/src/spotify/mod.rs index a392137..9d04991 100644 --- a/src/spotify/mod.rs +++ b/src/spotify/mod.rs @@ -6,6 +6,7 @@ use std::any::Any; use std::sync::Arc; use std::time::Duration; +#[derive(Debug, Clone)] pub enum ContentKind { Track(String), Album(String), @@ -14,17 +15,20 @@ pub enum ContentKind { Episode(String), } +#[derive(Debug, Clone)] pub enum PlayableKind { Track(TrackInfo), Podcast(EpisodeInfo), } +#[derive(Debug, Clone)] pub struct TrackInfo { pub(crate) name: String, pub(crate) artists: Vec, pub(crate) duration: Duration, } +#[derive(Debug, Clone)] pub struct EpisodeInfo { pub(crate) name: String, pub(crate) show: String, @@ -34,6 +38,7 @@ pub struct EpisodeInfo { pub(crate) release_date: String, } +#[derive(Debug, Clone)] pub struct AlbumInfo { pub(crate) name: String, pub(crate) artists: Vec, @@ -41,6 +46,7 @@ pub struct AlbumInfo { pub(crate) tracks: Vec, } +#[derive(Debug, Clone)] pub struct PlaylistInfo { pub(crate) name: String, pub(crate) artists: Vec, @@ -70,6 +76,7 @@ impl Client { } pub async fn get_track(&self, id: &str) -> Option { + // FIXME should we really return Option here? We're hiding a possible error or a entry not found let track_id = match TrackId::from_id(id) { Ok(track) => track, Err(_e) => return None, diff --git a/src/youtube/mod.rs b/src/youtube/mod.rs index 2077244..3d2d98d 100644 --- a/src/youtube/mod.rs +++ b/src/youtube/mod.rs @@ -2,21 +2,22 @@ use std::error::Error; use std::sync::Arc; pub(crate) struct VideoSearch { - items: Vec