use crate::{renditions::RenditionsPage, MediaPage};
use reqwest::header::{ACCEPT, AUTHORIZATION};
use std::{cmp::min, io::Cursor};
use tokio::{fs::File, io, sync::mpsc};
use tokio_stream::Stream;
use urlencoding::encode;
#[derive(Default, Debug, Clone, PartialEq)]
pub struct Client {
token: String,
site_id: String,
}
impl Client {
pub fn new(token: &str, site_id: &str) -> Self {
Self {
token: token.to_string(),
site_id: site_id.to_string(),
}
}
pub async fn get_renditions(&self, media_id: &str) -> Result<Option<RenditionsPage>, String> {
let endpoint = format!(
"https://api.jwplayer.com/v2/sites/{}/media/{}/media_renditions/",
self.site_id, media_id
);
let client = reqwest::Client::new();
let response = client
.get(endpoint)
.header(ACCEPT, "application/json")
.header(AUTHORIZATION, &self.token)
.send()
.await;
match response {
Ok(data) => {
let str_data = data.text().await.expect("Error getting API response");
let media_renditions: RenditionsPage = serde_json::from_str(&str_data.as_str())
.expect("Error deserializing JSON response");
Ok(Some(media_renditions))
}
Err(_) => Err("Error getting video renditions".to_string()),
}
}
pub async fn get_library_excluding_tags(
&self,
tags: Vec<&str>,
chunk_size: Option<i32>,
) -> Result<Option<MediaPage>, reqwest::Error> {
let query = match tags.len() {
0 => String::new(),
1 => format!("tags: NOT ({})", tags[0]),
_ => format!("tags: NOT ({})", tags.join(" OR ")),
};
let page_length = min(chunk_size.unwrap_or(10), 10000).clone();
println!("{}", query);
let endpoint = format!(
"https://api.jwplayer.com/v2/sites/{}/media/?&page_length={}&q={}&sort=created:dsc",
self.site_id, page_length, query
);
let client = reqwest::Client::new();
let response = client
.get(endpoint)
.header(ACCEPT, "application/json")
.header(AUTHORIZATION, &self.token)
.send()
.await;
match response {
Ok(data) => {
let str_data = data.text().await.expect("Error getting API response");
let media: MediaPage = serde_json::from_str(&str_data.as_str())
.expect("Error deserializing JSON response");
Ok(Some(media))
}
Err(e) => Err(e),
}
}
pub async fn get_library_by_tags(
&self,
tags: Vec<&str>,
chunk_size: Option<i32>,
) -> Result<Option<MediaPage>, reqwest::Error> {
let query = match tags.len() {
0 => String::new(),
1 => format!("tags: ({})", tags[0]),
_ => format!("tags: ({})", tags.join(" OR ")),
};
let page_length = min(chunk_size.unwrap_or(10), 10000).clone();
let endpoint = format!(
"https://api.jwplayer.com/v2/sites/{}/media/?&page_length={}&q={}&sort=created:dsc",
self.site_id, page_length, query
);
let client = reqwest::Client::new();
let response = client
.get(endpoint)
.header(ACCEPT, "application/json")
.header(AUTHORIZATION, &self.token)
.send()
.await;
match response {
Ok(data) => {
let str_data = data.text().await.expect("Error getting API response");
let media: MediaPage = serde_json::from_str(&str_data.as_str())
.expect("Error deserializing JSON response");
Ok(Some(media))
}
Err(e) => Err(e),
}
}
pub async fn get_library(
&self,
chunk_size: Option<i32>,
) -> Result<Option<MediaPage>, reqwest::Error> {
let page_length = min(chunk_size.unwrap_or(10), 10000).clone();
let endpoint = format!(
"https://api.jwplayer.com/v2/sites/{}/media/?page_length={}&sort=created:dsc",
self.site_id, page_length
);
let client = reqwest::Client::new();
let response = client
.get(endpoint)
.header(ACCEPT, "application/json")
.header(AUTHORIZATION, &self.token)
.send()
.await;
match response {
Ok(data) => {
let str_data = data.text().await.expect("Error getting API response");
let media: MediaPage = serde_json::from_str(&str_data.as_str())
.expect("Error deserializing JSON response");
Ok(Some(media))
}
Err(e) => Err(e),
}
}
pub async fn get_library_stream(
&self,
chunk_size: Option<i32>,
query: Option<String>,
) -> impl Stream<Item = Result<MediaPage, reqwest::Error>> {
let (tx, rx) = mpsc::channel(10);
tokio::spawn({
let this = self.clone();
let page_length = min(chunk_size.unwrap_or(10), 10000).clone();
async move {
let mut start_date = "2000-01-01".to_string();
let end_date = "3000-01-01".to_string();
loop {
match this
.stream_helper(&start_date, &end_date, &page_length, query.clone())
.await
{
Ok(Some(media_list)) if !media_list.media.is_empty() => {
if tx.send(Ok(media_list.clone())).await.is_err() {
break;
}
if let Some(last_item) = media_list.media.last() {
let new_start_date = encode(&last_item.created[..19]).to_string();
if new_start_date == start_date
|| media_list.total < media_list.page_length
{
break;
}
start_date = new_start_date;
}
}
Ok(_) => {
break;
}
Err(e) => {
let _ = tx.send(Err(e)).await;
break;
}
}
}
}
});
tokio_stream::wrappers::ReceiverStream::new(rx)
}
async fn stream_helper(
&self,
start_date: &str,
end_date: &str,
page_length: &i32,
query: Option<String>,
) -> Result<Option<MediaPage>, reqwest::Error> {
let full_query = match query {
Some(q) => {
format!("( created:[{} TO {}] ) AND ( {} )", start_date, end_date, q)
}
None => {
format!("created:[{} TO {}]", start_date, end_date)
}
};
let endpoint = format!(
"https://api.jwplayer.com/v2/sites/{}/media/?page_length={}&q={}&sort=created:asc",
self.site_id, page_length, full_query
);
let client = reqwest::Client::new();
let response = client
.get(endpoint)
.header(ACCEPT, "application/json")
.header(AUTHORIZATION, &self.token)
.send()
.await;
match response {
Ok(data) => {
let str_data = data.text().await.expect("Error getting API response");
let media: MediaPage = serde_json::from_str(&str_data.as_str())
.expect("Error deserializing JSON response");
Ok(Some(media))
}
Err(e) => Err(e),
}
}
pub async fn download(&self, media_id: &str, path: &str) {
let renditions = self.get_renditions(media_id).await;
if let Ok(Some(data)) = renditions {
let url = data
.media_renditions
.last()
.expect("No rendition entries in the media")
.delivery_url
.as_str();
let resp = reqwest::get(url).await.expect("URL Request Failed");
let body = resp.bytes().await.expect("Invalid Video Data");
let mut body_reader = Cursor::new(body);
let path_str = format!("{}/{}.mp4", path, data.media_renditions[0].id);
let mut out = File::create(&path_str)
.await
.expect("Failed to Create File");
io::copy(&mut body_reader, &mut out)
.await
.expect("Failed to Download Video");
}
}
pub async fn delete_meida(&self, media_id: &str) -> Result<(), String> {
let endpoint = format!(
"https://api.jwplayer.com/v2/sites/{}/media/{}/",
self.site_id, media_id
);
let client = reqwest::Client::new();
let response = client
.delete(endpoint)
.header(ACCEPT, "application/json")
.header(AUTHORIZATION, &self.token)
.send()
.await;
match response {
Ok(_) => Ok(()),
Err(_) => Err("Error deleting video".to_string()),
}
}
}