Skip to content

Commit

Permalink
04.05.2024
Browse files Browse the repository at this point in the history
* `suggestion` function added to blocking API
* `suggestion` function language parameter implemented
  • Loading branch information
Mithronn committed May 4, 2024
1 parent 048c979 commit 970f512
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 25 deletions.
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,13 @@ required-features = ["blocking"]
name = "download_with_ffmpeg"
path = "examples/download_with_ffmpeg.rs"
required-features = ["ffmpeg"]

[[example]]
name = "search"
path = "examples/search.rs"
required-features = ["search"]

[[example]]
name = "suggestion"
path = "examples/suggestion.rs"
required-features = ["search"]
6 changes: 2 additions & 4 deletions examples/suggestion.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use rusty_ytdl::search::YouTube;

#[tokio::main]
async fn main() {
use rusty_ytdl::search::YouTube;

let youtube = YouTube::new().unwrap();

let res = youtube.suggestion("i know ").await;
let res = youtube.suggestion("i know ", None).await;

println!("{res:#?}");
}
}
22 changes: 20 additions & 2 deletions src/blocking/search/youtube.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pub use crate::search::{
Channel, EmbedOptions, PlaylistSearchOptions, RequestOptions, SearchOptions, SearchResult,
SearchType, Video,
Channel, EmbedOptions, LanguageTags, PlaylistSearchOptions, RequestOptions, SearchOptions,
SearchResult, SearchType, Video,
};
use crate::search::{Playlist as AsyncPlaylist, YouTube as AsyncYouTube};
use crate::{block_async, VideoError};
Expand Down Expand Up @@ -47,6 +47,24 @@ impl YouTube {
) -> Result<Option<SearchResult>, VideoError> {
Ok(block_async!(self.0.search_one(query, search_options))?)
}

/// Fetch search suggestion with specific `query` and `language`.
/// If language is None, then will use the default language for suggestions
/// # Example
/// ```ignore
/// let youtube = YouTube::new().unwrap();
///
/// let res = youtube.suggestion("i know ");
///
/// println!("{res:#?}");
/// ```
pub fn suggestion(
&self,
query: impl Into<String>,
language: Option<LanguageTags>,
) -> Result<Vec<String>, VideoError> {
Ok(block_async!(self.0.suggestion(query, language))?)
}
}

impl std::ops::Deref for YouTube {
Expand Down
177 changes: 177 additions & 0 deletions src/search/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,180 @@ pub use youtube::{
Channel, EmbedOptions, Playlist, PlaylistSearchOptions, RequestOptions, SearchOptions,
SearchResult, SearchType, Video, YouTube,
};

#[derive(Debug, Clone)]
pub enum LanguageTags {
AF,
AM,
AR,
AS,
AZ,
BE,
BG,
BN,
BS,
CA,
CS,
DA,
DE,
EL,
EnGB,
EnIN,
EN,
ES,
Es419,
EsUS,
ET,
EU,
FA,
FI,
FIL,
FrCA,
FR,
GL,
GU,
HI,
HR,
HU,
HY,
ID,
IS,
IT,
IW,
JA,
KA,
KK,
KM,
KN,
KO,
KY,
LO,
LT,
LV,
MK,
ML,
MN,
MR,
MS,
MY,
NO,
NE,
NL,
OR,
PA,
PL,
PT,
PtPT,
RO,
RU,
SI,
SK,
SL,
SQ,
SrLATN,
SR,
SV,
SW,
TA,
TE,
TH,
TR,
UK,
UR,
UZ,
VI,
ZhCN,
ZhHK,
ZhTW,
ZU,
}

impl std::fmt::Display for LanguageTags {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
LanguageTags::AF => write!(f, "af"),
LanguageTags::AM => write!(f, "am"),
LanguageTags::AR => write!(f, "ar"),
LanguageTags::AS => write!(f, "as"),
LanguageTags::AZ => write!(f, "az"),
LanguageTags::BE => write!(f, "be"),
LanguageTags::BG => write!(f, "bg"),
LanguageTags::BN => write!(f, "bn"),
LanguageTags::BS => write!(f, "bs"),
LanguageTags::CA => write!(f, "ca"),
LanguageTags::CS => write!(f, "cs"),
LanguageTags::DA => write!(f, "da"),
LanguageTags::DE => write!(f, "de"),
LanguageTags::EL => write!(f, "el"),
LanguageTags::EnGB => write!(f, "en-GB"),
LanguageTags::EnIN => write!(f, "en-IN"),
LanguageTags::EN => write!(f, "en"),
LanguageTags::ES => write!(f, "es"),
LanguageTags::Es419 => write!(f, "es-419"),
LanguageTags::EsUS => write!(f, "es-US"),
LanguageTags::ET => write!(f, "et"),
LanguageTags::EU => write!(f, "eu"),
LanguageTags::FA => write!(f, "fa"),
LanguageTags::FI => write!(f, "fi"),
LanguageTags::FIL => write!(f, "fil"),
LanguageTags::FrCA => write!(f, "fr-CA"),
LanguageTags::FR => write!(f, "fr"),
LanguageTags::GL => write!(f, "gl"),
LanguageTags::GU => write!(f, "gu"),
LanguageTags::HI => write!(f, "hi"),
LanguageTags::HR => write!(f, "hr"),
LanguageTags::HU => write!(f, "hu"),
LanguageTags::HY => write!(f, "hy"),
LanguageTags::ID => write!(f, "id"),
LanguageTags::IS => write!(f, "is"),
LanguageTags::IT => write!(f, "it"),
LanguageTags::IW => write!(f, "iw"),
LanguageTags::JA => write!(f, "ja"),
LanguageTags::KA => write!(f, "ka"),
LanguageTags::KK => write!(f, "kk"),
LanguageTags::KM => write!(f, "km"),
LanguageTags::KN => write!(f, "kn"),
LanguageTags::KO => write!(f, "ko"),
LanguageTags::KY => write!(f, "ky"),
LanguageTags::LO => write!(f, "lo"),
LanguageTags::LT => write!(f, "lt"),
LanguageTags::LV => write!(f, "lv"),
LanguageTags::MK => write!(f, "mk"),
LanguageTags::ML => write!(f, "ml"),
LanguageTags::MN => write!(f, "mn"),
LanguageTags::MR => write!(f, "mr"),
LanguageTags::MS => write!(f, "ms"),
LanguageTags::MY => write!(f, "my"),
LanguageTags::NO => write!(f, "no"),
LanguageTags::NE => write!(f, "ne"),
LanguageTags::NL => write!(f, "nl"),
LanguageTags::OR => write!(f, "or"),
LanguageTags::PA => write!(f, "pa"),
LanguageTags::PL => write!(f, "pl"),
LanguageTags::PT => write!(f, "pt"),
LanguageTags::PtPT => write!(f, "pt-PT"),
LanguageTags::RO => write!(f, "ro"),
LanguageTags::RU => write!(f, "ru"),
LanguageTags::SI => write!(f, "si"),
LanguageTags::SK => write!(f, "sk"),
LanguageTags::SL => write!(f, "sl"),
LanguageTags::SQ => write!(f, "sq"),
LanguageTags::SrLATN => write!(f, "sr-Latn"),
LanguageTags::SR => write!(f, "sr"),
LanguageTags::SV => write!(f, "sv"),
LanguageTags::SW => write!(f, "sw"),
LanguageTags::TA => write!(f, "ta"),
LanguageTags::TE => write!(f, "te"),
LanguageTags::TH => write!(f, "th"),
LanguageTags::TR => write!(f, "tr"),
LanguageTags::UK => write!(f, "uk"),
LanguageTags::UR => write!(f, "ur"),
LanguageTags::UZ => write!(f, "uz"),
LanguageTags::VI => write!(f, "vi"),
LanguageTags::ZhCN => write!(f, "zh-CN"),
LanguageTags::ZhHK => write!(f, "zh-HK"),
LanguageTags::ZhTW => write!(f, "zh-TW"),
LanguageTags::ZU => write!(f, "zu"),
}
}
}
39 changes: 22 additions & 17 deletions src/search/youtube.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ use scraper::{Html, Selector};
use serde::{Deserialize, Serialize};
use urlencoding::encode;

use super::LanguageTags;
pub use crate::structs::RequestOptions;
use crate::{
constants::DEFAULT_HEADERS,
structs::VideoError,
utils::{get_html, get_random_v6_ip, time_to_ms},
Thumbnail,
};

pub use crate::structs::RequestOptions;

const DEFAULT_INNERTUBE_KEY: &str = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8";
const DEFAULT_CLIENT_VERSOIN: &str = "2.20230331.00.00";
const SAFE_SEARCH_COOKIE: &str = "PREF=f2=8000000";
Expand Down Expand Up @@ -201,39 +201,44 @@ impl YouTube {
Ok(res.first().cloned())
}

/// Fetch search suggestion with specific `query`
/// Fetch search suggestion with specific `query` and `language`.
/// If language is None, then will use the default language for suggestions
/// # Example
/// ```ignore
/// let youtube = YouTube::new().unwrap();
///
///
/// let res = youtube.suggestion("i know ").await;
///
///
/// println!("{res:#?}");
/// ```
pub async fn suggestion(
&self,
query: impl Into<String>,
language: Option<LanguageTags>,
) -> Result<Vec<String>, VideoError> {
let query: String = query.into();
let url = format!(
"https://suggestqueries-clients6.youtube.com/complete/search?client=android&q={query}",
query = encode(query.trim())
);

let mut url = url::Url::parse_with_params(
"https://suggestqueries-clients6.youtube.com/complete/search",
&[("client", "android"), ("q", query.trim())],
)
.map_err(VideoError::URLParseError)?;

if let Some(language) = language {
url.query_pairs_mut()
.append_pair("hl", &language.to_string());
}

let body = get_html(&self.client, url, None).await?;

let serde_value = serde_json::from_str::<serde_json::Value>(&body).unwrap();

let suggestion = serde_value
.as_array()
.unwrap()
.get(1)
.unwrap()
.as_array()
.unwrap()
.iter()
.map(|x| x.to_string())
.collect();
.and_then(|x| x.get(1))
.and_then(|x| x.as_array())
.map(|x| x.iter().map(|x| x.to_string()).collect())
.unwrap_or_default();

Ok(suggestion)
}
Expand Down
4 changes: 2 additions & 2 deletions tests/suggestion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ async fn suggestion() {

let youtube = YouTube::new().unwrap();

let res = youtube.suggestion("i know ").await;
let res = youtube.suggestion("i know ", None).await;

println!("{res:#?}");
}
}

0 comments on commit 970f512

Please sign in to comment.