1use std::fmt::Display;
2
3use clap::{Parser, ValueEnum};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Deserialize, Serialize, Clone)]
7pub struct YoutubeSearchResponse {
8 pub kind: String,
9 pub etag: String,
10 #[serde(rename = "pageInfo")]
11 pub page_info: PageInfo,
12 pub items: Vec<YoutubeItem>,
13}
14
15#[derive(Debug, Deserialize, Serialize, Clone)]
16pub struct PageInfo {
17 pub total_results: Option<i64>,
18 pub results_per_page: Option<i64>,
19}
20
21#[derive(Debug, Deserialize, Serialize, Clone)]
22pub struct YoutubeItem {
23 pub kind: String,
24 pub etag: String,
25 pub id: YoutubeId,
26 pub snippet: YoutubeSnippet,
27}
28
29#[derive(Debug, Deserialize, Serialize, Clone)]
30pub struct YoutubeId {
31 pub kind: String,
32 #[serde(rename = "videoId")]
33 pub video_id: Option<String>,
34 #[serde(rename = "channelId")]
35 pub channel_id: Option<String>,
36}
37
38#[derive(Debug, Deserialize, Serialize, Clone)]
39pub struct YoutubeSnippet {
40 #[serde(rename = "publishedAt")]
41 pub published_at: String,
42 pub title: String,
43 pub description: String,
44 #[serde(rename = "channelTitle")]
45 pub channel_title: String,
46 #[serde(rename = "publishTime")]
47 pub publish_time: String,
48}
49
50#[derive(Debug, Serialize, Clone, Parser)]
51pub struct YoutubeSearchRequest {
52 #[clap(
53 short,
54 long,
55 help = "The part parameter specifies a comma-separated list of one or more search resource properties that the API response will include",
56 default_value = "snippet"
57 )]
58 pub part: Option<String>,
59 #[clap(
60 short,
61 long,
62 help = "The channel_id parameter indicates that the API response should only contain resources created by the channel",
63 )]
64 pub channel_id: Option<String>,
65 #[clap(
66 short,
67 long,
68 help = "The channel_type parameter lets you restrict a search to a particular type of channel (any or show)",
69 )]
70 pub channel_type: Option<ChannelType>,
71 #[clap(
72 short,
73 long,
74 help = "The event_type parameter restricts a search to broadcast events",
75 )]
76 pub event_type: Option<EventType>,
77 #[clap(
78 short,
79 long,
80 help = "The location parameter restricts a search to videos that have a geographical location e.g.(37.42307,-122.08427)",
81 )]
82 pub location: Option<String>,
83 #[clap(
84 short,
85 long,
86 help = "The location_radius parameter specifies the maximum distance that the location associated with a video can be from the specified location",
87 )]
88 pub location_radius: Option<String>,
89 #[clap(
90 short,
91 long,
92 help = "The max_results parameter specifies the maximum number of items that should be returned in the result set",
93 default_value = "25"
94 )]
95 pub max_results: Option<i64>,
96 #[clap(
97 short,
98 long,
99 )]
100 pub on_behalf_of_content_owner: Option<String>,
101 #[clap(
102 short,
103 long,
104 help = "The order parameter specifies the method that will be used to order resources in the API response (date, rating, relevance, title, videoCount, viewCount)",
105 default_value = "relevance"
106 )]
107 pub order: Option<Order>,
108 #[clap(
109 short,
110 long,
111 help = "The page_token parameter identifies a specific page in the result set that should be returned",
112 )]
113 pub page_token: Option<String>,
114 #[clap(
115 short,
116 long,
117 help = "The published_after parameter indicates that the API response should only contain resources created after this date (RFC 3339)",
118 )]
119 pub published_after: Option<String>,
120 #[clap(
121 short,
122 long,
123 help = "The published_before parameter indicates that the API response should only contain resources created before this date (RFC 3339)",
124 )]
125 pub published_before: Option<String>,
126 #[clap(
127 short,
128 long,
129 help = "The q parameter specifies the query term to search for e.g. (cats|dogs)",
130 )]
131 pub q: Option<String>,
132 #[clap(
133 short,
134 long,
135 help = "The region_code parameter instructs the API to return search results for the specified country",
136 )]
137 pub region_code: Option<String>,
138 #[clap(
139 short,
140 long,
141 help = "The relevance_language parameter instructs the API to return search results that are most relevant to the specified language",
142 )]
143 pub relevance_language: Option<String>,
144 #[clap(
145 short,
146 long,
147 help = "The safe_search parameter indicates whether the search results should include restricted content as well as standard content",
148 default_value = "moderate"
149 )]
150 pub safe_search: Option<SafeSearch>,
151 #[clap(
152 long,
153 help = "The topic_id parameter indicates that the API response should only contain resources associated with the specified topic",
154 )]
155 pub topic_id: Option<Topic>,
156 #[clap(
157 short,
158 long,
159 help = "The type parameter restricts a search query to only retrieve a particular type of resource (channel, playlist, video)",
160 )]
161 pub type_: Option<Type>,
162 #[clap(
163 short,
164 long,
165 help = "The video_caption parameter indicates whether the API should filter video search results based on whether they have captions",
166 )]
167 pub video_caption: Option<VideoCaption>,
168 #[clap(
169 short,
170 long,
171 help = "The video_category_id parameter filters video search results based on their category",
172 )]
173 pub video_category_id: Option<String>,
174 #[clap(
175 short,
176 long,
177 help = "The video_definition parameter lets you restrict a search to only include either high definition (HD) or standard definition (SD) videos (any, high, standard)",
178 )]
179 pub video_definition: Option<VideoDefinition>,
180 #[clap(
181 short,
182 long,
183 help = "The video_dimension parameter lets you restrict a search to only retrieve 2D or 3D videos (any, 2d, 3d)",
184 )]
185 pub video_dimension: Option<VideoDimension>,
186 #[clap(
187 short,
188 long,
189 help = "The video_duration parameter filters video search results based on their duration (any, long, medium, short)",
190 )]
191 pub video_duration: Option<VideoDuration>,
192 #[clap(
193 short,
194 long,
195 help = "The video_embeddable parameter lets you to restrict a search to only videos that can be embedded into a webpage (any, true)",
196 )]
197 pub video_embeddable: Option<VideoEmbeddable>,
198 #[clap(
199 short,
200 long,
201 help = "The video_license parameter filters search results to only include videos with a particular license (any, creativeCommon, youtube)",
202 )]
203 pub video_license: Option<VideoLicense>,
204 #[clap(
205 short,
206 long,
207 help = "The video_paid_product_placement parameter lets you to restrict a search to only videos that contain a paid product placement (any, true)",
208 )]
209 pub video_paid_product_placement: Option<VideoPaidProductPlacement>,
210 #[clap(
211 short,
212 long,
213 help = "The video_syndicated parameter lets you to restrict a search to only videos that can be played outside youtube.com (any, true)",
214 )]
215 pub video_syndicated: Option<VideoSyndicated>,
216 #[clap(
217 short,
218 long,
219 help = "The video_type parameter lets you restrict a search to a particular type of videos (any, episode, movie)",
220 )]
221 pub video_type: Option<VideoType>,
222}
223
224#[derive(Debug, Serialize, Clone, ValueEnum)]
225pub enum VideoType {
226 #[serde(rename = "any")]
227 Any,
228 #[serde(rename = "episode")]
229 Episode,
230 #[serde(rename = "movie")]
231 Movie,
232}
233
234impl Display for VideoType {
235 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236 match self {
237 VideoType::Any => write!(f, "any"),
238 VideoType::Episode => write!(f, "episode"),
239 VideoType::Movie => write!(f, "movie"),
240 }
241 }
242}
243
244#[derive(Debug, Serialize, Clone, ValueEnum)]
245pub enum VideoSyndicated {
246 #[serde(rename = "any")]
247 Any,
248 #[serde(rename = "true")]
249 True,
250}
251
252impl Display for VideoSyndicated {
253 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
254 match self {
255 VideoSyndicated::Any => write!(f, "any"),
256 VideoSyndicated::True => write!(f, "true"),
257 }
258 }
259}
260
261#[derive(Debug, Serialize, Clone, ValueEnum)]
262pub enum VideoPaidProductPlacement {
263 #[serde(rename = "any")]
264 Any,
265 #[serde(rename = "true")]
266 True,
267}
268
269impl Display for VideoPaidProductPlacement {
270 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
271 match self {
272 VideoPaidProductPlacement::Any => write!(f, "any"),
273 VideoPaidProductPlacement::True => write!(f, "true"),
274 }
275 }
276}
277
278#[derive(Debug, Serialize, Clone, ValueEnum)]
279pub enum VideoLicense {
280 #[serde(rename = "any")]
281 Any,
282 #[serde(rename = "creativeCommon")]
283 CreativeCommon,
284 #[serde(rename = "youtube")]
285 Youtube,
286}
287
288impl Display for VideoLicense {
289 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290 match self {
291 VideoLicense::Any => write!(f, "any"),
292 VideoLicense::CreativeCommon => write!(f, "creativeCommon"),
293 VideoLicense::Youtube => write!(f, "youtube"),
294 }
295 }
296}
297
298#[derive(Debug, Serialize, Clone, ValueEnum)]
299pub enum VideoEmbeddable {
300 #[serde(rename = "any")]
301 Any,
302 #[serde(rename = "true")]
303 True,
304}
305
306impl Display for VideoEmbeddable {
307 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308 match self {
309 VideoEmbeddable::Any => write!(f, "any"),
310 VideoEmbeddable::True => write!(f, "true"),
311 }
312 }
313}
314
315#[derive(Debug, Serialize, Clone, ValueEnum)]
316pub enum VideoDefinition {
317 #[serde(rename = "any")]
318 Any,
319 #[serde(rename = "high")]
320 High,
321 #[serde(rename = "standard")]
322 Standard,
323}
324
325impl Display for VideoDefinition {
326 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
327 match self {
328 VideoDefinition::Any => write!(f, "any"),
329 VideoDefinition::High => write!(f, "high"),
330 VideoDefinition::Standard => write!(f, "standard"),
331 }
332 }
333}
334
335#[derive(Debug, Serialize, Clone, ValueEnum)]
336pub enum VideoCaption {
337 #[serde(rename = "any")]
338 Any,
339 #[serde(rename = "closedCaption")]
340 ClosedCaption,
341 #[serde(rename = "none")]
342 None,
343}
344
345impl Display for VideoCaption {
346 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
347 match self {
348 VideoCaption::Any => write!(f, "any"),
349 VideoCaption::ClosedCaption => write!(f, "closedCaption"),
350 VideoCaption::None => write!(f, "none"),
351 }
352 }
353}
354
355#[derive(Debug, Serialize, Clone, ValueEnum)]
356pub enum Type {
357 #[serde(rename = "channel")]
358 Channel,
359 #[serde(rename = "playlist")]
360 Playlist,
361 #[serde(rename = "video")]
362 Video,
363}
364
365impl Display for Type {
366 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
367 match self {
368 Type::Channel => write!(f, "channel"),
369 Type::Playlist => write!(f, "playlist"),
370 Type::Video => write!(f, "video"),
371 }
372 }
373}
374
375#[derive(Debug, Serialize, Clone, ValueEnum)]
376pub enum VideoDuration {
377 #[serde(rename = "any")]
378 Any,
379 #[serde(rename = "long")]
380 Long,
381 #[serde(rename = "medium")]
382 Medium,
383 #[serde(rename = "short")]
384 Short,
385}
386
387impl Display for VideoDuration {
388 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
389 match self {
390 VideoDuration::Any => write!(f, "any"),
391 VideoDuration::Long => write!(f, "long"),
392 VideoDuration::Medium => write!(f, "medium"),
393 VideoDuration::Short => write!(f, "short"),
394 }
395 }
396}
397
398#[derive(Debug, Serialize, Clone, ValueEnum)]
399pub enum VideoDimension {
400 #[serde(rename = "any")]
401 Any,
402 #[serde(rename = "2d")]
403 _2d,
404 #[serde(rename = "3d")]
405 _3d,
406}
407
408impl Display for VideoDimension {
409 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
410 match self {
411 VideoDimension::Any => write!(f, "any"),
412 VideoDimension::_2d => write!(f, "2d"),
413 VideoDimension::_3d => write!(f, "3d"),
414 }
415 }
416}
417
418#[derive(Debug, Serialize, Clone, ValueEnum)]
419pub enum Topic {
420 #[serde(rename = "/m/04rlf")]
421 Music,
422 #[serde(rename = "/m/02mscn")]
423 ChristianMusic,
424 #[serde(rename = "/m/0ggq0m")]
425 ClassicalMusic,
426 #[serde(rename = "/m/01lyv")]
427 Country,
428 #[serde(rename = "/m/02lkt")]
429 ElectronicMusic,
430 #[serde(rename = "/m/0glt670")]
431 HipHopMusic,
432 #[serde(rename = "/m/05rwpb")]
433 IndependentMusic,
434 #[serde(rename = "/m/03_d0")]
435 Jazz,
436 #[serde(rename = "/m/028sqc")]
437 MusicOfAsia,
438 #[serde(rename = "/m/0g293")]
439 MusicOfLatinAmerica,
440 #[serde(rename = "/m/064t9")]
441 PopMusic,
442 #[serde(rename = "/m/06cqb")]
443 Reggae,
444 #[serde(rename = "/m/06j6l")]
445 RhythmAndBlues,
446 #[serde(rename = "/m/06by7")]
447 RockMusic,
448 #[serde(rename = "/m/0gywn")]
449 SoulMusic,
450 #[serde(rename = "/m/0bzvm2")]
451 Gaming,
452 #[serde(rename = "/m/025zzc")]
453 ActionGame,
454 #[serde(rename = "/m/02ntfj")]
455 ActionAdventureGame,
456 #[serde(rename = "/m/0b1vjn")]
457 CasualGame,
458 #[serde(rename = "/m/02hygl")]
459 MusicVideoGame,
460 #[serde(rename = "/m/04q1x3q")]
461 PuzzleVideoGame,
462 #[serde(rename = "/m/01sjng")]
463 RacingVideoGame,
464 #[serde(rename = "/m/0403l3g")]
465 RolePlayingVideoGame,
466 #[serde(rename = "/m/021bp2")]
467 SimulationVideoGame,
468 #[serde(rename = "/m/022dc6")]
469 SportsVideoGame,
470 #[serde(rename = "/m/03hf_rm")]
471 StrategyVideoGame,
472 #[serde(rename = "/m/06ntj")]
473 Sports,
474 #[serde(rename = "/m/0jm_")]
475 AmericanFootball,
476 #[serde(rename = "/m/018jz")]
477 Baseball,
478 #[serde(rename = "/m/018w8")]
479 Basketball,
480 #[serde(rename = "/m/01cgz")]
481 Boxing,
482 #[serde(rename = "/m/09xp_")]
483 Cricket,
484 #[serde(rename = "/m/02vx4")]
485 Football,
486 #[serde(rename = "/m/037hz")]
487 Golf,
488 #[serde(rename = "/m/03tmr")]
489 IceHockey,
490 #[serde(rename = "/m/01h7lh")]
491 MixedMartialArts,
492 #[serde(rename = "/m/0410tth")]
493 Motorsport,
494 #[serde(rename = "/m/07bs0")]
495 Tennis,
496 #[serde(rename = "/m/07_53")]
497 Volleyball,
498 #[serde(rename = "/m/02jjt")]
499 Entertainment,
500 #[serde(rename = "/m/09kqc")]
501 Humor,
502 #[serde(rename = "/m/02vxn")]
503 Movies,
504 #[serde(rename = "/m/05qjc")]
505 PerformingArts,
506 #[serde(rename = "/m/066wd")]
507 PreofessionalWrestling,
508 #[serde(rename = "/m/0f2f9")]
509 TvShows,
510 #[serde(rename = "/m/019_rr")]
511 Lifestyle,
512 #[serde(rename = "/m/032tl")]
513 Fashion,
514 #[serde(rename = "/m/027x7n")]
515 Fitness,
516 #[serde(rename = "/m/02wbm")]
517 Food,
518 #[serde(rename = "/m/03glg")]
519 Hobby,
520 #[serde(rename = "/m/068hy")]
521 Pets,
522 #[serde(rename = "/m/041xxh")]
523 PhysicalAttractiveness,
524 #[serde(rename = "/m/07c1v")]
525 Technology,
526 #[serde(rename = "/m/07bxq")]
527 Tourism,
528 #[serde(rename = "/m/07yv9")]
529 Vehicles,
530 #[serde(rename = "/m/098wr")]
531 Society,
532 #[serde(rename = "/m/09s1f")]
533 Business,
534 #[serde(rename = "/m/0kt51")]
535 Health,
536 #[serde(rename = "/m/01h6rj")]
537 Military,
538 #[serde(rename = "/m/05qt0")]
539 Politics,
540 #[serde(rename = "/m/06bvp")]
541 Religion,
542 #[serde(rename = "/m/01k8wb")]
543 Knowledge,
544}
545
546impl Display for Topic {
547 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
548 match self {
549 Topic::Music => write!(f, "/m/04rlf"),
550 Topic::ChristianMusic => write!(f, "/m/02mscn"),
551 Topic::ClassicalMusic => write!(f, "/m/0ggq0m"),
552 Topic::Country => write!(f, "/m/01lyv"),
553 Topic::ElectronicMusic => write!(f, "/m/02lkt"),
554 Topic::HipHopMusic => write!(f, "/m/0glt670"),
555 Topic::IndependentMusic => write!(f, "/m/05rwpb"),
556 Topic::Jazz => write!(f, "/m/03_d0"),
557 Topic::MusicOfAsia => write!(f, "/m/028sqc"),
558 Topic::MusicOfLatinAmerica => write!(f, "/m/0g293"),
559 Topic::PopMusic => write!(f, "/m/064t9"),
560 Topic::Reggae => write!(f, "/m/06cqb"),
561 Topic::RhythmAndBlues => write!(f, "/m/06j6l"),
562 Topic::RockMusic => write!(f, "/m/06by7"),
563 Topic::SoulMusic => write!(f, "/m/0gywn"),
564 Topic::Gaming => write!(f, "/m/0bzvm2"),
565 Topic::ActionGame => write!(f, "/m/025zzc"),
566 Topic::ActionAdventureGame => write!(f, "/m/02ntfj"),
567 Topic::CasualGame => write!(f, "/m/0b1vjn"),
568 Topic::MusicVideoGame => write!(f, "/m/02hygl"),
569 Topic::PuzzleVideoGame => write!(f, "/m/04q1x3q"),
570 Topic::RacingVideoGame => write!(f, "/m/01sjng"),
571 Topic::RolePlayingVideoGame => write!(f, "/m/0403l3g"),
572 Topic::SimulationVideoGame => write!(f, "/m/021bp2"),
573 Topic::SportsVideoGame => write!(f, "/m/022dc6"),
574 Topic::StrategyVideoGame => write!(f, "/m/03hf_rm"),
575 Topic::Sports => write!(f, "/m/06ntj"),
576 Topic::AmericanFootball => write!(f, "/m/0jm_"),
577 Topic::Baseball => write!(f, "/m/018jz"),
578 Topic::Basketball => write!(f, "/m/018w8"),
579 Topic::Boxing => write!(f, "/m/01cgz"),
580 Topic::Cricket => write!(f, "/m/09xp_"),
581 Topic::Football => write!(f, "/m/02vx4"),
582 Topic::Golf => write!(f, "/m/037hz"),
583 Topic::IceHockey => write!(f, "/m/03tmr"),
584 Topic::MixedMartialArts => write!(f, "/m/01h7lh"),
585 Topic::Motorsport => write!(f, "/m/0410tth"),
586 Topic::Tennis => write!(f, "/m/07bs0"),
587 Topic::Volleyball => write!(f, "/m/07_53"),
588 Topic::Entertainment => write!(f, "/m/02jjt"),
589 Topic::Humor => write!(f, "/m/09kqc"),
590 Topic::Movies => write!(f, "/m/02vxn"),
591 Topic::PerformingArts => write!(f, "/m/05qjc"),
592 Topic::PreofessionalWrestling => write!(f, "/m/066wd"),
593 Topic::TvShows => write!(f, "/m/0f2f9"),
594 Topic::Lifestyle => write!(f, "/m/019_rr"),
595 Topic::Fashion => write!(f, "/m/032tl"),
596 Topic::Fitness => write!(f, "/m/027x7n"),
597 Topic::Food => write!(f, "/m/02wbm"),
598 Topic::Hobby => write!(f, "/m/03glg"),
599 Topic::Pets => write!(f, "/m/068hy"),
600 Topic::PhysicalAttractiveness => write!(f, "/m/041xxh"),
601 Topic::Technology => write!(f, "/m/07c1v"),
602 Topic::Tourism => write!(f, "/m/07bxq"),
603 Topic::Vehicles => write!(f, "/m/07yv9"),
604 Topic::Society => write!(f, "/m/098wr"),
605 Topic::Business => write!(f, "/m/09s1f"),
606 Topic::Health => write!(f, "/m/0kt51"),
607 Topic::Military => write!(f, "/m/01h6rj"),
608 Topic::Politics => write!(f, "/m/05qt0"),
609 Topic::Religion => write!(f, "/m/06bvp"),
610 Topic::Knowledge => write!(f, "/m/01k8wb"),
611 }
612 }
613}
614
615#[derive(Debug, Serialize, Clone, ValueEnum)]
616pub enum SafeSearch {
617 #[serde(rename = "moderate")]
618 Moderate,
619 #[serde(rename = "none")]
620 None,
621 #[serde(rename = "strict")]
622 Strict,
623}
624
625impl Display for SafeSearch {
626 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
627 match self {
628 SafeSearch::Moderate => write!(f, "moderate"),
629 SafeSearch::None => write!(f, "none"),
630 SafeSearch::Strict => write!(f, "strict"),
631 }
632 }
633}
634
635#[derive(Debug, Serialize, Clone, ValueEnum)]
636pub enum Order {
637 #[serde(rename = "date")]
638 Date,
639 #[serde(rename = "rating")]
640 Rating,
641 #[serde(rename = "relevance")]
642 Relevance,
643 #[serde(rename = "title")]
644 Title,
645 #[serde(rename = "videoCount")]
646 VideoCount,
647 #[serde(rename = "viewCount")]
648 ViewCount,
649}
650
651impl Display for Order {
652 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
653 match self {
654 Order::Date => write!(f, "date"),
655 Order::Rating => write!(f, "rating"),
656 Order::Relevance => write!(f, "relevance"),
657 Order::Title => write!(f, "title"),
658 Order::VideoCount => write!(f, "videoCount"),
659 Order::ViewCount => write!(f, "viewCount"),
660 }
661 }
662}
663
664#[derive(Debug, Serialize, Clone, ValueEnum)]
665pub enum ChannelType {
666 #[serde(rename = "any")]
667 Any,
668 #[serde(rename = "show")]
669 Show,
670}
671
672impl Display for ChannelType {
673 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
674 match self {
675 ChannelType::Any => write!(f, "any"),
676 ChannelType::Show => write!(f, "show"),
677 }
678 }
679}
680
681#[derive(Debug, Serialize, Clone, ValueEnum)]
682pub enum EventType {
683 #[serde(rename = "completed")]
684 Completed,
685 #[serde(rename = "live")]
686 Live,
687 #[serde(rename = "upcoming")]
688 Upcoming,
689}
690
691impl Display for EventType {
692 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
693 match self {
694 EventType::Completed => write!(f, "completed"),
695 EventType::Live => write!(f, "live"),
696 EventType::Upcoming => write!(f, "upcoming"),
697 }
698 }
699}
700
701pub async fn search_youtube(
702 request: YoutubeSearchRequest,
703) -> Result<YoutubeSearchResponse, Box<dyn std::error::Error>> {
704 let youtube_api_key = std::env::var("YOUTUBE_API_KEY").expect("YOUTUBE_API_KEY not set");
705 let url = "https://youtube.googleapis.com/youtube/v3/search";
706 let client = reqwest::Client::new();
707 let mut params: Vec<(&str, String)> = vec![("key", youtube_api_key)];
708 if let Some(part) = request.part {
709 params.push(("part", part));
710 }
711 if let Some(channel_id) = request.channel_id {
712 params.push(("channelId", channel_id));
713 }
714 if let Some(channel_type) = request.channel_type {
715 params.push(("channelType", channel_type.to_string()));
716 }
717 if let Some(event_type) = request.event_type {
718 params.push(("eventType", event_type.to_string()));
719 }
720 if let Some(location) = request.location {
721 params.push(("location", location));
722 }
723 if let Some(location_radius) = request.location_radius {
724 params.push(("locationRadius", location_radius));
725 }
726 if let Some(max_results) = request.max_results {
727 params.push(("maxResults", max_results.to_string()));
728 }
729 if let Some(on_behalf_of_content_owner) = request.on_behalf_of_content_owner {
730 params.push(("onBehalfOfContentOwner", on_behalf_of_content_owner));
731 }
732 if let Some(order) = request.order {
733 params.push(("order", order.to_string()));
734 }
735 if let Some(page_token) = request.page_token {
736 params.push(("pageToken", page_token));
737 }
738 if let Some(published_after) = request.published_after {
739 params.push(("publishedAfter", published_after));
740 }
741 if let Some(published_before) = request.published_before {
742 params.push(("publishedBefore", published_before));
743 }
744 if let Some(q) = request.q {
745 params.push(("q", q));
746 }
747 if let Some(region_code) = request.region_code {
748 params.push(("regionCode", region_code));
749 }
750 if let Some(relevance_language) = request.relevance_language {
751 params.push(("relevanceLanguage", relevance_language));
752 }
753 if let Some(safe_search) = request.safe_search {
754 params.push(("safeSearch", safe_search.to_string()));
755 }
756 if let Some(topic_id) = request.topic_id {
757 params.push(("topicId", topic_id.to_string()));
758 }
759 if let Some(type_) = request.type_ {
760 params.push(("type", type_.to_string()));
761 }
762 if let Some(video_caption) = request.video_caption {
763 params.push(("videoCaption", video_caption.to_string()));
764 }
765 if let Some(video_category_id) = request.video_category_id {
766 params.push(("videoCategoryId", video_category_id));
767 }
768 if let Some(video_definition) = request.video_definition {
769 params.push(("videoDefinition", video_definition.to_string()));
770 }
771 if let Some(video_dimension) = request.video_dimension {
772 params.push(("videoDimension", video_dimension.to_string()));
773 }
774 if let Some(video_duration) = request.video_duration {
775 params.push(("videoDuration", video_duration.to_string()));
776 }
777 if let Some(video_embeddable) = request.video_embeddable {
778 params.push(("videoEmbeddable", video_embeddable.to_string()));
779 }
780 if let Some(video_license) = request.video_license {
781 params.push(("videoLicense", video_license.to_string()));
782 }
783 if let Some(video_paid_product_placement) = request.video_paid_product_placement {
784 params.push((
785 "videoPaidProductPlacement",
786 video_paid_product_placement.to_string(),
787 ));
788 }
789 if let Some(video_syndicated) = request.video_syndicated {
790 params.push(("videoSyndicated", video_syndicated.to_string()));
791 }
792 if let Some(video_type) = request.video_type {
793 params.push(("videoType", video_type.to_string()));
794 }
795 let response = client
796 .get(url)
797 .query(¶ms)
798 .send()
799 .await?;
800 let text = response.text().await?;
801 let youtube_response: YoutubeSearchResponse = serde_json::from_str(&text)?;
802 Ok(youtube_response)
803}