oximedia_proxy/lib.rs
1//! Proxy and offline editing workflow system for OxiMedia.
2//!
3//! This crate provides comprehensive proxy workflow management for professional video editing,
4//! enabling efficient offline-to-online workflows with full conforming support.
5//!
6//! # Features
7//!
8//! ## Proxy Generation
9//!
10//! - **Multiple Resolutions** - Quarter, half, and full resolution proxies
11//! - **Codec Selection** - H.264, VP9, and other efficient codecs
12//! - **Quality Presets** - Predefined quality levels for different workflows
13//! - **Batch Processing** - Generate proxies for multiple files simultaneously
14//! - **Automatic Creation** - Auto-generate proxies on media ingest
15//!
16//! ## Proxy Linking
17//!
18//! - **Original Association** - Link proxies to high-resolution originals
19//! - **Database Management** - Persistent link database with SQLite
20//! - **Link Verification** - Validate proxy-original relationships
21//! - **Metadata Tracking** - Store timecode, duration, and other metadata
22//!
23//! ## Conforming
24//!
25//! - **EDL Support** - Conform from CMX 3600 and other EDL formats
26//! - **XML Support** - Final Cut Pro XML and Premiere Pro XML
27//! - **Automatic Relink** - Relink edited proxies to original media
28//! - **Frame-Accurate** - Preserve exact frame accuracy during conform
29//!
30//! ## Workflows
31//!
32//! - **Offline Editing** - Edit with low-res proxies for performance
33//! - **Online Finishing** - Conform to high-res for final output
34//! - **Round-trip** - Complete offline-to-online-to-delivery pipeline
35//! - **Multi-resolution** - Switch between resolutions seamlessly
36//!
37//! ## Additional Features
38//!
39//! - **Timecode Preservation** - Maintain accurate timecode across workflow
40//! - **Metadata Sync** - Synchronize metadata between proxy and original
41//! - **Smart Caching** - Intelligent proxy cache management
42//! - **Validation** - Comprehensive validation and reporting
43//!
44//! # Quick Start
45//!
46//! ## Generate Proxies
47//!
48//! ```rust,no_run
49//! use oximedia_proxy::{ProxyGenerator, ProxyPreset};
50//!
51//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
52//! // Generate a quarter-resolution H.264 proxy
53//! let generator = ProxyGenerator::new();
54//! let proxy_path = generator
55//! .generate("original.mov", "proxy.mp4", ProxyPreset::QuarterResH264)
56//! .await?;
57//! # Ok(())
58//! # }
59//! ```
60//!
61//! ## Link Proxy to Original
62//!
63//! ```rust,no_run
64//! use oximedia_proxy::ProxyLinkManager;
65//!
66//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
67//! let mut manager = ProxyLinkManager::new("links.db").await?;
68//! manager.link_proxy("proxy.mp4", "original.mov").await?;
69//! # Ok(())
70//! # }
71//! ```
72//!
73//! ## Conform from EDL
74//!
75//! ```rust,no_run
76//! use oximedia_proxy::ConformEngine;
77//!
78//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
79//! let engine = ConformEngine::new("links.db").await?;
80//! let conformed = engine.conform_from_edl("edit.edl", "output.mov").await?;
81//! # Ok(())
82//! # }
83//! ```
84//!
85//! ## Complete Workflow
86//!
87//! ```rust,no_run
88//! use oximedia_proxy::{OfflineWorkflow, ProxyPreset};
89//!
90//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
91//! let mut workflow = OfflineWorkflow::new("project.db").await?;
92//!
93//! // Ingest and create proxies
94//! workflow.ingest("camera/clip001.mov", "proxy.mp4", ProxyPreset::QuarterResH264).await?;
95//!
96//! // After editing, conform to original
97//! workflow.conform("edit.edl", "final.mov").await?;
98//! # Ok(())
99//! # }
100//! ```
101
102#![forbid(unsafe_code)]
103#![warn(missing_docs)]
104#![allow(clippy::module_name_repetitions)]
105#![allow(clippy::missing_errors_doc)]
106#![allow(clippy::missing_panics_doc)]
107#![allow(clippy::too_many_arguments)]
108
109pub mod cache;
110pub mod conform;
111pub mod examples;
112pub mod format_compat;
113pub mod generate;
114pub mod generation;
115pub mod link;
116pub mod linking;
117pub mod media_link;
118pub mod metadata;
119pub mod offline_edit;
120pub mod offline_proxy;
121pub mod proxy_aging;
122pub mod proxy_bandwidth;
123pub mod proxy_cache;
124pub mod proxy_compare;
125pub mod proxy_fingerprint;
126pub mod proxy_format;
127pub mod proxy_index;
128pub mod proxy_manifest;
129pub mod proxy_pipeline;
130pub mod proxy_quality;
131pub mod proxy_registry_ext;
132pub mod proxy_scheduler;
133pub mod proxy_status;
134pub mod proxy_sync;
135pub mod registry;
136pub mod relink_proxy;
137pub mod render;
138pub mod resolution;
139pub mod sidecar;
140pub mod smart_proxy;
141pub mod spec;
142pub mod timecode;
143pub mod transcode_proxy;
144pub mod transcode_queue;
145pub mod utils;
146pub mod validation;
147pub mod workflow;
148
149use thiserror::Error;
150
151/// Errors that can occur during proxy operations.
152#[derive(Debug, Error)]
153pub enum ProxyError {
154 /// Invalid input file or configuration.
155 #[error("Invalid input: {0}")]
156 InvalidInput(String),
157
158 /// Invalid output configuration.
159 #[error("Invalid output: {0}")]
160 InvalidOutput(String),
161
162 /// Proxy generation error.
163 #[error("Generation error: {0}")]
164 GenerationError(String),
165
166 /// Proxy link error.
167 #[error("Link error: {0}")]
168 LinkError(String),
169
170 /// Conform error.
171 #[error("Conform error: {0}")]
172 ConformError(String),
173
174 /// Database error.
175 #[error("Database error: {0}")]
176 DatabaseError(String),
177
178 /// I/O error.
179 #[error("I/O error: {0}")]
180 IoError(#[from] std::io::Error),
181
182 /// Timecode error.
183 #[error("Timecode error: {0}")]
184 TimecodeError(String),
185
186 /// Metadata error.
187 #[error("Metadata error: {0}")]
188 MetadataError(String),
189
190 /// Cache error.
191 #[error("Cache error: {0}")]
192 CacheError(String),
193
194 /// Validation error.
195 #[error("Validation error: {0}")]
196 ValidationError(String),
197
198 /// Workflow error.
199 #[error("Workflow error: {0}")]
200 WorkflowError(String),
201
202 /// Unsupported operation or feature.
203 #[error("Unsupported: {0}")]
204 Unsupported(String),
205
206 /// File not found.
207 #[error("File not found: {0}")]
208 FileNotFound(String),
209
210 /// Link not found.
211 #[error("Link not found for: {0}")]
212 LinkNotFound(String),
213}
214
215/// Result type for proxy operations.
216pub type Result<T> = std::result::Result<T, ProxyError>;
217
218// Re-export main types
219pub use cache::{CacheCleanup, CacheManager, CacheStrategy, CleanupPolicy};
220pub use conform::{ConformEngine, ConformResult, EdlConformer, XmlConformer};
221pub use generate::{
222 BatchProxyGenerator, BatchResult, PresetInfo, ProxyEncodeResult, ProxyEncoder,
223 ProxyGenerationSettings, ProxyGenerator, ProxyOptimizer, ProxyPreset, ProxyPresets,
224};
225pub use link::{LinkDatabase, LinkStatistics, ProxyLink, ProxyLinkManager, ProxyVerifier};
226pub use metadata::{MetadataSync, MetadataTransfer};
227pub use registry::{ProxyEntry, ProxyRegistry, RegistryRecord};
228pub use render::{RenderConform, RenderReplace};
229pub use resolution::{ProxyResolution, ProxyVariant, ResolutionManager, ResolutionSwitcher};
230pub use sidecar::{
231 Checksum, ChecksumAlgorithm, ProcessingRecord, SideCar, SidecarData, SidecarTimecode,
232};
233pub use spec::{ProxyCodec, ProxyResolutionMode, ProxySpec};
234pub use timecode::{TimecodePreserver, TimecodeVerifier};
235pub use validation::{
236 DirectoryValidation, EdlValidationResult, PathValidator, ValidationChecker, ValidationReport,
237 WorkflowValidator,
238};
239pub use workflow::{
240 MediaInfo, OfflineWorkflow, OfflineWorkflowPlan, OnlineWorkflow, RoundtripWorkflow,
241 StorageEstimate, WorkflowPhase, WorkflowPlan, WorkflowPlanner,
242};
243
244/// Proxy quality preset for quick setup.
245#[derive(Debug, Clone, Copy, PartialEq, Eq)]
246pub enum Quality {
247 /// Low quality, smallest file size (good for remote editing).
248 Low,
249 /// Medium quality, balanced size and quality (recommended for most workflows).
250 Medium,
251 /// High quality, larger files (for critical color work).
252 High,
253}
254
255impl Quality {
256 /// Get the recommended bitrate for this quality level at 1080p.
257 #[must_use]
258 pub const fn bitrate_1080p(&self) -> u64 {
259 match self {
260 Self::Low => 2_000_000, // 2 Mbps
261 Self::Medium => 5_000_000, // 5 Mbps
262 Self::High => 10_000_000, // 10 Mbps
263 }
264 }
265
266 /// Get the recommended bitrate for this quality level at 4K.
267 #[must_use]
268 pub const fn bitrate_4k(&self) -> u64 {
269 match self {
270 Self::Low => 8_000_000, // 8 Mbps
271 Self::Medium => 20_000_000, // 20 Mbps
272 Self::High => 40_000_000, // 40 Mbps
273 }
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280
281 #[test]
282 fn test_quality_bitrates() {
283 assert_eq!(Quality::Low.bitrate_1080p(), 2_000_000);
284 assert_eq!(Quality::Medium.bitrate_1080p(), 5_000_000);
285 assert_eq!(Quality::High.bitrate_1080p(), 10_000_000);
286
287 assert_eq!(Quality::Low.bitrate_4k(), 8_000_000);
288 assert_eq!(Quality::Medium.bitrate_4k(), 20_000_000);
289 assert_eq!(Quality::High.bitrate_4k(), 40_000_000);
290 }
291
292 #[test]
293 fn test_error_display() {
294 let err = ProxyError::InvalidInput("test".to_string());
295 assert_eq!(err.to_string(), "Invalid input: test");
296
297 let err = ProxyError::FileNotFound("test.mov".to_string());
298 assert_eq!(err.to_string(), "File not found: test.mov");
299 }
300}