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}