fyyd_api/v2/fyyd_api.rs
1use std::sync::OnceLock;
2
3use fyyd_types::v2::fyyd_response::{FyydPodcast, FyydResponse};
4use reqwest::{Client, ClientBuilder};
5
6const FYYD_API_V2: &str = "https://api.fyyd.de/0.2";
7const PODCAST: &str = "/podcast";
8const EPISODES: &str = "/episodes";
9//const PODCAST_EPISODES: &str = "/podcast/episodes";
10//const PODCASTS: &str = " /podcasts";
11//const CATEGORIES: &str = "/categories";
12//const CATEGORY: &str = "/category";
13//const PODCAST_RECOMMEND: &str = "/podcast/recommend";
14const _PODCAST_LATEST: &str = "/podcast/latest";
15//const PODCAST_COLLECTIONS: &str = "/podcast/collections";
16//const EPISODE: &str = "/episode";
17//const EPISODE_LATEST: &str = "/episode/latest";
18//const EPISODE_CURATIONS: &str = "/episode/curations";
19
20const SEARCH_PODCAST: &str = "/search/podcast";
21//const SEARCH_CURATION: &str = "/search/curation";
22//const SEARCH_EPISODE: &str = "/search/episode";
23//const SEARCH_USER: &str = "/search/user";
24//const SEARCH_COLOR: &str = "/search/color";
25//const FEATURE_HOT: &str = "/feature/podcast/hot";
26//const FEATURE_HOT_LANGUAGES: &str = "/feature/podcast/hot/languages";
27
28static HTTP_CLIENT: OnceLock<Client> = OnceLock::new();
29
30fn get_http_client(client_builder: Option<ClientBuilder>) -> &'static Client {
31 HTTP_CLIENT.get_or_init(|| {
32 let builder = client_builder.unwrap_or_else(ClientBuilder::new);
33 builder.build().unwrap()
34 })
35}
36
37/// Client for interacting with the fyyd API.
38pub struct FyydClient {
39 client: Client,
40}
41
42impl FyydClient {
43 /// Creates a new instance of the FyydClient.
44 ///
45 /// Arguments:
46 ///
47 /// - `client_builder` - Optional `ClientBuilder` to customize the underlying HTTP client.
48 ///
49 /// Returns:
50 ///
51 /// A new instance of `FyydClient`.
52 ///
53 /// # Example
54 ///
55 /// ```rust
56 /// use fyyd_api::v2::fyyd_api::FyydClient;
57 /// use reqwest::ClientBuilder;
58 ///
59 /// let client_builder = ClientBuilder::new().timeout(std::time::Duration::from_secs(10));
60 /// let client = FyydClient::new(Some(client_builder));
61 /// ```
62 pub fn new(client_builder: Option<ClientBuilder>) -> Self {
63 let client = get_http_client(client_builder).clone();
64 FyydClient { client }
65 }
66
67 /// Search for a podcast feed inside fyyd's database,
68 /// matches any - or some set of a given critetia.
69 ///
70 /// Arguments:
71 ///
72 /// - `title` - the podcast's title
73 ///
74 /// - `url` - the podcast's url
75 ///
76 /// - `term` - a search term inside the podcast
77 ///
78 /// - `page` - the page of the search,
79 /// WARNING: if the page is overshot it will still return valid pages
80 pub async fn search_podcast_feed(
81 &self,
82 title: Option<String>,
83 url: Option<String>,
84 term: Option<String>,
85 page: Option<u16>,
86 ) -> Result<FyydResponse<Vec<FyydPodcast>>, Box<dyn std::error::Error>> {
87 let path = FYYD_API_V2.to_owned() + SEARCH_PODCAST;
88
89 let request = self
90 .client
91 .get(path)
92 .query(&[("title", title.unwrap_or_default().as_str())])
93 .query(&[("url", url.unwrap_or_default().as_str())])
94 .query(&[("term", term.unwrap_or_default().as_str())])
95 .query(&[("page", page.unwrap_or_default())]);
96
97 let response = request.send().await?;
98 let body = response.bytes().await?.to_vec();
99 let fyyd_response: FyydResponse<Vec<FyydPodcast>> = serde_json::from_slice(&body)?;
100
101 Ok(fyyd_response)
102 }
103
104 /// Retrieves information about a podcast
105 ///
106 /// Retrieve episodes by appending `/episodes`
107 ///
108 /// Arguments:
109 ///
110 /// - `id` - the podcast's id from feeds database
111 ///
112 /// - `slug` - the podcast's slug
113 ///
114 /// - `count` - the page size (default: 50)
115 ///
116 /// - `page` - the page of the search,
117 /// WARNING: if the page is overshot it will still return valid pages
118 ///
119 /// - `episodes` - bool - whether to query episodes information
120 pub async fn get_episodes_from_id(
121 &self,
122 id: u64,
123 slug: Option<String>,
124 count: Option<u16>,
125 page: Option<u16>,
126 _episodes: bool,
127 ) -> Result<FyydResponse<FyydPodcast>, Box<dyn std::error::Error>> {
128 let path = FYYD_API_V2.to_owned() + PODCAST + EPISODES;
129
130 let request = self
131 .client
132 .get(path)
133 .query(&[("podcast_id", id)])
134 .query(&[("podcast_slug", slug.unwrap_or_default().as_str())])
135 .query(&[("page", page.unwrap_or_default())])
136 .query(&[("count", count.unwrap_or(50))]);
137
138 let response = request.send().await?;
139 let body = response.bytes().await?.to_vec();
140 let fyyd_response: FyydResponse<FyydPodcast> = serde_json::from_slice(&body)?;
141
142 Ok(fyyd_response)
143 }
144
145 /// Retrieves information about the last added podcasts
146 ///
147 /// Arguments:
148 /// - `since_id` - the last podcast's id from feeds database (maximum 1000)
149 ///
150 /// - `count` - the page size (default: 20)
151 pub(crate) async fn _get_latest_episodes_from_id(
152 &self,
153 since_id: Option<u64>,
154 count: Option<u16>,
155 ) -> Result<FyydResponse<FyydPodcast>, Box<dyn std::error::Error>> {
156 let path = FYYD_API_V2.to_owned() + _PODCAST_LATEST;
157
158 let request = self
159 .client
160 .get(path)
161 .query(&[("since_id", since_id)])
162 .query(&[("count", count.unwrap_or(20))]);
163
164 let response = request.send().await?;
165 let body = response.bytes().await?.to_vec();
166 let fyyd_response: FyydResponse<FyydPodcast> = serde_json::from_slice(&body)?;
167
168 Ok(fyyd_response)
169 }
170}