1#![crate_name = "rarbg_api"]
2extern crate chrono;
3extern crate reqwest;
4extern crate serde;
5extern crate serde_json;
6
7use std::thread::sleep;
8use std::time::Duration;
9
10use reqwest::{Client, Error as ReqwestError, RequestBuilder, Response};
11use serde_json::Error as SerdeJsonError;
12
13use crate::api_parameters::ApiParameters;
14use crate::error::Error;
15use crate::mode::Mode;
16use crate::token::Token;
17use crate::torrents::Torrents;
18
19pub mod api_parameters;
20pub mod api_parameters_builder;
21pub mod category;
22pub mod episode_info;
23pub mod error;
24pub mod format;
25pub mod limit;
26pub mod mode;
27pub mod sort_by;
28pub mod token;
29pub mod torrent;
30pub mod torrents;
31
32const REQUEST_TIME_LIMIT: u64 = 5;
34const USER_AGENT: &str =
35 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:73.0) Gecko/20100101 Firefox/73.0";
36const ENDPOINT: &str = "https://torrentapi.org/pubapi_v2.php";
37
38#[derive(Debug)]
39pub struct RarBgApi {
40 app_id: String,
41 token: Token,
42}
43
44impl RarBgApi {
45 pub fn app_id(&self) -> &str {
59 self.app_id.as_str()
60 }
61
62 pub fn token(&self) -> &Token {
77 &self.token
78 }
79
80 pub async fn new(app_id: &str) -> Self {
98 RarBgApi {
99 token: Token::new(app_id).await,
100 app_id: app_id.to_string(),
101 }
102 }
103
104 async fn request(
105 &mut self,
106 search_value: Option<&[(&str, &str)]>,
107 mode: Mode,
108 parameters: Option<&ApiParameters>,
109 ) -> Result<Torrents, Error> {
110 if !self.token.is_valid() {
111 self.token = Token::new(self.app_id()).await;
112 }
113 sleep(Duration::new(REQUEST_TIME_LIMIT, 0));
114
115 let client: Client = Client::builder().user_agent(USER_AGENT).build().unwrap();
116
117 let mut request: RequestBuilder = client
118 .get(ENDPOINT)
119 .query(&[("mode", mode.as_str())])
120 .query(&[("token", self.token().value())])
121 .query(&[("app_id", self.app_id())]);
122
123 if search_value.is_some() {
124 request = request.query(search_value.unwrap());
125 }
126
127 if parameters.is_some() {
128 let pm = parameters.unwrap();
129 request = request
130 .query(&[("ranked", *pm.ranked() as isize)])
131 .query(&[("sort", &pm.sort_by().as_str())])
132 .query(&[("limit", pm.limit().as_str())])
133 .query(&[("format", pm.format().as_str())]);
134
135 if pm.minimum_seeders().is_some() {
136 request = request.query(&[("min_seeders", pm.minimum_seeders().unwrap())]);
137 }
138
139 if pm.minimum_leechers().is_some() {
140 request = request.query(&[("min_leechers", pm.minimum_leechers().unwrap())]);
141 }
142
143 if pm.categories().is_some() {
144 let categories = pm.categories().unwrap();
145 let stringified_categories: Vec<&str> =
146 categories.iter().map(|c| c.as_str()).collect();
147 let joined_categories: String = stringified_categories.join(";");
148 request = request.query(&[("category", joined_categories)]);
149 }
150 }
151 let response: Result<Response, ReqwestError> = request.send().await;
152
153 let content = match response {
154 Ok(res) => res.text().await,
155 Err(reason) => panic!("{}", reason),
156 };
157
158 let text = match content {
159 Ok(text) => text,
160 Err(reason) => panic!("{}", reason),
161 };
162
163 let torrents: Result<Torrents, SerdeJsonError> = serde_json::from_str(text.as_str());
164 match torrents {
165 Ok(torrents) => Ok(torrents),
166 Err(reason1) => {
167 let api_error: Result<Error, SerdeJsonError> = serde_json::from_str(text.as_str());
168 match api_error {
169 Ok(api_error) => Err(api_error),
170 Err(reason2) => panic!("First reason: {}. Second reason: {}", reason1, reason2),
171 }
172 }
173 }
174 }
175
176 pub async fn list(&mut self, parameters: Option<&ApiParameters>) -> Result<Torrents, Error> {
192 self.request(None, Mode::List, parameters).await
193 }
194
195 pub async fn search(
210 &mut self,
211 value: &str,
212 parameters: Option<&ApiParameters>,
213 ) -> Result<Torrents, Error> {
214 self.request(Some(&[("search_string", value)]), Mode::Search, parameters)
215 .await
216 }
217
218 pub async fn search_by_imdb(
234 &mut self,
235 value: &str,
236 parameters: Option<&ApiParameters>,
237 ) -> Result<Torrents, Error> {
238 self.request(Some(&[("search_imdb", value)]), Mode::Search, parameters)
239 .await
240 }
241
242 pub async fn search_by_tvdb(
258 &mut self,
259 value: &str,
260 parameters: Option<&ApiParameters>,
261 ) -> Result<Torrents, Error> {
262 self.request(Some(&[("search_tvdb", value)]), Mode::Search, parameters)
263 .await
264 }
265
266 pub async fn search_by_tmdb(
282 &mut self,
283 value: &str,
284 parameters: Option<&ApiParameters>,
285 ) -> Result<Torrents, Error> {
286 self.request(Some(&[("search_tmdb", value)]), Mode::Search, parameters)
287 .await
288 }
289}