to/src/main.rs
2025-07-24 00:35:32 +08:00

158 lines
4.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::env;
use std::path::Path;
use std::process::{Command, Stdio};
use std::io::{BufRead, BufReader, Write};
use regex::Regex;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("使用方法: 将.ts文件拖拽到此程序上或者通过命令行传入文件路径");
println!("例如: {} input.ts", args[0]);
std::process::exit(1);
}
let input_path = &args[1];
// 检查输入文件是否存在
if !Path::new(input_path).exists() {
eprintln!("错误: 文件 '{}' 不存在", input_path);
std::process::exit(1);
}
// 检查是否为ts文件
if !input_path.to_lowercase().ends_with(".ts") {
eprintln!("错误: 只支持.ts格式的文件");
std::process::exit(1);
}
// 生成输出文件路径(同目录同名不同后缀)
let output_path = input_path.replace(".ts", ".mp4").replace(".TS", ".mp4");
println!("输入文件: {}", input_path);
println!("输出文件: {}", output_path);
println!("开始转换...\n");
// 执行转换
match convert_ts_to_mp4(input_path, &output_path) {
Ok(_) => {
println!("\n✅ 转换完成!");
println!("输出文件: {}", output_path);
}
Err(e) => {
eprintln!("\n❌ 转换失败: {}", e);
std::process::exit(1);
}
}
// 等待用户按键后退出(防止窗口立即关闭)
println!("\n按回车键退出...");
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
}
fn convert_ts_to_mp4(input_path: &str, output_path: &str) -> Result<(), Box<dyn std::error::Error>> {
// 首先获取视频总时长
let duration = get_video_duration(input_path)?;
println!("视频总时长: {:.2}", duration);
// 启动ffmpeg进程
let mut child = Command::new("ffmpeg")
// .args(&[
// "-i", input_path,
// "-c:v", "libx264", // 视频编码器
// "-c:a", "aac", // 音频编码器
// "-preset", "medium", // 编码速度预设
// "-crf", "23", // 视频质量
// "-movflags", "+faststart", // 优化网络播放
// "-progress", "pipe:1", // 输出进度到stdout
// "-y", // 覆盖输出文件
// output_path
// ])
.args(&[
"-i", input_path,
"-codec", "copy",
"-progress", "pipe:1", // 输出进度到stdout
"-y", // 覆盖输出文件
output_path
])
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
// 读取进度信息
if let Some(stdout) = child.stdout.take() {
let reader = BufReader::new(stdout);
let time_regex = Regex::new(r"out_time_ms=(\d+)")?;
for line in reader.lines() {
let line = line?;
// 解析当前处理时间
if let Some(captures) = time_regex.captures(&line) {
if let Ok(time_ms) = captures[1].parse::<f64>() {
let current_seconds = time_ms / 1_000_000.0; // 转换为秒
let progress = if duration > 0.0 {
(current_seconds / duration * 100.0).min(100.0)
} else {
0.0
};
// 显示进度条
print_progress_bar(progress);
}
}
// 检查是否完成
if line.contains("progress=end") {
print_progress_bar(100.0);
break;
}
}
}
// 等待进程结束
let status = child.wait()?;
if !status.success() {
return Err("FFmpeg转换失败".into());
}
Ok(())
}
fn get_video_duration(input_path: &str) -> Result<f64, Box<dyn std::error::Error>> {
let output = Command::new("ffprobe")
.args(&[
"-v", "quiet",
"-show_entries", "format=duration",
"-of", "csv=p=0",
input_path
])
.output()?;
let duration_str = String::from_utf8(output.stdout)?;
let duration: f64 = duration_str.trim().parse().unwrap_or(0.0);
Ok(duration)
}
fn print_progress_bar(percentage: f64) {
let bar_length = 40;
let filled_length = (bar_length as f64 * percentage / 100.0) as usize;
let empty_length = bar_length - filled_length;
let filled_bar = "".repeat(filled_length);
let empty_bar = "".repeat(empty_length);
print!("\r进度: [{}{}] {:.1}%", filled_bar, empty_bar, percentage);
std::io::stdout().flush().unwrap();
}
// 需要在Cargo.toml中添加依赖
/*
[dependencies]
regex = "1.10"
*/