Improve scan escalation, watch stability, and plan output
This commit is contained in:
@@ -30,6 +30,12 @@ vid-repair scan /path/to/videos
|
||||
# Scan and watch for new files
|
||||
vid-repair scan --watch /path/to/videos
|
||||
|
||||
# Scan depth (quick|standard|deep)
|
||||
vid-repair scan --scan-depth quick /path/to/videos
|
||||
|
||||
# Disable recursive scanning
|
||||
vid-repair scan --no-recursive /path/to/videos
|
||||
|
||||
# Fix (safe policy, in-place)
|
||||
vid-repair fix /path/to/videos
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ pub fn plan_fix(issues: &[Issue], policy: FixPolicy) -> FixPlan {
|
||||
} else {
|
||||
actions.push(FixAction {
|
||||
kind,
|
||||
command: Vec::new(),
|
||||
command: command_template(kind),
|
||||
destructive: true,
|
||||
});
|
||||
}
|
||||
@@ -56,12 +56,52 @@ pub fn plan_fix(issues: &[Issue], policy: FixPolicy) -> FixPlan {
|
||||
}
|
||||
|
||||
pub fn plan_outcome(plan: FixPlan) -> FixOutcome {
|
||||
let message = if let Some(kind) = plan.recommended {
|
||||
format!("Fix plan generated: {:?}", kind)
|
||||
} else {
|
||||
"Fix plan generated".to_string()
|
||||
};
|
||||
|
||||
FixOutcome {
|
||||
plan,
|
||||
applied: false,
|
||||
success: false,
|
||||
message: "Fix plan generated".to_string(),
|
||||
message,
|
||||
output_path: None,
|
||||
re_scan_required: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn command_template(kind: FixKind) -> Vec<String> {
|
||||
let mut cmd = vec![
|
||||
"ffmpeg".to_string(),
|
||||
"-v".to_string(),
|
||||
"error".to_string(),
|
||||
"-i".to_string(),
|
||||
"<input>".to_string(),
|
||||
];
|
||||
|
||||
match kind {
|
||||
FixKind::Remux => {
|
||||
cmd.push("-c".to_string());
|
||||
cmd.push("copy".to_string());
|
||||
}
|
||||
FixKind::Faststart => {
|
||||
cmd.push("-c".to_string());
|
||||
cmd.push("copy".to_string());
|
||||
cmd.push("-movflags".to_string());
|
||||
cmd.push("+faststart".to_string());
|
||||
}
|
||||
FixKind::Reencode => {
|
||||
cmd.push("-c:v".to_string());
|
||||
cmd.push("libx264".to_string());
|
||||
cmd.push("-c:a".to_string());
|
||||
cmd.push("aac".to_string());
|
||||
cmd.push("-movflags".to_string());
|
||||
cmd.push("+faststart".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
cmd.push("<output>".to_string());
|
||||
cmd
|
||||
}
|
||||
|
||||
@@ -22,7 +22,17 @@ pub fn render_fix_line(scan: &ScanOutcome, fix: &FixOutcome) -> String {
|
||||
} else if fix.applied {
|
||||
format!("[FAILED] {} - {}", scan.path.display(), fix.message)
|
||||
} else {
|
||||
format!("[SKIPPED] {} - {}", scan.path.display(), fix.message)
|
||||
let action = fix
|
||||
.plan
|
||||
.recommended
|
||||
.map(|kind| format!("{:?}", kind))
|
||||
.unwrap_or_else(|| "none".to_string());
|
||||
format!(
|
||||
"[SKIPPED] {} - {} (plan: {})",
|
||||
scan.path.display(),
|
||||
fix.message,
|
||||
action
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,21 +18,33 @@ pub fn scan_file(path: &Path, config: &Config, ruleset: &RuleSet) -> Result<Scan
|
||||
|
||||
let context = build_context(&probe);
|
||||
let mut matches = ruleset.match_lines(&decode.lines, &context);
|
||||
let had_decode_errors = !decode.lines.is_empty();
|
||||
|
||||
if config.scan.auto_escalate
|
||||
&& config.scan.depth != ScanDepth::Deep
|
||||
&& !decode.early_stop
|
||||
&& !matches.is_empty()
|
||||
&& (had_decode_errors || !matches.is_empty())
|
||||
{
|
||||
decode = decode::run_decode(path, &config.ffmpeg_path, ruleset, ScanDepth::Deep)?;
|
||||
matches = ruleset.match_lines(&decode.lines, &context);
|
||||
}
|
||||
|
||||
let issues = matches
|
||||
let mut issues = matches
|
||||
.iter()
|
||||
.map(|hit| issue_from_match(hit))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if issues.is_empty() && !decode.lines.is_empty() {
|
||||
issues.push(Issue {
|
||||
code: "UNKNOWN_DECODE_ERROR".to_string(),
|
||||
severity: crate::rules::Severity::High,
|
||||
fix_tier: crate::rules::FixTier::Reencode,
|
||||
message: "Decoder reported errors not matched by ruleset".to_string(),
|
||||
evidence: decode.lines.iter().take(3).cloned().collect(),
|
||||
action: None,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(ScanOutcome {
|
||||
path: path.to_path_buf(),
|
||||
probe,
|
||||
|
||||
@@ -86,8 +86,27 @@ where
|
||||
.collect();
|
||||
|
||||
for path in ready {
|
||||
let before = std::fs::metadata(&path).ok();
|
||||
entries.remove(&path);
|
||||
handler(path);
|
||||
handler(path.clone());
|
||||
if let Some(meta) = before {
|
||||
let size = meta.len();
|
||||
let mtime = meta.modified().unwrap_or_else(|_| std::time::SystemTime::UNIX_EPOCH);
|
||||
if let Ok(after) = std::fs::metadata(&path) {
|
||||
if after.len() != size || after.modified().unwrap_or_else(|_| std::time::SystemTime::UNIX_EPOCH) != mtime {
|
||||
entries.insert(
|
||||
path,
|
||||
WatchEntry {
|
||||
last_event: Instant::now(),
|
||||
size: after.len(),
|
||||
mtime: after
|
||||
.modified()
|
||||
.unwrap_or_else(|_| std::time::SystemTime::UNIX_EPOCH),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user