#!/bin/bash # 快速启动脚本 - 创建 video_probe Rust 项目 set -e # 设置 PATH(macOS 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 "] 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, pub format: FormatInfo, pub video_stream: Option, pub audio_streams: Vec, pub subtitle_streams: Vec, pub other_streams: Vec, } #[derive(Debug, Serialize, Deserialize, Default)] pub struct FormatInfo { pub filename: Option, pub format_name: Option, pub format_long_name: Option, pub duration: f64, pub size: u64, pub bit_rate: u64, pub probe_score: Option, #[serde(skip_serializing_if = "Option::is_none")] pub tags: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct VideoStream { pub index: i32, pub codec_name: Option, pub codec_long_name: Option, pub profile: Option, pub width: i32, pub height: i32, pub pix_fmt: Option, pub r_frame_rate: Option, pub avg_frame_rate: Option, pub bit_rate: Option, pub duration: Option, #[serde(skip_serializing_if = "Option::is_none")] pub tags: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct AudioStream { pub index: i32, pub codec_name: Option, pub channels: i32, pub sample_rate: Option, pub bit_rate: Option, pub duration: Option, #[serde(skip_serializing_if = "Option::is_none")] pub tags: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct SubtitleStream { pub index: i32, pub codec_name: Option, pub language: Option, #[serde(skip_serializing_if = "Option::is_none")] pub tags: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct OtherStream { pub index: i32, pub codec_type: String, pub codec_name: Option, #[serde(skip_serializing_if = "Option::is_none")] pub tags: Option, } 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 = std::result::Result; 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 -- " echo " 4. cargo test" echo "" echo "创建 Gitea 仓库:" echo " 1. 在 Gitea 创建新仓库: video_probe" echo " 2. git remote add origin " echo " 3. git push -u origin main" echo ""