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}