Rusty-slicer/src-tauri/src/main.rs

185 lines
7.0 KiB
Rust

// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use native_dialog::FileDialog;
use tauri::{Manager, PhysicalSize, Size};
use std::{io::{Error, ErrorKind}, path::PathBuf, sync::Mutex};
#[macro_use]
extern crate lazy_static;
// define file and folder path variable, don't know if it's the right way of doing it
lazy_static! {
static ref FILE_PATH: Mutex<String> = Mutex::new("".to_string());
static ref FOLDER_PATH: Mutex<String> = Mutex::new("".to_string());
}
#[derive(Clone, serde::Serialize)]
struct Payload {
message: String,
}
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
#[tauri::command]
async fn select_file_button(app: tauri::AppHandle) {
FILE_PATH.lock().unwrap().replace_range(.., &choose_file());
println!("{}",FILE_PATH.lock().unwrap());
let _ = app.emit_all("file_path_changed", Payload { message: FILE_PATH.lock().unwrap().to_string() });
}
#[tauri::command]
async fn select_folder_button(app: tauri::AppHandle) {
FOLDER_PATH.lock().unwrap().replace_range(.., &choose_folder());
println!("{}",FOLDER_PATH.lock().unwrap());
let _ = app.emit_all("folder_path_changed", Payload { message: FOLDER_PATH.lock().unwrap().to_string() });
}
#[tauri::command]
async fn slice_button(app: tauri::AppHandle, chapter: String, fileformat: String){
// Try to format the chapters and panic if it was not able to
let formated_chapters = match format_chapter(&chapter) {
Ok(res) => res,
Err(error) => panic!("Problem slicing chapter: {:?}", error),
};
let time_codes: Vec<String> = formated_chapters.0;
let title_names: Vec<String> = formated_chapters.1;
println!("time codes: \n{:?}\ntitle names: \n{:?}", time_codes, title_names);
// create folder if it does not exist
/*match fs::create_dir_all(FOLDER_PATH.lock().unwrap().to_owned()) {
Ok(res) => res,
Err(error) => panic!("Problem creating directory : {:?}", error),
};*/
// create the progress window
let _about_window = tauri::WindowBuilder::new(
&app,
"progress", /* the unique window label */
tauri::WindowUrl::App("progress.html".into())
).build().expect("failed to create progress window");
_about_window.set_title("Slicing progress").unwrap();
_about_window.set_size(Size::Physical(PhysicalSize { width: 400, height: 100 })).unwrap();
for i in 0..time_codes.len(){
let args: Vec<String>;
// create the path to the output file
let mut output_file: PathBuf = PathBuf::from(&FOLDER_PATH.lock().unwrap().to_owned());
output_file.push(format!("{:02} - {}", i+1, title_names[i]));
output_file.set_extension(&fileformat);
if i+1<time_codes.len() {
args = vec!["-i".to_owned(),
FILE_PATH.lock().unwrap().to_owned(),
"-ss".to_owned(),
time_codes[i].to_owned(),
"-to".to_owned(),
time_codes[i+1].to_owned(),
"-vn".to_owned(), // no video
//format!("{:?}", output_file),
output_file.display().to_string()];
}else { // case for the last song
args = vec!["-i".to_owned(),
FILE_PATH.lock().unwrap().to_owned(),
"-ss".to_owned(),
time_codes[i].to_owned(),
"-vn".to_owned(), // no video
//format!("{:?}", output_file),
output_file.display().to_string()];
}
// launch the final ffmpeg command
launch_ffmpeg(app.clone(), args);
// update progress bar on frontend
app.emit_all("progress_state_changed", Payload { message: format!("{}", (i+1)*100/time_codes.len()) }).unwrap();
}
}
#[tauri::command]
async fn about_button(handle: tauri::AppHandle) {
let _about_window = tauri::WindowBuilder::new(
&handle,
"about", /* the unique window label */
tauri::WindowUrl::App("about.html".into())
).build().expect("failed to create about window");
_about_window.set_title("About Rusty Slicer").unwrap();
_about_window.set_size(Size::Physical(PhysicalSize { width: 400, height: 600 })).unwrap();
}
#[tauri::command]
fn debug_call(message: &str){
println!("[DBG] {}", message);
}
/// Separate time codes from title and return it to a tuple of vector of string
/// # Example
/// ```
/// let formated_chapters = match format_chapter(chapter) {
/// Ok(res) => res,
/// Err(error) => panic!("Problem slicing chapter: {:?}", error),
/// };
/// let time_codes: Vec<String> = formated_chapters.0;
/// let title_names: Vec<String> = formated_chapters.1;
/// ```
fn format_chapter(chapter: &str) -> Result<(Vec<String>, Vec<String>), Error>{
let lines: Vec<&str> = chapter.split("\n").collect();
let mut time_code: Vec<String> = vec![];
let mut title_names: Vec<String> = vec![];
for l in lines.iter(){
if l.is_empty() { break; }
let splited_line = l.split(" - ").collect::<Vec<&str>>();
if splited_line.len()<2 || splited_line[1] == "" { // To avoid blank title
return Err(Error::new(ErrorKind::Other, "No title associated with the time code"));
}
time_code.push(splited_line[0].to_owned());
title_names.push(splited_line[1..].join(" - "));
}
Ok((time_code, title_names))
}
// prompt user file chooser using native_dialogue crate
fn choose_file() -> String{
println!("Let's choose a file !");
let path = FileDialog::new()
.show_open_single_file()
.unwrap();
format!("{:?}", path).replace("Some(\"", "").replace("\")", "") // turn the FileDialog into a string and remove Some("")
}
fn choose_folder() -> String{
println!("Let's choose a folder !");
let path = FileDialog::new()
.show_open_single_dir()
.unwrap();
format!("{:?}", path).replace("Some(\"", "").replace("\")", "") // turn the FileDialog into a string
}
fn launch_ffmpeg(app: tauri::AppHandle, args: Vec<String>) {
// get the path from the bundled binary
let resource_path = app.path_resolver()
.resolve_resource("resources/ffmpeg-linux")
.expect("failed to resolve resource");
println!("using ffmpeg binary : {}\nwith the following argument : {:?}", resource_path.display(), args);
// launch the command
let output = std::process::Command::new(resource_path.as_os_str())
.args(args)
.output()
.expect("failed to execute process");
// print the output of the ffmpeg command
println!("status: {}", output.status);
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
}
fn main() {
// generate the tauri app
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![select_file_button, select_folder_button, debug_call, slice_button, about_button])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}