feat: add first Youtube search functionality
parent
3066753c69
commit
1b452517e3
|
@ -12,3 +12,4 @@ pretty_env_logger = "0.4.0"
|
||||||
tokio = { version = "1.18.2", features = ["rt-multi-thread", "macros"] }
|
tokio = { version = "1.18.2", features = ["rt-multi-thread", "macros"] }
|
||||||
rspotify = { version = "0.11.5", features = ["default"]}
|
rspotify = { version = "0.11.5", features = ["default"]}
|
||||||
sentry = "0.26.0"
|
sentry = "0.26.0"
|
||||||
|
invidious = "0.2.1"
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::utils::{human_readable_duration, truncate_with_dots};
|
||||||
|
|
||||||
mod spotify;
|
mod spotify;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
mod youtube;
|
||||||
|
|
||||||
static MAX_ARTISTS_CHARS: usize = 140;
|
static MAX_ARTISTS_CHARS: usize = 140;
|
||||||
|
|
||||||
|
|
|
@ -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…
Reference in New Issue