use std::process::Command

This commit is contained in:
ochibani 2025-01-14 20:42:53 +02:00
parent 6938ed4b2e
commit 0aaaa56348
No known key found for this signature in database
GPG Key ID: 2C6B61CE0C704ED4
10 changed files with 769 additions and 916 deletions

1174
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -4,14 +4,14 @@ version = "0.1.0"
edition = "2021"
[features]
cmd = []
cmd = ["ffmpeg-sidecar"]
gtk = ["adw", "chrono", "glib", "subprocess"]
[dependencies]
adw = { version = "0.2.1", package = "libadwaita", features = ["gtk_v4_6"], optional = true }
anyhow = "1.0.86"
chrono = { version = "0.4.19", optional = true }
ffmpeg-sidecar = "1.1.0"
ffmpeg-sidecar = { version = "2.0.5", optional = true }
glib = { version = "0.10.3", optional = true }
open = "5.1.4"
subprocess = {version = "0.2.6", optional = true }

View File

@ -2,21 +2,32 @@
use adw::gtk::{CheckButton, ComboBoxText, Entry, FileChooserNative, SpinButton};
#[cfg(feature = "gtk")]
use adw::gtk::prelude::*;
use anyhow::{anyhow, Error, Result};
use anyhow::{anyhow, Result};
#[cfg(feature = "cmd")]
use anyhow::Error;
#[cfg(feature = "gtk")]
use chrono::Utc;
#[cfg(feature = "cmd")]
use ffmpeg_sidecar::child::FfmpegChild;
#[cfg(feature = "cmd")]
use ffmpeg_sidecar::command::FfmpegCommand;
use tempfile;
use std::{cell::RefCell, time::Instant};
use std::cell::RefCell;
use std::path::Path;
#[cfg(feature = "gtk")]
use std::path::PathBuf;
use std::process::Command;
#[cfg(feature = "gtk")]
use std::process::Child;
use std::rc::Rc;
use std::thread::sleep;
use std::time::Duration;
#[cfg(feature = "cmd")]
use std::time::Instant;
use crate::utils::{is_input_audio_record, is_output_audio_record, is_valide, is_video_record, RecordMode};
use crate::utils::{is_input_audio_record, is_output_audio_record, is_video_record, RecordMode};
#[cfg(feature = "cmd")]
use crate::utils::is_valide;
#[cfg(feature = "cmd")]
#[derive(Clone)]
@ -53,9 +64,9 @@ pub struct Ffmpeg {
pub temp_video_filename: String,
pub saved_filename: String,
pub height: Option<u16>,
pub input_audio_process: Option<Rc<RefCell<FfmpegChild>>>,
pub output_audio_process: Option<Rc<RefCell<FfmpegChild>>>,
pub video_process: Option<Rc<RefCell<FfmpegChild>>>,
pub input_audio_process: Option<Rc<RefCell<Child>>>,
pub output_audio_process: Option<Rc<RefCell<Child>>>,
pub video_process: Option<Rc<RefCell<Child>>>,
pub audio_record_bitrate: SpinButton,
pub record_delay: SpinButton,
pub record_frames: SpinButton,
@ -330,7 +341,7 @@ impl Ffmpeg {
(|| anyhow!("Unable to get height value"))?);
let ffmpeg_convert = format!("ffmpeg -i file:{} -filter_complex '{}' \
-loop 0 {} -y", &self.temp_video_filename,filter,&self.filename);
std::process::Command::new("sh").arg("-c").arg(&ffmpeg_convert).output()?;
Command::new("sh").arg("-c").arg(&ffmpeg_convert).output()?;
}
} else if is_input_audio_record(&self.temp_input_audio_filename) {
// Validate audio file integrity
@ -394,7 +405,7 @@ impl Ffmpeg {
// Kill process
pub fn kill(&mut self) -> Result<()> {
if self.video_process.is_some() {
std::process::Command::new("kill")
Command::new("kill")
.arg(format!(
"{}",
self.video_process
@ -405,7 +416,7 @@ impl Ffmpeg {
)).output()?;
}
if self.input_audio_process.is_some() {
std::process::Command::new("kill")
Command::new("kill")
.arg(format!(
"{}",
self.input_audio_process
@ -416,7 +427,7 @@ impl Ffmpeg {
)).output()?;
}
if self.output_audio_process.is_some() {
std::process::Command::new("kill")
Command::new("kill")
.arg(format!(
"{}",
self.output_audio_process
@ -467,7 +478,7 @@ impl Ffmpeg {
x,
y
);
let mut ffmpeg_command = FfmpegCommand::new();
let mut ffmpeg_command = Command::new("ffmpeg");
let format = "x11grab";
self.height = Some(height);
let filename = self.saved_filename.clone();
@ -500,14 +511,14 @@ impl Ffmpeg {
RecordMode::Screen => {
let width = width as f32 * 0.95;
let height = height as f32 * 0.95;
ffmpeg_command.size(width as u32, height as u32);
ffmpeg_command.args(["-s", &width.to_string(), &height.to_string()]);
},
_=> {
ffmpeg_command.size(width.into(), height.into());
ffmpeg_command.args(["-s", &width.to_string(), &height.to_string()]);
}
}
} else {
ffmpeg_command.size(width.into(), height.into());
ffmpeg_command.args(["-s", &width.to_string(), &height.to_string()]);
}
// Show grabbed area
@ -533,8 +544,8 @@ impl Ffmpeg {
}
// Video format && input
ffmpeg_command.format(format)
.input(display);
ffmpeg_command.args(["-f", format])
.args(["-i", &display]);
// Disable bitrate if value is zero
if self.video_record_bitrate.value() as u16 > 0 {
@ -572,7 +583,7 @@ impl Ffmpeg {
}
},
]);
ffmpeg_command.overwrite();
ffmpeg_command.arg("-y");
// Sleep for delay
sleep(Duration::from_secs(self.record_delay.value() as u64));
@ -585,13 +596,23 @@ impl Ffmpeg {
// Stop video recording
pub fn stop_video(&mut self) -> Result<()> {
// Quit the process to stop recording
// Kill the process to stop recording
if self.video_process.is_some() {
Command::new("kill")
.arg(format!(
"{}",
self.video_process
.clone()
.ok_or_else(|| anyhow!("Failed to get video_process ID."))?
.borrow_mut().id()
))
.output()?;
self.video_process
.clone()
.ok_or_else(|| anyhow!("Not exiting the video recording process successfully."))?
.borrow_mut()
.quit()?;
.wait()?;
}
Ok(())
}
@ -604,12 +625,12 @@ impl Ffmpeg {
.keep()?;
self.temp_input_audio_filename = Path::new(&input_audio_tempfile.1).to_string_lossy()
.to_string();
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("pulse")
.input(&self.audio_input_id.active_id()
let mut ffmpeg_command = Command::new("ffmpeg");
ffmpeg_command.args(["-f", "pulse"])
.args(["-i", &self.audio_input_id.active_id()
.ok_or_else(|| anyhow!("Failed to get audio input ID."))?
)
.format("ogg");
])
.args(["-f", "ogg"]);
// Disable bitrate if value is zero
if self.audio_record_bitrate.value() as u16 > 0 {
ffmpeg_command.args([
@ -620,7 +641,7 @@ impl Ffmpeg {
// Remove metadate
ffmpeg_command.args(["-map_metadata", "-1"]);
ffmpeg_command.arg(&self.temp_input_audio_filename);
ffmpeg_command.overwrite();
ffmpeg_command.arg("-y");
// Sleep for delay
if !is_video_record(&self.temp_video_filename) {
@ -634,13 +655,23 @@ impl Ffmpeg {
// Stop audio input recording
pub fn stop_input_audio(&mut self) -> Result<()> {
// Quit the process to stop recording
// Kill the process to stop recording
if self.input_audio_process.is_some() {
Command::new("kill")
.arg(format!(
"{}",
self.input_audio_process
.clone()
.ok_or_else(|| anyhow!("Failed to get input_audio_process ID."))?
.borrow_mut().id()
))
.output()?;
self.input_audio_process
.clone()
.ok_or_else(|| anyhow!("Not exiting the input audio recording process successfully."))?
.borrow_mut()
.quit()?;
.wait()?;
}
Ok(())
}
@ -653,14 +684,14 @@ impl Ffmpeg {
.keep()?;
self.temp_output_audio_filename = Path::new(&output_audio_tempfile.1).to_string_lossy()
.to_string();
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("pulse")
.input(&self.audio_output_id)
.format("ogg");
let mut ffmpeg_command = Command::new("ffmpeg");
ffmpeg_command.args(["-f", "pulse"])
.args(["-i", &self.audio_output_id])
.args(["-f", "ogg"]);
// Remove metadate
ffmpeg_command.args(["-map_metadata", "-1"]);
ffmpeg_command.arg(&self.temp_output_audio_filename);
ffmpeg_command.overwrite();
ffmpeg_command.arg("-y");
// Sleep for delay
if !is_video_record(&self.temp_video_filename) && !is_input_audio_record(&self.temp_input_audio_filename) {
@ -674,13 +705,23 @@ impl Ffmpeg {
// Stop audio output recording
pub fn stop_output_audio(&mut self) -> Result<()> {
// Quit the process to stop recording
// Kill the process to stop recording
if self.output_audio_process.is_some() {
Command::new("kill")
.arg(format!(
"{}",
self.output_audio_process
.clone()
.ok_or_else(|| anyhow!("Failed to get output_audio_process ID."))?
.borrow_mut().id()
))
.output()?;
self.output_audio_process
.clone()
.ok_or_else(|| anyhow!("Not exiting the output audio recording process successfully."))?
.borrow_mut()
.quit()?;
.wait()?;
}
Ok(())
}
@ -689,44 +730,29 @@ impl Ffmpeg {
pub fn merge(&mut self) -> Result<()> {
if is_video_record(&self.temp_video_filename) {
if self.output != "gif" {
// Validate video file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
loop {
if is_valide(&self.temp_video_filename)? {
break;
} else if Instant::now().duration_since(start_time) >= duration {
return Err(Error::msg("Unable to validate tmp video file."));
}
}
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.input(&self.temp_video_filename);
ffmpeg_command.format("ogg");
let mut ffmpeg_command = Command::new("ffmpeg");
ffmpeg_command.args(["-i", &self.temp_video_filename]);
ffmpeg_command.args(["-f", "ogg"]);
if is_input_audio_record(&self.temp_input_audio_filename) {
ffmpeg_command.input(&self.temp_input_audio_filename);
ffmpeg_command.args(["-i", &self.temp_input_audio_filename]);
}
if is_output_audio_record(&self.temp_output_audio_filename) {
ffmpeg_command.input(&self.temp_output_audio_filename);
ffmpeg_command.args(["-i", &self.temp_output_audio_filename]);
}
ffmpeg_command.args([
"-c:a",
"aac",
&self.saved_filename.clone()
]);
ffmpeg_command.overwrite()
.spawn()?
.wait()?;
} else {
// Validate video file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
loop {
if is_valide(&self.temp_video_filename)? {
break;
} else if Instant::now().duration_since(start_time) >= duration {
return Err(Error::msg("Unable to validate tmp video file."));
}
ffmpeg_command.arg("-y");
let program = ffmpeg_command.get_program().to_string_lossy().to_string();
let args: Vec<String> = ffmpeg_command.get_args().map(|arg| arg.to_string_lossy().into_owned()).collect();
let mut merge_command = program;
for arg in args {
merge_command.push_str(&format!(" {}", arg));
}
adw::glib::spawn_command_line_async(merge_command)?;
} else {
// Convert MP4 to GIF
let filter = format!("fps={},scale={}:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse",
self.record_frames.value() as u16,
@ -736,54 +762,44 @@ impl Ffmpeg {
-loop 0 {} -y", &self.temp_video_filename,filter,
&self.saved_filename
.clone());
std::process::Command::new("sh").arg("-c").arg(&ffmpeg_convert).output()?;
adw::glib::spawn_command_line_async(ffmpeg_convert)?;
}
} else if is_input_audio_record(&self.temp_input_audio_filename) {
// Validate audio file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
loop {
if is_valide(&self.temp_input_audio_filename)? {
break;
} else if Instant::now().duration_since(start_time) >= duration {
return Err(Error::msg("Unable to validate tmp video file."));
}
}
// If only audio is recording then convert it to chosen format
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("ogg");
ffmpeg_command.input(&self.temp_input_audio_filename);
let mut ffmpeg_command = Command::new("ffmpeg");
ffmpeg_command.args(["-f", "ogg"]);
ffmpeg_command.args(["-i", &self.temp_input_audio_filename]);
if is_output_audio_record(&self.temp_output_audio_filename) {
ffmpeg_command.input(&self.temp_output_audio_filename);
ffmpeg_command.args(["-i", &self.temp_output_audio_filename]);
}
ffmpeg_command.args([
"-c:a",
"aac",
&self.saved_filename
.clone()
]).overwrite()
.spawn()?
.wait()?;
} else {
// Validate audio file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
loop {
if is_valide(&self.temp_output_audio_filename)? {
break;
} else if Instant::now().duration_since(start_time) >= duration {
return Err(Error::msg("Unable to validate tmp video file."));
}
.clone(),
"-y"
]);
let program = ffmpeg_command.get_program().to_string_lossy().to_string();
let args: Vec<String> = ffmpeg_command.get_args().map(|arg| arg.to_string_lossy().into_owned()).collect();
let mut merge_command = program;
for arg in args {
merge_command.push_str(&format!(" {}", arg));
}
adw::glib::spawn_command_line_async(merge_command)?;
} else {
// If only output audio is recording then convert it to chosen format
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("ogg");
ffmpeg_command.input(&self.temp_output_audio_filename);
ffmpeg_command.arg(&self.saved_filename
.clone())
.overwrite()
.spawn()?
.wait()?;
let mut ffmpeg_command = Command::new("ffmpeg");
ffmpeg_command.args(["-f", "ogg"]);
ffmpeg_command.args(["-i", &self.temp_output_audio_filename]);
ffmpeg_command.arg(&self.saved_filename.clone());
ffmpeg_command.arg("-y");
let program = ffmpeg_command.get_program().to_string_lossy().to_string();
let args: Vec<String> = ffmpeg_command.get_args().map(|arg| arg.to_string_lossy().into_owned()).collect();
let mut merge_command = program;
for arg in args {
merge_command.push_str(&format!(" {}", arg));
}
adw::glib::spawn_command_line_async(merge_command)?;
}
Ok(())
}
@ -802,36 +818,33 @@ impl Ffmpeg {
// Kill process
pub fn kill(&mut self) -> Result<()> {
if self.video_process.is_some() {
std::process::Command::new("kill")
Command::new("kill")
.arg(format!(
"{}",
self.video_process
.clone()
.ok_or_else(|| anyhow!("Unable to kill the video recording process successfully."))?
.borrow_mut()
.as_inner().id()
.borrow_mut().id()
)).output()?;
}
if self.input_audio_process.is_some() {
std::process::Command::new("kill")
Command::new("kill")
.arg(format!(
"{}",
self.input_audio_process
.clone()
.ok_or_else(|| anyhow!("Unable to kill the intput audio recording process successfully."))?
.borrow_mut()
.as_inner().id()
.borrow_mut().id()
)).output()?;
}
if self.output_audio_process.is_some() {
std::process::Command::new("kill")
Command::new("kill")
.arg(format!(
"{}",
self.output_audio_process
.clone()
.ok_or_else(|| anyhow!("Unable to kill the output audio recording process successfully."))?
.borrow_mut()
.as_inner().id()
.borrow_mut().id()
)).output()?;
}
Ok(())

View File

@ -2,21 +2,32 @@
use adw::gtk::{CheckButton, ComboBoxText, Entry, FileChooserNative, SpinButton};
#[cfg(feature = "gtk")]
use adw::gtk::prelude::*;
use anyhow::{anyhow, Error, Result};
use anyhow::{anyhow, Result};
#[cfg(feature = "cmd")]
use anyhow::Error;
#[cfg(feature = "gtk")]
use chrono::Utc;
#[cfg(feature = "cmd")]
use ffmpeg_sidecar::child::FfmpegChild;
#[cfg(feature = "cmd")]
use ffmpeg_sidecar::command::FfmpegCommand;
use tempfile;
use std::{cell::RefCell, time::Instant};
use std::cell::RefCell;
use std::path::Path;
#[cfg(feature = "gtk")]
use std::path::PathBuf;
use std::process::Command;
#[cfg(feature = "gtk")]
use std::process::Child;
use std::rc::Rc;
use std::thread::sleep;
use std::time::Duration;
#[cfg(feature = "cmd")]
use std::time::Instant;
use crate::utils::{is_input_audio_record, is_output_audio_record, is_valide, is_video_record, RecordMode};
use crate::utils::{is_input_audio_record, is_output_audio_record, is_video_record, RecordMode};
#[cfg(feature = "cmd")]
use crate::utils::is_valide;
#[cfg(feature = "cmd")]
#[derive(Clone)]
@ -55,9 +66,9 @@ pub struct Ffmpeg {
pub window_title: String,
pub saved_filename: String,
pub height: Option<u16>,
pub input_audio_process: Option<Rc<RefCell<FfmpegChild>>>,
pub output_audio_process: Option<Rc<RefCell<FfmpegChild>>>,
pub video_process: Option<Rc<RefCell<FfmpegChild>>>,
pub input_audio_process: Option<Rc<RefCell<Child>>>,
pub output_audio_process: Option<Rc<RefCell<Child>>>,
pub video_process: Option<Rc<RefCell<Child>>>,
pub audio_record_bitrate: SpinButton,
pub record_delay: SpinButton,
pub record_frames: SpinButton,
@ -318,7 +329,7 @@ impl Ffmpeg {
(|| anyhow!("Unable to get height value"))?);
let ffmpeg_convert = format!("ffmpeg -i file:{} -filter_complex '{}' \
-loop 0 {} -y", &self.temp_video_filename,filter,&self.filename);
std::process::Command::new("sh").arg("-c").arg(&ffmpeg_convert).output()?;
Command::new("sh").arg("-c").arg(&ffmpeg_convert).output()?;
}
} else if is_input_audio_record(&self.temp_input_audio_filename) {
// Validate audio file integrity
@ -387,7 +398,7 @@ impl Ffmpeg {
.ok_or_else(|| anyhow!("Unable to kill the video recording process successfully."))?
.borrow_mut()
.as_inner().id();
std::process::Command::new("taskkill")
Command::new("taskkill")
.arg("/PID")
.arg(pid.to_string())
.arg("/F")
@ -399,7 +410,7 @@ impl Ffmpeg {
.ok_or_else(|| anyhow!("Unable to kill the input audio recording process successfully."))?
.borrow_mut()
.as_inner().id();
std::process::Command::new("taskkill")
Command::new("taskkill")
.arg("/PID")
.arg(pid.to_string())
.arg("/F")
@ -411,7 +422,7 @@ impl Ffmpeg {
.ok_or_else(|| anyhow!("Unable to kill the output audio recording process successfully."))?
.borrow_mut()
.as_inner().id();
std::process::Command::new("taskkill")
Command::new("taskkill")
.arg("/PID")
.arg(pid.to_string())
.arg("/F")
@ -454,7 +465,7 @@ impl Ffmpeg {
RecordMode::Screen => "desktop",
RecordMode::Window => &format!("title={}", &self.window_title),
};
let mut ffmpeg_command = FfmpegCommand::new();
let mut ffmpeg_command = Command::new("ffmpeg");
let format = "gdigrab";
let filename = self.saved_filename.clone();
self.output = Path::new(&filename).extension()
@ -480,7 +491,7 @@ impl Ffmpeg {
.to_string();
}
// Video format
ffmpeg_command.format(format);
ffmpeg_command.arg(format);
// Show grabbed area
if self.show_area.is_active() {
@ -501,14 +512,14 @@ impl Ffmpeg {
// Record video with specified width and hight
if let RecordMode::Area = mode {
ffmpeg_command.size(width.into(), height.into()).args([
ffmpeg_command.args(["-s", &width.to_string(), &height.to_string()]).args([
"-offset_x", &x.to_string(),
"-offset_y", &y.to_string()
]);
}
// input
ffmpeg_command.input(display);
ffmpeg_command.args(["-i", display]);
// Disable bitrate if value is zero
if self.video_record_bitrate.value() as u16 > 0 {
@ -545,7 +556,7 @@ impl Ffmpeg {
}
},
]);
ffmpeg_command.overwrite();
ffmpeg_command.arg("-y");
// Sleep for delay
sleep(Duration::from_secs(self.record_delay.value() as u64));
@ -558,13 +569,23 @@ impl Ffmpeg {
// Stop video recording
pub fn stop_video(&mut self) -> Result<()> {
// Quit the process to stop recording
// Kill the process to stop recording
if self.video_process.is_some() {
Command::new("kill")
.arg(format!(
"{}",
self.video_process
.clone()
.ok_or_else(|| anyhow!("Failed to get video_process ID."))?
.borrow_mut().id()
))
.output()?;
self.video_process
.clone()
.ok_or_else(|| anyhow!("Not exiting the video recording process successfully."))?
.borrow_mut()
.quit()?;
.wait()?;
}
Ok(())
}
@ -577,12 +598,12 @@ impl Ffmpeg {
.keep()?;
self.temp_input_audio_filename = Path::new(&input_audio_tempfile.1).to_string_lossy()
.to_string();
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("dshow")
.input(format!("audio=\"{}\"", &self.audio_input_id.active_text()
let mut ffmpeg_command = Command::new("ffmpeg");
ffmpeg_command.args(["-f", "dshow"])
.args(["-i", &format!("audio=\"{}\"", &self.audio_input_id.active_text()
.ok_or_else(|| anyhow!("Failed to get audio input source."))?)
)
.format("ogg");
])
.args(["-f", "ogg"]);
// Disable bitrate if value is zero
if self.audio_record_bitrate.value() as u16 > 0 {
ffmpeg_command.args([
@ -593,7 +614,7 @@ impl Ffmpeg {
// Remove metadate
ffmpeg_command.args(["-map_metadata", "-1"]);
ffmpeg_command.arg(&self.temp_input_audio_filename);
ffmpeg_command.overwrite();
ffmpeg_command.arg("-y");
// Sleep for delay
if !is_video_record(&self.temp_video_filename) {
@ -607,13 +628,23 @@ impl Ffmpeg {
// Stop audio input recording
pub fn stop_input_audio(&mut self) -> Result<()> {
// Quit the process to stop recording
// Kill the process to stop recording
if self.input_audio_process.is_some() {
Command::new("kill")
.arg(format!(
"{}",
self.input_audio_process
.clone()
.ok_or_else(|| anyhow!("Failed to get input_audio_process ID."))?
.borrow_mut().id()
))
.output()?;
self.input_audio_process
.clone()
.ok_or_else(|| anyhow!("Not exiting the input audio recording process successfully."))?
.borrow_mut()
.quit()?;
.wait()?;
}
Ok(())
}
@ -626,14 +657,14 @@ impl Ffmpeg {
.keep()?;
self.temp_output_audio_filename = Path::new(&output_audio_tempfile.1).to_string_lossy()
.to_string();
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("dshow")
.input(format!("audio=\"{}\"", &self.audio_output_id))
.format("ogg");
let mut ffmpeg_command = Command::new("ffmpeg");
ffmpeg_command.args(["-f", "dshow"])
.args(["-i", &format!("audio=\"{}\"", &self.audio_output_id)])
.args(["-f", "ogg"]);
// Remove metadate
ffmpeg_command.args(["-map_metadata", "-1"]);
ffmpeg_command.arg(&self.temp_output_audio_filename);
ffmpeg_command.overwrite();
ffmpeg_command.arg("-y");
// Sleep for delay
if !is_video_record(&self.temp_video_filename) && !is_input_audio_record(&self.temp_input_audio_filename) {
@ -647,13 +678,23 @@ impl Ffmpeg {
// Stop audio output recording
pub fn stop_output_audio(&mut self) -> Result<()> {
// Quit the process to stop recording
// Kill the process to stop recording
if self.output_audio_process.is_some() {
Command::new("kill")
.arg(format!(
"{}",
self.output_audio_process
.clone()
.ok_or_else(|| anyhow!("Failed to get output_audio_process ID."))?
.borrow_mut().id()
))
.output()?;
self.output_audio_process
.clone()
.ok_or_else(|| anyhow!("Not exiting the output audio recording process successfully."))?
.borrow_mut()
.quit()?;
.wait()?;
}
Ok(())
}
@ -662,45 +703,29 @@ impl Ffmpeg {
pub fn merge(&mut self) -> Result<()> {
if is_video_record(&self.temp_video_filename) {
if self.output != "gif" {
// Validate video file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
loop {
if is_valide(&self.temp_video_filename)? {
break;
} else if Instant::now().duration_since(start_time) >= duration {
return Err(Error::msg("Unable to validate tmp video file."));
}
}
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.input(&self.temp_video_filename);
ffmpeg_command.format("ogg");
let mut ffmpeg_command = Command::new("ffmpeg");
ffmpeg_command.args(["-i", &self.temp_video_filename]);
ffmpeg_command.args(["-f", "ogg"]);
if is_input_audio_record(&self.temp_input_audio_filename) {
ffmpeg_command.input(&self.temp_input_audio_filename);
ffmpeg_command.args(["-i", &self.temp_input_audio_filename]);
}
if is_output_audio_record(&self.temp_output_audio_filename) {
ffmpeg_command.input(&self.temp_output_audio_filename);
ffmpeg_command.args(["-i", &self.temp_output_audio_filename]);
}
ffmpeg_command.args([
"-c:a",
"aac",
&self.saved_filename
.clone()
&self.saved_filename.clone()
]);
ffmpeg_command.overwrite()
.spawn()?
.wait()?;
} else {
// Validate video file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
loop {
if is_valide(&self.temp_video_filename)? {
break;
} else if Instant::now().duration_since(start_time) >= duration {
return Err(Error::msg("Unable to validate tmp video file."));
}
ffmpeg_command.arg("-y");
let program = ffmpeg_command.get_program().to_string_lossy().to_string();
let args: Vec<String> = ffmpeg_command.get_args().map(|arg| arg.to_string_lossy().into_owned()).collect();
let mut merge_command = program;
for arg in args {
merge_command.push_str(&format!(" {}", arg));
}
adw::glib::spawn_command_line_async(merge_command)?;
} else {
// Convert MP4 to GIF
let filter = format!("fps={},scale={}:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse",
self.record_frames.value() as u16,
@ -710,54 +735,44 @@ impl Ffmpeg {
-loop 0 {} -y", &self.temp_video_filename,filter,
&self.saved_filename
.clone());
std::process::Command::new("sh").arg("-c").arg(&ffmpeg_convert).output()?;
adw::glib::spawn_command_line_async(ffmpeg_convert)?;
}
} else if is_input_audio_record(&self.temp_input_audio_filename) {
// Validate audio file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
loop {
if is_valide(&self.temp_input_audio_filename)? {
break;
} else if Instant::now().duration_since(start_time) >= duration {
return Err(Error::msg("Unable to validate tmp video file."));
}
}
// If only audio is recording then convert it to chosen format
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("ogg");
ffmpeg_command.input(&self.temp_input_audio_filename);
let mut ffmpeg_command = Command::new("ffmpeg");
ffmpeg_command.args(["-f", "ogg"]);
ffmpeg_command.args(["-i", &self.temp_input_audio_filename]);
if is_output_audio_record(&self.temp_output_audio_filename) {
ffmpeg_command.input(&self.temp_output_audio_filename);
ffmpeg_command.args(["-i", &self.temp_output_audio_filename]);
}
ffmpeg_command.args([
"-c:a",
"aac",
&self.saved_filename
.clone()
]).overwrite()
.spawn()?
.wait()?;
} else {
// Validate audio file integrity
let start_time = Instant::now();
let duration = Duration::from_secs(60);
loop {
if is_valide(&self.temp_output_audio_filename)? {
break;
} else if Instant::now().duration_since(start_time) >= duration {
return Err(Error::msg("Unable to validate tmp video file."));
}
.clone(),
"-y"
]);
let program = ffmpeg_command.get_program().to_string_lossy().to_string();
let args: Vec<String> = ffmpeg_command.get_args().map(|arg| arg.to_string_lossy().into_owned()).collect();
let mut merge_command = program;
for arg in args {
merge_command.push_str(&format!(" {}", arg));
}
adw::glib::spawn_command_line_async(merge_command)?;
} else {
// If only output audio is recording then convert it to chosen format
let mut ffmpeg_command = FfmpegCommand::new();
ffmpeg_command.format("ogg");
ffmpeg_command.input(&self.temp_output_audio_filename);
ffmpeg_command.arg(&self.saved_filename
.clone())
.overwrite()
.spawn()?
.wait()?;
let mut ffmpeg_command = Command::new("ffmpeg");
ffmpeg_command.args(["-f", "ogg"]);
ffmpeg_command.args(["-i", &self.temp_output_audio_filename]);
ffmpeg_command.arg(&self.saved_filename.clone());
ffmpeg_command.arg("-y");
let program = ffmpeg_command.get_program().to_string_lossy().to_string();
let args: Vec<String> = ffmpeg_command.get_args().map(|arg| arg.to_string_lossy().into_owned()).collect();
let mut merge_command = program;
for arg in args {
merge_command.push_str(&format!(" {}", arg));
}
adw::glib::spawn_command_line_async(merge_command)?;
}
Ok(())
}
@ -779,9 +794,8 @@ impl Ffmpeg {
let pid = self.video_process
.clone()
.ok_or_else(|| anyhow!("Unable to kill the video recording process successfully."))?
.borrow_mut()
.as_inner().id();
std::process::Command::new("taskkill")
.borrow_mut().id();
Command::new("taskkill")
.arg("/PID")
.arg(pid.to_string())
.arg("/F")
@ -791,9 +805,8 @@ impl Ffmpeg {
let pid = self.input_audio_process
.clone()
.ok_or_else(|| anyhow!("Unable to kill the input audio recording process successfully."))?
.borrow_mut()
.as_inner().id();
std::process::Command::new("taskkill")
.borrow_mut().id();
Command::new("taskkill")
.arg("/PID")
.arg(pid.to_string())
.arg("/F")
@ -803,9 +816,8 @@ impl Ffmpeg {
let pid = self.output_audio_process
.clone()
.ok_or_else(|| anyhow!("Unable to kill the output audio recording process successfully."))?
.borrow_mut()
.as_inner().id();
std::process::Command::new("taskkill")
.borrow_mut().id();
Command::new("taskkill")
.arg("/PID")
.arg(pid.to_string())
.arg("/F")

View File

@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
adw = { version = "0.2.1", package = "libadwaita", features = ["gtk_v4_6"] }
anyhow = "1.0.86"
async-std = {version = "1.12.0", features = ["attributes"]}
blue-recorder-core = { path = "../core", features = ["gtk"] }
@ -11,7 +12,6 @@ dark-light = "1.0.0"
dirs = "4.0.0"
fluent-bundle = "0.15.3"
glib = "0.10.3"
adw = { version = "0.2.1", package = "libadwaita", features = ["gtk_v4_6"] }
regex = "1.4.3"
rust-ini = "0.16"
secfmt = "0.1.1"

View File

@ -994,8 +994,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
if _audio_input_switch.is_active() {
match _ffmpeg_record_interface.borrow_mut().stop_input_audio() {
Ok(_) => {
_play_button.set_sensitive(false);
_play_button.set_tooltip_text(Some(&get_bundle("play-inactive-tooltip", None)));
// Continue
},
Err(error) => {
if _video_switch.is_active() {
@ -1016,8 +1015,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
if _audio_output_switch.is_active() {
match _ffmpeg_record_interface.borrow_mut().stop_output_audio() {
Ok(_) => {
_play_button.set_sensitive(false);
_play_button.set_tooltip_text(Some(&get_bundle("play-inactive-tooltip", None)));
// Continue
},
Err(error) => {
if _video_switch.is_active() {
@ -1038,8 +1036,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
if _video_switch.is_active() {
match _ffmpeg_record_interface.borrow_mut().stop_video() {
Ok(_) => {
_play_button.set_sensitive(false);
_play_button.set_tooltip_text(Some(&get_bundle("play-inactive-tooltip", None)));
// Continue
},
Err(error) => {
if _video_switch.is_active() {
@ -1057,6 +1054,19 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
},
}
}
// Save tmp files
match _ffmpeg_record_interface.borrow_mut().merge() {
Ok(_) => {
// Continue
},
Err(error) => {
show_play = false;
let text_buffer = TextBuffer::new(None);
text_buffer.set_text(&format!("{}", error));
_error_message.set_buffer(Some(&text_buffer));
_error_dialog.show();
},
};
if _video_switch.is_active() {
_mouse_switch.set_sensitive(true);
_follow_mouse_switch.set_sensitive(true);
@ -1064,32 +1074,12 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag
enable_input_widgets(input_widgets.clone());
_stop_button.hide();
if show_play {
_play_button.set_tooltip_text(Some(&get_bundle("play-tooltip", None)));
_play_button.show();
}
_record_button.show();
});
// Save tmp files
let _error_dialog = error_dialog.clone();
let _error_message = error_message.clone();
let _play_button = play_button.clone();
let mut _ffmpeg_record_interface = ffmpeg_record_interface.clone();
play_button.connect_show(move |_| {
match _ffmpeg_record_interface.borrow_mut().merge() {
Ok(_) => {
_play_button.set_sensitive(true);
_play_button.set_tooltip_text(Some(&get_bundle("play-tooltip", None)));
},
Err(error) => {
_play_button.hide();
let text_buffer = TextBuffer::new(None);
text_buffer.set_text(&format!("{}", error));
_error_message.set_buffer(Some(&text_buffer));
_error_dialog.show();
},
}
});
// Delay window button
let _delay_window_button = delay_window_button.clone();
delay_window_button.connect_clicked(move |_| {});

View File

@ -173,7 +173,6 @@ frames-tooltip = خصص عدد الصور في الثانية
hide-tooltip = اطوِ نافذة المسجل الأزرق عند بدء التسجيل
mouse-tooltip = يظهر مؤشر الفأرة عند التسجيل
play-tooltip = انقر لمشاهدة ما سُجِّل
play-inactive-tooltip = ترقَّب ريثما يتم حفظ ما سُجِّل
record-tooltip = يشرع في التسجيل
screen-tooltip = يحدد الشاشة كاملة ليسجلها
show-area-tooltip = يُظهر البقعة المنتقاة أثناء التسجيل

View File

@ -173,7 +173,6 @@ frames-tooltip = خصص عدد الصور في الثانية
hide-tooltip = اطوِ نافذة المسجل الأزرق عند بدء التسجيل
mouse-tooltip = يظهر مؤشر الفأرة عند التسجيل
play-tooltip = انقر لمشاهدة ما سُجِّل
play-inactive-tooltip = ترقَّب ريثما يتم حفظ ما سُجِّل
record-tooltip = يشرع في التسجيل
screen-tooltip = يحدد الشاشة كاملة ليسجلها
show-area-tooltip = يُظهر البقعة المنتقاة أثناء التسجيل

View File

@ -173,7 +173,6 @@ frames-tooltip = Set frames rate
hide-tooltip = Hide window when start recording
mouse-tooltip = Mouse appears in video recording
play-tooltip = Click to play video
play-inactive-tooltip = Please wait until the file is saved
record-tooltip = Start screen record
screen-tooltip = Select screen to record
show-area-tooltip = Show selected area during record

View File

@ -173,7 +173,6 @@ frames-tooltip = Set frames rate
hide-tooltip = Hide window when start recording
mouse-tooltip = Mouse appears in video recording
play-tooltip = Click to play video
play-inactive-tooltip = Please wait until the file is saved
record-tooltip = Start screen record
screen-tooltip = Select screen to record
show-area-tooltip = Show selected area during record