rsynko_media/
selection.rs1use crate::{FORMAT_EXTENSION, FORMAT_HAS_AUDIO, FORMAT_HAS_VIDEO, MediaSorts};
2use alux_ext::ext;
3use ambassador::delegatable_trait;
4
5#[delegatable_trait]
7pub trait FormatPredicateAlg: MediaSorts {
8 fn any_format(&self) -> Self::Predicate;
10 fn format_id(&self, id: impl Into<String>) -> Self::Predicate;
12 fn observed_format(&self, key: impl Into<String>) -> Self::Predicate;
14 fn text_format(&self, key: impl Into<String>, value: impl Into<String>) -> Self::Predicate;
16 fn flag_format(&self, key: impl Into<String>, value: bool) -> Self::Predicate;
18 fn number_format(
20 &self,
21 key: impl Into<String>,
22 comparison: FormatComparison,
23 value: f64,
24 ) -> Self::Predicate;
25 fn all_formats(&self, left: Self::Predicate, right: Self::Predicate) -> Self::Predicate;
27 fn not_format(&self, predicate: Self::Predicate) -> Self::Predicate;
29}
30
31#[delegatable_trait]
33pub trait FormatSelectionAlg: MediaSorts {
34 fn best_format(&self, predicate: Self::Predicate) -> Self::Selection;
36 fn worst_format(&self, predicate: Self::Predicate) -> Self::Selection;
38 fn merge_formats(
40 &self,
41 selections: impl IntoIterator<Item = Self::Selection>,
42 ) -> Self::Selection;
43 fn fallback_formats(
45 &self,
46 selections: impl IntoIterator<Item = Self::Selection>,
47 ) -> Self::Selection;
48}
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq)]
52pub enum FormatComparison {
53 AtMost,
55 Exactly,
57 AtLeast,
59}
60
61#[delegatable_trait]
66pub trait FormatPredicateMatchAlg: MediaSorts {
67 fn format_matches(&self, predicate: &Self::Predicate, format: &Self::Format) -> bool;
69}
70
71#[delegatable_trait]
76pub trait FormatSelectionApplyAlg: MediaSorts {
77 fn select_formats<'a>(
79 &self,
80 formats: &'a [Self::Format],
81 selection: &Self::Selection,
82 ) -> Option<Vec<&'a Self::Format>>;
83}
84
85#[ext(name = FormatPredicateExt)]
87pub impl<This> This
88where
89 This: FormatPredicateAlg,
90{
91 fn has_audio(&self) -> This::Predicate {
93 self.flag_format(FORMAT_HAS_AUDIO, true)
94 }
95
96 fn has_video(&self) -> This::Predicate {
98 self.flag_format(FORMAT_HAS_VIDEO, true)
99 }
100
101 fn format_extension(&self, extension: impl Into<String>) -> This::Predicate {
103 self.text_format(FORMAT_EXTENSION, extension)
104 }
105
106 fn at_most(&self, key: impl Into<String>, value: f64) -> This::Predicate {
108 self.number_format(key, FormatComparison::AtMost, value)
109 }
110
111 fn at_least(&self, key: impl Into<String>, value: f64) -> This::Predicate {
113 self.number_format(key, FormatComparison::AtLeast, value)
114 }
115
116 fn exactly(&self, key: impl Into<String>, value: f64) -> This::Predicate {
118 self.number_format(key, FormatComparison::Exactly, value)
119 }
120
121 fn less_than(&self, key: impl Into<String>, value: f64) -> This::Predicate {
123 self.not_format(self.at_least(key, value))
124 }
125
126 fn greater_than(&self, key: impl Into<String>, value: f64) -> This::Predicate {
128 self.not_format(self.at_most(key, value))
129 }
130
131 fn progressive_format(&self) -> This::Predicate {
133 self.all_formats(self.has_audio(), self.has_video())
134 }
135
136 fn audio_only_format(&self) -> This::Predicate {
138 self.all_formats(self.has_audio(), self.not_format(self.has_video()))
139 }
140
141 fn video_only_format(&self) -> This::Predicate {
143 self.all_formats(self.has_video(), self.not_format(self.has_audio()))
144 }
145}
146
147#[ext(name = FormatSelectionProgramExt)]
149pub impl<This, Predicate> This
150where
151 This: FormatPredicateAlg<Predicate = Predicate> + FormatSelectionAlg<Predicate = Predicate>,
152{
153 fn best_progressive_format(&self) -> This::Selection {
155 self.best_format(self.progressive_format())
156 }
157
158 fn preferred_progressive_format(&self, id: impl Into<String>) -> This::Selection {
160 self.fallback_formats([
161 self.best_format(self.format_id(id)),
162 self.best_progressive_format(),
163 ])
164 }
165}