use std::collections::HashMap; #[derive(serde::Serialize, serde::Deserialize, Debug)] pub struct VideosRequest { //"versionInstallDate":"2025-06-03T18:20:20Z","languageCode":"en","appInstallDate":"2025-06-03T18:20:20Z","server":"spacemoehre","sexu pub clientHash: Option, // "a07b23c9b07813c65050e2a4041ca777", pub blockedKeywords: Option, // "kittens", pub countryCode: Option, // "DE", pub clientVersion: Option, // "2.1.4-22b", pub timestamp: Option, // "1748976686", pub blockedUploaders: Option, // "", pub anonId: Option, // "1AB8A060-A47D-47EF-B9CB-63980ED84C8A", pub debugTools: Option, // false, pub versionInstallDate: Option, // "2025-06-03T18:20:20Z", pub languageCode: Option, // "en", pub appInstallDate: Option, // "2025-06-03T18:20:20Z", pub server: Option, // "spacemoehre", pub sexuality: Option, // "straight", pub channel: Option, //"youtube", pub sort: Option, //"new", pub query: Option, //"kittens", pub page: Option, //1, pub perPage: Option, //10, // Your server's global options will be sent in the videos request // pub flavor: "mint chocolate chip" pub featured: Option, // "featured", pub category: Option, // "pmv" pub sites: Option, // } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct ServerOptions{ pub featured: Option, // "featured", pub category: Option, // "pmv" pub sites: Option, // } #[derive(serde::Serialize, Debug)] pub struct PageInfo { pub hasNextPage: bool, // true, pub resultsPerPage: u32, // 10 } #[derive(serde::Serialize, Debug, Clone)] pub struct VideoEmbed{ pub html: String, pub source: String, } impl VideoEmbed { pub fn new(html: String, source: String) -> Self { VideoEmbed { html, source, } } } #[derive(serde::Serialize, Debug, Clone)] pub struct VideoItem { pub duration: u32, // 110, #[serde(skip_serializing_if = "Option::is_none")] pub views: Option, // 14622653, #[serde(skip_serializing_if = "Option::is_none")] pub rating: Option, // 0.0, pub id: String, // "c85017ca87477168d648727753c4ded8a35f173e22ef93743e707b296becb299", pub title: String, // "20 Minutes of Adorable Kittens BEST Compilation", pub url: String, // "https://www.youtube.com/watch?v=y0sF5xhGreA", pub channel: String, // "youtube", pub thumb: String, // "https://i.ytimg.com/vi/y0sF5xhGreA/hqdefault.jpg", #[serde(skip_serializing_if = "Option::is_none")] pub uploader: Option, // "The Pet Collective", #[serde(skip_serializing_if = "Option::is_none")] pub uploaderUrl: Option, // "https://www.youtube.com/@petcollective", #[serde(skip_serializing_if = "Option::is_none")] pub verified: Option, // false, #[serde(skip_serializing_if = "Option::is_none")] pub tags: Option>, // [], #[serde(skip_serializing_if = "Option::is_none")] pub uploadedAt: Option, // 1741142954 #[serde(skip_serializing_if = "Option::is_none")] pub formats: Option>, // Additional HTTP headers if needed #[serde(skip_serializing_if = "Option::is_none")] pub embed: Option, // Optional embed information #[serde(skip_serializing_if = "Option::is_none")] pub preview: Option, #[serde(skip_serializing_if = "Option::is_none")] pub aspectRation: Option } #[allow(dead_code)] impl VideoItem { pub fn new( id: String, title: String, url: String, channel: String, thumb: String, duration: u32, ) -> Self { VideoItem { duration: duration, // Placeholder, adjust as needed views: None, // Placeholder, adjust as needed rating: None, // Placeholder, adjust as needed id, title, url, channel, thumb, uploader: None, uploaderUrl: None, verified: None, tags: None, // Placeholder, adjust as needed uploadedAt: None, formats: None, // Placeholder for formats embed: None, // Placeholder for embed information preview: None, aspectRation: None } } pub fn tags(mut self, tags: Vec) -> Self { self.tags = Some(tags); self } pub fn uploader(mut self, uploader: String) -> Self { self.uploader = Some(uploader); self } pub fn uploader_url(mut self, uploader_url: String) -> Self { self.uploaderUrl = Some(uploader_url); self } pub fn verified(mut self, verified: bool) -> Self { self.verified = Some(verified); self } pub fn views(mut self, views: u32) -> Self { self.views = Some(views); self } pub fn rating(mut self, rating: f32) -> Self { self.rating = Some(rating); self } pub fn uploaded_at(mut self, uploaded_at: u64) -> Self { self.uploadedAt = Some(uploaded_at); self } pub fn formats(mut self, formats: Vec) -> Self { self.formats = Some(formats); self } pub fn add_format(mut self, format: VideoFormat){ if let Some(formats) = self.formats.as_mut() { formats.push(format); } else { self.formats = Some(vec![format]); } } pub fn embed(mut self, embed: VideoEmbed) -> Self { self.embed = Some(embed); self } pub fn preview(mut self, preview: String) -> Self { self.preview = Some(preview); self } pub fn aspect_ratio(mut self, aspect_ratio: f32) -> Self { self.aspectRation = Some(aspect_ratio); self } } #[derive(serde::Serialize, Debug, Clone)] pub struct VideoFormat { url: String, quality: String, format: String, #[serde(skip_serializing_if = "Option::is_none")] format_id: Option, #[serde(skip_serializing_if = "Option::is_none")] format_note: Option, #[serde(skip_serializing_if = "Option::is_none")] filesize: Option, #[serde(skip_serializing_if = "Option::is_none")] asr: Option, #[serde(skip_serializing_if = "Option::is_none")] fps: Option, #[serde(skip_serializing_if = "Option::is_none")] width: Option, #[serde(skip_serializing_if = "Option::is_none")] height: Option, #[serde(skip_serializing_if = "Option::is_none")] tbr: Option, #[serde(skip_serializing_if = "Option::is_none")] language: Option, #[serde(skip_serializing_if = "Option::is_none")] language_preference: Option, #[serde(skip_serializing_if = "Option::is_none")] ext: Option, #[serde(skip_serializing_if = "Option::is_none")] vcodec: Option, #[serde(skip_serializing_if = "Option::is_none")] acodec: Option, #[serde(skip_serializing_if = "Option::is_none")] dynamic_range: Option, #[serde(skip_serializing_if = "Option::is_none")] abr: Option, #[serde(skip_serializing_if = "Option::is_none")] vbr: Option, #[serde(skip_serializing_if = "Option::is_none")] container: Option, #[serde(skip_serializing_if = "Option::is_none")] protocol: Option, #[serde(skip_serializing_if = "Option::is_none")] audio_ext: Option, #[serde(skip_serializing_if = "Option::is_none")] video_ext: Option, #[serde(skip_serializing_if = "Option::is_none")] resolution: Option, #[serde(skip_serializing_if = "Option::is_none")] http_headers: Option>, } impl VideoFormat { pub fn new(url: String, quality: String, format: String) -> Self { let _ = format; VideoFormat { url, quality, format: "mp4".to_string(), // Default format format_id: Some("mp4-1080".to_string()), format_note: None, filesize: None, asr: None, fps: None, width: None, height: None, tbr: None, language: None, language_preference: None, ext: Some("mp4".to_string()), vcodec: None, acodec: None, dynamic_range: None, abr: None, vbr: None, container: None, protocol: Some("m3u8_native".to_string()), audio_ext: Some("none".to_string()), video_ext: Some("mp4".to_string()), resolution: None, http_headers: None, } } pub fn add_http_header(&mut self, key: String, value: String) { if self.http_headers.is_none() { self.http_headers = Some(HashMap::new()); } if let Some(headers) = &mut self.http_headers { headers.insert(key, value); } } pub fn protocol(mut self, protocol: String) -> Self { self.protocol = Some(protocol); self } } #[derive(serde::Serialize, Debug)] pub struct Videos { pub pageInfo: PageInfo, pub items: Vec, }