rsynko_media/
extraction.rs1use crate::MediaSorts;
2use alux_ext::ext;
3use ambassador::delegatable_trait;
4use thiserror::Error;
5
6#[delegatable_trait]
8pub trait ExtractionAlg: MediaSorts {
9 fn media(
11 &self,
12 id: impl Into<String>,
13 metadata: Self::Metadata,
14 formats: impl IntoIterator<Item = Self::Format>,
15 ) -> Self::Extraction;
16 fn url_reference(
18 &self,
19 url: impl Into<String>,
20 extractor: Option<Self::Extractor>,
21 transparent: bool,
22 ) -> Self::Extraction;
23 fn extraction_collection(
25 &self,
26 id: Option<String>,
27 kind: CollectionKind,
28 metadata: Self::Metadata,
29 entries: impl IntoIterator<Item = Self::Extraction>,
30 ) -> Self::Extraction;
31}
32
33#[delegatable_trait]
35pub trait ExtractionViewAlg: MediaSorts {
36 fn as_media(&self, extraction: Self::Extraction) -> Option<Self::Media>;
38}
39
40#[delegatable_trait]
42pub trait MediaViewAlg: MediaSorts {
43 fn media_id<'a>(&self, media: &'a Self::Media) -> &'a str;
45 fn media_title<'a>(&self, media: &'a Self::Media) -> Option<&'a str>;
47 fn media_formats<'a>(&self, media: &'a Self::Media) -> &'a [Self::Format];
49}
50
51#[delegatable_trait]
53pub trait ExtractionCatalogAlg: MediaSorts {
54 fn extractor_keys(&self) -> impl Iterator<Item = &Self::Extractor>;
56
57 fn extractor_accepts(&self, extractor: &Self::Extractor, url: &str) -> bool;
59}
60
61#[delegatable_trait]
63pub trait ExtractionApplyAlg: MediaSorts {
64 type Error;
66
67 fn extract_with(&self, extractor: &Self::Extractor, url: &str) -> Result<Self::Extraction, Self::Error>;
73}
74
75#[derive(Clone, Copy, Debug, PartialEq, Eq)]
77pub enum CollectionKind {
78 Playlist,
80 MultiVideo,
82}
83
84#[derive(Debug, Error, PartialEq, Eq)]
86pub enum ExtractUrlError<ExtractorError> {
87 #[error("no extractor accepts {url}")]
89 Unsupported {
90 url: String,
92 },
93 #[error("selected extractor failed: {0}")]
95 Extractor(ExtractorError),
96}
97
98#[ext(name = ExtractionExt)]
100pub impl<This, Extractor, Extraction> This
101where
102 This: ExtractionCatalogAlg<Extractor = Extractor>
103 + ExtractionApplyAlg<Extractor = Extractor, Extraction = Extraction>,
104{
105 fn choose_extractor(&self, url: &str) -> Option<&Extractor> {
107 self.extractor_keys().find(|extractor| self.extractor_accepts(extractor, url))
108 }
109
110 fn extract_url(&self, url: &str) -> Result<Extraction, ExtractUrlError<This::Error>> {
117 let extractor =
118 self.choose_extractor(url).ok_or_else(|| ExtractUrlError::Unsupported { url: url.to_owned() })?;
119 self.extract_with(extractor, url).map_err(ExtractUrlError::Extractor)
120 }
121}