use crate::Subscription;
use async_trait::async_trait;
use image::DynamicImage;
#[cfg(test)]
use {crate::mock::MockSubscription, mockall::predicate::*, mockall::*};
#[async_trait]
pub trait Video:
Clone
+ std::hash::Hash
+ std::cmp::Eq
+ std::marker::Send
+ std::marker::Sync
+ Into<Vec<String>>
+ std::convert::TryFrom<Vec<String>>
{
type Subscription: Subscription;
fn url(&self) -> String;
fn title(&self) -> String;
fn uploaded(&self) -> chrono::NaiveDateTime;
fn subscription(&self) -> Self::Subscription;
fn thumbnail_url(&self) -> String;
async fn thumbnail_with_client(&self, client: &reqwest::Client) -> image::DynamicImage {
let thumbnail_url = self.thumbnail_url();
log::debug!("Getting thumbnail from url {}", thumbnail_url);
let response = client.get(&thumbnail_url).send().await;
if response.is_err() {
log::error!(
"Failed getting thumbnail for url {}, use default",
thumbnail_url
);
return self.default_thumbnail();
}
let parsed = response.unwrap().bytes().await;
if parsed.is_err() {
log::error!(
"Failed getting thumbnail for url {}, use default",
thumbnail_url
);
return self.default_thumbnail();
}
let parsed_bytes = parsed.unwrap();
if let Some(image) = <Self as Video>::convert_image(&parsed_bytes) {
image
} else {
self.default_thumbnail()
}
}
fn convert_image(data: &[u8]) -> Option<DynamicImage> {
image::load_from_memory(&data).ok()
}
fn default_thumbnail(&self) -> DynamicImage {
DynamicImage::new_rgba8(1, 1)
}
async fn thumbnail(&self) -> DynamicImage {
self.thumbnail_with_client(&reqwest::Client::new()).await
}
}
#[cfg(test)]
mock! {
pub(crate) Video {}
impl Clone for Video {
fn clone(&self) -> Self;
}
impl std::convert::TryFrom<Vec<String>> for Video {
type Error = ();
fn try_from(_vec: Vec<String>) -> Result<Self, ()> {
Err(())
}
}
impl Video for Video {
type Subscription = MockSubscription;
fn url(&self) -> String;
fn title(&self) -> String;
fn uploaded(&self) -> chrono::NaiveDateTime;
fn subscription(&self) -> MockSubscription;
fn thumbnail_url(&self) -> String;
}
}
#[cfg(test)]
impl std::hash::Hash for MockVideo {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.uploaded().hash(state);
}
}
#[cfg(test)]
impl PartialEq for MockVideo {
fn eq(&self, other: &Self) -> bool {
self.uploaded().eq(&other.uploaded())
}
}
#[cfg(test)]
impl Eq for MockVideo {}
#[cfg(test)]
impl std::convert::From<MockVideo> for Vec<String> {
fn from(_v: MockVideo) -> Self {
vec![]
}
}