-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
221 additions
and
106 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
|
||
|
||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(()) | ||
} |