diff --git a/Cargo.lock b/Cargo.lock index 78928c5..e86efe3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,21 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.94" @@ -468,6 +483,7 @@ dependencies = [ "anyhow", "async-std", "blue-recorder-core", + "chrono", "cpal", "dark-light", "dirs", @@ -607,6 +623,20 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.6", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -2006,6 +2036,29 @@ dependencies = [ "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys 0.8.7", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -4448,6 +4501,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.54.0" diff --git a/gui/Cargo.toml b/gui/Cargo.toml index 508a259..1c91050 100644 --- a/gui/Cargo.toml +++ b/gui/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" anyhow = "1.0.86" async-std = {version = "1.12.0", features = ["attributes"]} blue-recorder-core = { path = "../core" } +chrono = "0.4.19" cpal = "0.15.3" dark-light = "1.0.0" dirs = "4.0.0" @@ -21,5 +22,5 @@ secfmt = "0.1.1" # Windows-only dependency [target.'cfg(windows)'.dependencies] display-info = "0.5.1" -x-win = "2.0.2" winapi = { version = "0.3", features = ["winuser"] } +x-win = "2.0.2" diff --git a/gui/src/area_capture.rs b/gui/src/area_capture.rs index 13d5ac0..6da6b2f 100644 --- a/gui/src/area_capture.rs +++ b/gui/src/area_capture.rs @@ -27,6 +27,11 @@ pub struct AreaCapture { pub height: u16, } +#[derive(Debug, Clone)] +pub struct Title { + pub title: String, +} + impl AreaCapture { pub fn new() -> Result { #[cfg(any(target_os = "freebsd", target_os = "linux"))] @@ -78,12 +83,6 @@ impl AreaCapture { Ok(*self) } - #[cfg(target_os = "windows")] - pub fn get_title(&mut self) -> Result { - let title = get_active_window()?.title; - Ok(title) - } - #[cfg(any(target_os = "freebsd", target_os = "linux"))] pub fn get_window_by_name(&mut self, name: &str) -> Result { let coordinate = xwininfo_to_coordinate( @@ -121,6 +120,21 @@ impl AreaCapture { } } +#[cfg(target_os = "windows")] +impl Title { + pub fn new() -> Result { + let title = Title { + title: String::new(), + }; + Ok(title) + } + + pub fn get_title(&mut self) -> Result<Self> { + self.title = get_active_window()?.title; + Ok(self.clone()) + } +} + #[cfg(any(target_os = "freebsd", target_os = "linux"))] fn xwininfo_to_coordinate(xwininfo_output: String) -> Result<(u16, u16, u16, u16)> { let x: u16 = Regex::new(r"A.*X:\s+(\d+)\n")? diff --git a/gui/src/ui.rs b/gui/src/ui.rs index e7cf6bc..49d6a85 100644 --- a/gui/src/ui.rs +++ b/gui/src/ui.rs @@ -1,9 +1,10 @@ -use anyhow::{anyhow, Result}; +use anyhow::Result; #[cfg(any(target_os = "freebsd", target_os = "linux"))] use blue_recorder_core::ffmpeg_linux::Ffmpeg; #[cfg(target_os = "windows")] use blue_recorder_core::ffmpeg_windows::Ffmpeg; -use blue_recorder_core::utils::is_wayland; +use blue_recorder_core::utils::{is_wayland, RecordMode}; +use chrono::Utc; use cpal::traits::{DeviceTrait, HostTrait}; use libadwaita::{Application, Window}; use libadwaita::gio::File; @@ -12,7 +13,7 @@ use libadwaita::gtk::{AboutDialog, Builder, Button, CheckButton, ComboBoxText, C use libadwaita::prelude::*; use std::cell::RefCell; use std::ops::Add; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::rc::Rc; use crate::{area_capture, config_management, fluent::get_bundle}; @@ -556,6 +557,9 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag // Buttons let area_capture: Rc<RefCell<area_capture::AreaCapture>> = Rc::new(RefCell::new(area_capture::AreaCapture::new()?)); + #[cfg(target_os = "windows")] + let window_title: Rc<RefCell<area_capture::Title>> = + Rc::new(RefCell::new(area_capture::Title::new()?)); area_grab_label.set_label(&get_bundle("select-area", None)); let _area_chooser_window = area_chooser_window.clone(); @@ -638,6 +642,8 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag let _error_message = error_message.clone(); window_grab_button.set_tooltip_text(Some(&get_bundle("window-tooltip", None))); window_grab_label.set_label(&get_bundle("select-window", None)); + #[cfg(target_os = "windows")] + let mut _window_title: Rc<RefCell<area_capture::Title>> = window_title.clone(); window_grab_button.connect_clicked(move |_| { let text_buffer = TextBuffer::new(None); config_management::set_bool("default", "areacheck", _area_switch.is_active()); @@ -657,12 +663,13 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag let error_message = _error_message.clone(); let error_dialog = error_dialog.clone(); let _select_window = select_window.clone(); + let window_title = _window_title.clone(); glib::timeout_add_local(1000, move || { let clicked = area_capture::check_input(); if clicked { _select_window.hide(); - if area_capture.borrow_mut().get_title().is_err() { - text_buffer.set_text("Failed to get window info."); + if window_title.borrow_mut().get_title().is_err() { + text_buffer.set_text("Failed to get window title."); error_message.set_buffer(Some(&text_buffer)); error_dialog.show(); } @@ -686,21 +693,84 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag }); // Record struct values - let audio_input_id = audio_source_combobox - .active_id() - .ok_or_else(|| anyhow!("Failed to get audio_input_id value from audio_source_combobox."))?; + let audio_input_id = audio_source_combobox.active_id().unwrap().to_string(); let audio_output_id = output_device; - let filename = String::from(""); - let output_file = Path::new(&filename) - .extension() - .ok_or_else(|| anyhow!("Failed to get output extension from filename."))?; + let audio_record_bitrate = audio_bitrate_spin.value() as u16; + #[derive(Debug, Clone)] + struct FileName { + filename: (FileChooserNative, Entry, ComboBoxText), + } + let struct_filename = FileName { + filename: ( + folder_chooser_native, + filename_entry, + format_chooser_combobox, + ) + }; + let filename = struct_filename + .filename + .0 + .file() + .unwrap() + .path() + .unwrap() + .join(PathBuf::from(format!( + "{}.{}", + if struct_filename.filename.1.text().to_string().trim().eq("") { + Utc::now().to_string().replace(" UTC", "").replace(' ', "-") + } else { + struct_filename.filename.1.text().to_string().trim().to_string() + }, + struct_filename.filename.2.active_id().unwrap() + ))) + .as_path() + .display().to_string(); + let follow_mouse = follow_mouse_switch.is_active(); + let mode = if area_grab_button.is_active() { + RecordMode::Area + } else if window_grab_button.is_active() { + RecordMode::Window + } else { + RecordMode::Screen + }; + let output = Path::new(&filename).extension().unwrap().to_string_lossy().to_string(); + let record_delay = delay_spin.value() as u16; + let record_frames = frames_spin.value() as u16; + let record_mouse = mouse_switch.is_active(); + let show_area = area_switch.is_active(); + let video_record_bitrate = video_bitrate_spin.value() as u16; + #[cfg(target_os = "windows")] + let window_title = window_title.borrow_mut().title.clone(); // Init record struct - /*let ffmpeg_record_interface: Rc<RefCell<Ffmpeg>> = Rc::new(RefCell::new(Ffmpeg { - audio_input_id: String::new(), - audio_output_id: String::new(), - filename: String::from("/home/chibani/test_record.mp4"), - output: String::from("mp4"), + #[cfg(target_os = "windows")] + let ffmpeg_record_interface: Rc<RefCell<Ffmpeg>> = Rc::new(RefCell::new(Ffmpeg { + audio_input_id, + audio_output_id, + filename, + output, + temp_input_audio_filename: String::new(), + temp_output_audio_filename: String::new(), + temp_video_filename: String::new(), + window_title, + height: None, + input_audio_process: None, + output_audio_process: None, + video_process: None, + audio_record_bitrate, + record_delay, + record_frames, + video_record_bitrate, + follow_mouse, + record_mouse, + show_area, + })); + #[cfg(any(target_os = "freebsd", target_os = "linux"))] + let ffmpeg_record_interface: Rc<RefCell<Ffmpeg>> = Rc::new(RefCell::new(Ffmpeg { + audio_input_id, + audio_output_id, + filename, + output, temp_input_audio_filename: String::new(), temp_output_audio_filename: String::new(), temp_video_filename: String::new(), @@ -708,45 +778,14 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag input_audio_process: None, output_audio_process: None, video_process: None, - audio_record_bitrate: 0, - record_delay: 0, - record_frames: 10, - video_record_bitrate: 0, - follow_mouse: false, - record_mouse: true, - show_area: false, - }*/ - /*let ffmpeg_record_interface: Rc<RefCell<Ffmpeg>> = Rc::new(RefCell::new(Ffmpeg { - filename: ( - folder_chooser_native, - filename_entry, - format_chooser_combobox, - ), - record_video: video_switch, - record_audio: audio_input_switch, - audio_id: audio_source_combobox, - record_mouse: mouse_switch, - follow_mouse: follow_mouse_switch, - record_frames: frames_spin, - command: command_entry, - video_process: None, - audio_process: None, - saved_filename: None, - height: None, - unbound: None, - window: main_window.clone(), - record_delay: delay_spin, - record_wayland: wayland_record, - record_window, - main_context, - temp_video_filename: String::new(), - bundle: bundle_msg, - video_record_bitrate: video_bitrate_spin, - audio_record_bitrate: audio_bitrate_spin, - error_window: error_dialog, - error_window_text: error_dialog_label, - error_details: error_message, - }));*/ + audio_record_bitrate, + record_delay, + record_frames, + video_record_bitrate, + follow_mouse, + record_mouse, + show_area, + })); // Record button //let bundle_msg = get_bundle("already-exist", None); @@ -755,7 +794,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag delay_window_title.set_label(&get_bundle("delay-title", None)); let _delay_window = delay_window.clone(); let _delay_window_button = delay_window_button.clone(); - //let _ffmpeg_record_interface = ffmpeg_record_interface.clone(); + let _ffmpeg_record_interface = ffmpeg_record_interface.clone(); //let main_context = glib::MainContext::default(); let _main_window = main_window.clone(); let _play_button = play_button.clone(); @@ -777,13 +816,15 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag _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( + let _area_capture = area_capture.borrow_mut(); + let start_video_record = _ffmpeg_record_interface.borrow_mut().start_video( _area_capture.x, _area_capture.y, _area_capture.width, _area_capture.height, - ) { + mode, + ); + /*match start_video_record { None => { // Do nothing if the start_record function return nothing } @@ -802,7 +843,7 @@ fn build_ui(application: &Application, error_dialog: MessageDialog, error_messag }); // Stop record button - //let mut _ffmpeg_record_interface = ffmpeg_record_interface.clone(); + let mut _ffmpeg_record_interface = ffmpeg_record_interface.clone(); let _play_button = play_button.clone(); let _stop_button = stop_button.clone(); stop_button.set_tooltip_text(Some(&get_bundle("stop-tooltip", None)));