4daca8247c
Maintainability: - Choice options now carry a stable `code` separate from the display `label`. The command builder and recipe storage key off codes, so relabeling a menu entry no longer silently changes the encode or breaks a saved recipe. Format/Audio dispatch is now a clean code match instead of `starts_with` on display strings. Old recipes that stored labels still load via a label fallback. Correctness: - Fade start times now scale by the speed factor already applied in the filter chain, so Speed + Fade produces a fade in the right place regardless of op order (video and audio). - Two-pass pass 1 uses the platform null device (NUL on Windows). - The stderr drain thread is joined before Finished is sent, so the failure tail reported to the user is never truncated. Features / cleanup: - Accept multiple file arguments on the CLI to open batch mode directly. - try_recv() instead of recv_timeout(ZERO); drop unused import. Testing: - 23 unit tests covering build() arg generation, speed/fade ordering, recipe round-trip + old-label fallback, and the pure helpers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
74 lines
2.2 KiB
Rust
74 lines
2.2 KiB
Rust
mod app;
|
|
mod ffmpeg;
|
|
mod ops;
|
|
mod recipes;
|
|
mod ui;
|
|
|
|
use std::process::Command;
|
|
use std::time::Duration;
|
|
|
|
use anyhow::{bail, Result};
|
|
use ratatui::crossterm::event::{self, Event, KeyEventKind};
|
|
use ratatui::DefaultTerminal;
|
|
|
|
use app::App;
|
|
|
|
fn main() -> Result<()> {
|
|
let mut initial: Vec<std::path::PathBuf> = Vec::new();
|
|
for arg in std::env::args().skip(1) {
|
|
match arg.as_str() {
|
|
"--version" | "-V" => {
|
|
println!("lazyff {}", env!("CARGO_PKG_VERSION"));
|
|
return Ok(());
|
|
}
|
|
"--help" | "-h" => {
|
|
println!("lazyff {} — a friendly TUI for FFmpeg", env!("CARGO_PKG_VERSION"));
|
|
println!("Usage: lazyff [FILE...]");
|
|
println!(" no argument open a file browser in the current directory");
|
|
println!(" FILE open this video/audio file straight in the editor");
|
|
println!(" FILE FILE... open several files together in batch mode");
|
|
return Ok(());
|
|
}
|
|
_ => {
|
|
let path = std::path::PathBuf::from(&arg);
|
|
if !path.is_file() {
|
|
bail!("{arg}: no such file");
|
|
}
|
|
initial.push(path);
|
|
}
|
|
}
|
|
}
|
|
|
|
for bin in ["ffmpeg", "ffprobe"] {
|
|
if Command::new(bin).arg("-version").output().is_err() {
|
|
bail!("{bin} was not found on your PATH. Please install FFmpeg first.");
|
|
}
|
|
}
|
|
|
|
// Probe the initial file before entering the alternate screen so errors
|
|
// print normally.
|
|
let app = App::new(initial)?;
|
|
|
|
let mut terminal = ratatui::init();
|
|
let result = run(&mut terminal, app);
|
|
ratatui::restore();
|
|
result
|
|
}
|
|
|
|
fn run(terminal: &mut DefaultTerminal, mut app: App) -> Result<()> {
|
|
loop {
|
|
app.poll_run_messages();
|
|
terminal.draw(|f| ui::draw(f, &mut app))?;
|
|
if event::poll(Duration::from_millis(100))? {
|
|
if let Event::Key(key) = event::read()? {
|
|
if key.kind == KeyEventKind::Press {
|
|
app.on_key(key);
|
|
}
|
|
}
|
|
}
|
|
if app.should_quit {
|
|
return Ok(());
|
|
}
|
|
}
|
|
}
|