Skip to main content

crispy_stream_checker/
types.rs

1//! Domain types for stream check configuration and results.
2
3use serde::{Deserialize, Serialize};
4
5use crate::backoff::BackoffStrategy;
6
7/// Stream categorization based on HTTP status and data validation.
8///
9/// Translated from IPTVChecker-Python status strings: 'Alive', 'Dead',
10/// 'Geoblocked', 'Retry'.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(rename_all = "lowercase")]
13pub enum StreamCategory {
14    /// Stream responded with 2xx and met data threshold.
15    Alive,
16    /// Stream is unreachable or returned a fatal HTTP status.
17    Dead,
18    /// Stream returned a geoblock-indicating HTTP status (403, 426, 451, 401, 423).
19    Geoblocked,
20    /// Stream returned a retryable HTTP status (408, 425, 429, 500, 502-504).
21    Retry,
22}
23
24/// Configuration for stream checking behavior.
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct CheckOptions {
27    /// Per-stream timeout in milliseconds.
28    pub timeout_ms: u64,
29    /// Maximum number of concurrent checks.
30    pub max_concurrent: usize,
31    /// Whether to follow HTTP redirects.
32    pub follow_redirects: bool,
33    /// Custom User-Agent header value.
34    pub user_agent: Option<String>,
35    /// Whether to accept invalid/self-signed TLS certificates.
36    pub accept_invalid_certs: bool,
37    /// Backoff strategy for retries.
38    pub backoff: BackoffStrategy,
39    /// Maximum number of retry attempts (default: 6).
40    pub retries: u32,
41    /// Proxy list for geoblock confirmation testing.
42    pub proxy_list: Option<Vec<String>>,
43    /// Whether to test geoblocked streams via proxies.
44    pub test_geoblock: bool,
45    /// Minimum bytes for direct streams at depth 0 (default: 500 KB).
46    pub min_bytes_direct: u64,
47    /// Minimum bytes for nested/segment streams (default: 128 KB).
48    pub min_bytes_nested: u64,
49    /// Skip ffprobe/ffmpeg media analysis on alive streams.
50    pub skip_media_probe: bool,
51    /// Skip screenshot capture.
52    pub skip_screenshots: bool,
53    /// Directory for screenshot output.
54    pub screenshot_dir: Option<String>,
55}
56
57impl Default for CheckOptions {
58    fn default() -> Self {
59        Self {
60            timeout_ms: 10_000,
61            max_concurrent: 10,
62            follow_redirects: true,
63            user_agent: Some("VLC/3.0.14 LibVLC/3.0.14".to_string()),
64            accept_invalid_certs: false,
65            backoff: BackoffStrategy::default(),
66            retries: 6,
67            proxy_list: None,
68            test_geoblock: false,
69            min_bytes_direct: 512_000, // 500 KB
70            min_bytes_nested: 131_072, // 128 KB
71            skip_media_probe: false,
72            skip_screenshots: false,
73            screenshot_dir: None,
74        }
75    }
76}
77
78/// Information about a single stream's availability.
79#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct StreamInfo {
81    /// Whether the stream responded successfully (2xx status).
82    pub available: bool,
83    /// HTTP status code returned, if any.
84    pub status_code: Option<u16>,
85    /// Time from request start to first byte, in milliseconds.
86    pub response_time_ms: u64,
87    /// Content-Type header value, if present.
88    pub content_type: Option<String>,
89    /// Content-Length header value, if present.
90    pub content_length: Option<u64>,
91    /// Error description, if the check failed.
92    pub error: Option<String>,
93}
94
95/// Result of checking a single stream URL.
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct CheckResult {
98    /// The URL that was checked.
99    pub url: String,
100    /// Stream availability information.
101    pub info: StreamInfo,
102    /// Timestamp when the check was performed.
103    pub checked_at: chrono::DateTime<chrono::Utc>,
104    /// Media probe information (codec, resolution, FPS) if available.
105    pub media_info: Option<crispy_media_probe::MediaInfo>,
106    /// Categorization of the stream status.
107    pub category: StreamCategory,
108    /// Human-readable error reason (from `summarize_error`).
109    pub error_reason: Option<String>,
110    /// Label mismatch warnings (e.g., "Expected 4K, got 1080p").
111    pub mismatch_warnings: Vec<String>,
112}
113
114/// Aggregated report from a bulk stream check.
115#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct BulkCheckReport {
117    /// Total number of URLs checked.
118    pub total: usize,
119    /// Number of available streams (2xx responses).
120    pub available: usize,
121    /// Number of unavailable streams (non-2xx responses).
122    pub unavailable: usize,
123    /// Number of streams that produced errors (connection/timeout failures).
124    pub errors: usize,
125    /// Number of geoblocked streams.
126    pub geoblocked: usize,
127    /// Individual results for each URL.
128    pub results: Vec<CheckResult>,
129    /// Total wall-clock time for the bulk check, in milliseconds.
130    pub duration_ms: u64,
131    /// Results categorized as Alive.
132    pub alive_results: Vec<CheckResult>,
133    /// Results categorized as Dead.
134    pub dead_results: Vec<CheckResult>,
135    /// Results categorized as Geoblocked.
136    pub geoblocked_results: Vec<CheckResult>,
137}