feat: add first engine song search
parent
06fff8e972
commit
ba2b689e77
124
src/engine.rs
124
src/engine.rs
|
@ -1,37 +1,15 @@
|
||||||
use crate::spotify::ContentKind;
|
use crate::spotify::ContentKind;
|
||||||
use crate::{spotify, youtube};
|
use crate::youtube::Video;
|
||||||
use chrono::{Date, Utc};
|
use crate::{spotify, youtube, TrackInfo};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
pub(crate) enum MusicData {
|
|
||||||
Track(Track),
|
|
||||||
Album(Album),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct Track {
|
pub(crate) struct TrackItem {
|
||||||
name: String,
|
spotify_track: Option<TrackInfo>,
|
||||||
authors: Vec<Author>,
|
youtube_track: Option<Vec<Video>>,
|
||||||
duration: Duration,
|
|
||||||
album: Option<Album>,
|
|
||||||
description: Option<String>,
|
|
||||||
lyrics: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct Album {
|
|
||||||
name: String,
|
|
||||||
authors: Vec<Author>,
|
|
||||||
description: Option<String>,
|
|
||||||
year: Option<Date<Utc>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct Author {
|
|
||||||
name: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The enum holds all the currently supported type of Id which the engine can search for
|
// The enum holds all the currently supported type of Id which the engine can search for
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) enum ServiceIdKind {
|
pub(crate) enum ServiceIdKind {
|
||||||
Spotify(String),
|
Spotify(String),
|
||||||
Youtube(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
|
// This struct will allow us in the future to search, cache and store data and metadata regarding
|
||||||
// tracks, albums and playlists
|
// tracks, albums and playlists
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct MusicEngine {
|
pub(crate) struct MusicEngine {
|
||||||
spotify: spotify::Client,
|
spotify: spotify::Client,
|
||||||
youtube: youtube::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")
|
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<MusicData> {
|
pub(crate) async fn search_song_by_id(&self, id: &str) -> Option<TrackItem> {
|
||||||
match id {
|
let entry_kind = spotify::get_entry_kind(id);
|
||||||
ServiceIdKind::Spotify(id) => {
|
let track_info = match entry_kind {
|
||||||
let entry_kind = spotify::get_entry_kind(id.as_str());
|
|
||||||
match entry_kind {
|
|
||||||
None => None,
|
|
||||||
Some(entry) => match entry {
|
Some(entry) => match entry {
|
||||||
ContentKind::Track(id) => {
|
ContentKind::Track(id) => self.spotify.get_track(id.as_str()).await,
|
||||||
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,
|
_ => 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;
|
||||||
_ => todo!("Search type not present yet"), //TODO implement
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_search_track_by_spotify_id() {
|
||||||
|
todo!("Implement me!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::any::Any;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub enum ContentKind {
|
pub enum ContentKind {
|
||||||
Track(String),
|
Track(String),
|
||||||
Album(String),
|
Album(String),
|
||||||
|
@ -14,17 +15,20 @@ pub enum ContentKind {
|
||||||
Episode(String),
|
Episode(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub enum PlayableKind {
|
pub enum PlayableKind {
|
||||||
Track(TrackInfo),
|
Track(TrackInfo),
|
||||||
Podcast(EpisodeInfo),
|
Podcast(EpisodeInfo),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct TrackInfo {
|
pub struct TrackInfo {
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
pub(crate) artists: Vec<String>,
|
pub(crate) artists: Vec<String>,
|
||||||
pub(crate) duration: Duration,
|
pub(crate) duration: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct EpisodeInfo {
|
pub struct EpisodeInfo {
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
pub(crate) show: String,
|
pub(crate) show: String,
|
||||||
|
@ -34,6 +38,7 @@ pub struct EpisodeInfo {
|
||||||
pub(crate) release_date: String,
|
pub(crate) release_date: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct AlbumInfo {
|
pub struct AlbumInfo {
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
pub(crate) artists: Vec<String>,
|
pub(crate) artists: Vec<String>,
|
||||||
|
@ -41,6 +46,7 @@ pub struct AlbumInfo {
|
||||||
pub(crate) tracks: Vec<TrackInfo>,
|
pub(crate) tracks: Vec<TrackInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct PlaylistInfo {
|
pub struct PlaylistInfo {
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
pub(crate) artists: Vec<String>,
|
pub(crate) artists: Vec<String>,
|
||||||
|
@ -70,6 +76,7 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_track(&self, id: &str) -> Option<TrackInfo> {
|
pub async fn get_track(&self, id: &str) -> Option<TrackInfo> {
|
||||||
|
// 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) {
|
let track_id = match TrackId::from_id(id) {
|
||||||
Ok(track) => track,
|
Ok(track) => track,
|
||||||
Err(_e) => return None,
|
Err(_e) => return None,
|
||||||
|
|
|
@ -2,21 +2,22 @@ use std::error::Error;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub(crate) struct VideoSearch {
|
pub(crate) struct VideoSearch {
|
||||||
items: Vec<Video>,
|
pub(crate) items: Vec<Video>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct Video {
|
pub(crate) struct Video {
|
||||||
title: String,
|
title: String,
|
||||||
videoId: String,
|
video_id: String,
|
||||||
author: String,
|
author: String,
|
||||||
authorId: String,
|
author_id: String,
|
||||||
authorUrl: String,
|
author_url: String,
|
||||||
lengthSeconds: u64,
|
length_seconds: u64,
|
||||||
description: String,
|
description: String,
|
||||||
descriptionHtml: String,
|
description_html: String,
|
||||||
viewCount: u64,
|
view_count: u64,
|
||||||
published: u64,
|
published: u64,
|
||||||
publishedText: String,
|
published_text: String,
|
||||||
live_now: bool,
|
live_now: bool,
|
||||||
paid: bool,
|
paid: bool,
|
||||||
premium: bool,
|
premium: bool,
|
||||||
|
@ -88,16 +89,16 @@ impl Client {
|
||||||
premium,
|
premium,
|
||||||
} => Some(Video {
|
} => Some(Video {
|
||||||
title: title.clone(),
|
title: title.clone(),
|
||||||
videoId: videoId.clone(),
|
video_id: videoId.clone(),
|
||||||
author: author.clone(),
|
author: author.clone(),
|
||||||
authorId: authorId.clone(),
|
author_id: authorId.clone(),
|
||||||
authorUrl: authorUrl.clone(),
|
author_url: authorUrl.clone(),
|
||||||
lengthSeconds: lengthSeconds.clone(),
|
length_seconds: lengthSeconds.clone(),
|
||||||
description: description.clone(),
|
description: description.clone(),
|
||||||
descriptionHtml: descriptionHtml.clone(),
|
description_html: descriptionHtml.clone(),
|
||||||
viewCount: viewCount.clone(),
|
view_count: viewCount.clone(),
|
||||||
published: published.clone(),
|
published: published.clone(),
|
||||||
publishedText: publishedText.clone(),
|
published_text: publishedText.clone(),
|
||||||
live_now: liveNow.clone(),
|
live_now: liveNow.clone(),
|
||||||
paid: paid.clone(),
|
paid: paid.clone(),
|
||||||
premium: premium.clone(),
|
premium: premium.clone(),
|
||||||
|
|
Loading…
Reference in New Issue