torbox_ddl_rs/lib.rs
1use torbox_core_rs::{
2 api::ApiResponse,
3 client::{Endpoint, EndpointSpec, TorboxClient},
4 data::{
5 creation::DownloadLinkResponse,
6 webdownload::{
7 WebDownloadCacheAvailability, WebdownloadCreationResponse, WebdownloadHosterList,
8 WebdownloadStatus,
9 },
10 },
11 enums::OneOrMany,
12 error::ApiError,
13};
14
15use crate::{
16 body::{WebdownloadControlReq, WebdownloadCreateBody},
17 endpoint::{
18 ListWebdownloadsGetEp, WebdownloadCachedAvailabilityGetEp, WebdownloadControlPostEp,
19 WebdownloadCreatePostEp, WebdownloadHosterListGetEp, WebdownloadRequestLinkGetEp,
20 },
21 query::{
22 ListWebdownloadsQuery, WebdownloadCachedAvailabilityQuery, WebdownloadRequestLinkQuery,
23 },
24};
25
26//todo: Add the rest for ddl
27pub mod body;
28pub mod endpoint;
29pub mod payload;
30pub mod query;
31pub mod tests;
32pub mod types;
33
34#[cfg_attr(feature = "specta", derive(specta::Type))]
35pub struct WebdownloadApi<'a> {
36 client: &'a TorboxClient,
37}
38
39impl<'a> WebdownloadApi<'a> {
40 pub fn new(client: &'a TorboxClient) -> Self {
41 Self { client }
42 }
43
44 pub async fn create(
45 &self,
46 body: WebdownloadCreateBody,
47 ) -> Result<ApiResponse<WebdownloadCreationResponse>, ApiError> {
48 Endpoint::<WebdownloadCreatePostEp>::new(self.client)
49 .call_multipart(body)
50 .await
51 }
52
53 pub async fn control(&self, req: WebdownloadControlReq) -> Result<ApiResponse<()>, ApiError> {
54 let (query, body) = req.into_parts();
55
56 Endpoint::<WebdownloadControlPostEp>::new(self.client)
57 .call_query_json(query, body)
58 .await
59 }
60
61 /// Requests a download link for a torrent
62 ///
63 /// Links are valid for 3 hours. Once downloading starts, the transfer
64 /// can continue indefinitely. Permalinks can and should be created by setting
65 /// `redirect=true`.
66 ///
67 /// Setting `redirect = true` truly helps torbox servers not to be overwhelmed.
68 ///
69 /// # Arguments
70 ///
71 /// * `query` - Contains torrent ID and download options
72 ///
73 /// # Returns
74 ///
75 /// Either a JSON response or redirect URL
76 pub async fn request_download_link(
77 &self,
78 query: WebdownloadRequestLinkQuery,
79 ) -> Result<DownloadLinkResponse, ApiError> {
80 let endpoint = format!(
81 "{}/{}",
82 self.client.base_url,
83 WebdownloadRequestLinkGetEp::PATH
84 );
85 let request = self.client.client.get(&endpoint).query(&query);
86
87 let response = request.send().await?;
88
89 if query.redirect {
90 if response.status().is_redirection() {
91 let location = response
92 .headers()
93 .get("Location")
94 .ok_or(ApiError::RedirectError("Missing Location header".into()))?
95 .to_str()
96 .map_err(|_| ApiError::RedirectError("Invalid Location header".into()))?;
97
98 Ok(DownloadLinkResponse::Redirect(location.to_string()))
99 } else {
100 match response.json::<ApiResponse<String>>().await {
101 Ok(json) => Ok(DownloadLinkResponse::Json(json)),
102 Err(_) => Err(ApiError::UnexpectedPayload),
103 }
104 }
105 } else {
106 let json = response.json::<ApiResponse<String>>().await?;
107 Ok(DownloadLinkResponse::Json(json))
108 }
109 }
110
111 /// Gets the user's torrent list. This gives you the needed information to perform other torrent actions.
112 ///
113 /// This information only gets updated every 600 seconds, or when the _Request Update On Torrent request_ is sent to the relay API.
114 ///
115 /// # Returns
116 ///
117 /// A deserialized `ApiResponse` containing the list of torrents.
118 pub async fn list_query(
119 &self,
120 query: ListWebdownloadsQuery,
121 ) -> Result<ApiResponse<Option<Vec<WebdownloadStatus>>>, ApiError> {
122 let resp: ApiResponse<Option<OneOrMany<WebdownloadStatus>>> =
123 Endpoint::<ListWebdownloadsGetEp>::new(self.client)
124 .call_query(query)
125 .await?;
126
127 let normalized = resp.map(|opt| {
128 opt.map(|one_or_many| match one_or_many {
129 OneOrMany::One(item) => vec![item],
130 OneOrMany::Many(list) => list,
131 })
132 });
133
134 Ok(normalized)
135 }
136
137 pub async fn is_cached(
138 &self,
139 query: WebdownloadCachedAvailabilityQuery,
140 ) -> Result<ApiResponse<Option<Vec<WebDownloadCacheAvailability>>>, ApiError> {
141 let resp = Endpoint::<WebdownloadCachedAvailabilityGetEp>::new(self.client)
142 .call_query(query)
143 .await?;
144
145 let normalized = resp.map(|opt| {
146 opt.map(|one_or_many| match one_or_many {
147 OneOrMany::One(item) => vec![item],
148 OneOrMany::Many(list) => list,
149 })
150 });
151
152 Ok(normalized)
153 }
154
155 /// ## Overview
156 /// A dynamic list of hosters that TorBox is capable of downloading through its paid service.
157 /// - Name - a clean name for display use, the well known name of the service, should be recognizable to users.
158 /// - Domains - an array of known domains that the hoster uses. While each may serve a different purpose it is still included.
159 /// - URL - the main url of the service. This should take you to the home page or a service page of the hoster.
160 /// - Icon - a square image, usually a favicon or logo, that represents the service, should be recognizable as the hoster's icon.
161 /// - Status - whether this hoster can be used on TorBox or not at the current time. It is usually a good idea to check this value before submitting a download to TorBox's servers for download.
162 /// - Type - values are either "hoster" or "stream". Both do the same thing, but is good to differentiate services used for different things.
163 /// - Note - a string value (or null) that may give helpful information about the current status or state of a hoster. This can and should be shown to end users.
164 /// - Daily Link Limit - the number of downloads a user can use per day. As a user submits links, once they hit this number, the API will deny them from adding anymore of this type of link. A zero value means that it is unlimited.
165 /// - Daily Link Used - the number of downloads a user has already used. Usually zero unless you send authentication to the endpoint. This will return accurate values.
166 /// - Daily Bandwidth Limit - the value in bytes that a user is allowed to download from this hoster. A zero value means that it is unlimited. It is recommended to use the Daily Link Limit instead.
167 /// - Daily Bandwidth Used - the value in bytes that a user has already used to download from this hoster. Usually zero unless you send authentication to the endpoint. This will return accurate values.
168 ///
169 /// ## Authorization
170 /// Optional authorization. Authorization is not required in this endpoint unless you want to get the user's live data. Requires an API key using the Authorization Bearer Header to get the live and accurate data for Daily Link Used and Daily Bandwidth Used
171 pub async fn list_hosters(&self) -> Result<ApiResponse<Vec<WebdownloadHosterList>>, ApiError> {
172 Endpoint::<WebdownloadHosterListGetEp>::new(self.client)
173 .call_query(())
174 .await
175 }
176
177 /// ## TODO:
178 /// This function hasn't yet been made due to laziness.
179 ///
180 /// ## DANGEROUS
181 ///
182 /// ## Overview
183 /// Updates a download item based on the item's ID.
184 /// Sending a PUT request with valid items overwrites the previous items entirely, so use this endpoint with caution. Please make sure to read the restrictions below as they are important.
185 ///
186 /// ## Restrictions
187 /// - Item must be cached to update.
188 /// - Does not affect the cached database (or any other user's item).
189 /// - Name must pass validation:
190 /// - Above 1 character long.
191 /// - Below 200 characters long.
192 /// - Cannot contain any non-url or non filesystem safe characters. (This is removed automatically).
193 /// - Cannot contain any leading or trailing whitespace. You can have spaces in the middle. (This is removed automatically).
194 ///
195 /// - Tags follow the same rules as the name.
196 /// - Alternative hashes must be of MD5, SHA1, or SHA256.
197 pub async fn edit_item_put(&self) {}
198}