Skip to main content

kick_api/api/
livestreams.rs

1use crate::error::{KickApiError, Result};
2use crate::models::{Livestream, LivestreamSort, LivestreamStats};
3use reqwest;
4
5/// Livestreams API - query live streams and global stats
6pub struct LivestreamsApi<'a> {
7    client: &'a reqwest::Client,
8    token: &'a Option<String>,
9    base_url: &'a str,
10}
11
12impl<'a> LivestreamsApi<'a> {
13    pub(crate) fn new(
14        client: &'a reqwest::Client,
15        token: &'a Option<String>,
16        base_url: &'a str,
17    ) -> Self {
18        Self {
19            client,
20            token,
21            base_url,
22        }
23    }
24
25    /// Get currently live streams
26    ///
27    /// Works with both User Access Tokens and App Access Tokens (no special scope required).
28    ///
29    /// # Parameters
30    /// - `broadcaster_user_ids`: Filter by specific broadcaster IDs (max 50)
31    /// - `category_id`: Filter by category
32    /// - `language`: Filter by language code (e.g., "en")
33    /// - `sort`: Sort order (defaults to `ViewerCount`)
34    /// - `limit`: Number of results (1-100, defaults to 25)
35    ///
36    /// # Example
37    /// ```no_run
38    /// // Get top live streams
39    /// let streams = client.livestreams().get(None, None, None, None, None).await?;
40    ///
41    /// // Get specific broadcasters
42    /// let streams = client.livestreams().get(
43    ///     Some(vec![12345, 67890]),
44    ///     None, None, None, None,
45    /// ).await?;
46    /// ```
47    pub async fn get(
48        &self,
49        broadcaster_user_ids: Option<Vec<u64>>,
50        category_id: Option<u32>,
51        language: Option<&str>,
52        sort: Option<LivestreamSort>,
53        limit: Option<u32>,
54    ) -> Result<Vec<Livestream>> {
55        super::require_token(self.token)?;
56
57        let url = format!("{}/livestreams", self.base_url);
58        let mut request = self
59            .client
60            .get(&url)
61            .header("Accept", "*/*")
62            .bearer_auth(self.token.as_ref().unwrap());
63
64        if let Some(ids) = broadcaster_user_ids {
65            for id in ids {
66                request = request.query(&[("broadcaster_user_id", id)]);
67            }
68        }
69        if let Some(cat) = category_id {
70            request = request.query(&[("category_id", cat)]);
71        }
72        if let Some(lang) = language {
73            request = request.query(&[("language", lang)]);
74        }
75        if let Some(s) = sort {
76            request = request.query(&[("sort", s.as_str())]);
77        }
78        if let Some(l) = limit {
79            request = request.query(&[("limit", l)]);
80        }
81
82        let response = crate::http::send_with_retry(self.client, request).await?;
83        if response.status().is_success() {
84            let body = response.text().await?;
85
86            #[derive(serde::Deserialize)]
87            struct LivestreamsResponse {
88                data: Vec<Livestream>,
89            }
90
91            let resp: LivestreamsResponse = serde_json::from_str(&body)
92                .map_err(|e| KickApiError::ApiError(format!("JSON parse error: {}", e)))?;
93
94            Ok(resp.data)
95        } else {
96            Err(KickApiError::ApiError(format!(
97                "Failed to get livestreams: {}",
98                response.status()
99            )))
100        }
101    }
102
103    /// Get global livestream statistics
104    ///
105    /// Returns the total number of live streams on Kick.
106    /// Works with both User Access Tokens and App Access Tokens.
107    ///
108    /// # Example
109    /// ```no_run
110    /// let stats = client.livestreams().stats().await?;
111    /// println!("Total live streams: {}", stats.total_count);
112    /// ```
113    pub async fn stats(&self) -> Result<LivestreamStats> {
114        super::require_token(self.token)?;
115
116        let url = format!("{}/livestreams/stats", self.base_url);
117        let request = self
118            .client
119            .get(&url)
120            .header("Accept", "*/*")
121            .bearer_auth(self.token.as_ref().unwrap());
122
123        let response = crate::http::send_with_retry(self.client, request).await?;
124        if response.status().is_success() {
125            let body = response.text().await?;
126
127            #[derive(serde::Deserialize)]
128            struct StatsResponse {
129                data: LivestreamStats,
130            }
131
132            let resp: StatsResponse = serde_json::from_str(&body)
133                .map_err(|e| KickApiError::ApiError(format!("JSON parse error: {}", e)))?;
134
135            Ok(resp.data)
136        } else {
137            Err(KickApiError::ApiError(format!(
138                "Failed to get livestream stats: {}",
139                response.status()
140            )))
141        }
142    }
143}