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
|
# Scan and watch for new files
|
||||||
vid-repair scan --watch /path/to/videos
|
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)
|
# Fix (safe policy, in-place)
|
||||||
vid-repair fix /path/to/videos
|
vid-repair fix /path/to/videos
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ pub fn plan_fix(issues: &[Issue], policy: FixPolicy) -> FixPlan {
|
|||||||
} else {
|
} else {
|
||||||
actions.push(FixAction {
|
actions.push(FixAction {
|
||||||
kind,
|
kind,
|
||||||
command: Vec::new(),
|
command: command_template(kind),
|
||||||
destructive: true,
|
destructive: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -56,12 +56,52 @@ pub fn plan_fix(issues: &[Issue], policy: FixPolicy) -> FixPlan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn plan_outcome(plan: FixPlan) -> FixOutcome {
|
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 {
|
FixOutcome {
|
||||||
plan,
|
plan,
|
||||||
applied: false,
|
applied: false,
|
||||||
success: false,
|
success: false,
|
||||||
message: "Fix plan generated".to_string(),
|
message,
|
||||||
output_path: None,
|
output_path: None,
|
||||||
re_scan_required: false,
|
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 {
|
} else if fix.applied {
|
||||||
format!("[FAILED] {} - {}", scan.path.display(), fix.message)
|
format!("[FAILED] {} - {}", scan.path.display(), fix.message)
|
||||||
} else {
|
} 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 context = build_context(&probe);
|
||||||
let mut matches = ruleset.match_lines(&decode.lines, &context);
|
let mut matches = ruleset.match_lines(&decode.lines, &context);
|
||||||
|
let had_decode_errors = !decode.lines.is_empty();
|
||||||
|
|
||||||
if config.scan.auto_escalate
|
if config.scan.auto_escalate
|
||||||
&& config.scan.depth != ScanDepth::Deep
|
&& config.scan.depth != ScanDepth::Deep
|
||||||
&& !decode.early_stop
|
&& !decode.early_stop
|
||||||
&& !matches.is_empty()
|
&& (had_decode_errors || !matches.is_empty())
|
||||||
{
|
{
|
||||||
decode = decode::run_decode(path, &config.ffmpeg_path, ruleset, ScanDepth::Deep)?;
|
decode = decode::run_decode(path, &config.ffmpeg_path, ruleset, ScanDepth::Deep)?;
|
||||||
matches = ruleset.match_lines(&decode.lines, &context);
|
matches = ruleset.match_lines(&decode.lines, &context);
|
||||||
}
|
}
|
||||||
|
|
||||||
let issues = matches
|
let mut issues = matches
|
||||||
.iter()
|
.iter()
|
||||||
.map(|hit| issue_from_match(hit))
|
.map(|hit| issue_from_match(hit))
|
||||||
.collect::<Vec<_>>();
|
.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 {
|
Ok(ScanOutcome {
|
||||||
path: path.to_path_buf(),
|
path: path.to_path_buf(),
|
||||||
probe,
|
probe,
|
||||||
|
|||||||
@@ -86,8 +86,27 @@ where
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for path in ready {
|
for path in ready {
|
||||||
|
let before = std::fs::metadata(&path).ok();
|
||||||
entries.remove(&path);
|
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