feat: music search suggestions
This commit is contained in:
parent
ef86181627
commit
bd936a8c42
10 changed files with 474 additions and 3 deletions
|
|
@ -24,6 +24,13 @@ struct QSearch<'a> {
|
|||
params: Option<Params>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct QSearchSuggestion<'a> {
|
||||
context: YTContext<'a>,
|
||||
input: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
enum Params {
|
||||
#[serde(rename = "EgWKAQIIAWoMEAMQBBAJEA4QChAF")]
|
||||
|
|
@ -182,6 +189,23 @@ impl RustyPipeQuery {
|
|||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn music_search_suggestion(&self, query: &str) -> Result<Vec<String>, Error> {
|
||||
let context = self.get_context(ClientType::DesktopMusic, true, None).await;
|
||||
let request_body = QSearchSuggestion {
|
||||
context,
|
||||
input: query,
|
||||
};
|
||||
|
||||
self.execute_request::<response::MusicSearchSuggestion, _, _>(
|
||||
ClientType::DesktopMusic,
|
||||
"music_search_suggestion",
|
||||
query,
|
||||
"music/get_search_suggestions",
|
||||
&request_body,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl MapResponse<MusicSearchResult> for response::MusicSearch {
|
||||
|
|
@ -293,6 +317,41 @@ impl<T: FromYtItem> MapResponse<MusicSearchFiltered<T>> for response::MusicSearc
|
|||
}
|
||||
}
|
||||
|
||||
impl MapResponse<Vec<String>> for response::MusicSearchSuggestion {
|
||||
fn map_response(
|
||||
self,
|
||||
_id: &str,
|
||||
_lang: crate::param::Language,
|
||||
_deobf: Option<&crate::deobfuscate::Deobfuscator>,
|
||||
) -> Result<MapResult<Vec<String>>, ExtractionError> {
|
||||
let items = self
|
||||
.contents
|
||||
.into_iter()
|
||||
.next()
|
||||
.map(|content| {
|
||||
content
|
||||
.search_suggestions_section_renderer
|
||||
.contents
|
||||
.into_iter()
|
||||
.filter_map(|itm| {
|
||||
match itm {
|
||||
response::music_search::SearchSuggestionItem::SearchSuggestionRenderer {
|
||||
suggestion,
|
||||
} => Some(suggestion),
|
||||
response::music_search::SearchSuggestionItem::None => None,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(MapResult {
|
||||
c: items,
|
||||
warnings: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{fs::File, io::BufReader, path::Path};
|
||||
|
|
@ -417,4 +476,26 @@ mod tests {
|
|||
|
||||
insta::assert_ron_snapshot!(format!("map_music_search_playlists_{}", name), map_res.c);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::default("default")]
|
||||
#[case::empty("empty")]
|
||||
fn map_music_search_suggestion(#[case] name: &str) {
|
||||
let filename = format!("testfiles/music_search/suggestion_{}.json", name);
|
||||
let json_path = Path::new(&filename);
|
||||
let json_file = File::open(json_path).unwrap();
|
||||
|
||||
let suggestion: response::MusicSearchSuggestion =
|
||||
serde_json::from_reader(BufReader::new(json_file)).unwrap();
|
||||
let map_res: MapResult<Vec<String>> =
|
||||
suggestion.map_response("", Language::En, None).unwrap();
|
||||
|
||||
assert!(
|
||||
map_res.warnings.is_empty(),
|
||||
"deserialization/mapping warnings: {:?}",
|
||||
map_res.warnings
|
||||
);
|
||||
|
||||
insta::assert_ron_snapshot!(format!("map_music_search_suggestion_{}", name), map_res.c);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ pub(crate) use music_details::MusicRelated;
|
|||
pub(crate) use music_item::MusicContinuation;
|
||||
pub(crate) use music_playlist::MusicPlaylist;
|
||||
pub(crate) use music_search::MusicSearch;
|
||||
pub(crate) use music_search::MusicSearchSuggestion;
|
||||
pub(crate) use player::Player;
|
||||
pub(crate) use playlist::Playlist;
|
||||
pub(crate) use playlist::PlaylistCont;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,16 @@ pub(crate) struct MusicSearch {
|
|||
pub contents: Contents,
|
||||
}
|
||||
|
||||
/// Response model for YouTube Music suggestion
|
||||
#[serde_as]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct MusicSearchSuggestion {
|
||||
#[serde(default)]
|
||||
#[serde_as(as = "VecSkipError<_>")]
|
||||
pub contents: Vec<SearchSuggestionsSection>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Contents {
|
||||
|
|
@ -45,3 +55,21 @@ pub(crate) struct ShowingResultsForRenderer {
|
|||
#[serde_as(as = "Text")]
|
||||
pub corrected_query: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct SearchSuggestionsSection {
|
||||
pub search_suggestions_section_renderer: ContentsRenderer<SearchSuggestionItem>,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) enum SearchSuggestionItem {
|
||||
SearchSuggestionRenderer {
|
||||
#[serde_as(as = "Text")]
|
||||
suggestion: String,
|
||||
},
|
||||
#[serde(other, deserialize_with = "deserialize_ignore_any")]
|
||||
None,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
source: src/client/music_search.rs
|
||||
expression: map_res.c
|
||||
---
|
||||
[
|
||||
"taylor swift",
|
||||
"tkkg",
|
||||
"techno",
|
||||
"t low",
|
||||
"the weeknd",
|
||||
"tiktok songs",
|
||||
"toten hosen",
|
||||
]
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
source: src/client/music_search.rs
|
||||
expression: map_res.c
|
||||
---
|
||||
[]
|
||||
Reference in a new issue