1
0

Initial implementation of video_probe (Rust)

Core modules:
- probe.rs: ffprobe execution logic
- parser.rs: JSON parsing logic
- output.rs: Output formatting
- lib.rs: Library interface
- main.rs: CLI entry point

Features:
- Extract video metadata using ffprobe
- Parse video/audio/subtitle streams
- Save to JSON file
- Console summary output

Documentation:
- Added QUICKSTART.md
- Added ENVIRONMENT_SETUP_REPORT.md
This commit is contained in:
accusys
2026-03-07 10:10:19 +08:00
commit f3e2d2dca7
464 changed files with 125611 additions and 0 deletions

319
init_video_probe_rust.sh Executable file
View File

@@ -0,0 +1,319 @@
#!/bin/bash
# 快速启动脚本 - 创建 video_probe Rust 项目
set -e
# 设置 PATHmacOS with Homebrew
export PATH="/opt/homebrew/bin:$PATH"
echo "======================================"
echo "Video Probe (Rust) - 项目初始化"
echo "======================================"
echo ""
# 检查 Rust 是否已安装
if ! command -v rustc &> /dev/null; then
echo "❌ Rust 未安装"
echo ""
echo "请先安装 Rust:"
echo " curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
exit 1
fi
echo "✓ Rust 已安装: $(rustc --version)"
echo "✓ Cargo 已安装: $(cargo --version)"
echo ""
# 检查 ffprobe
if ! command -v ffprobe &> /dev/null; then
echo "⚠️ 警告: ffprobe 未找到"
echo " macOS: brew install ffmpeg"
echo " Linux: sudo apt-get install ffmpeg"
echo " Windows: 从 https://ffmpeg.org 下载"
echo ""
fi
# 创建项目
PROJECT_NAME="video_probe"
echo "1. 创建 Cargo 项目..."
if [ -d "$PROJECT_NAME" ]; then
echo " 目录已存在: $PROJECT_NAME"
read -p " 删除并重新创建? (y/N): " confirm
if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
rm -rf "$PROJECT_NAME"
else
echo " 取消操作"
exit 1
fi
fi
cargo new "$PROJECT_NAME"
cd "$PROJECT_NAME"
echo " ✓ 项目创建成功: $PROJECT_NAME/"
echo ""
# 创建目录结构
echo "2. 创建目录结构..."
mkdir -p src tests docs tests/fixtures
touch src/{lib.rs,probe.rs,parser.rs,metadata.rs,output.rs,error.rs}
touch tests/integration_test.rs
touch docs/{USAGE.md,DEVELOPMENT.md}
echo " ✓ 目录结构创建完成"
echo ""
# 更新 Cargo.toml
echo "3. 配置 Cargo.toml..."
cat > Cargo.toml << 'EOF'
[package]
name = "video_probe"
version = "0.1.0"
edition = "2021"
authors = ["Your Name <your.email@example.com>"]
description = "Extract video metadata using ffprobe"
license = "MIT"
repository = "https://gitea.example.com/yourname/video_probe"
keywords = ["video", "metadata", "ffprobe", "ffmpeg"]
categories = ["command-line-utilities", "multimedia"]
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = { version = "0.4", features = ["serde"] }
anyhow = "1.0"
thiserror = "1.0"
clap = { version = "4.0", features = ["derive"] }
[dev-dependencies]
tempfile = "3.8"
[[bin]]
name = "video_probe"
path = "src/main.rs"
EOF
echo " ✓ Cargo.toml 已配置"
echo ""
# 创建 .gitignore
echo "4. 创建 .gitignore..."
cat > .gitignore << 'EOF'
/target/
**/*.rs.bk
*.pdb
Cargo.lock
*.probe.json
*.mp4
*.avi
*.mov
*.mkv
.DS_Store
.vscode/
.idea/
EOF
echo " ✓ .gitignore 已创建"
echo ""
# 创建基础代码模板
echo "5. 创建基础代码模板..."
# src/metadata.rs
cat > src/metadata.rs << 'EOF'
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
#[derive(Debug, Serialize, Deserialize)]
pub struct VideoMetadata {
pub video_path: String,
pub probed_at: DateTime<Utc>,
pub format: FormatInfo,
pub video_stream: Option<VideoStream>,
pub audio_streams: Vec<AudioStream>,
pub subtitle_streams: Vec<SubtitleStream>,
pub other_streams: Vec<OtherStream>,
}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct FormatInfo {
pub filename: Option<String>,
pub format_name: Option<String>,
pub format_long_name: Option<String>,
pub duration: f64,
pub size: u64,
pub bit_rate: u64,
pub probe_score: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<serde_json::Value>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct VideoStream {
pub index: i32,
pub codec_name: Option<String>,
pub codec_long_name: Option<String>,
pub profile: Option<String>,
pub width: i32,
pub height: i32,
pub pix_fmt: Option<String>,
pub r_frame_rate: Option<String>,
pub avg_frame_rate: Option<String>,
pub bit_rate: Option<u64>,
pub duration: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<serde_json::Value>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AudioStream {
pub index: i32,
pub codec_name: Option<String>,
pub channels: i32,
pub sample_rate: Option<String>,
pub bit_rate: Option<u64>,
pub duration: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<serde_json::Value>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SubtitleStream {
pub index: i32,
pub codec_name: Option<String>,
pub language: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<serde_json::Value>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct OtherStream {
pub index: i32,
pub codec_type: String,
pub codec_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<serde_json::Value>,
}
EOF
# src/error.rs
cat > src/error.rs << 'EOF'
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ProbeError {
#[error("Video file not found: {0}")]
FileNotFound(String),
#[error("Failed to execute ffprobe: {0}")]
FfprobeExecution(#[from] std::io::Error),
#[error("Failed to parse ffprobe output: {0}")]
ParseError(#[from] serde_json::Error),
#[error("ffprobe returned non-zero exit code: {0}")]
FfprobeFailed(String),
#[error("No video stream found")]
NoVideoStream,
}
pub type Result<T> = std::result::Result<T, ProbeError>;
EOF
echo " ✓ 基础代码模板已创建"
echo ""
# 创建 README
echo "6. 创建 README.md..."
cat > README.md << 'EOF'
# video_probe (Rust)
Extract video metadata using ffprobe
## Installation
### From Source
```bash
git clone https://gitea.example.com/yourname/video_probe.git
cd video_probe
cargo install --path .
```
## Usage
```bash
video_probe video.mp4
```
Output: `video.probe.json`
## Features
- ✅ Fast and efficient (written in Rust)
- ✅ Cross-platform (Linux, macOS, Windows)
- ✅ Comprehensive metadata extraction
- ✅ JSON output format
- ✅ User-friendly console output
## Development
```bash
# Build
cargo build
# Run
cargo run -- video.mp4
# Test
cargo test
# Format code
cargo fmt
# Lint
cargo clippy
```
## Requirements
- Rust 1.70+
- ffprobe (from FFmpeg)
## License
MIT
EOF
echo " ✓ README.md 已创建"
echo ""
# 初始化 Git
echo "7. 初始化 Git 仓库..."
git init
git add .
git commit -m "Initial commit: video_probe Rust project"
echo " ✓ Git 仓库已初始化"
echo ""
# 构建项目
echo "8. 构建项目..."
cargo build
echo " ✓ 项目构建成功"
echo ""
# 完成
echo "======================================"
echo "✅ 项目初始化完成!"
echo "======================================"
echo ""
echo "项目位置: $(pwd)"
echo ""
echo "下一步:"
echo " 1. cd $PROJECT_NAME"
echo " 2. 实现核心功能(参考: VIDEO_PROBE_RUST_DEVELOPMENT.md"
echo " 3. cargo run -- <video_path>"
echo " 4. cargo test"
echo ""
echo "创建 Gitea 仓库:"
echo " 1. 在 Gitea 创建新仓库: video_probe"
echo " 2. git remote add origin <your-gitea-url>"
echo " 3. git push -u origin main"
echo ""