[feat] integrate cliclack for TTY-aware UI, add summaries and intro/outro helpers
This commit is contained in:
112
Cargo.lock
generated
112
Cargo.lock
generated
@@ -285,6 +285,20 @@ dependencies = [
|
||||
"roff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cliclack"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c420bdc04c123a2df04d9c5a07289195f00007af6e45ab18f55e56dc7e04b8"
|
||||
dependencies = [
|
||||
"console",
|
||||
"indicatif",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmake"
|
||||
version = "0.1.54"
|
||||
@@ -300,6 +314,19 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"unicode-width",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
@@ -362,6 +389,12 @@ version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
@@ -814,6 +847,19 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.17.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235"
|
||||
dependencies = [
|
||||
"console",
|
||||
"number_prefix",
|
||||
"portable-atomic",
|
||||
"unicode-width",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.7.9"
|
||||
@@ -980,6 +1026,12 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "number_prefix"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.7"
|
||||
@@ -1078,6 +1130,7 @@ dependencies = [
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"clap_mangen",
|
||||
"cliclack",
|
||||
"libc",
|
||||
"reqwest",
|
||||
"serde",
|
||||
@@ -1088,6 +1141,12 @@ dependencies = [
|
||||
"whisper-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.2"
|
||||
@@ -1406,6 +1465,12 @@ version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "smawk"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.0"
|
||||
@@ -1499,6 +1564,17 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
|
||||
dependencies = [
|
||||
"smawk",
|
||||
"unicode-linebreak",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.1"
|
||||
@@ -1682,6 +1758,18 @@ version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-linebreak"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
@@ -1828,6 +1916,16 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-time"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "whisper-rs"
|
||||
version = "0.14.3"
|
||||
@@ -2147,6 +2245,20 @@ name = "zeroize"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
|
@@ -29,6 +29,7 @@ sha2 = "0.10"
|
||||
# whisper-rs is always used (CPU-only by default); GPU features map onto it
|
||||
whisper-rs = { git = "https://github.com/tazz4843/whisper-rs" }
|
||||
libc = "0.2"
|
||||
cliclack = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
|
100
src/lib.rs
100
src/lib.rs
@@ -173,50 +173,82 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Centralized UI helpers (TTY-aware, quiet/verbose-aware)
|
||||
pub mod ui {
|
||||
use std::io;
|
||||
// Prefer cliclack for all user-visible messages to ensure consistent, TTY-aware output.
|
||||
// Falls back to stderr printing if needed.
|
||||
/// Startup intro/banner (suppressed when quiet).
|
||||
pub fn intro(msg: impl AsRef<str>) {
|
||||
if crate::is_quiet() { return; }
|
||||
// Use cliclack intro to render a nice banner when TTY
|
||||
let _ = cliclack::intro(msg.as_ref());
|
||||
}
|
||||
/// Print an informational line (suppressed when quiet).
|
||||
pub fn info(msg: impl AsRef<str>) {
|
||||
if crate::is_quiet() { return; }
|
||||
let _ = cliclack::log::info(msg.as_ref());
|
||||
}
|
||||
/// Print a warning (always printed).
|
||||
pub fn warn(msg: impl AsRef<str>) {
|
||||
// cliclack provides a warning-level log utility
|
||||
let _ = cliclack::log::warning(msg.as_ref());
|
||||
}
|
||||
/// Print an error (always printed).
|
||||
pub fn error(msg: impl AsRef<str>) {
|
||||
let _ = cliclack::log::error(msg.as_ref());
|
||||
}
|
||||
/// Print a line above any progress bars (maps to cliclack log; synchronized).
|
||||
pub fn println_above_bars(msg: impl AsRef<str>) {
|
||||
if crate::is_quiet() { return; }
|
||||
// cliclack logs are synchronized with its spinners/bars
|
||||
let _ = cliclack::log::info(msg.as_ref());
|
||||
}
|
||||
/// Final outro/summary printed below any progress indicators (suppressed when quiet).
|
||||
pub fn outro(msg: impl AsRef<str>) {
|
||||
if crate::is_quiet() { return; }
|
||||
let _ = cliclack::outro(msg.as_ref());
|
||||
}
|
||||
/// Prompt the user (TTY-aware via cliclack) and read a line from stdin. Returns the raw line with trailing newline removed.
|
||||
pub fn prompt_line(prompt: &str) -> io::Result<String> {
|
||||
// Route prompt through cliclack to keep consistent styling and avoid direct eprint!/println!
|
||||
let _ = cliclack::log::info(prompt);
|
||||
let mut s = String::new();
|
||||
io::stdin().read_line(&mut s)?;
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
/// Logging macros and helpers
|
||||
/// Log an error to stderr (always printed). Recommended for user-visible errors.
|
||||
/// Log an error using the UI helper (always printed). Recommended for user-visible errors.
|
||||
#[macro_export]
|
||||
macro_rules! elog {
|
||||
($($arg:tt)*) => {{
|
||||
eprintln!("ERROR: {}", format!($($arg)*));
|
||||
}}
|
||||
}
|
||||
/// Internal helper macro used by other logging macros to centralize the
|
||||
/// common behavior: build formatted message, check quiet/verbose flags,
|
||||
/// and print to stderr with a label.
|
||||
#[macro_export]
|
||||
macro_rules! log_with_level {
|
||||
($label:expr, $min_lvl:expr, $always:expr, $($arg:tt)*) => {{
|
||||
let should_print = if $always {
|
||||
true
|
||||
} else if let Some(minv) = $min_lvl {
|
||||
!$crate::is_quiet() && $crate::verbose_level() >= minv
|
||||
} else {
|
||||
!$crate::is_quiet()
|
||||
};
|
||||
if should_print {
|
||||
eprintln!("{}: {}", $label, format!($($arg)*));
|
||||
}
|
||||
$crate::ui::error(format!($($arg)*));
|
||||
}}
|
||||
}
|
||||
|
||||
/// Log a warning to stderr (printed even in quiet mode).
|
||||
/// Log a warning using the UI helper (printed even in quiet mode).
|
||||
#[macro_export]
|
||||
macro_rules! wlog {
|
||||
($($arg:tt)*) => {{ $crate::log_with_level!("WARN", None, true, $($arg)*); }}
|
||||
($($arg:tt)*) => {{
|
||||
$crate::ui::warn(format!($($arg)*));
|
||||
}}
|
||||
}
|
||||
|
||||
/// Log an informational line to stderr unless quiet mode is enabled.
|
||||
/// Log an informational line using the UI helper unless quiet mode is enabled.
|
||||
#[macro_export]
|
||||
macro_rules! ilog {
|
||||
($($arg:tt)*) => {{ $crate::log_with_level!("INFO", None, false, $($arg)*); }}
|
||||
($($arg:tt)*) => {{
|
||||
if !$crate::is_quiet() { $crate::ui::info(format!($($arg)*)); }
|
||||
}}
|
||||
}
|
||||
|
||||
/// Log a debug/trace line when verbose level is at least the given level (u8).
|
||||
#[macro_export]
|
||||
macro_rules! dlog {
|
||||
($lvl:expr, $($arg:tt)*) => {{
|
||||
$crate::log_with_level!(&format!("DEBUG{}", &$lvl), Some($lvl), false, $($arg)*);
|
||||
if !$crate::is_quiet() && $crate::verbose_level() >= $lvl { $crate::ui::info(format!("DEBUG{}: {}", $lvl, format!($($arg)*))); }
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -230,7 +262,6 @@ use anyhow::{Context, Result, anyhow};
|
||||
use chrono::Local;
|
||||
use std::env;
|
||||
use std::fs::create_dir_all;
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
@@ -462,10 +493,7 @@ pub fn find_model_file() -> Result<PathBuf> {
|
||||
"No models available and interactive mode is disabled. Please set WHISPER_MODEL or run with --download-models."
|
||||
));
|
||||
}
|
||||
eprint!("Would you like to download models now? [Y/n]: ");
|
||||
io::stderr().flush().ok();
|
||||
let mut input = String::new();
|
||||
io::stdin().read_line(&mut input).ok();
|
||||
let input = crate::ui::prompt_line("Would you like to download models now? [Y/n]: ").unwrap_or_default();
|
||||
let ans = input.trim().to_lowercase();
|
||||
if ans.is_empty() || ans == "y" || ans == "yes" {
|
||||
if let Err(e) = models::run_interactive_model_downloader() {
|
||||
@@ -519,16 +547,12 @@ pub fn find_model_file() -> Result<PathBuf> {
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!("Multiple Whisper models found in {}:", models_dir.display());
|
||||
crate::ui::println_above_bars(format!("Multiple Whisper models found in {}:", models_dir.display()));
|
||||
for (i, p) in candidates.iter().enumerate() {
|
||||
eprintln!(" {}) {}", i + 1, p.display());
|
||||
crate::ui::println_above_bars(format!(" {}) {}", i + 1, p.display()));
|
||||
}
|
||||
eprint!("Select model by number [1-{}]: ", candidates.len());
|
||||
io::stderr().flush().ok();
|
||||
let mut input = String::new();
|
||||
io::stdin()
|
||||
.read_line(&mut input)
|
||||
.context("Failed to read selection")?;
|
||||
let input = crate::ui::prompt_line(&format!("Select model by number [1-{}]: ", candidates.len()))
|
||||
.map_err(|_| anyhow!("Failed to read selection"))?;
|
||||
let sel: usize = input
|
||||
.trim()
|
||||
.parse()
|
||||
|
43
src/main.rs
43
src/main.rs
@@ -142,13 +142,9 @@ fn prompt_speaker_name_for_path(path: &Path, default_name: &str, enabled: bool)
|
||||
.and_then(|s| s.to_str())
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| path.to_string_lossy().to_string());
|
||||
eprint!(
|
||||
let buf = polyscribe::ui::prompt_line(&format!(
|
||||
"Enter speaker name for {display_owned} [default: {default_name}]: "
|
||||
);
|
||||
io::stderr().flush().ok();
|
||||
let mut buf = String::new();
|
||||
match io::stdin().read_line(&mut buf) {
|
||||
Ok(_) => {
|
||||
)).unwrap_or_default();
|
||||
let raw = buf.trim();
|
||||
if raw.is_empty() {
|
||||
return default_name.to_string();
|
||||
@@ -160,9 +156,6 @@ fn prompt_speaker_name_for_path(path: &Path, default_name: &str, enabled: bool)
|
||||
sanitized
|
||||
}
|
||||
}
|
||||
Err(_) => default_name.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
// --- Helpers for audio transcription ---
|
||||
fn is_json_file(path: &Path) -> bool {
|
||||
@@ -217,6 +210,7 @@ where
|
||||
}
|
||||
|
||||
fn run() -> Result<()> {
|
||||
let _t0 = std::time::Instant::now();
|
||||
// Parse CLI
|
||||
let args = Args::parse();
|
||||
|
||||
@@ -225,6 +219,9 @@ fn run() -> Result<()> {
|
||||
polyscribe::set_quiet(args.quiet);
|
||||
polyscribe::set_no_interaction(args.no_interaction);
|
||||
|
||||
// Startup banner via UI (TTY-aware through cliclack), suppressed when quiet
|
||||
polyscribe::ui::intro(format!("PolyScribe v{}", env!("CARGO_PKG_VERSION")));
|
||||
|
||||
// Handle auxiliary subcommands that write to stdout and exit early
|
||||
if let Some(aux) = &args.aux {
|
||||
use clap::CommandFactory;
|
||||
@@ -266,6 +263,10 @@ fn run() -> Result<()> {
|
||||
polyscribe::dlog!(1, "Using backend: {:?}", sel.chosen);
|
||||
|
||||
// If requested, run the interactive model downloader first. If no inputs were provided, exit after downloading.
|
||||
let mut summary_inputs_total: usize = 0;
|
||||
let mut summary_audio_count: usize = 0;
|
||||
let mut summary_json_count: usize = 0;
|
||||
let mut summary_segments_total: usize = 0;
|
||||
if args.download_models {
|
||||
if let Err(e) = polyscribe::models::run_interactive_model_downloader() {
|
||||
polyscribe::elog!("Model downloader failed: {:#}", e);
|
||||
@@ -290,6 +291,7 @@ fn run() -> Result<()> {
|
||||
// Determine inputs and optional output path
|
||||
polyscribe::dlog!(1, "Parsed {} input(s)", args.inputs.len());
|
||||
let mut inputs = args.inputs;
|
||||
summary_inputs_total = inputs.len();
|
||||
let mut output_path = args.output;
|
||||
if output_path.is_none() && inputs.len() >= 2 {
|
||||
if let Some(last) = inputs.last().cloned() {
|
||||
@@ -353,6 +355,7 @@ fn run() -> Result<()> {
|
||||
// Collect entries per file and extend merged
|
||||
let mut entries: Vec<OutputEntry> = Vec::new();
|
||||
if is_audio_file(path) {
|
||||
summary_audio_count += 1;
|
||||
// Progress log to stderr (suppressed by -q); avoid partial lines
|
||||
polyscribe::ilog!("Processing file: {} ...", path.display());
|
||||
let res = with_quiet_stdio_if_needed(args.quiet, || {
|
||||
@@ -372,6 +375,7 @@ fn run() -> Result<()> {
|
||||
}
|
||||
}
|
||||
} else if is_json_file(path) {
|
||||
summary_json_count += 1;
|
||||
let mut buf = String::new();
|
||||
File::open(path)
|
||||
.with_context(|| format!("Failed to open: {input_path}"))?
|
||||
@@ -409,6 +413,7 @@ fn run() -> Result<()> {
|
||||
for (i, e) in entries.iter_mut().enumerate() {
|
||||
e.id = i as u64;
|
||||
}
|
||||
summary_segments_total += entries.len();
|
||||
|
||||
// Write separate outputs to out_dir
|
||||
let out = OutputRoot {
|
||||
@@ -498,6 +503,7 @@ fn run() -> Result<()> {
|
||||
|
||||
let mut buf = String::new();
|
||||
if is_audio_file(path) {
|
||||
summary_audio_count += 1;
|
||||
// Progress log to stderr (suppressed by -q)
|
||||
polyscribe::ilog!("Processing file: {} ...", path.display());
|
||||
let res = with_quiet_stdio_if_needed(args.quiet, || {
|
||||
@@ -520,6 +526,7 @@ fn run() -> Result<()> {
|
||||
}
|
||||
}
|
||||
} else if is_json_file(path) {
|
||||
summary_json_count += 1;
|
||||
File::open(path)
|
||||
.with_context(|| format!("Failed to open: {}", input_path))?
|
||||
.read_to_string(&mut buf)
|
||||
@@ -559,6 +566,7 @@ fn run() -> Result<()> {
|
||||
e.id = i as u64;
|
||||
}
|
||||
let out = OutputRoot { items: entries };
|
||||
summary_segments_total = out.items.len();
|
||||
|
||||
if let Some(path) = output_path {
|
||||
let base_path = Path::new(&path);
|
||||
@@ -636,6 +644,7 @@ fn run() -> Result<()> {
|
||||
// Collect entries per file
|
||||
let mut entries: Vec<OutputEntry> = Vec::new();
|
||||
if is_audio_file(path) {
|
||||
summary_audio_count += 1;
|
||||
// Progress log to stderr (suppressed by -q)
|
||||
polyscribe::ilog!("Processing file: {} ...", path.display());
|
||||
let res = with_quiet_stdio_if_needed(args.quiet, || {
|
||||
@@ -655,6 +664,7 @@ fn run() -> Result<()> {
|
||||
}
|
||||
}
|
||||
} else if is_json_file(path) {
|
||||
summary_json_count += 1;
|
||||
let mut buf = String::new();
|
||||
File::open(path)
|
||||
.with_context(|| format!("Failed to open: {input_path}"))?
|
||||
@@ -692,6 +702,7 @@ fn run() -> Result<()> {
|
||||
for (i, e) in entries.iter_mut().enumerate() {
|
||||
e.id = i as u64;
|
||||
}
|
||||
summary_segments_total += entries.len();
|
||||
let out = OutputRoot { items: entries };
|
||||
|
||||
if let Some(dir) = &out_dir {
|
||||
@@ -736,6 +747,20 @@ fn run() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
// Final summary (TTY-aware via UI), only when not quiet
|
||||
if !polyscribe::is_quiet() {
|
||||
let elapsed = _t0.elapsed();
|
||||
let secs = elapsed.as_secs_f32();
|
||||
let mut out = String::new();
|
||||
out.push_str("Summary:\n");
|
||||
out.push_str(&format!("{:<12} {:>8}\n", "Files:", summary_inputs_total));
|
||||
out.push_str(&format!("{:<12} {:>8}\n", "Audio:", summary_audio_count));
|
||||
out.push_str(&format!("{:<12} {:>8}\n", "JSON:", summary_json_count));
|
||||
out.push_str(&format!("{:<12} {:>8}\n", "Segments:", summary_segments_total));
|
||||
out.push_str(&format!("{:<12} {:>8.2}s\n", "Time:", secs));
|
||||
polyscribe::ui::outro(out);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@@ -5,7 +5,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::env;
|
||||
use std::fs::{File, create_dir_all};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -326,8 +326,8 @@ fn fetch_all_models(client: &Client) -> Result<Vec<ModelEntry>> {
|
||||
match hf_fetch_repo_models(client, "akashmjn/tinydiarize-whisper.cpp") {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
ilog!(
|
||||
"Warning: failed to fetch optional repo akashmjn/tinydiarize-whisper.cpp: {:#}",
|
||||
wlog!(
|
||||
"Failed to fetch optional repo akashmjn/tinydiarize-whisper.cpp: {:#}",
|
||||
e
|
||||
);
|
||||
Vec::new()
|
||||
@@ -413,18 +413,16 @@ fn prompt_select_models_two_stage(models: &[ModelEntry]) -> Result<Vec<ModelEntr
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
// Print base selection on stderr
|
||||
eprintln!("Available base model families:");
|
||||
// Print base selection via UI
|
||||
crate::ui::println_above_bars("Available base model families:");
|
||||
for (i, b) in bases.iter().enumerate() {
|
||||
eprintln!(" {}) {}", i + 1, b);
|
||||
crate::ui::println_above_bars(format!(" {}) {}", i + 1, b));
|
||||
}
|
||||
loop {
|
||||
eprint!("Select base (number or name, 'q' to cancel): ");
|
||||
io::stderr().flush().ok();
|
||||
let mut line = String::new();
|
||||
io::stdin()
|
||||
.read_line(&mut line)
|
||||
.context("Failed to read base selection")?;
|
||||
let mut line = match crate::ui::prompt_line("Select base (number or name, 'q' to cancel): ") {
|
||||
Ok(s) => s,
|
||||
Err(_) => String::new(),
|
||||
};
|
||||
let s = line.trim();
|
||||
if s.eq_ignore_ascii_case("q")
|
||||
|| s.eq_ignore_ascii_case("quit")
|
||||
@@ -450,12 +448,12 @@ fn prompt_select_models_two_stage(models: &[ModelEntry]) -> Result<Vec<ModelEntr
|
||||
let filtered: Vec<ModelEntry> =
|
||||
models.iter().filter(|m| m.base == base).cloned().collect();
|
||||
if filtered.is_empty() {
|
||||
eprintln!("No models found for base '{base}'.");
|
||||
crate::ui::warn(format!("No models found for base '{base}'."));
|
||||
continue;
|
||||
}
|
||||
// Reuse the formatter but only for the chosen base list
|
||||
let listing = format_model_list(&filtered);
|
||||
eprint!("{listing}");
|
||||
crate::ui::println_above_bars(listing);
|
||||
|
||||
// Build index map for filtered list
|
||||
let mut index_map: Vec<usize> = Vec::with_capacity(filtered.len());
|
||||
@@ -466,12 +464,8 @@ fn prompt_select_models_two_stage(models: &[ModelEntry]) -> Result<Vec<ModelEntr
|
||||
}
|
||||
// Second prompt: sub-type selection
|
||||
loop {
|
||||
eprint!("Selection: ");
|
||||
io::stderr().flush().ok();
|
||||
let mut line2 = String::new();
|
||||
io::stdin()
|
||||
.read_line(&mut line2)
|
||||
.context("Failed to read selection")?;
|
||||
let line2 = crate::ui::prompt_line("Selection: ")
|
||||
.map_err(|_| anyhow!("Failed to read selection"))?;
|
||||
let s2 = line2.trim().to_lowercase();
|
||||
if s2 == "q" || s2 == "quit" || s2 == "exit" {
|
||||
return Ok(Vec::new());
|
||||
@@ -501,7 +495,7 @@ fn prompt_select_models_two_stage(models: &[ModelEntry]) -> Result<Vec<ModelEntr
|
||||
selected.sort_unstable();
|
||||
selected.dedup();
|
||||
if selected.is_empty() {
|
||||
eprintln!("No valid selection. Please try again or 'q' to cancel.");
|
||||
crate::ui::warn("No valid selection. Please try again or 'q' to cancel.");
|
||||
continue;
|
||||
}
|
||||
let chosen: Vec<ModelEntry> = selected
|
||||
@@ -511,10 +505,10 @@ fn prompt_select_models_two_stage(models: &[ModelEntry]) -> Result<Vec<ModelEntr
|
||||
return Ok(chosen);
|
||||
}
|
||||
} else {
|
||||
eprintln!(
|
||||
crate::ui::warn(format!(
|
||||
"Invalid base selection. Please enter a number from 1-{} or a base name.",
|
||||
bases.len()
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -591,8 +585,8 @@ fn download_one_model(client: &Client, models_dir: &Path, entry: &ModelEntry) ->
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
qlog!(
|
||||
"Warning: failed to hash existing {}: {}. Will re-download to ensure correctness.",
|
||||
wlog!(
|
||||
"Failed to hash existing {}: {}. Will re-download to ensure correctness.",
|
||||
final_path.display(),
|
||||
e
|
||||
);
|
||||
@@ -618,8 +612,8 @@ fn download_one_model(client: &Client, models_dir: &Path, entry: &ModelEntry) ->
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
qlog!(
|
||||
"Warning: failed to stat existing {}: {}. Will re-download to ensure correctness.",
|
||||
wlog!(
|
||||
"Failed to stat existing {}: {}. Will re-download to ensure correctness.",
|
||||
final_path.display(),
|
||||
e
|
||||
);
|
||||
@@ -723,8 +717,8 @@ fn download_one_model(client: &Client, models_dir: &Path, entry: &ModelEntry) ->
|
||||
));
|
||||
}
|
||||
} else {
|
||||
qlog!(
|
||||
"Warning: no SHA-256 available for {}. Skipping verification.",
|
||||
wlog!(
|
||||
"No SHA-256 available for {}. Skipping verification.",
|
||||
entry.name
|
||||
);
|
||||
}
|
||||
@@ -826,7 +820,7 @@ pub fn update_local_models() -> Result<()> {
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
qlog!("Warning: failed hashing {}: {}. Re-downloading.", fname, e);
|
||||
wlog!("Failed hashing {}: {}. Re-downloading.", fname, e);
|
||||
}
|
||||
}
|
||||
download_one_model(&client, models_dir, remote)?;
|
||||
@@ -839,7 +833,7 @@ pub fn update_local_models() -> Result<()> {
|
||||
download_one_model(&client, models_dir, remote)?;
|
||||
}
|
||||
Err(e) => {
|
||||
qlog!("Warning: stat failed for {}: {}. Updating...", fname, e);
|
||||
wlog!("Stat failed for {}: {}. Updating...", fname, e);
|
||||
download_one_model(&client, models_dir, remote)?;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user