[feat] add input validation, enhanced error messages, and new integration tests
This commit is contained in:
@@ -694,10 +694,11 @@ pub fn decode_audio_to_pcm_f32_ffmpeg(audio_path: &Path) -> Result<Vec<f32>> {
|
||||
}
|
||||
};
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(anyhow!(
|
||||
"ffmpeg failed for {}: {}",
|
||||
"Failed to decode audio from {} using ffmpeg. This may indicate the file is not a valid or supported audio/video file, is corrupted, or cannot be opened. ffmpeg stderr: {}",
|
||||
audio_path.display(),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
stderr.trim()
|
||||
));
|
||||
}
|
||||
let bytes = output.stdout;
|
||||
|
||||
28
src/main.rs
28
src/main.rs
@@ -186,6 +186,22 @@ fn is_audio_file(path: &Path) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn validate_input_path(path: &Path) -> anyhow::Result<()> {
|
||||
use anyhow::{anyhow, Context};
|
||||
let display = path.display();
|
||||
if !path.exists() {
|
||||
return Err(anyhow!("Input not found: {}", display));
|
||||
}
|
||||
let md = std::fs::metadata(path).with_context(|| format!("Failed to stat input: {}", display))?;
|
||||
if md.is_dir() {
|
||||
return Err(anyhow!("Input is a directory (expected a file): {}", display));
|
||||
}
|
||||
// Attempt to open to catch permission errors early
|
||||
std::fs::File::open(path)
|
||||
.with_context(|| format!("Failed to open input file: {}", display))
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
struct LastModelCleanup {
|
||||
path: PathBuf,
|
||||
}
|
||||
@@ -315,6 +331,18 @@ fn run() -> Result<()> {
|
||||
return Err(anyhow!("No input files provided"));
|
||||
}
|
||||
|
||||
// Preflight: validate each input path and type
|
||||
for inp in &inputs {
|
||||
let p = Path::new(inp);
|
||||
validate_input_path(p)?;
|
||||
if !(is_audio_file(p) || is_json_file(p)) {
|
||||
return Err(anyhow!(
|
||||
"Unsupported input type (expected .json transcript or common audio/video): {}",
|
||||
p.display()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Language must be provided via CLI when transcribing audio (no detection from JSON/env)
|
||||
let lang_hint: Option<String> = if let Some(ref l) = args.language {
|
||||
normalize_lang_code(l).or_else(|| Some(l.trim().to_lowercase()))
|
||||
|
||||
@@ -620,10 +620,13 @@ fn download_one_model(client: &Client, models_dir: &Path, entry: &ModelEntry) ->
|
||||
url
|
||||
);
|
||||
let mut resp = client
|
||||
.get(url)
|
||||
.get(url.clone())
|
||||
.send()
|
||||
.and_then(|r| r.error_for_status())
|
||||
.context("Failed to download model")?;
|
||||
.with_context(|| format!(
|
||||
"Failed to download model {} from {}. If your terminal has display/TTY issues, try running with --no-progress.",
|
||||
entry.name, url
|
||||
))?;
|
||||
|
||||
let tmp_path = models_dir.join(format!("ggml-{}.bin.part", entry.name));
|
||||
if tmp_path.exists() {
|
||||
|
||||
Reference in New Issue
Block a user