use anyhow::{Context, Result};
use serde::Deserialize;
use std::{
fs,
path::{Path, PathBuf},
};
const DEFAULT_CONFIG_PATH: &str = "vtt.config.json";
const DEFAULT_CHUNK_DURATION_SECS: usize = 5;
const DEFAULT_MODEL: &str = "whisper-1";
const DEFAULT_ENDPOINT: &str = "https://api.openai.com/v1/audio/transcriptions";
#[derive(Debug, Deserialize, Clone)]
#[serde(default)]
pub struct Config {
pub chunk_duration_secs: usize,
pub model: String,
pub endpoint: String,
pub out_file: Option<PathBuf>,
pub on_device: Option<OnDeviceConfig>,
}
impl Default for Config {
fn default() -> Self {
Self {
chunk_duration_secs: DEFAULT_CHUNK_DURATION_SECS,
model: DEFAULT_MODEL.to_string(),
endpoint: DEFAULT_ENDPOINT.to_string(),
out_file: None,
on_device: None,
}
}
}
impl Config {
pub fn from_file(path: impl AsRef<Path>) -> Result<Self> {
let path = path.as_ref();
let contents = fs::read_to_string(path)
.with_context(|| format!("reading config from {}", path.display()))?;
serde_json::from_str(&contents)
.with_context(|| format!("parsing config from {}", path.display()))
}
pub fn load_or_default() -> Result<(Self, Option<PathBuf>)> {
let default_path = PathBuf::from(DEFAULT_CONFIG_PATH);
if default_path.exists() {
let config = Self::from_file(&default_path)?;
Ok((config, Some(default_path)))
} else {
Ok((Self::default(), None))
}
}
pub fn resolve_out_path(&self, source: Option<&Path>) -> Option<PathBuf> {
self.out_file.as_ref().map(|path| {
if path.is_absolute() {
path.clone()
} else if let Some(source) = source {
source
.parent()
.map(Path::to_path_buf)
.unwrap_or_else(|| PathBuf::from("."))
.join(path)
} else {
path.clone()
}
})
}
pub fn uses_on_device(&self) -> bool {
self.on_device
.as_ref()
.map(|cfg| cfg.enabled)
.unwrap_or(false)
}
pub fn on_device_config(&self) -> Option<&OnDeviceConfig> {
self.on_device.as_ref().filter(|cfg| cfg.enabled)
}
}
#[derive(Debug, Deserialize, Clone)]
#[serde(default)]
pub struct OnDeviceConfig {
pub enabled: bool,
pub cpu: bool,
pub model: String,
pub model_id: Option<String>,
pub revision: Option<String>,
pub quantized: bool,
pub seed: u64,
pub language: Option<String>,
pub task: Option<String>,
pub timestamps: bool,
pub verbose: bool,
pub audio_device: Option<String>,
}
impl Default for OnDeviceConfig {
fn default() -> Self {
Self {
enabled: false,
cpu: true,
model: "tiny.en".to_string(),
model_id: None,
revision: None,
quantized: false,
seed: 299_792_458,
language: None,
task: None,
timestamps: false,
verbose: false,
audio_device: None,
}
}
}