init
This commit is contained in:
179
src/main.rs
Normal file
179
src/main.rs
Normal file
@@ -0,0 +1,179 @@
|
||||
use std::process::exit;
|
||||
|
||||
static PID_FILE: &str = "/tmp/ffmpeg_audio_recording.pid";
|
||||
static TEMP_FILE: &str = "/tmp/ffmpeg_audio_recording.ogg";
|
||||
static API_ENDPOINT: &str = "http://127.0.0.1:5000/v1/audio/transcriptions";
|
||||
static AUTH_TOKEN: &str = "woshimima";
|
||||
|
||||
use ctrlc;
|
||||
use nix::sys::signal;
|
||||
use nix::sys::signal::Signal;
|
||||
use nix::unistd::Pid;
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
fn main() {
|
||||
// 如果已经在录制音频,停止录制
|
||||
match read_pid() {
|
||||
Some(_) => {
|
||||
kill_and_delete_pid();
|
||||
send_notification("Recording stopped", "Recording audio stopped.");
|
||||
exit(0)
|
||||
}
|
||||
None => {
|
||||
set_pid();
|
||||
}
|
||||
}
|
||||
|
||||
// 开始录制音频
|
||||
send_notification("Recording started", "Recording audio...");
|
||||
start_recording();
|
||||
let text = upload_audio();
|
||||
set_clipboard_content(&text);
|
||||
send_notification("Recording finished", text.as_str());
|
||||
}
|
||||
|
||||
fn upload_audio() -> String {
|
||||
use reqwest::blocking::Client;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
let form = reqwest::blocking::multipart::Form::new()
|
||||
.file("file", TEMP_FILE).unwrap()
|
||||
.text("response_format", "text")
|
||||
.text("prompt", get_clipboard_content())
|
||||
.text("model", "whisper-1");
|
||||
|
||||
let resp = client
|
||||
.post(API_ENDPOINT)
|
||||
.header("Authorization", format!("Bearer {}", AUTH_TOKEN))
|
||||
.multipart(form)
|
||||
.send()
|
||||
.unwrap();
|
||||
|
||||
let text = resp.text().unwrap();
|
||||
text
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn start_recording() {
|
||||
let mut ffmpeg = std::process::Command::new("ffmpeg")
|
||||
.arg("-f")
|
||||
.arg("pulse")
|
||||
.arg("-i")
|
||||
.arg("default")
|
||||
.arg("-c:a")
|
||||
.arg("flac")
|
||||
.arg("-ac")
|
||||
.arg("1")
|
||||
.arg("-f")
|
||||
.arg("ogg")
|
||||
.arg("-y")
|
||||
.arg(TEMP_FILE)
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
// 注册监听 Ctrl-C 信号
|
||||
let (tx, rx) = channel();
|
||||
ctrlc::set_handler(move || tx.send(()).expect("Could not send signal on channel."))
|
||||
.expect("Error setting Ctrl-C handler");
|
||||
|
||||
// 等待 Ctrl-C 信号
|
||||
println!("Waiting for Ctrl-C...");
|
||||
rx.recv().expect("Could not receive from channel.");
|
||||
println!("Got it! killing ffmpeg)");
|
||||
signal::kill(Pid::from_raw(ffmpeg.id() as i32), Signal::SIGINT).unwrap();
|
||||
|
||||
// 等待 ffmpeg 退出
|
||||
ffmpeg.wait().unwrap();
|
||||
|
||||
println!("ffmpeg exited");
|
||||
|
||||
}
|
||||
|
||||
fn read_pid() -> Option<String> {
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
let mut file = match File::open(PID_FILE) {
|
||||
Ok(ctx) => ctx,
|
||||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let mut pid = String::new();
|
||||
|
||||
match file.read_to_string(&mut pid) {
|
||||
Ok(_) => Some(pid),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn kill_and_delete_pid() {
|
||||
use std::fs;
|
||||
let pid = read_pid().unwrap();
|
||||
let pid = pid.trim();
|
||||
let pid = pid.parse::<i32>().unwrap();
|
||||
match signal::kill(Pid::from_raw(pid), Signal::SIGINT) {
|
||||
Ok(_) => {},
|
||||
Err(_) => {
|
||||
send_notification("Error to kill", "Error to kill pid, cleaning");
|
||||
},
|
||||
}
|
||||
fs::remove_file(PID_FILE).unwrap();
|
||||
}
|
||||
|
||||
fn set_pid() {
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
let mut file = File::create(PID_FILE).unwrap();
|
||||
file.write_all(format!("{}", std::process::id()).as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn send_notification(title: &str, content: &str) {
|
||||
let result = notify_rust::Notification::new()
|
||||
.summary(title)
|
||||
.body(content)
|
||||
.timeout(std::time::Duration::from_secs(2))
|
||||
.show();
|
||||
|
||||
match result {
|
||||
Ok(_) => {}
|
||||
Err(error) => {
|
||||
eprintln!("Error sending notification: {}", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_clipboard_content() -> String {
|
||||
use clipboard::ClipboardContext;
|
||||
use clipboard::ClipboardProvider;
|
||||
|
||||
match ClipboardContext::new().and_then(|mut ctx| ctx.get_contents()) {
|
||||
Ok(content) => content,
|
||||
Err(error) => {
|
||||
send_notification(
|
||||
"Error getting clipboard content",
|
||||
&format!("Error: {}", error),
|
||||
);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_clipboard_content(content: &str) {
|
||||
use clipboard::ClipboardContext;
|
||||
use clipboard::ClipboardProvider;
|
||||
|
||||
match ClipboardContext::new().and_then(|mut ctx| ctx.set_contents(content.to_owned())) {
|
||||
Ok(_) => {}
|
||||
Err(error) => {
|
||||
send_notification(
|
||||
"Error setting clipboard content",
|
||||
&format!("Error: {}", error),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user