Skip to main content

ytdown/
error.rs

1//! Error types for the crate.
2
3/// All errors produced by this crate.
4#[derive(Debug, thiserror::Error)]
5#[non_exhaustive]
6pub enum Error {
7    /// No registered extractor matches the URL.
8    #[error("no extractor supports this URL: {0}")]
9    UnsupportedUrl(String),
10    /// Underlying HTTP failure.
11    #[error("network error during {stage}: {source}")]
12    Network {
13        /// The operation that was in progress when the failure occurred.
14        stage: &'static str,
15        /// The underlying reqwest error.
16        #[source]
17        source: reqwest::Error,
18    },
19    /// The site responded but the expected data could not be extracted.
20    #[error("extraction failed at {stage}: {message}")]
21    Extraction {
22        /// The extraction phase that failed.
23        stage: &'static str,
24        /// A human-readable description of what went wrong.
25        message: String,
26    },
27    /// The media exists but cannot be accessed.
28    #[error("media unavailable: {reason}")]
29    Unavailable {
30        /// The classified reason the media is unavailable.
31        reason: UnavailableReason,
32        /// The raw message reported by the site, if any.
33        message: String,
34    },
35    /// JS cipher solving failed.
36    #[error("cipher error: {0}")]
37    Cipher(String),
38    /// Requested format does not exist / no format matched the selector.
39    #[error("no matching format: {0}")]
40    FormatNotFound(String),
41    /// Filesystem error during download.
42    #[error("io error: {0}")]
43    Io(#[from] std::io::Error),
44    /// Postprocessing (ffmpeg) failure.
45    #[error("postprocess error: {0}")]
46    Postprocess(String),
47}
48
49/// Why media is unavailable.
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51#[non_exhaustive]
52pub enum UnavailableReason {
53    /// Removed, private, or never existed.
54    Gone,
55    /// Age-restricted and no authenticated client succeeded.
56    AgeRestricted,
57    /// Blocked in the request region.
58    GeoBlocked,
59    /// Live content (manifests not supported in v1).
60    Live,
61    /// Any other documented status from the site.
62    Other,
63}
64
65impl std::fmt::Display for UnavailableReason {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        let s = match self {
68            UnavailableReason::Gone => "gone",
69            UnavailableReason::AgeRestricted => "age-restricted",
70            UnavailableReason::GeoBlocked => "geo-blocked",
71            UnavailableReason::Live => "live",
72            UnavailableReason::Other => "other",
73        };
74        f.write_str(s)
75    }
76}
77
78/// Crate-wide result alias.
79pub type Result<T> = std::result::Result<T, Error>;
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn error_display_is_useful() {
87        let e = Error::UnsupportedUrl("https://example.com/x".into());
88        assert!(e.to_string().contains("example.com"));
89    }
90}