Skip to main content

rskit_media/chunking/
types.rs

1//! Core types for chunked media processing.
2
3use std::collections::HashMap;
4use std::path::PathBuf;
5use std::time::Duration;
6
7use serde::{Deserialize, Serialize};
8
9use crate::time::{TimeRange, Timestamp};
10
11/// Unique identifier for a chunk within a chunked operation.
12#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub struct ChunkId(pub String);
14
15impl ChunkId {
16    /// Create a chunk ID from an index.
17    pub fn from_index(index: usize) -> Self {
18        Self(format!("chunk-{index:04}"))
19    }
20}
21
22impl std::fmt::Display for ChunkId {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        f.write_str(&self.0)
25    }
26}
27
28/// A boundary point for splitting media.
29///
30/// Represents a precise point in the media where a split can occur.
31/// Keyframe boundaries are preferred for lossless splits.
32#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
33pub struct ChunkBoundary {
34    /// The timestamp of the boundary.
35    pub timestamp: Timestamp,
36    /// Whether this boundary falls on a keyframe.
37    pub is_keyframe: bool,
38    /// Confidence that this is a good split point (0.0–1.0).
39    /// Higher values indicate cleaner boundaries (e.g., silence, scene change).
40    pub quality: f32,
41}
42
43/// A planned chunk — describes a segment to process independently.
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct ChunkPlan {
46    /// Unique identifier for this chunk.
47    pub id: ChunkId,
48    /// Zero-based index in the chunk sequence.
49    pub index: usize,
50    /// Time range of this chunk within the source.
51    pub range: TimeRange,
52    /// Whether the start boundary is on a keyframe.
53    pub start_is_keyframe: bool,
54    /// Suggested timeout for this chunk.
55    pub suggested_timeout: Duration,
56}
57
58impl ChunkPlan {
59    /// Duration of this chunk.
60    pub fn duration(&self) -> Duration {
61        self.range.duration()
62    }
63}
64
65/// Result of processing a single chunk.
66#[derive(Debug, Clone)]
67pub struct ChunkResult {
68    /// Which chunk this result belongs to.
69    pub id: ChunkId,
70    /// Index in the sequence.
71    pub index: usize,
72    /// The original time range of this chunk.
73    pub range: TimeRange,
74    /// Status of the chunk.
75    pub status: ChunkStatus,
76    /// Output path for the processed chunk (if successful).
77    pub output_path: Option<PathBuf>,
78    /// Processing duration (wall clock).
79    pub processing_duration: Duration,
80    /// Arbitrary metadata produced during processing.
81    pub metadata: HashMap<String, serde_json::Value>,
82}
83
84/// Status of a chunk after processing.
85#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
86pub enum ChunkStatus {
87    /// Chunk processed successfully.
88    Completed,
89    /// Chunk processing failed.
90    Failed {
91        /// Error message.
92        message: String,
93        /// Whether this failure is retryable.
94        retryable: bool,
95    },
96    /// Chunk was cancelled.
97    Cancelled,
98}
99
100impl ChunkStatus {
101    /// Whether the chunk completed successfully.
102    pub fn is_success(&self) -> bool {
103        matches!(self, Self::Completed)
104    }
105}
106
107/// Progress report for a single chunk.
108#[derive(Debug, Clone)]
109pub struct ChunkProgress {
110    /// Which chunk this progress belongs to.
111    pub chunk_id: ChunkId,
112    /// Index in the sequence.
113    pub chunk_index: usize,
114    /// Total number of chunks.
115    pub total_chunks: usize,
116    /// Progress within this chunk (0.0 – 1.0).
117    pub chunk_percent: f32,
118    /// Overall progress across all chunks (0.0 – 1.0).
119    pub overall_percent: f32,
120    /// Estimated time remaining for the entire operation.
121    pub eta: Option<Duration>,
122}
123
124/// A complete chunked operation plan — all chunks and reassembly info.
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct ChunkedOperation {
127    /// Ordered list of chunk plans.
128    pub chunks: Vec<ChunkPlan>,
129    /// How to reassemble the chunks after processing.
130    pub reassembly: ReassemblyPlan,
131    /// Total duration of the source media.
132    pub total_duration: Duration,
133    /// The strategy that was used to create this plan.
134    pub strategy_name: String,
135}
136
137impl ChunkedOperation {
138    /// Number of chunks in this operation.
139    pub fn chunk_count(&self) -> usize {
140        self.chunks.len()
141    }
142
143    /// Whether this is a trivial single-chunk operation (no actual splitting needed).
144    pub fn is_single_chunk(&self) -> bool {
145        self.chunks.len() <= 1
146    }
147}
148
149/// Describes how to reassemble processed chunks into the final output.
150#[derive(Debug, Clone, Serialize, Deserialize)]
151#[non_exhaustive]
152pub enum ReassemblyPlan {
153    /// Concatenate chunks in order using FFmpeg concat demuxer.
154    /// This is the standard reassembly for video/audio segments.
155    Concat,
156    /// Merge text-based results (e.g., transcript segments).
157    /// Each chunk produces text output with time offsets that need adjustment.
158    MergeText {
159        /// Separator between merged chunks.
160        separator: String,
161    },
162    /// Collect results without merging (e.g., thumbnail extraction per chunk).
163    /// Each chunk produces independent output files.
164    Collect,
165    /// No reassembly needed — each chunk result is independent.
166    None,
167}