use crate::MediaSorts;
use alux_ext::ext;
use ambassador::delegatable_trait;
use thiserror::Error;
#[delegatable_trait]
pub trait ExtractionAlg: MediaSorts {
fn media(
&self,
id: impl Into<String>,
metadata: Self::Metadata,
formats: impl IntoIterator<Item = Self::Format>,
) -> Self::Extraction;
fn url_reference(
&self,
url: impl Into<String>,
extractor: Option<Self::Extractor>,
transparent: bool,
) -> Self::Extraction;
fn extraction_collection(
&self,
id: Option<String>,
kind: CollectionKind,
metadata: Self::Metadata,
entries: impl IntoIterator<Item = Self::Extraction>,
) -> Self::Extraction;
}
#[delegatable_trait]
pub trait ExtractionViewAlg: MediaSorts {
fn as_media(&self, extraction: Self::Extraction) -> Option<Self::Media>;
}
#[delegatable_trait]
pub trait MediaViewAlg: MediaSorts {
fn media_id<'a>(&self, media: &'a Self::Media) -> &'a str;
fn media_title<'a>(&self, media: &'a Self::Media) -> Option<&'a str>;
fn media_formats<'a>(&self, media: &'a Self::Media) -> &'a [Self::Format];
}
#[delegatable_trait]
pub trait ExtractionCatalogAlg: MediaSorts {
fn extractor_keys(&self) -> impl Iterator<Item = &Self::Extractor>;
fn extractor_accepts(&self, extractor: &Self::Extractor, url: &str) -> bool;
}
#[delegatable_trait]
pub trait ExtractionApplyAlg: MediaSorts {
type Error;
fn extract_with(&self, extractor: &Self::Extractor, url: &str) -> Result<Self::Extraction, Self::Error>;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CollectionKind {
Playlist,
MultiVideo,
}
#[derive(Debug, Error, PartialEq, Eq)]
pub enum ExtractUrlError<ExtractorError> {
#[error("no extractor accepts {url}")]
Unsupported {
url: String,
},
#[error("selected extractor failed: {0}")]
Extractor(ExtractorError),
}
#[ext(name = ExtractionExt)]
pub impl<This, Extractor, Extraction> This
where
This: ExtractionCatalogAlg<Extractor = Extractor>
+ ExtractionApplyAlg<Extractor = Extractor, Extraction = Extraction>,
{
fn choose_extractor(&self, url: &str) -> Option<&Extractor> {
self.extractor_keys().find(|extractor| self.extractor_accepts(extractor, url))
}
fn extract_url(&self, url: &str) -> Result<Extraction, ExtractUrlError<This::Error>> {
let extractor =
self.choose_extractor(url).ok_or_else(|| ExtractUrlError::Unsupported { url: url.to_owned() })?;
self.extract_with(extractor, url).map_err(ExtractUrlError::Extractor)
}
}