bug fixes, force select audio or video to record

This commit is contained in:
Salem Yaslem 2023-02-22 20:44:08 +03:00
parent 86303da319
commit c5189deaba
4 changed files with 151 additions and 228 deletions

124
Cargo.lock generated
View File

@ -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"

View File

@ -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<u32>,
pub audio_process_id: Option<u32>,
@ -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())

View File

@ -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<String> = {
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<RefCell<Ffmpeg>> = 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 <albaroty@gmail.com>"]);
about_dialog.set_artists(&[
"Mustapha Assabar",
"Abdullah Al-Baroty <albaroty@gmail.com>",
]);
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,

View File

@ -27,6 +27,7 @@
#delay_window {
border: 3px solid @theme_selected_bg_color;
}
#delay_window_label {
font-size: 350%;
font-weight: 250;