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}