feat: add first Youtube search functionality

pull/8/head
Davide Polonio 2022-05-31 17:06:39 +02:00
parent 3066753c69
commit 1b452517e3
3 changed files with 108 additions and 0 deletions

View File

@ -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"

View File

@ -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
View 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 })
}
}