[feat] improve error handling for file operations and subprocess execution; refactor main to modularize execution flow

This commit is contained in:
2025-08-08 17:04:42 +02:00
parent c27af0b89a
commit a0216a0e18
3 changed files with 47 additions and 18 deletions

View File

@@ -1,8 +1,6 @@
use crate::OutputEntry;
use crate::{decode_audio_to_pcm_f32_ffmpeg, find_model_file};
use anyhow::{Context, Result, anyhow};
#[cfg(not(test))]
use libloading::Library;
use std::env;
use std::path::Path;
@@ -52,15 +50,8 @@ fn check_lib(names: &[&str]) -> bool {
}
#[cfg(not(test))]
{
if std::env::var("POLYSCRIBE_DISABLE_DLOPEN").ok().as_deref() == Some("1") {
return false;
}
for n in names {
// Attempt to dlopen; ignore errors
if let Ok(_lib) = unsafe { Library::new(n) } {
return true;
}
}
// Disabled runtime dlopen probing to avoid loader instability; rely on environment overrides.
let _ = names;
false
}
}

View File

@@ -302,7 +302,7 @@ pub fn find_model_file() -> Result<PathBuf> {
/// Decode an input media file to 16kHz mono f32 PCM using ffmpeg available on PATH.
pub fn decode_audio_to_pcm_f32_ffmpeg(audio_path: &Path) -> Result<Vec<f32>> {
let output = Command::new("ffmpeg")
let output = match Command::new("ffmpeg")
.arg("-i")
.arg(audio_path)
.arg("-f")
@@ -313,7 +313,22 @@ pub fn decode_audio_to_pcm_f32_ffmpeg(audio_path: &Path) -> Result<Vec<f32>> {
.arg("16000")
.arg("pipe:1")
.output()
.with_context(|| format!("Failed to execute ffmpeg for {}", audio_path.display()))?;
{
Ok(o) => o,
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
return Err(anyhow!(
"ffmpeg not found on PATH. Please install ffmpeg and ensure it is available."
));
} else {
return Err(anyhow!(
"Failed to execute ffmpeg for {}: {}",
audio_path.display(),
e
));
}
}
};
if !output.status.success() {
return Err(anyhow!(
"ffmpeg failed for {}: {}",

View File

@@ -159,11 +159,15 @@ fn prompt_speaker_name_for_path(path: &Path, default_name: &str, enabled: bool)
let mut buf = String::new();
match io::stdin().read_line(&mut buf) {
Ok(_) => {
let s = buf.trim();
if s.is_empty() {
let raw = buf.trim();
if raw.is_empty() {
return default_name.to_string();
}
let sanitized = sanitize_speaker_name(raw);
if sanitized.is_empty() {
default_name.to_string()
} else {
s.to_string()
sanitized
}
}
Err(_) => default_name.to_string(),
@@ -196,11 +200,16 @@ struct LastModelCleanup {
impl Drop for LastModelCleanup {
fn drop(&mut self) {
// Ensure .last_model does not persist across program runs
let _ = std::fs::remove_file(&self.path);
if let Err(e) = std::fs::remove_file(&self.path) {
// Best-effort cleanup; ignore missing file; warn for other errors
if e.kind() != std::io::ErrorKind::NotFound {
warnlog!("Failed to remove {}: {}", self.path.display(), e);
}
}
}
}
fn main() -> Result<()> {
fn run() -> Result<()> {
// Parse CLI
let args = Args::parse();
@@ -692,6 +701,20 @@ fn main() -> Result<()> {
Ok(())
}
fn main() {
if let Err(e) = run() {
errorlog!("{}", e);
if VERBOSE.load(Ordering::Relaxed) >= 1 {
let mut src = e.source();
while let Some(s) = src {
errorlog!("caused by: {}", s);
src = s.source();
}
}
std::process::exit(1);
}
}
#[cfg(test)]
mod tests {
use super::*;