From c5189deaba2cef4eeac932ad893e89f86ecd4b08 Mon Sep 17 00:00:00 2001 From: Salem Yaslem Date: Wed, 22 Feb 2023 20:44:08 +0300 Subject: [PATCH] bug fixes, force select audio or video to record --- Cargo.lock | 124 -------------------------------------- src/ffmpeg_interface.rs | 130 ++++++++++++++++++++++++---------------- src/main.rs | 124 +++++++++++++++++++++----------------- src/styles/global.css | 1 + 4 files changed, 151 insertions(+), 228 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dba51a2..9ee4a61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,15 +26,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anyhow" version = "1.0.66" @@ -53,17 +44,6 @@ dependencies = [ "system-deps 6.0.3", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -95,7 +75,6 @@ dependencies = [ "glib 0.10.3", "gtk-sys", "gtk4", - "ksni", "regex", "rust-ini", "secfmt", @@ -197,21 +176,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - [[package]] name = "codespan-reporting" version = "0.11.1" @@ -272,37 +236,6 @@ dependencies = [ "syn", ] -[[package]] -name = "dbus" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8bcdd56d2e5c4ed26a529c5a9029f5db8290d433497506f958eae3be148eb6" -dependencies = [ - "libc", - "libdbus-sys", - "winapi", -] - -[[package]] -name = "dbus-codegen" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49da9fdfbe872d4841d56605dc42efa5e6ca3291299b87f44e1cde91a28617c" -dependencies = [ - "clap", - "dbus", - "xml-rs", -] - -[[package]] -name = "dbus-tree" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f456e698ae8e54575e19ddb1f9b7bce2298568524f215496b248eb9498b4f508" -dependencies = [ - "dbus", -] - [[package]] name = "dirs" version = "4.0.0" @@ -1042,15 +975,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "iana-time-zone" version = "0.1.53" @@ -1093,18 +1017,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "ksni" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48b786146a6b576a000a289d8e1a834a3de60db75973f43ebbfec733270973f0" -dependencies = [ - "dbus", - "dbus-codegen", - "dbus-tree", - "thiserror", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -1117,15 +1029,6 @@ version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" -[[package]] -name = "libdbus-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c185b5b7ad900923ef3a8ff594083d4d9b5aea80bb4f32b8342363138c0d456b" -dependencies = [ - "pkg-config", -] - [[package]] name = "link-cplusplus" version = "1.0.7" @@ -1536,12 +1439,6 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "strum" version = "0.18.0" @@ -1624,15 +1521,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.37" @@ -1697,12 +1585,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version-compare" version = "0.0.10" @@ -1817,9 +1699,3 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "xml-rs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" diff --git a/src/ffmpeg_interface.rs b/src/ffmpeg_interface.rs index 07c6bba..bab88ce 100644 --- a/src/ffmpeg_interface.rs +++ b/src/ffmpeg_interface.rs @@ -1,10 +1,11 @@ extern crate subprocess; - use chrono::prelude::*; use gettextrs::gettext; use gtk::prelude::*; -use gtk::{CheckButton, ComboBoxText, Entry, FileChooserNative, ProgressBar, SpinButton, Window}; -use gtk::{ButtonsType, DialogFlags, MessageDialog, MessageType, ResponseType}; +use gtk::{ + CheckButton, ComboBoxText, Entry, FileChooserNative, ProgressBar, SpinButton, Window, +}; +use gtk::{ButtonsType, DialogFlags, MessageDialog, MessageType}; use std::path::PathBuf; use std::process::Command; use std::sync::mpsc::Sender; @@ -19,10 +20,7 @@ pub struct ProgressWidget { } impl ProgressWidget { - pub fn new( - progress_dialog: MessageDialog, - progressbar: ProgressBar, - ) -> ProgressWidget { + pub fn new(progress_dialog: MessageDialog, progressbar: ProgressBar) -> ProgressWidget { ProgressWidget { progress_dialog, progressbar, @@ -54,6 +52,7 @@ pub struct Ffmpeg { pub record_mouse: CheckButton, pub follow_mouse: CheckButton, pub record_frames: SpinButton, + pub record_delay: SpinButton, pub command: Entry, pub video_process_id: Option, pub audio_process_id: Option, @@ -100,19 +99,17 @@ impl Ffmpeg { let message_dialog = MessageDialog::new( Some(&self.window), DialogFlags::empty(), - MessageType::Warning, - ButtonsType::Ok, - &gettext("File already exist."), + MessageType::Question, + ButtonsType::YesNo, + &gettext("File already exist. Do you want to overwrite it?"), ); + + message_dialog.connect_response(|message_dialog: &MessageDialog, _| { + message_dialog.hide() + }); + message_dialog.show(); - message_dialog.connect_response( - glib::clone!(@strong message_dialog => move |_, response| { - if response == ResponseType::Ok { - message_dialog.hide(); - } - message_dialog.hide(); - }), - ); + return (None, None); } @@ -135,7 +132,7 @@ impl Ffmpeg { if self.record_video.is_active() { let mut ffmpeg_command: Command = Command::new("ffmpeg"); - // Record video with specified width and hight + // record video with specified width and hight ffmpeg_command.arg("-video_size"); ffmpeg_command.arg(format!("{}x{}", width, height)); ffmpeg_command.arg("-framerate"); @@ -152,7 +149,7 @@ impl Ffmpeg { y )); - // If show mouse switch is enabled, draw the mouse to video + // if show mouse switch is enabled, draw the mouse to video ffmpeg_command.arg("-draw_mouse"); if self.record_mouse.is_active() { ffmpeg_command.arg("1"); @@ -160,7 +157,7 @@ impl Ffmpeg { ffmpeg_command.arg("0"); } - // If follow mouse switch is enabled, follow the mouse + // if follow mouse switch is enabled, follow the mouse if self.follow_mouse.is_active() { ffmpeg_command.arg("-follow_mouse"); ffmpeg_command.arg("centered"); @@ -169,7 +166,9 @@ impl Ffmpeg { ffmpeg_command.arg("1"); ffmpeg_command.arg(self.saved_filename.as_ref().unwrap()); ffmpeg_command.arg("-y"); - // Start recording and return the process id + // sleep for delay + sleep(Duration::from_secs(self.record_delay.value() as u64)); + // start recording and return the process id self.video_process_id = Some(ffmpeg_command.spawn().unwrap().id()); return (self.video_process_id, self.audio_process_id); } @@ -179,7 +178,7 @@ impl Ffmpeg { pub fn stop_record(&self) { self.progress_widget.show(); - // Kill the process to stop recording + // kill the process to stop recording self.progress_widget.set_progress("".to_string(), 1, 6); if self.video_process_id.is_some() { @@ -241,47 +240,78 @@ impl Ffmpeg { self.progress_widget.set_progress("".to_string(), 4, 6); - // If audio record, then merge video with audio + // if audio record, then merge video with audio if is_audio_record { self.progress_widget .set_progress("Save Audio Recording".to_string(), 4, 6); - let mut ffmpeg_audio_merge_command = Command::new("ffmpeg"); - ffmpeg_audio_merge_command.arg("-i"); - ffmpeg_audio_merge_command.arg(format!( + + let video_filename = format!( "{}.temp.without.audio.{}", self.saved_filename.as_ref().unwrap(), self.filename.2.active_id().unwrap() - )); - ffmpeg_audio_merge_command.arg("-i"); - ffmpeg_audio_merge_command.arg(format!( - "{}.temp.audio", - self.saved_filename.as_ref().unwrap() - )); - ffmpeg_audio_merge_command.arg("-c:v"); - ffmpeg_audio_merge_command.arg("copy"); - ffmpeg_audio_merge_command.arg("-c:a"); - ffmpeg_audio_merge_command.arg("aac"); - ffmpeg_audio_merge_command.arg(self.saved_filename.as_ref().unwrap()); - ffmpeg_audio_merge_command.arg("-y"); + ); + + let audio_filename = + format!("{}.temp.audio", self.saved_filename.as_ref().unwrap()); + + Command::new("ffmpeg") + .args([ + "-i", + video_filename.as_str(), + "-i", + audio_filename.as_str(), + "-c:v", + "copy", + "-c:a", + "aac", + self.saved_filename.as_ref().unwrap(), + "-y", + ]) + .output() + .unwrap(); + sleep(Duration::from_secs(1)); - ffmpeg_audio_merge_command.output().unwrap(); - std::fs::remove_file(format!( + + // std::fs::remove_file(format!( + // "{}.temp.audio", + // self.saved_filename.as_ref().unwrap() + // )) + // .unwrap(); + // std::fs::remove_file(format!( + // "{}.temp.without.audio.{}", + // self.saved_filename.as_ref().unwrap(), + // self.filename.2.active_id().unwrap() + // )) + // .unwrap(); + } + } + + // if only audio is recording then convert it to chosen format + else if is_audio_record { + self.progress_widget + .set_progress("Convert Audio to choosen format".to_string(), 4, 6); + sleep(Duration::from_secs(1)); + Command::new("ffmpeg") + .arg("-f") + .arg("ogg") + .arg("-i") + .arg(format!( "{}.temp.audio", self.saved_filename.as_ref().unwrap() )) + .arg(self.saved_filename.as_ref().unwrap()) + .output() .unwrap(); - std::fs::remove_file(format!( - "{}.temp.without.audio.{}", - self.saved_filename.as_ref().unwrap(), - self.filename.2.active_id().unwrap() - )) - .unwrap(); - } + std::fs::remove_file(format!( + "{}.temp.audio", + self.saved_filename.as_ref().unwrap() + )) + .unwrap(); } self.progress_widget.set_progress("".to_string(), 5, 6); - // Execute command after finish recording + // execute command after finish recording if self.command.text().trim() != "" { self.progress_widget.set_progress( "execute custom command after finish".to_string(), @@ -299,7 +329,7 @@ impl Ffmpeg { pub fn play_record(self) { if self.saved_filename.is_some() { if is_snap() { - // Open the video using snapctrl for snap package + // open the video using snapctrl for snap package Command::new("snapctl") .arg("user-open") .arg(self.saved_filename.unwrap()) diff --git a/src/main.rs b/src/main.rs index 2f7d018..12bd5b9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ -extern crate gio; extern crate gdk; extern crate gettextrs; +extern crate gio; extern crate gtk; mod area_capture; mod config_management; @@ -8,10 +8,14 @@ mod ffmpeg_interface; mod timer; use ffmpeg_interface::{Ffmpeg, ProgressWidget}; -use gettextrs::{bindtextdomain, gettext, LocaleCategory, setlocale, textdomain}; -use gtk::{AboutDialog, Application, Builder, Button, CheckButton, ComboBoxText, CssProvider, Entry, FileChooserNative, FileChooserAction, Image, Label, MessageDialog, ProgressBar, SpinButton, ToggleButton, Window}; -use gtk::prelude::*; +use gettextrs::{bindtextdomain, gettext, setlocale, textdomain, LocaleCategory}; use gtk::glib; +use gtk::prelude::*; +use gtk::{ + AboutDialog, Application, Builder, Button, CheckButton, ComboBoxText, CssProvider, Entry, + FileChooserAction, FileChooserNative, Image, Label, MessageDialog, ProgressBar, SpinButton, + ToggleButton, Window, +}; use std::cell::RefCell; use std::ops::Add; use std::path::Path; @@ -21,7 +25,7 @@ use timer::{recording_delay, start_timer, stop_timer}; fn main() { // Create new application - let application = Application::new(Some("sa.sy.blue-recorder"), Default::default(),); + let application = Application::new(Some("sa.sy.blue-recorder"), Default::default()); application.connect_activate(build_ui); application.run(); } @@ -37,7 +41,7 @@ pub fn build_ui(application: &Application) { let ui_src = include_str!("../interfaces/main.ui").to_string(); let builder: Builder = Builder::from_string(ui_src.as_str()); - // Translate + // Translate let mut po_path_abs = { let mut current_exec_dir = std::env::current_exe().unwrap(); current_exec_dir.pop(); @@ -136,27 +140,19 @@ pub fn build_ui(application: &Application) { // Get audio sources let sources_descriptions: Vec = { let list_sources_child = Command::new("pactl") - .args(&["list", "sources"]) - .stdout(Stdio::piped()) - .spawn(); - let sources_descriptions = String::from_utf8( - if let Ok(..) = list_sources_child { + .args(&["list", "sources"]) + .stdout(Stdio::piped()) + .spawn(); + let sources_descriptions = String::from_utf8(if let Ok(..) = list_sources_child { Command::new("grep") .args(&["-e", "device.description"]) - .stdin( - list_sources_child - .unwrap() - .stdout - .take() - .unwrap(), - ) + .stdin(list_sources_child.unwrap().stdout.take().unwrap()) .output() .unwrap() .stdout - } else { - Vec::new() - } - ) + } else { + Vec::new() + }) .unwrap(); sources_descriptions .split('\n') @@ -187,22 +183,19 @@ pub fn build_ui(application: &Application) { follow_mouse_switch.set_active(config_management::get_bool("default", "followmousecheck")); overwrite_switch.set_active(config_management::get_bool("default", "overwritecheck")); + let _video_switch = video_switch.clone(); let _audio_switch = audio_switch.clone(); let _mouse_switch = mouse_switch.clone(); let _follow_mouse_switch = follow_mouse_switch.clone(); video_switch.connect_toggled(move |switch: &CheckButton| { config_management::set_bool("default", "videocheck", switch.is_active()); if switch.is_active() { - _audio_switch.set_active(false); - _audio_switch.set_sensitive(true); _mouse_switch.set_sensitive(true); } else { _mouse_switch.set_sensitive(false); _follow_mouse_switch.set_sensitive(false); - } - if !switch.is_active() { - _audio_switch.set_active(false); - _audio_switch.set_sensitive(false); + _audio_switch.set_active(true); + _audio_switch.set_sensitive(true); _mouse_switch.set_active(false); } }); @@ -216,8 +209,13 @@ pub fn build_ui(application: &Application) { _follow_mouse_switch.set_sensitive(false); } }); - audio_switch.connect_toggled(|switch: &CheckButton| { + let _mouse_switch = mouse_switch.clone(); + audio_switch.connect_toggled(move |switch: &CheckButton| { config_management::set_bool("default", "audiocheck", switch.is_active()); + if !switch.is_active() && !_video_switch.is_active() { + _video_switch.set_active(true); + _mouse_switch.set_sensitive(true); + } }); follow_mouse_switch.connect_toggled(|switch: &CheckButton| { config_management::set_bool("default", "followmousecheck", switch.is_active()); @@ -305,25 +303,28 @@ pub fn build_ui(application: &Application) { }); let _delay_spin = delay_spin.to_owned(); delay_spin.connect_value_changed(move |_| { - config_management::set( - "default", - "delay", - _delay_spin.value().to_string().as_str(), - ); + config_management::set("default", "delay", _delay_spin.value().to_string().as_str()); }); // FileChooser let folder_chooser_native = FileChooserNative::new( - Some("Select Folder"), - Some(&main_window), - FileChooserAction::SelectFolder, - Some("Select"), - Some("Cancel"), + Some("Select Folder"), + Some(&main_window), + FileChooserAction::SelectFolder, + Some("Select"), + Some("Cancel"), ); folder_chooser_native.set_transient_for(Some(&main_window)); folder_chooser_native.set_modal(true); - folder_chooser_native.set_file(&gio::File::for_uri(&config_management::get("default", "folder"))).unwrap(); - let folder_chooser = Some(gio::File::for_uri(&config_management::get("default", "folder"))).unwrap(); + folder_chooser_native + .set_file(&gio::File::for_uri(&config_management::get( + "default", "folder", + ))) + .unwrap(); + let folder_chooser = Some(gio::File::for_uri(&config_management::get( + "default", "folder", + ))) + .unwrap(); let folder_chooser_name = folder_chooser.basename().unwrap(); folder_chooser_label.set_label(&folder_chooser_name.to_string_lossy()); let folder_chooser_icon = config_management::folder_icon(folder_chooser_name.to_str()); @@ -385,9 +386,15 @@ pub fn build_ui(application: &Application) { _area_capture.borrow_mut().get_area(); }); + let _delay_spin = delay_spin.clone(); + // Init record struct let ffmpeg_record_interface: Rc> = Rc::new(RefCell::new(Ffmpeg { - filename: (folder_chooser_native, filename_entry, format_chooser_combobox), + filename: ( + folder_chooser_native, + filename_entry, + format_chooser_combobox, + ), record_video: video_switch, record_audio: audio_switch, audio_id: audio_source_combobox, @@ -402,6 +409,7 @@ pub fn build_ui(application: &Application) { progress_widget: ProgressWidget::new(progress_dialog, progressbar), window: main_window.clone(), overwrite: overwrite_switch, + record_delay: delay_spin, })); // Record Button @@ -412,12 +420,19 @@ pub fn build_ui(application: &Application) { let _record_button = record_button.clone(); let _record_time_label = record_time_label.clone(); let _stop_button = stop_button.clone(); + record_button.connect_clicked(move |_| { _delay_window_button.set_active(false); - if delay_spin.value()as u64 > 0 { - recording_delay(delay_spin.clone(), delay_spin.value()as u64, delay_window.clone(), _delay_window_button.clone(), delay_window_label.clone(), _record_button.clone()); - } - if delay_spin.value()as u64 == 0 { + if _delay_spin.value() as u64 > 0 { + recording_delay( + _delay_spin.clone(), + _delay_spin.value() as u64, + delay_window.clone(), + _delay_window_button.clone(), + delay_window_label.clone(), + _record_button.clone(), + ); + } else if _delay_spin.value() as u64 == 0 { let _area_capture = area_capture.borrow_mut(); match _ffmpeg_record_interface.borrow_mut().start_record( _area_capture.x, @@ -427,7 +442,7 @@ pub fn build_ui(application: &Application) { ) { (None, None) => { // Do nothing if the start_record function return nothing - } + } _ => { start_timer(record_time_label.clone()); record_time_label.set_visible(true); @@ -454,8 +469,7 @@ pub fn build_ui(application: &Application) { // Delay Window Button let _delay_window_button = delay_window_button.clone(); - delay_window_button.connect_clicked(move |_| { - }); + delay_window_button.connect_clicked(move |_| {}); // Play Button let mut _ffmpeg_record_interface = ffmpeg_record_interface.clone(); @@ -498,7 +512,10 @@ pub fn build_ui(application: &Application) { "O.Chibani <11yzyv86j@relay.firefox.com>", "Patreon Supporters: Ahmad Gharib, Medium,\nWilliam Grunow, Alex Benishek.", ]); - about_dialog.set_artists(&["Mustapha Assabar", "Abdullah Al-Baroty "]); + about_dialog.set_artists(&[ + "Mustapha Assabar", + "Abdullah Al-Baroty ", + ]); about_dialog.set_website(Some("https://github.com/xlmnxp/blue-recorder/")); about_dialog.set_logo_icon_name(Some("blue-recorder")); about_dialog.set_logo(logo.paintable().as_ref()); @@ -507,12 +524,12 @@ pub fn build_ui(application: &Application) { // Windows // Hide area chooser after it deleted. let _area_chooser_window = area_chooser_window.clone(); - area_chooser_window.connect_close_request (move |_| { + area_chooser_window.connect_close_request(move |_| { _area_chooser_window.hide(); gtk::Inhibit(true) }); - // Close the application when main window destroy + // Close the application when main window destroy main_window.connect_destroy(move |main_window| { let mut _ffmpeg_record_interface = ffmpeg_record_interface.clone(); // Stop recording before close the application @@ -522,8 +539,7 @@ pub fn build_ui(application: &Application) { // Apply CSS let provider = CssProvider::new(); - provider - .load_from_data(include_str!("styles/global.css").as_bytes()); + provider.load_from_data(include_str!("styles/global.css").as_bytes()); gtk::StyleContext::add_provider_for_display( &area_chooser_window.display(), &provider, diff --git a/src/styles/global.css b/src/styles/global.css index fe7309c..387c753 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -27,6 +27,7 @@ #delay_window { border: 3px solid @theme_selected_bg_color; } + #delay_window_label { font-size: 350%; font-weight: 250;