feat: add quiet/verbose modes with command logging

This commit is contained in:
2026-01-01 18:05:13 -05:00
parent 2e55677e35
commit 5ebf256a58
5 changed files with 337 additions and 31 deletions

View File

@@ -32,6 +32,8 @@ keep_original = false
json = false
# Pretty-print JSON output.
pretty = true
# quiet|normal|verbose - controls console verbosity.
verbosity = "normal"
[performance]
# 0 = auto (num CPU threads). Higher values spawn more concurrent scans.

View File

@@ -129,6 +129,8 @@ pub struct ReportConfig {
pub json: bool,
#[serde(default)]
pub pretty: bool,
#[serde(default)]
pub verbosity: Verbosity,
}
impl Default for ReportConfig {
@@ -136,10 +138,25 @@ impl Default for ReportConfig {
Self {
json: false,
pretty: true,
verbosity: Verbosity::Normal,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum Verbosity {
Quiet,
Normal,
Verbose,
}
impl Default for Verbosity {
fn default() -> Self {
Verbosity::Normal
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceConfig {
#[serde(default)]
@@ -185,6 +202,7 @@ pub struct ConfigOverrides {
pub output_dir: Option<String>,
pub keep_original: Option<bool>,
pub json: Option<bool>,
pub verbosity: Option<Verbosity>,
pub jobs: Option<usize>,
pub watch: Option<bool>,
}
@@ -215,6 +233,9 @@ impl Config {
if let Some(value) = overrides.json {
self.report.json = value;
}
if let Some(value) = overrides.verbosity {
self.report.verbosity = value;
}
if let Some(value) = overrides.jobs {
self.performance.jobs = value;
}

View File

@@ -15,7 +15,7 @@ use crate::scan::{scan_file, DecodeProgress};
use crate::rules::RuleSet;
pub fn apply_fix(path: &Path, plan: &FixPlan, config: &Config, ruleset: &RuleSet) -> Result<FixOutcome> {
apply_fix_with_progress(path, plan, config, ruleset, None, None)
apply_fix_with_progress(path, plan, config, ruleset, None, None, None)
}
pub fn apply_fix_with_progress(
@@ -25,6 +25,7 @@ pub fn apply_fix_with_progress(
ruleset: &RuleSet,
duration: Option<f64>,
progress: Option<Arc<dyn Fn(DecodeProgress) + Send + Sync>>,
log_cmd: Option<Arc<dyn Fn(String) + Send + Sync>>,
) -> Result<FixOutcome> {
if plan.actions.is_empty() {
return Ok(FixOutcome {
@@ -43,7 +44,15 @@ pub fn apply_fix_with_progress(
let action = &plan.actions[0];
let output = prepare_output_path(path, config)?;
run_ffmpeg_fix(path, &output.temp_path, action.kind, config, duration, progress)?;
run_ffmpeg_fix(
path,
&output.temp_path,
action.kind,
config,
duration,
progress,
log_cmd,
)?;
let verification = scan_file(&output.temp_path, config, ruleset)
.with_context(|| format!("Failed to verify output {}", output.temp_path.display()))?;
@@ -78,26 +87,44 @@ fn run_ffmpeg_fix(
config: &Config,
duration: Option<f64>,
progress: Option<Arc<dyn Fn(DecodeProgress) + Send + Sync>>,
log_cmd: Option<Arc<dyn Fn(String) + Send + Sync>>,
) -> Result<()> {
let mut cmd = Command::new(&config.ffmpeg_path);
cmd.arg("-y").arg("-v").arg("error").arg("-i").arg(path);
let mut args = vec![
"-y".to_string(),
"-v".to_string(),
"error".to_string(),
"-i".to_string(),
path.display().to_string(),
];
match kind {
FixKind::Remux => {
cmd.arg("-c").arg("copy");
args.extend(["-c", "copy"].iter().map(|s| s.to_string()));
}
FixKind::Faststart => {
cmd.arg("-c").arg("copy");
cmd.arg("-movflags").arg("+faststart");
args.extend(["-c", "copy", "-movflags", "+faststart"].iter().map(|s| s.to_string()));
}
FixKind::Reencode => {
cmd.arg("-c:v").arg("libx264");
cmd.arg("-c:a").arg("aac");
cmd.arg("-movflags").arg("+faststart");
args.extend(
["-c:v", "libx264", "-c:a", "aac", "-movflags", "+faststart"]
.iter()
.map(|s| s.to_string()),
);
}
}
cmd.arg(output);
if progress.is_some() {
args.extend(["-nostats", "-progress", "pipe:2"].iter().map(|s| s.to_string()));
}
args.push(output.display().to_string());
if let Some(log_cmd) = log_cmd {
log_cmd(format_command_line(&config.ffmpeg_path, &args));
}
let mut cmd = Command::new(&config.ffmpeg_path);
cmd.args(&args);
if progress.is_none() {
let output = cmd
@@ -112,7 +139,6 @@ fn run_ffmpeg_fix(
return Ok(());
}
cmd.arg("-nostats").arg("-progress").arg("pipe:2");
cmd.stdout(Stdio::null()).stderr(Stdio::piped());
let mut child = cmd
@@ -371,3 +397,25 @@ fn parse_out_time(value: &str) -> Option<f64> {
None
}
fn format_command_line(program: &str, args: &[String]) -> String {
let mut line = String::new();
line.push_str(&quote_arg(program));
for arg in args {
line.push(' ');
line.push_str(&quote_arg(arg));
}
line
}
fn quote_arg(arg: &str) -> String {
if arg.is_empty() {
return "\"\"".to_string();
}
if arg.chars().any(|c| c.is_whitespace() || c == '"' || c == '\'') {
let escaped = arg.replace('"', "\\\"");
format!("\"{}\"", escaped)
} else {
arg.to_string()
}
}