This document provides technical details about Auto Clipper's internal APIs and data structures.
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β CLI Interface βββββΆβ Core Engine βββββΆβ Output Files β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β Input Parser β β AI Analyzer β β Video Clipper β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β yt-dlp β β Gemini API β β FFmpeg β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
#[derive(Deserialize, Serialize, Debug)]
struct Clip {
/// Start timestamp in HH:MM:SS format
start: String,
/// End timestamp in HH:MM:SS format
end: String,
/// Output filename
output: String,
}#[derive(Deserialize, Debug)]
struct ClipConfig {
/// Path to input video file
input_video: String,
/// List of clips to generate
clips: Vec<Clip>,
}#[derive(Deserialize)]
struct GeminiResponse {
candidates: Vec<GeminiCandidate>,
}
#[derive(Deserialize)]
struct GeminiCandidate {
content: GeminiContentResponse,
}
#[derive(Deserialize)]
struct GeminiContentResponse {
parts: Vec<GeminiPartResponse>,
}
#[derive(Deserialize)]
struct GeminiPartResponse {
text: String,
}#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>>Description: Main application entry point that handles command-line arguments and orchestrates the clipping process.
Parameters: None (uses std::env::args())
Returns: Result<(), Box<dyn std::error::Error>>
Usage:
./auto-clipper "https://youtube.com/watch?v=VIDEO_ID"
./auto-clipper config.jsonasync fn get_video_info(url: &str) -> Result<String, Box<dyn std::error::Error>>Description: Extracts video title and metadata from YouTube URL using yt-dlp.
Parameters:
url: &str- YouTube video URL
Returns: Result<String, Box<dyn std::error::Error>> - Video title and duration
Example:
let info = get_video_info("https://www.youtube.com/watch?v=dQw4w9WgXcQ").await?;
// Returns: "Rick Astley - Never Gonna Give You Up (Official Video)\n213"async fn get_video_duration(file: &str) -> Result<f64, Box<dyn std::error::Error>>Description: Gets video duration in seconds using ffprobe.
Parameters:
file: &str- Path to video file
Returns: Result<f64, Box<dyn std::error::Error>> - Duration in seconds
Example:
let duration = get_video_duration("video.mp4").await?;
// Returns: 213.043084async fn download_video(url: &str) -> Result<String, Box<dyn std::error::Error>>Description: Downloads video from YouTube URL using yt-dlp.
Parameters:
url: &str- YouTube video URL
Returns: Result<String, Box<dyn std::error::Error>> - Path to downloaded file
Configuration:
- Format:
best[height<=720] - Output:
video.%(ext)s
Example:
let file_path = download_video("https://www.youtube.com/watch?v=VIDEO_ID").await?;
// Returns: "video.mp4"async fn analyze_with_gemini(
video_info: &str,
duration: f64
) -> Result<String, Box<dyn std::error::Error>>Description: Analyzes video content using Gemini AI to suggest viral clips.
Parameters:
video_info: &str- Video title and metadataduration: f64- Video duration in seconds
Returns: Result<String, Box<dyn std::error::Error>> - JSON response with clip suggestions
API Endpoint: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent
Request Format:
{
"contents": [{
"parts": [{
"text": "Analyze this YouTube video and suggest 3-5 viral-worthy clips..."
}]
}]
}Response Format:
{
"candidates": [{
"content": {
"parts": [{
"text": "```json\n{\"input_video\": \"video.mp4\", \"clips\": [...]}\n```"
}]
}
}]
}ffmpeg -i {input_video} -ss {start} -to {end} -c copy -avoid_negative_ts make_zero {output} -yParameters:
-i {input_video}: Input video file-ss {start}: Start timestamp-to {end}: End timestamp-c copy: Stream copy (no re-encoding)-avoid_negative_ts make_zero: Handle timestamp issues-y: Overwrite output files
Performance: Stream copy mode provides fastest processing by avoiding re-encoding.
let output = Command::new("curl")
.args([
"-X", "POST",
"-H", "Content-Type: application/json",
"-d", &format!("@{}", temp_file),
&format!("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={}", api_key)
])
.output()?;- Network timeouts
- Invalid API keys
- Rate limiting
- Malformed responses
fn is_youtube_url(input: &str) -> bool {
input.starts_with("http") &&
(input.contains("youtube.com") || input.contains("youtu.be"))
}fn validate_timestamp(timestamp: &str) -> Result<(), ValidationError> {
// Validates HH:MM:SS or HH:MM:SS.mmm format
}fn validate_file_path(path: &str) -> Result<(), ValidationError> {
// Checks file existence and permissions
}{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"input_video": {
"type": "string",
"description": "Path to input video file"
},
"clips": {
"type": "array",
"items": {
"type": "object",
"properties": {
"start": {
"type": "string",
"pattern": "^\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?$"
},
"end": {
"type": "string",
"pattern": "^\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?$"
},
"output": {
"type": "string"
}
},
"required": ["start", "end", "output"]
}
}
},
"required": ["input_video", "clips"]
}#[derive(Debug)]
enum AutoClipperError {
VideoDownloadError(String),
AIAnalysisError(String),
FFmpegError(String),
ConfigurationError(String),
NetworkError(String),
}
impl std::error::Error for AutoClipperError {}// Network errors
if !output.status.success() {
return Err(format!("API error: {}", String::from_utf8_lossy(&output.stderr)).into());
}
// File system errors
let config_content = fs::read_to_string(config_path).await
.map_err(|e| format!("Failed to read config: {}", e))?;
// Parsing errors
let config: ClipConfig = serde_json::from_str(&config_content)
.map_err(|e| format!("Invalid JSON config: {}", e))?;GEMINI_API_KEY=your_api_key_here# Custom API endpoint
GEMINI_API_ENDPOINT=https://custom-endpoint.com
# Request timeout (seconds)
API_TIMEOUT=30
# Maximum video duration (seconds)
MAX_VIDEO_DURATION=3600
# Logging level
RUST_LOG=info| Operation | Typical Time | Factors |
|---|---|---|
| Video Download | 10-30s | Video size, network speed |
| AI Analysis | 3-5s | API response time |
| Clip Generation | 1-3s per clip | Stream copy mode |
| JSON Parsing | <1ms | File size |
- Base application: ~10MB
- Video processing: Depends on video size
- Temporary files: Video size + clips
// Use stream copy for faster processing
.args(["-c", "copy"])
// Process clips sequentially to manage memory
for clip in clips.iter() {
process_clip(clip).await?;
}
// Clean up temporary files
fs::remove_file(temp_file).await.ok();#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_timestamp_validation() {
assert!(validate_timestamp("00:01:30").is_ok());
assert!(validate_timestamp("invalid").is_err());
}
#[tokio::test]
async fn test_video_duration() {
let duration = get_video_duration("test_video.mp4").await;
assert!(duration.is_ok());
}
}#[tokio::test]
async fn test_full_workflow() {
let config = ClipConfig {
input_video: "test_video.mp4".to_string(),
clips: vec![
Clip {
start: "00:00:10".to_string(),
end: "00:00:20".to_string(),
output: "test_clip.mp4".to_string(),
}
],
};
// Test clip generation
let result = process_clips(&config).await;
assert!(result.is_ok());
}[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.0", features = ["full"] }
dotenv = "0.15"- FFmpeg: Video processing engine
- yt-dlp: YouTube download utility
- curl: HTTP client for API calls
- Gemini AI API: Content analysis
- YouTube API: Video metadata (via yt-dlp)
Next Steps: Check the Development Guide for contributing to the codebase.