Skip to main content

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}