feat: add quiet/verbose modes with command logging
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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("e_arg(program));
|
||||
for arg in args {
|
||||
line.push(' ');
|
||||
line.push_str("e_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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user