// YoutubeSearchQueryHandlerFactory + search filters. Filter params are // opaque base64 protobufs — NPE doesn't decode them, just sends the // magic strings. We mirror that. See audit Track D §3. // // Music* filter variants were ported from NPE but never wired through // (search_extractor rejected anything with `uses_music_endpoint()`). // Dropped 2026-05-26 along with WEB_REMIX_* constants + the music // charts client factory. use url::form_urlencoded; #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum SearchFilter { /// All result types — no params field sent. All, /// Videos only. Videos, /// Channels only. Channels, /// Playlists only. Playlists, } impl SearchFilter { /// Returns the InnerTube `params` base64 string. None means omit /// the field entirely (== All). pub fn params(&self) -> Option<&'static str> { match self { SearchFilter::All => None, SearchFilter::Videos => Some("EgIQAfABAQ%3D%3D"), SearchFilter::Channels => Some("EgIQAvABAQ%3D%3D"), SearchFilter::Playlists => Some("EgIQA_ABAQ%3D%3D"), } } } pub fn search_url(query: &str) -> String { let encoded: String = form_urlencoded::Serializer::new(String::new()) .append_pair("search_query", query) .finish(); format!("https://www.youtube.com/results?{encoded}") } #[cfg(test)] mod tests { use super::*; #[test] fn all_filter_omits_params() { assert!(SearchFilter::All.params().is_none()); } #[test] fn typed_filters_have_params() { assert!(SearchFilter::Videos.params().is_some()); assert!(SearchFilter::Channels.params().is_some()); assert!(SearchFilter::Playlists.params().is_some()); } #[test] fn search_url_encodes_query() { assert_eq!( search_url("rust + ferris"), "https://www.youtube.com/results?search_query=rust+%2B+ferris" ); } }