use reqwest::blocking::Client;
use reqwest::header::HeaderMap;
use serde::Deserialize;
use std::error;
use std::fmt;
use crate::structs::*;
const USER_AGENT: &'static str = "Mozilla/5.0 roosterteeth-rs/0.3.1 reqwest/0.11";
const LOGIN_URL: &'static str = "https://auth.roosterteeth.com/oauth/token";
const API_URL: &'static str = "https://svod-be.roosterteeth.com/api/v1";
type Result<T> = std::result::Result<T, VideoUnavailable>;
#[derive(Debug, Clone)]
pub struct VideoUnavailable;
impl fmt::Display for VideoUnavailable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Video is not (yet?) avaliable for non-sponsors.")
}
}
impl error::Error for VideoUnavailable {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
fn append_channel_and_order<'a>(
url: &mut String,
channel: Option<&'a str>,
order: Option<&'a str>,
) {
if channel != None {
url.push_str("&channel_id=");
url.push_str(channel.unwrap());
}
url.push_str("&order=");
if order == None {
url.push_str("desc");
} else {
url.push_str(order.unwrap());
}
}
#[derive(Debug, Deserialize)]
struct Token {
access_token: String,
token_type: String,
expires_in: u32,
refresh_token: String,
scope: String,
created_at: u32,
user_id: u32,
uuid: String,
}
pub enum Credential<'a> {
Anonymous,
Login(&'a str, &'a str),
}
pub struct Requests {
client: Client,
headers: HeaderMap,
}
impl Requests {
pub fn new(credential: Credential) -> Self {
let client = Client::builder()
.user_agent(USER_AGENT)
.build()
.expect("Unable to build the reqwest client");
let login: Option<(&str, &str)> = match credential {
Credential::Anonymous => None,
Credential::Login(u, p) => Some((u, p)),
};
let mut headers = HeaderMap::new();
if login.is_some() {
let (user, pass) = login.unwrap();
let body = format!(
"{{\"client_id\":\"4338d2b4bdc8db1239360f28e72f0d9ddb1fd01e7a38fbb07b4b1f4ba4564cc5\",\"grant_type\":\"password\",\"password\":\"{}\",\"scope\":\"user public\",\"username\":\"{}\"}}",
pass,
user,
);
let token: Token = client
.post(LOGIN_URL)
.body(body)
.send()
.unwrap()
.json()
.unwrap();
headers.insert(
"authorization",
format!("Bearer {}", token.access_token).parse().unwrap(),
);
}
Requests {
client: client,
headers: headers,
}
}
pub fn list_channels(&self) -> Vec<channels::Channel> {
let url = format!("{}/channels", API_URL);
let result: channels::Root = self.client.get(&url).send().unwrap().json().unwrap();
result.data
}
pub fn list_episodes<'a>(
&self,
page: u16,
channel: Option<&'a str>,
order: Option<&'a str>,
) -> Vec<episodes::Episode> {
let mut url = format!("{}/episodes?per_page=100", API_URL);
append_channel_and_order(&mut url, channel, order);
url = format!("{}&page={}", url, page);
let result: episodes::Root = self.client.get(&url).send().unwrap().json().unwrap();
result.data
}
pub fn list_series<'a>(
&self,
channel: Option<&'a str>,
order: Option<&'a str>,
) -> Vec<series::Series> {
let mut url = format!("{}/shows?per_page=1000", API_URL);
append_channel_and_order(&mut url, channel, order);
url.push_str("&page=1");
let result: series::Root = self.client.get(&url).send().unwrap().json().unwrap();
result.data
}
pub fn get_seasons<'a>(&self, slug: &'a str, order: Option<&'a str>) -> Vec<seasons::Season> {
let url = format!(
"{}/shows/{}/seasons?order={}",
API_URL,
slug,
order.unwrap_or("desc"),
);
let result: seasons::Root = self.client.get(&url).send().unwrap().json().unwrap();
result.data
}
pub fn get_season_episodes<'a>(
&self,
slug: &'a str,
order: Option<&'a str>,
) -> Vec<episodes::Episode> {
let url = format!(
"{}/seasons/{}/episodes?order={}&per_page=100",
API_URL,
slug,
order.unwrap_or("asc"),
);
let result: episodes::Root = self.client.get(&url).send().unwrap().json().unwrap();
result.data
}
pub fn get_series<'a>(&self, slug: &'a str) -> series::Series {
let url = format!("{}/shows/{}", API_URL, slug);
let mut result: series::Root = self.client.get(&url).send().unwrap().json().unwrap();
result.data.remove(0)
}
pub fn get_episode<'a>(&self, slug: &'a str) -> episodes::Episode {
let url = format!("{}/watch/{}", API_URL, slug);
let mut result: episodes::Root = self.client.get(&url).send().unwrap().json().unwrap();
result.data.remove(0)
}
pub fn get_video<'a>(&self, slug: &'a str) -> Result<videos::Video> {
let url = format!("{}/watch/{}/videos", API_URL, slug);
let response = self
.client
.get(&url)
.headers(self.headers.clone())
.send()
.unwrap();
if response.status().is_success() {
let mut result: videos::Root = response.json().unwrap();
Ok(result.data.remove(0))
} else {
Err(VideoUnavailable)
}
}
}