use super::*;
use std::borrow::Cow;
const SPECIALIZED_PLAYLIST_EXACT_MATCH_PARAMS: &str = "BagwQDhAKEAMQBBAJEAU%3D";
const SPECIALIZED_PLAYLIST_WITH_SUGGESTIONS_PARAMS: &str = "BQgIIAWoMEA4QChADEAQQCRAF";
const SPECIALIZED_PLAYLIST_PREFIX_PARAMS: &str = "EgeKAQQoA";
#[derive(PartialEq, Debug, Clone)]
pub struct SearchQuery<'a, S: SearchType> {
query: Cow<'a, str>,
spelling_mode: SpellingMode,
searchtype: S,
}
pub trait SearchType: Default {
fn specialised_params(&self, spelling_mode: &SpellingMode) -> Option<Cow<str>>;
}
pub trait FilteredSearchType: Default {
fn filtered_param_bits(&self) -> Cow<str>;
fn filtered_spelling_param(&self, spelling_mode: &SpellingMode) -> Cow<str> {
match spelling_mode {
SpellingMode::ExactMatch => "AWoMEA4QChADEAQQCRAF".into(),
SpellingMode::WithSuggestions => "AUICCAFqDBAOEAoQAxAEEAkQBQ%3D%3D".into(),
}
}
fn filtered_prefix_param(&self) -> Cow<str> {
"EgWKAQ".into()
}
}
#[derive(PartialEq, Debug, Clone, Default)]
pub enum SpellingMode {
#[default]
ExactMatch,
WithSuggestions,
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct BasicSearch;
#[derive(Default, Debug, Clone, PartialEq)]
pub struct LibrarySearch;
#[derive(Default, Debug, Clone, PartialEq)]
pub struct UploadSearch;
#[derive(Default, Debug, Clone, PartialEq)]
pub struct FilteredSearch<F: FilteredSearchType> {
filter: F,
}
#[derive(Default, PartialEq, Debug, Clone)]
pub struct SongsFilter;
#[derive(Default, PartialEq, Debug, Clone)]
pub struct VideosFilter;
#[derive(Default, PartialEq, Debug, Clone)]
pub struct AlbumsFilter;
#[derive(Default, PartialEq, Debug, Clone)]
pub struct ArtistsFilter;
#[derive(Default, PartialEq, Debug, Clone)]
pub struct PlaylistsFilter;
#[derive(Default, PartialEq, Debug, Clone)]
pub struct CommunityPlaylistsFilter;
#[derive(Default, PartialEq, Debug, Clone)]
pub struct FeaturedPlaylistsFilter;
#[derive(Default, PartialEq, Debug, Clone)]
pub struct EpisodesFilter;
#[derive(Default, PartialEq, Debug, Clone)]
pub struct PodcastsFilter;
#[derive(Default, PartialEq, Debug, Clone)]
pub struct ProfilesFilter;
impl FilteredSearchType for SongsFilter {
fn filtered_param_bits(&self) -> Cow<str> {
"II".into()
}
}
impl FilteredSearchType for VideosFilter {
fn filtered_param_bits(&self) -> Cow<str> {
"IQ".into()
}
}
impl FilteredSearchType for AlbumsFilter {
fn filtered_param_bits(&self) -> Cow<str> {
"IY".into()
}
}
impl FilteredSearchType for ArtistsFilter {
fn filtered_param_bits(&self) -> Cow<str> {
"Ig".into()
}
}
impl FilteredSearchType for PlaylistsFilter {
fn filtered_param_bits(&self) -> Cow<str> {
"".into()
}
fn filtered_spelling_param(&self, spelling_mode: &SpellingMode) -> Cow<str> {
match spelling_mode {
SpellingMode::ExactMatch => "MABCAggBagoQBBADEAkQBRAK",
SpellingMode::WithSuggestions => "MABqChAEEAMQCRAFEAo%3D",
}
.into()
}
fn filtered_prefix_param(&self) -> Cow<str> {
"Eg-KAQwIABAAGAAgACgB".into()
}
}
impl FilteredSearchType for CommunityPlaylistsFilter {
fn filtered_param_bits(&self) -> Cow<str> {
"EA".into()
}
fn filtered_spelling_param(&self, spelling_mode: &SpellingMode) -> Cow<str> {
match spelling_mode {
SpellingMode::ExactMatch => SPECIALIZED_PLAYLIST_EXACT_MATCH_PARAMS,
SpellingMode::WithSuggestions => SPECIALIZED_PLAYLIST_WITH_SUGGESTIONS_PARAMS,
}
.into()
}
fn filtered_prefix_param(&self) -> Cow<str> {
SPECIALIZED_PLAYLIST_PREFIX_PARAMS.into()
}
}
impl FilteredSearchType for FeaturedPlaylistsFilter {
fn filtered_param_bits(&self) -> Cow<str> {
"Dg".into()
}
fn filtered_spelling_param(&self, spelling_mode: &SpellingMode) -> Cow<str> {
match spelling_mode {
SpellingMode::ExactMatch => SPECIALIZED_PLAYLIST_EXACT_MATCH_PARAMS,
SpellingMode::WithSuggestions => SPECIALIZED_PLAYLIST_WITH_SUGGESTIONS_PARAMS,
}
.into()
}
fn filtered_prefix_param(&self) -> Cow<str> {
SPECIALIZED_PLAYLIST_PREFIX_PARAMS.into()
}
}
impl FilteredSearchType for EpisodesFilter {
fn filtered_param_bits(&self) -> Cow<str> {
"JI".into()
}
}
impl FilteredSearchType for PodcastsFilter {
fn filtered_param_bits(&self) -> Cow<str> {
"JQ".into()
}
}
impl FilteredSearchType for ProfilesFilter {
fn filtered_param_bits(&self) -> Cow<str> {
"JY".into()
}
}
impl SearchType for BasicSearch {
fn specialised_params(&self, spelling_mode: &SpellingMode) -> Option<Cow<str>> {
match spelling_mode {
SpellingMode::ExactMatch => return Some("EhGKAQ4IARABGAEgASgAOAFAAUICCAE%3D".into()),
SpellingMode::WithSuggestions => return None,
}
}
}
impl<F: FilteredSearchType> SearchType for FilteredSearch<F> {
fn specialised_params(&self, spelling_mode: &SpellingMode) -> Option<Cow<str>> {
Some(
format!(
"{}{}{}",
self.filter.filtered_prefix_param(),
self.filter.filtered_param_bits(),
self.filter.filtered_spelling_param(spelling_mode),
)
.into(),
)
}
}
impl SearchType for UploadSearch {
fn specialised_params(&self, _: &SpellingMode) -> Option<Cow<str>> {
Some("agIYAw%3D%3D".into())
}
}
impl SearchType for LibrarySearch {
fn specialised_params(&self, _: &SpellingMode) -> Option<Cow<str>> {
Some("agIYBA%3D%3D".into())
}
}
impl<'a, S: SearchType> Query for SearchQuery<'a, S> {
fn header(&self) -> serde_json::Map<String, serde_json::Value> {
let value = self.query.as_ref().into();
serde_json::Map::from_iter([("query".to_string(), value)])
}
fn path(&self) -> &str {
"search"
}
fn params(&self) -> Option<Cow<str>> {
self.searchtype.specialised_params(&self.spelling_mode)
}
}
impl<'a, Q: Into<Cow<'a, str>>, S: SearchType> From<Q> for SearchQuery<'a, S> {
fn from(value: Q) -> SearchQuery<'a, S> {
SearchQuery {
query: value.into(),
spelling_mode: SpellingMode::default(),
searchtype: S::default(),
}
}
}
impl<'a> SearchQuery<'a, BasicSearch> {
pub fn new<Q: Into<Cow<'a, str>>>(q: Q) -> SearchQuery<'a, BasicSearch> {
SearchQuery {
query: q.into(),
spelling_mode: SpellingMode::default(),
searchtype: BasicSearch {},
}
}
}
impl<'a, S: SearchType> SearchQuery<'a, S> {
pub fn with_spelling_mode(mut self, spelling_mode: SpellingMode) -> Self {
self.spelling_mode = spelling_mode;
self
}
pub fn with_query<Q: Into<Cow<'a, str>>>(mut self, query: Q) -> Self {
self.query = query.into();
self
}
}
impl<'a> SearchQuery<'a, BasicSearch> {
pub fn with_filter<F: FilteredSearchType>(
self,
filter: F,
) -> SearchQuery<'a, FilteredSearch<F>> {
SearchQuery {
query: self.query,
spelling_mode: self.spelling_mode,
searchtype: FilteredSearch { filter },
}
}
pub fn uploads(self) -> SearchQuery<'a, UploadSearch> {
SearchQuery {
query: self.query,
spelling_mode: self.spelling_mode,
searchtype: UploadSearch,
}
}
pub fn library(self) -> SearchQuery<'a, LibrarySearch> {
SearchQuery {
query: self.query,
spelling_mode: self.spelling_mode,
searchtype: LibrarySearch,
}
}
}
impl<'a, F: FilteredSearchType> SearchQuery<'a, FilteredSearch<F>> {
pub fn with_filter<F2: FilteredSearchType>(
self,
filter: F2,
) -> SearchQuery<'a, FilteredSearch<F2>> {
SearchQuery {
query: self.query,
spelling_mode: self.spelling_mode,
searchtype: FilteredSearch { filter },
}
}
pub fn unfiltered(self) -> SearchQuery<'a, BasicSearch> {
SearchQuery {
query: self.query,
spelling_mode: self.spelling_mode,
searchtype: BasicSearch,
}
}
}
impl<'a> SearchQuery<'a, UploadSearch> {
pub fn with_scope_public(self) -> SearchQuery<'a, BasicSearch> {
SearchQuery {
query: self.query,
spelling_mode: self.spelling_mode,
searchtype: BasicSearch,
}
}
}
impl<'a> SearchQuery<'a, LibrarySearch> {
pub fn with_scope_public(self) -> SearchQuery<'a, BasicSearch> {
SearchQuery {
query: self.query,
spelling_mode: self.spelling_mode,
searchtype: BasicSearch,
}
}
}
#[derive(PartialEq, Debug, Clone)]
pub struct GetSearchSuggestionsQuery<'a> {
query: Cow<'a, str>,
}
impl<'a> GetSearchSuggestionsQuery<'a> {
fn new<S: Into<Cow<'a, str>>>(value: S) -> GetSearchSuggestionsQuery<'a> {
GetSearchSuggestionsQuery {
query: value.into(),
}
}
}
impl<'a, S: Into<Cow<'a, str>>> From<S> for GetSearchSuggestionsQuery<'a> {
fn from(value: S) -> GetSearchSuggestionsQuery<'a> {
GetSearchSuggestionsQuery::new(value)
}
}
impl<'a> Query for GetSearchSuggestionsQuery<'a> {
fn header(&self) -> serde_json::Map<String, serde_json::Value> {
let value = self.query.as_ref().into();
serde_json::Map::from_iter([("input".into(), value)])
}
fn path(&self) -> &str {
"music/get_search_suggestions"
}
fn params(&self) -> Option<Cow<str>> {
None
}
}