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(
73 &self,
74 extractor: &Self::Extractor,
75 url: &str,
76 ) -> Result<Self::Extraction, Self::Error>;
77}
78
79#[derive(Clone, Copy, Debug, PartialEq, Eq)]
81pub enum CollectionKind {
82 Playlist,
84 MultiVideo,
86}
87
88#[derive(Debug, Error, PartialEq, Eq)]
90pub enum ExtractUrlError<ExtractorError> {
91 #[error("no extractor accepts {url}")]
93 Unsupported {
94 url: String,
96 },
97 #[error("selected extractor failed: {0}")]
99 Extractor(ExtractorError),
100}
101
102#[ext(name = ExtractionExt)]
104pub impl<This, Extractor, Extraction> This
105where
106 This: ExtractionCatalogAlg<Extractor = Extractor>
107 + ExtractionApplyAlg<Extractor = Extractor, Extraction = Extraction>,
108{
109 fn choose_extractor(&self, url: &str) -> Option<&Extractor> {
111 self.extractor_keys()
112 .find(|extractor| self.extractor_accepts(extractor, url))
113 }
114
115 fn extract_url(&self, url: &str) -> Result<Extraction, ExtractUrlError<This::Error>> {
122 let extractor = self
123 .choose_extractor(url)
124 .ok_or_else(|| ExtractUrlError::Unsupported {
125 url: url.to_owned(),
126 })?;
127 self.extract_with(extractor, url)
128 .map_err(ExtractUrlError::Extractor)
129 }
130}