Skip to main content

rsynko_ui/
format.rs

1use crate::*;
2use alux_ext::ext;
3use rsynko_manager::MediaStreams;
4
5/// Specifies what one selectable format states where a chooser can read it.
6///
7/// A chooser reads a format to tell it from its alternatives. These are the observations that
8/// distinguish them; everything else an extractor stated stays in the extraction.
9pub trait FormatDescriptionAlg {
10    /// Observes the identity the extractor gave the format.
11    fn format_identity(&self) -> &str;
12    /// Observes the container or file-name extension.
13    fn format_extension(&self) -> Option<&str>;
14    /// Observes the stream roles the format carries, when it carries any.
15    fn format_streams(&self) -> Option<MediaStreams>;
16    /// Observes the quality the extractor named.
17    fn format_quality(&self) -> Option<&str>;
18    /// Observes the picture height in pixels.
19    fn format_height(&self) -> Option<u64>;
20    /// Observes the picture width in pixels.
21    fn format_width(&self) -> Option<u64>;
22    /// Observes the stated bits per second.
23    fn format_bitrate(&self) -> Option<u64>;
24    /// Observes the stated byte count of the whole format.
25    fn format_size(&self) -> Option<u64>;
26    /// Observes the codecs the format is encoded with.
27    fn format_codecs(&self) -> Option<&str>;
28}
29
30/// Denotes what discovery has stated about one request's selectable formats.
31#[derive(Clone, Copy, Debug, PartialEq, Eq)]
32pub enum DiscoveryState<'a> {
33    /// Denotes a source whose formats no one has asked for.
34    Unrequested,
35    /// Denotes a request waiting for an interpreter to inspect the source.
36    Waiting,
37    /// Denotes source inspection currently in progress.
38    Inspecting,
39    /// Denotes formats described in extractor preference order.
40    Described,
41    /// Denotes inspection that failed, and why.
42    Failed(&'a str),
43}
44
45/// Specifies what one queue entry states about the formats a chooser may offer.
46pub trait FormatChoiceViewAlg {
47    /// Represents one described selectable format.
48    type Format;
49
50    /// Observes what discovery has stated about this request's formats.
51    fn discovery(&self) -> DiscoveryState<'_>;
52    /// Observes the described formats in extractor preference order.
53    fn described_formats(&self) -> impl Iterator<Item = &Self::Format>;
54}
55
56/// Derives what a chooser reads about one format.
57#[ext(name = FormatLabelExt)]
58pub impl<This> This
59where
60    This: FormatDescriptionAlg,
61{
62    /// States the format in aligned columns, so alternatives are compared by reading down.
63    fn format_label(&self) -> String {
64        let described = self.format_observations();
65        let columns = format!(
66            "{:<4} {:<5} {:<13}",
67            self.format_identity(),
68            self.format_extension().unwrap_or(UNSTATED),
69            self.format_streams().map_or("no media streams", MediaStreams::streams_label)
70        );
71        if described.is_empty() { columns } else { format!("{columns} {described}") }
72    }
73
74    /// States what distinguishes the format from an alternative carrying the same streams.
75    fn format_observations(&self) -> String {
76        let quality = self.format_stated_quality();
77        [
78            quality.as_deref().map(str::to_owned),
79            self.format_bitrate().map(|bitrate| format!("{}/s", bytes_label(bitrate / 8))),
80            self.format_size().map(bytes_label),
81            self.format_codecs().map(str::to_owned),
82        ]
83        .into_iter()
84        .flatten()
85        .collect::<Vec<_>>()
86        .join("  ")
87    }
88
89    /// States the quality the extractor named, or the picture size standing in for it.
90    fn format_stated_quality(&self) -> Option<String> {
91        if let Some(quality) = self.format_quality() {
92            return Some(quality.to_owned());
93        }
94        match (self.format_width(), self.format_height()) {
95            (Some(width), Some(height)) => Some(format!("{width}x{height}")),
96            (None, Some(height)) => Some(format!("{height}p")),
97            (Some(_) | None, None) => None,
98        }
99    }
100}
101
102/// Derives which stream roles a request may actually be asked for.
103#[ext(name = FormatRolesExt)]
104pub impl<This> This
105where
106    This: FormatChoiceViewAlg,
107    This::Format: FormatDescriptionAlg,
108{
109    /// States the roles some described format carries, and every role while none are described.
110    ///
111    /// A chooser offers what can be chosen. Asking for sound alone from a source that only ever
112    /// states one file carrying both would select nothing at all, so a role no format carries is
113    /// not offered at all. Before anything is described there is nothing to rule out, and every
114    /// role stands.
115    fn offered_streams(&self) -> Vec<MediaStreams> {
116        let described = self.described_formats().collect::<Vec<_>>();
117        // Nothing described is not the same as nothing carrying a role: before a source has said
118        // what it holds there is nothing to rule out, and a source that has said, and named only
119        // pictures, offers no role at all.
120        if described.is_empty() {
121            return MediaStreams::OFFERED.to_vec();
122        }
123        let carried = described.into_iter().filter_map(FormatDescriptionAlg::format_streams).collect::<Vec<_>>();
124        MediaStreams::OFFERED.into_iter().filter(|role| carried.contains(role)).collect()
125    }
126}