feat: add youtube search #8
| @ -12,3 +12,4 @@ pretty_env_logger = "0.4.0" | ||||
| tokio = { version =  "1.18.2", features = ["rt-multi-thread", "macros"] } | ||||
| rspotify = { version = "0.11.5", features = ["default"]} | ||||
| sentry = "0.26.0" | ||||
| invidious = "0.2.1" | ||||
|  | ||||
| @ -11,6 +11,7 @@ use crate::utils::{human_readable_duration, truncate_with_dots}; | ||||
| 
 | ||||
| mod spotify; | ||||
| mod utils; | ||||
| mod youtube; | ||||
| 
 | ||||
| static MAX_ARTISTS_CHARS: usize = 140; | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										106
									
								
								src/youtube/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								src/youtube/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,106 @@ | ||||
| use invidious::asynchronous::Client; | ||||
| use std::error::Error; | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub(crate) struct YoutubeClient { | ||||
|     client: Client, | ||||
| } | ||||
| 
 | ||||
| pub(crate) struct VideoSearch { | ||||
|     items: Vec<Video>, | ||||
| } | ||||
| 
 | ||||
| pub(crate) struct Video { | ||||
|     title: String, | ||||
|     videoId: String, | ||||
|     author: String, | ||||
|     authorId: String, | ||||
|     authorUrl: String, | ||||
|     lengthSeconds: u64, | ||||
|     description: String, | ||||
|     descriptionHtml: String, | ||||
|     viewCount: u64, | ||||
|     published: u64, | ||||
|     publishedText: String, | ||||
|     liveNow: bool, | ||||
|     paid: bool, | ||||
|     premium: bool, | ||||
| } | ||||
| 
 | ||||
| type SearchSortBy = str; | ||||
| 
 | ||||
| const BY_RELEVANCE: &SearchSortBy = "relevance"; | ||||
| const BY_RATING: &SearchSortBy = "rating"; | ||||
| const BY_UPLOAD_DATE: &SearchSortBy = "upload_date"; | ||||
| const BY_VIEW_COUNT: &SearchSortBy = "view_count"; | ||||
| 
 | ||||
| impl YoutubeClient { | ||||
|     async fn new() -> YoutubeClient { | ||||
|         // TODO check for a stable instance
 | ||||
|         let client = Client::new(String::from("https://vid.puffyan.us")); | ||||
| 
 | ||||
|         YoutubeClient { client } | ||||
|     } | ||||
| 
 | ||||
|     async fn search_video( | ||||
|         &self, | ||||
|         keyword: &str, | ||||
|         sort_by: Option<&SearchSortBy>, | ||||
|     ) -> Result<VideoSearch, Box<dyn Error>> { | ||||
|         let mut query = Vec::<String>::new(); | ||||
|         query.push(format!("{}={}", "q", keyword)); | ||||
|         match sort_by { | ||||
|             Some(sorting_type) => query.push(format!("{}={}", "sort_by", sorting_type)), | ||||
|             None => {} | ||||
|         } | ||||
|         query.push(format!("{}={}", "type", "video")); | ||||
| 
 | ||||
|         let generated_query = query.join(","); | ||||
|         let search = self.client.search(Some(generated_query.as_str())).await?; | ||||
| 
 | ||||
|         let videos: Vec<Video> = search | ||||
|             .items | ||||
|             .iter() | ||||
|             .map(|search_result| match search_result { | ||||
|                 invidious::structs::hidden::SearchItem::Video { | ||||
|                     title, | ||||
|                     videoId, | ||||
|                     author, | ||||
|                     authorId, | ||||
|                     authorUrl, | ||||
|                     lengthSeconds, | ||||
|                     videoThumbnails, | ||||
|                     description, | ||||
|                     descriptionHtml, | ||||
|                     viewCount, | ||||
|                     published, | ||||
|                     publishedText, | ||||
|                     liveNow, | ||||
|                     paid, | ||||
|                     premium, | ||||
|                 } => Some(Video { | ||||
|                     title: title.clone(), | ||||
|                     videoId: videoId.clone(), | ||||
|                     author: author.clone(), | ||||
|                     authorId: authorId.clone(), | ||||
|                     authorUrl: authorUrl.clone(), | ||||
|                     lengthSeconds: lengthSeconds.clone(), | ||||
|                     description: description.clone(), | ||||
|                     descriptionHtml: descriptionHtml.clone(), | ||||
|                     viewCount: viewCount.clone(), | ||||
|                     published: published.clone(), | ||||
|                     publishedText: publishedText.clone(), | ||||
|                     liveNow: liveNow.clone(), | ||||
|                     paid: paid.clone(), | ||||
|                     premium: premium.clone(), | ||||
|                 }), | ||||
|                 _ => None, | ||||
|             }) | ||||
|             .filter(|x| x.is_some()) | ||||
|             .map(|x| x.unwrap()) | ||||
|             .filter(|x| !x.premium && !x.paid && !x.liveNow) | ||||
|             .collect(); | ||||
| 
 | ||||
|         Result::Ok(VideoSearch { items: videos }) | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user