Skip to content

Latest commit

Β 

History

History
566 lines (469 loc) Β· 12.4 KB

File metadata and controls

566 lines (469 loc) Β· 12.4 KB

Development Guide πŸ› οΈ

This guide is for developers who want to contribute to Auto Clipper or understand its internals.

πŸ—οΈ Project Structure

clipper-rust/
β”œβ”€β”€ src/
β”‚   └── main.rs              # Main application logic
β”œβ”€β”€ docs/                    # Documentation
β”‚   β”œβ”€β”€ README.md           # Documentation index
β”‚   β”œβ”€β”€ installation.md     # Installation guide
β”‚   β”œβ”€β”€ usage.md           # Usage examples
β”‚   β”œβ”€β”€ configuration.md   # Configuration options
β”‚   β”œβ”€β”€ api.md            # API reference
β”‚   └── development.md    # This file
β”œβ”€β”€ tests/                  # Test files (future)
β”œβ”€β”€ examples/              # Example configurations
β”œβ”€β”€ .env.example          # Environment template
β”œβ”€β”€ .gitignore           # Git ignore rules
β”œβ”€β”€ Cargo.toml          # Rust dependencies
β”œβ”€β”€ Cargo.lock         # Dependency lock file
β”œβ”€β”€ LICENSE           # MIT license
β”œβ”€β”€ README.md        # Project overview
└── CONTRIBUTING.md # Contributing guidelines

πŸš€ Development Setup

Prerequisites

  • Rust 1.75.0 or higher
  • Git
  • FFmpeg, yt-dlp, curl (for testing)

Initial Setup

# Clone the repository
git clone https://github.com/Hans02-Neo/clipper-rust.git
cd clipper-rust

# Set up environment
cp .env.example .env
# Add your Gemini API key to .env

# Install development dependencies
cargo install cargo-watch cargo-audit cargo-outdated

# Build and test
cargo build
cargo test

Development Workflow

# Watch for changes during development
cargo watch -x check -x test -x run

# Format code
cargo fmt

# Lint code
cargo clippy

# Check for security vulnerabilities
cargo audit

# Check for outdated dependencies
cargo outdated

πŸ›οΈ Architecture

Core Components

1. Input Processing

// URL detection and validation
if input.starts_with("http") {
    // YouTube URL mode
    process_youtube_url(input).await?
} else {
    // Config file mode
    process_config_file(input).await?
}

2. Video Download

async fn download_video(url: &str) -> Result<String, Box<dyn std::error::Error>> {
    let output = Command::new("yt-dlp")
        .args(["-f", "best[height<=720]", "-o", "video.%(ext)s", url])
        .output()?;
    // Error handling and validation
}

3. AI Analysis

async fn analyze_with_gemini(
    video_info: &str, 
    duration: f64
) -> Result<String, Box<dyn std::error::Error>> {
    // Construct prompt
    // Make API request
    // Parse response
}

4. Video Processing

// FFmpeg integration for clip generation
let output = Command::new("ffmpeg")
    .args(["-i", input, "-ss", start, "-to", end, "-c", "copy", output_file])
    .output()?;

Data Flow

graph TD
    A[CLI Input] --> B{Input Type?}
    B -->|URL| C[Download Video]
    B -->|Config| D[Load Config]
    C --> E[Get Video Info]
    E --> F[AI Analysis]
    F --> G[Generate JSON Config]
    D --> H[Parse Config]
    G --> H
    H --> I[Process Clips]
    I --> J[FFmpeg Processing]
    J --> K[Output Files]
Loading

πŸ§ͺ Testing Strategy

Unit Tests

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_timestamp_parsing() {
        // Test various timestamp formats
        assert!(is_valid_timestamp("00:01:30"));
        assert!(is_valid_timestamp("01:23:45.500"));
        assert!(!is_valid_timestamp("invalid"));
    }

    #[test]
    fn test_config_validation() {
        let config = ClipConfig {
            input_video: "test.mp4".to_string(),
            clips: vec![
                Clip {
                    start: "00:00:10".to_string(),
                    end: "00:00:20".to_string(),
                    output: "clip.mp4".to_string(),
                }
            ],
        };
        assert!(validate_config(&config).is_ok());
    }
}

Integration Tests

#[tokio::test]
async fn test_full_workflow() {
    // Test complete workflow with sample data
    let result = process_youtube_url("https://www.youtube.com/watch?v=test").await;
    // Assertions
}

Test Data

# Create test fixtures
mkdir -p tests/fixtures
# Add sample videos, configs, expected outputs

πŸ”§ Code Style Guidelines

Rust Conventions

// Use descriptive names
async fn analyze_video_with_gemini_ai() -> Result<ClipSuggestions, AnalysisError>

// Document public functions
/// Analyzes video content using Gemini AI
/// 
/// # Arguments
/// * `video_info` - Video metadata and title
/// * `duration` - Video duration in seconds
/// 
/// # Returns
/// * `Result<String, Box<dyn std::error::Error>>` - JSON response
async fn analyze_with_gemini(video_info: &str, duration: f64) -> Result<String, Box<dyn std::error::Error>>

// Use proper error handling
let config = serde_json::from_str(&content)
    .map_err(|e| format!("Failed to parse config: {}", e))?;

Error Handling Patterns

// Custom error types
#[derive(Debug)]
enum AutoClipperError {
    VideoDownload(String),
    AIAnalysis(String),
    VideoProcessing(String),
    Configuration(String),
}

// Error conversion
impl From<serde_json::Error> for AutoClipperError {
    fn from(err: serde_json::Error) -> Self {
        AutoClipperError::Configuration(err.to_string())
    }
}

Async Patterns

// Use async/await consistently
async fn process_clips(config: &ClipConfig) -> Result<(), Box<dyn std::error::Error>> {
    for clip in &config.clips {
        process_single_clip(clip).await?;
    }
    Ok(())
}

πŸš€ Performance Optimization

Memory Management

// Use references where possible
fn process_clip(clip: &Clip) -> Result<(), ProcessingError>

// Clean up temporary files
async fn cleanup_temp_files() {
    let _ = fs::remove_file("temp_request.json").await;
    let _ = fs::remove_file("temp_video.mp4").await;
}

Concurrent Processing

// Future enhancement: parallel clip processing
use tokio::task::JoinSet;

async fn process_clips_parallel(clips: &[Clip]) -> Result<(), Box<dyn std::error::Error>> {
    let mut set = JoinSet::new();
    
    for clip in clips {
        let clip = clip.clone();
        set.spawn(async move {
            process_single_clip(&clip).await
        });
    }
    
    while let Some(result) = set.join_next().await {
        result??;
    }
    
    Ok(())
}

Caching Strategy

// Future enhancement: cache AI responses
use std::collections::HashMap;

struct AICache {
    cache: HashMap<String, String>,
}

impl AICache {
    fn get_or_analyze(&mut self, video_id: &str) -> Option<&String> {
        self.cache.get(video_id)
    }
}

πŸ” Debugging

Logging Setup

use log::{info, warn, error, debug};

// Add logging throughout the application
info!("Starting video analysis for: {}", video_url);
debug!("AI response: {}", response);
warn!("Clip duration exceeds recommended length: {}s", duration);
error!("Failed to process clip: {}", error);

Debug Configuration

# Enable debug logging
export RUST_LOG=debug

# Enable trace logging for specific modules
export RUST_LOG=auto_clipper=trace

# Log to file
export RUST_LOG=info
./auto-clipper "url" 2>&1 | tee auto_clipper.log

Debugging Tools

# Use cargo expand to see macro expansions
cargo install cargo-expand
cargo expand

# Use cargo flamegraph for performance profiling
cargo install flamegraph
cargo flamegraph --bin auto-clipper

# Memory profiling with valgrind
valgrind --tool=massif ./target/debug/auto-clipper config.json

πŸ“¦ Build and Release

Build Configurations

# Cargo.toml
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
panic = "abort"

[profile.dev]
opt-level = 0
debug = true

Cross-compilation

# Install targets
rustup target add x86_64-pc-windows-gnu
rustup target add x86_64-apple-darwin

# Build for different platforms
cargo build --release --target x86_64-pc-windows-gnu
cargo build --release --target x86_64-apple-darwin

Release Process

# 1. Update version in Cargo.toml
# 2. Update CHANGELOG.md
# 3. Run tests
cargo test --release

# 4. Build release
cargo build --release

# 5. Create git tag
git tag v0.1.0
git push origin v0.1.0

# 6. Create GitHub release with binaries

πŸ”§ Adding New Features

Feature Development Workflow

  1. Create feature branch
git checkout -b feature/new-feature-name
  1. Implement feature with tests
// Add new functionality
// Write comprehensive tests
// Update documentation
  1. Test thoroughly
cargo test
cargo clippy
cargo fmt --check
  1. Create pull request

Example: Adding New Video Platform Support

  1. Define trait for video platforms
trait VideoPlatform {
    async fn download(&self, url: &str) -> Result<String, PlatformError>;
    async fn get_info(&self, url: &str) -> Result<VideoInfo, PlatformError>;
}
  1. Implement for new platform
struct VimeoDownloader;

impl VideoPlatform for VimeoDownloader {
    async fn download(&self, url: &str) -> Result<String, PlatformError> {
        // Implementation
    }
}
  1. Update URL detection
fn detect_platform(url: &str) -> Box<dyn VideoPlatform> {
    if url.contains("youtube.com") {
        Box::new(YouTubeDownloader)
    } else if url.contains("vimeo.com") {
        Box::new(VimeoDownloader)
    } else {
        Box::new(GenericDownloader)
    }
}

🧩 Extension Points

Plugin Architecture (Future)

trait ClipAnalyzer {
    async fn analyze(&self, video: &VideoFile) -> Result<Vec<Clip>, AnalysisError>;
}

struct GeminiAnalyzer {
    api_key: String,
}

struct CustomAnalyzer {
    model_path: String,
}

Configuration Extensions

#[derive(Deserialize)]
struct ExtendedConfig {
    #[serde(flatten)]
    base: ClipConfig,
    
    // Extensions
    quality_settings: Option<QualitySettings>,
    platform_settings: Option<PlatformSettings>,
    ai_settings: Option<AISettings>,
}

πŸ“š Documentation

Code Documentation

/// Processes a single video clip using FFmpeg
/// 
/// This function uses stream copy mode for optimal performance,
/// avoiding re-encoding when possible.
/// 
/// # Arguments
/// * `input_file` - Path to the source video file
/// * `clip` - Clip configuration with start/end times
/// 
/// # Returns
/// * `Ok(())` if clip was created successfully
/// * `Err(ProcessingError)` if FFmpeg failed
/// 
/// # Examples
/// ```
/// let clip = Clip {
///     start: "00:01:30".to_string(),
///     end: "00:02:00".to_string(),
///     output: "highlight.mp4".to_string(),
/// };
/// process_clip("input.mp4", &clip).await?;
/// ```
async fn process_clip(input_file: &str, clip: &Clip) -> Result<(), ProcessingError>

README Updates

  • Keep examples current
  • Update feature list
  • Maintain installation instructions
  • Update performance benchmarks

πŸ”’ Security Considerations

Input Validation

fn validate_youtube_url(url: &str) -> Result<(), ValidationError> {
    if !url.starts_with("https://") {
        return Err(ValidationError::InsecureUrl);
    }
    
    if !url.contains("youtube.com") && !url.contains("youtu.be") {
        return Err(ValidationError::InvalidDomain);
    }
    
    Ok(())
}

API Key Security

// Never log API keys
debug!("Making API request to: {}", endpoint); // βœ… Good
debug!("API key: {}", api_key); // ❌ Never do this

// Sanitize error messages
fn sanitize_error(error: &str, api_key: &str) -> String {
    error.replace(api_key, "[REDACTED]")
}

File System Security

fn validate_output_path(path: &str) -> Result<(), SecurityError> {
    // Prevent directory traversal
    if path.contains("..") {
        return Err(SecurityError::DirectoryTraversal);
    }
    
    // Ensure reasonable file extension
    if !path.ends_with(".mp4") {
        return Err(SecurityError::InvalidExtension);
    }
    
    Ok(())
}

πŸš€ Future Roadmap

Planned Features

  • Batch processing multiple videos
  • Custom AI model integration
  • GUI interface
  • Plugin system
  • Cloud deployment options
  • Real-time processing
  • Advanced video filters

Technical Improvements

  • Parallel clip processing
  • Caching layer for AI responses
  • Configuration validation
  • Better error messages
  • Progress bars
  • Resumable downloads

Ready to contribute? Check out our Contributing Guidelines and start building!