Skip to content

Commit

Permalink
add whisper-cli
Browse files Browse the repository at this point in the history
  • Loading branch information
yk0n9 committed Jun 25, 2023
1 parent c80d566 commit aecc9d8
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 106 deletions.
8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ edition = "2021"

[dependencies]
rfd = "0.11"
whisper-rs = "0.8"
hound = "3"
tokio = { version = "1", features = ["rt-multi-thread"] }
tokio = { version = "1", features = ["rt-multi-thread"] }
eframe = "0.22"
egui = "0.22"
font-kit = "0.11"
whisper_cli = "0.1"
22 changes: 22 additions & 0 deletions src/font.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use font_kit::source::SystemSource;

pub fn load_fonts(ctx: &egui::Context) {
let sys = SystemSource::new();
let font_name = format!("SimHei");
let font = sys.select_family_by_name(&font_name).unwrap().fonts()[0]
.load()
.unwrap()
.copy_font_data()
.unwrap()
.to_vec();
let mut font_defs = egui::FontDefinitions::default();
font_defs
.font_data
.insert(font_name.to_string(), egui::FontData::from_owned(font));
font_defs
.families
.get_mut(&egui::FontFamily::Proportional)
.unwrap()
.insert(0, font_name);
ctx.set_fonts(font_defs);
}
121 changes: 18 additions & 103 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,109 +1,24 @@
use std::{env, fs, io, thread};
use std::io::Error;
use std::io::ErrorKind;
use std::path::PathBuf;
use std::process::Command;
use std::sync::{Arc, Mutex};
use tokio::runtime::Runtime;
use eframe::NativeOptions;
use egui::Vec2;
use crate::ui::Conv;

#[derive(Debug, Clone, Default)]
struct Files {
audio: Option<PathBuf>,
image: Option<PathBuf>,
subtitle: Option<PathBuf>,
}
mod ui;
mod font;
mod utils;

#[derive(Debug, Clone)]
struct Conv {
rt: Arc<Runtime>,
files: Arc<Mutex<Files>>,
fn main() {
run();
}

impl Conv {
pub fn new() -> Self {
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
Self {
rt: Arc::new(rt),
files: Arc::new(Mutex::new(Files::default())),
}
}

fn open_audio(&self) {
let mut audio = self.files.clone();
// thread::spawn(move || {
if let Some(path) = rfd::FileDialog::new()
.pick_file() {
audio.lock().unwrap().audio = Some(path);
}
// });
}

fn open_image(&self) {
let mut image = self.files.clone();
thread::spawn(move || {
if let Some(path) = rfd::FileDialog::new()
.pick_file() {
image.lock().unwrap().image = Some(path);
}
});
}

fn open_subtitle(&self) {
let mut subtitle = self.files.clone();
thread::spawn(move || {
if let Some(path) = rfd::FileDialog::new()
.pick_file() {
subtitle.lock().unwrap().subtitle = Some(path);
}
});
}
}

fn main() -> io::Result<()> {
let current = env::current_dir()?;
let mut con = Conv::new();
con.rt.spawn(async move {
println!("OK");
});

con.open_audio();

let output = current.join("output.mp4");
if output.exists() {
fs::remove_file(output).unwrap_or(());
}
let ffmpeg = current.join("ffmpeg");
let mut cmd = Command::new(&ffmpeg);

if let Some(ref image) = con.files.lock().unwrap().image {
cmd
.arg("-loop")
.arg("1")
.arg("-i")
.arg(image);
}

if let Some(ref audio) = con.files.lock().unwrap().audio {
cmd
.arg("-i")
.arg(audio);
} else {
return Err(Error::from(ErrorKind::Other));
}

cmd
.arg("-shortest")
.arg("output.mp4");

let exit_status = cmd
.spawn()?
.wait()?;

return match exit_status.success() {
true => Ok(()),
false => Err(Error::from(ErrorKind::Unsupported)),
fn run() {
let option = NativeOptions {
icon_data: None,
initial_window_size: Some(Vec2::new(400.0, 300.0)),
follow_system_theme: true,
centered: true,
resizable: false,
..NativeOptions::default()
};
eframe::run_native("Conv", option, Box::new(|cc| Box::new(Conv::new(cc))))
.unwrap();
}
131 changes: 131 additions & 0 deletions src/ui.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::thread;
use eframe::{CreationContext, Frame};
use egui::{Context, FontId};
use egui::FontFamily::Proportional;
use egui::TextStyle::*;
use tokio::runtime::Runtime;
use crate::font::load_fonts;

#[derive(Debug, Clone)]
pub struct Conv {
rt: Arc<Runtime>,
files: Arc<Mutex<Files>>,
}

#[derive(Debug, Clone, Default)]
struct Files {
audio: Option<PathBuf>,
image: Option<PathBuf>,
subtitle: Option<PathBuf>,
}

impl Conv {
pub fn new(cc: &CreationContext) -> Self {
load_fonts(&cc.egui_ctx);
let mut style = (*cc.egui_ctx.style()).clone();
style.text_styles = [
(Heading, FontId::new(30.0, Proportional)),
(Name("Heading2".into()), FontId::new(25.0, Proportional)),
(Name("Context".into()), FontId::new(23.0, Proportional)),
(Body, FontId::new(18.0, Proportional)),
(Monospace, FontId::new(14.0, Proportional)),
(Button, FontId::new(14.0, Proportional)),
(Small, FontId::new(10.0, Proportional)),
]
.into();
cc.egui_ctx.set_style(style);

let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
Self {
rt: Arc::new(rt),
files: Default::default(),
}
}

fn open_audio(files: Arc<Mutex<Files>>) {
thread::spawn(move || {
if let Some(path) = rfd::FileDialog::new()
.add_filter("Audio File", &["mp3", "wav"])
.pick_file() {
files.lock().unwrap().audio = Some(path);
}
});
}

fn open_image(files: Arc<Mutex<Files>>) {
thread::spawn(move || {
if let Some(path) = rfd::FileDialog::new()
.add_filter("Image File", &["jpg", "png"])
.pick_file() {
files.lock().unwrap().image = Some(path);
}
});
}

fn open_subtitle(files: Arc<Mutex<Files>>) {
thread::spawn(move || {
if let Some(path) = rfd::FileDialog::new()
.add_filter("Subtitle File", &["srt", "lrc", "vtt"])
.pick_file() {
files.lock().unwrap().subtitle = Some(path);
}
});
}
}

impl eframe::App for Conv {
fn update(&mut self, ctx: &Context, _: &mut Frame) {
ctx.request_repaint();

egui::CentralPanel::default().show(ctx, |ui| {
ui.vertical_centered(|ui| ui.heading("Conv"));

ui.separator();

ui.horizontal(|ui| {
ui.label("选择音频");
if ui.button("打开").clicked() {
Conv::open_audio(self.files.clone());
}
});
ui.label(format!("音频: {}", if let Some(ref p) = self.files.lock().unwrap().audio {
p.to_str().unwrap()
} else {
"None"
}));

ui.horizontal(|ui| {
ui.label("选择背景图片");
if ui.button("打开").clicked() {
Conv::open_image(self.files.clone());
}
});
ui.label(format!("背景图片: {}", if let Some(ref p) = self.files.lock().unwrap().image {
p.to_str().unwrap()
} else {
"None"
}));

ui.horizontal(|ui| {
ui.label("选择字幕");
if ui.button("打开").clicked() {
Conv::open_subtitle(self.files.clone());
}
});
ui.label(format!("字幕: {}", if let Some(ref p) = self.files.lock().unwrap().subtitle {
p.to_str().unwrap()
} else {
"None"
}));

ui.separator();


});
}
}
45 changes: 45 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use std::{env, fs::File, io::{BufRead, BufReader, Write}};
use std::path::{Path, PathBuf};

fn is_number(s: &str) -> bool {
s.chars().all(|c| c.is_digit(10))
}

// 将srt时间格式转换为lrc时间格式
fn format_time(time: &str) -> String {
let msec = &time[9..12];
let sec = &time[6..8];
let min = &time[3..5];

format!("[{}:{}.{:.2}]", min, sec, msec)
}

fn srt2lrc(path: &str) -> Result<(), Box<dyn std::error::Error>> {
let output_filename = Path::new(path).with_extension("lrc");

let file = File::open(path)?;
let reader = BufReader::new(file);
let lines = reader.lines().filter_map(|l| l.ok());

let lrc_content = lines.fold(String::new(), |lrc, line| {
if is_number(&line) {
// 行号
lrc
} else if line.is_empty() {
// 空行
lrc
} else {
// 时间和文本内容
let time = line.split_whitespace().nth(0).unwrap();
let text = line.splitn(2, char::is_whitespace).nth(1).unwrap();

let lrc_time = format_time(time);
format!("{}{}\n", lrc, lrc_time + " " + text)
}
});

let mut output_file = File::create(output_filename)?;
output_file.write_all(lrc_content.as_bytes())?;

Ok(())
}

0 comments on commit aecc9d8

Please sign in to comment.