Merge remote branch 'xlmnxp-repo/master'

This commit is contained in:
suliman altassan 2021-03-10 07:56:41 +03:00
commit 5586b2b18d
7 changed files with 192 additions and 49 deletions

2
Cargo.lock generated
View File

@ -131,7 +131,7 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "blue-recorder"
version = "0.1.0"
version = "0.1.2"
dependencies = [
"chrono",
"gdk",

View File

@ -1,6 +1,6 @@
[package]
name = "blue-recorder"
version = "0.1.0"
version = "0.1.2"
authors = ["Salem Yaslem <s@sy.sa>"]
edition = "2018"

BIN
data/blue-recorder@x128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -130,13 +130,12 @@
</object>
<object class="GtkWindow" id="main_window">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="opacity">0.99999999977648257</property>
<property name="can_focus">True</property>
<property name="resizable">False</property>
<property name="window_position">center</property>
<property name="gravity">center</property>
<child type="titlebar">
<object class="GtkHeaderBar" id="headerbar1">
<object class="GtkHeaderBar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="has_subtitle">False</property>
@ -147,7 +146,6 @@
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="relief">half</property>
<property name="always_show_image">True</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
@ -185,12 +183,35 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="relief">none</property>
<property name="relief">half</property>
<child>
<object class="GtkImage">
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">media-playback-stop</property>
<property name="column_spacing">2</property>
<property name="row_homogeneous">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">media-playback-stop</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Stop Recording</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
</child>
</object>

View File

@ -4,7 +4,7 @@ version: git # just for humans, typically '1.2+git' or '1.3.2'
summary: A simple screen recorder for Linux desktop. Supports Wayland & Xorg # 79 char long summary
description: |
A simple desktop recorder for Linux systems. Built using Rust, GTK+ 3 and ffmpeg. It supports recording audio and video on almost all Linux interfaces with support for Wayland display server on GNOME session.
The following formats are currently supported: mkv, avi, mp4, wmv, gif and nut (And only WebM for Wayland's GNOME session). You can stop the recording process easily by right-clicking the icon and choosing "Stop Record". Or middle-clicking the recording icon in the notifications area (but doesn't work on all interfaces).
The following formats are currently supported: mkv, avi, mp4, wmv, gif and nut. You can stop the recording process easily by right-clicking the icon and choosing "Stop Record". Or middle-clicking the recording icon in the notifications area (but doesn't work on all interfaces).
You can choose the audio input source you want from the list. You can also set the default values you want by simply changing them in the interface, and the program will save them for you for the next time you open it.
Based on GREEN RECORDER but rewritten in RUST
@ -21,7 +21,6 @@ parts:
stage-packages:
- libappindicator3-1
- x11-utils
- xdg-utils
build-packages:
- libappindicator3-dev
- clang

View File

@ -1,9 +1,7 @@
extern crate subprocess;
use chrono::prelude::*;
use gtk::{
CheckButton, ComboBoxExt, ComboBoxText, Entry, EntryExt, FileChooser, FileChooserExt,
SpinButton, SpinButtonExt, ToggleButtonExt,
};
use gtk::prelude::*;
use gtk::{CheckButton, ComboBoxText, Dialog, Entry, FileChooser, ProgressBar, SpinButton, Window};
use std::collections::HashMap;
use std::path::PathBuf;
use std::process::Command;
@ -15,6 +13,48 @@ use subprocess::Exec;
use zbus::dbus_proxy;
use zvariant::Value;
#[derive(Clone)]
pub struct ProgressWidget {
pub dialog: Dialog,
pub progress: ProgressBar,
}
impl ProgressWidget {
pub fn new(window: &Window) -> ProgressWidget {
ProgressWidget {
dialog: Dialog::new(),
progress: ProgressBar::new(),
}
.init(&window)
}
pub fn init(&self, window: &Window) -> ProgressWidget {
self.dialog.set_title("Progress");
self.dialog.set_transient_for(Some(window));
self.progress.set_fraction(0.0);
self.dialog.get_content_area().add(&self.progress);
self.progress.set_show_text(true);
self.dialog.set_deletable(false);
self.dialog.set_modal(true);
self.clone()
}
pub fn set_progress(&self, title: String, value: i32, max: i32) {
let progress_precentage: f64 = value as f64 / max as f64;
self.progress.set_text(Some(&title));
self.progress.set_fraction(progress_precentage);
}
pub fn show(&self) {
self.progress.set_fraction(0.0);
self.dialog.show_all();
}
pub fn hide(&self) {
self.dialog.hide();
}
}
trait GnomeScreencastResult {}
#[dbus_proxy(
@ -54,6 +94,7 @@ pub struct Ffmpeg {
pub process_id: Option<u32>,
pub saved_filename: Option<String>,
pub unbound: Option<Sender<bool>>,
pub progress_widget: ProgressWidget,
}
impl Ffmpeg {
@ -145,7 +186,9 @@ impl Ffmpeg {
ffmpeg_command.arg("-i");
ffmpeg_command.arg(format!(
"{}+{},{}",
std::env::var("DISPLAY").unwrap_or(":1".to_string()).as_str(),
std::env::var("DISPLAY")
.unwrap_or(":1".to_string())
.as_str(),
x,
y
));
@ -175,8 +218,12 @@ impl Ffmpeg {
}
pub fn stop_record(&self) {
&self.progress_widget.show();
// kill the process to stop recording
if self.process_id.is_some() {
&self
.progress_widget
.set_progress("Stop Recording".to_string(), 1, 5);
Command::new("kill")
.arg(format!("{}", self.process_id.unwrap()))
.output()
@ -198,6 +245,11 @@ impl Ffmpeg {
)
.exists();
if self.unbound.is_some() {
&self.progress_widget.set_progress(
"Stop Wayland Video Recording".to_string(),
2,
5,
);
self.unbound
.as_ref()
.unwrap()
@ -230,6 +282,12 @@ impl Ffmpeg {
.unwrap();
if is_audio_record {
&self.progress_widget.set_progress(
"Stop Wayland Audio Recording".to_string(),
3,
5,
);
// merge audio with video
let mut ffmpeg_audio_merge_command = Command::new("ffmpeg");
ffmpeg_audio_merge_command.arg("-i");
@ -264,6 +322,11 @@ impl Ffmpeg {
}
}
} else if is_audio_record {
&self.progress_widget.set_progress(
"Convert Audio to choosen format".to_string(),
3,
5,
);
println!("convert audio");
Command::new("ffmpeg")
.arg("-f")
@ -286,8 +349,18 @@ impl Ffmpeg {
// execute command after finish recording
if !(self.command.get_text().trim() == "") {
&self.progress_widget.set_progress(
"execute custom command after finish".to_string(),
4,
5,
);
Exec::shell(self.command.get_text().trim()).popen().unwrap();
}
&self
.progress_widget
.set_progress("Finish".to_string(), 5, 5);
&self.progress_widget.hide();
}
// Gnome screencast for record wayland
@ -329,10 +402,19 @@ impl Ffmpeg {
pub fn play_record(self) {
if self.saved_filename.is_some() {
Command::new("xdg-open")
.arg(self.saved_filename.unwrap())
.spawn()
.unwrap();
if is_snap() {
// open the video using snapctrl for snap package
Command::new("snapctl")
.arg("user-open")
.arg(self.saved_filename.unwrap())
.spawn()
.unwrap();
} else {
Command::new("xdg-open")
.arg(self.saved_filename.unwrap())
.spawn()
.unwrap();
}
}
}
}
@ -342,3 +424,7 @@ fn is_wayland() -> bool {
.unwrap()
.eq_ignore_ascii_case("wayland")
}
fn is_snap() -> bool {
std::env::var("SNAP").unwrap_or(String::new()).len() > 0
}

View File

@ -8,6 +8,7 @@ mod config_management;
mod ffmpeg_interface;
// use gio::prelude::*;
use ffmpeg_interface::{Ffmpeg, ProgressWidget};
use gettextrs::{bindtextdomain, gettext, setlocale, textdomain, LocaleCategory};
use glib::signal::Inhibit;
use gtk::prelude::*;
@ -18,6 +19,7 @@ use gtk::{
};
use libappindicator::{AppIndicator, AppIndicatorStatus};
use std::cell::RefCell;
use std::ops::Add;
use std::path::Path;
use std::process::{Command, Stdio};
use std::rc::Rc;
@ -40,20 +42,30 @@ fn main() {
if user_interface_path_abs.exists() {
builder = Builder::from_file(user_interface_path_abs);
} else {
builder = Builder::from_file("interfaces/main.ui");
builder = Builder::from_file(
std::env::var("INTERFACES_DIR")
.unwrap_or(String::from("interfaces/"))
.add("main.ui"),
);
}
// translate
let mut po_path_abs = {
let mut current_exec_dir = std::env::current_exe().unwrap();
current_exec_dir.pop();
current_exec_dir
}
.join(Path::new("po"));
if !po_path_abs.exists() {
po_path_abs = std::fs::canonicalize(Path::new(
&std::env::var("PO_DIR").unwrap_or(String::from("po")),
))
.unwrap();
}
setlocale(LocaleCategory::LcAll, "");
bindtextdomain(
"blue-recorder",
{
let mut current_exec_dir = std::env::current_exe().unwrap();
current_exec_dir.pop();
current_exec_dir
}
.join(Path::new("po")).to_str().unwrap(),
);
bindtextdomain("blue-recorder", po_path_abs.to_str().unwrap());
textdomain("blue-recorder");
// config initialize
@ -85,7 +97,6 @@ fn main() {
let mouse_switch: CheckButton = builder.get_object("mouseswitch").unwrap();
let follow_mouse_switch: CheckButton = builder.get_object("followmouseswitch").unwrap();
let about_menu_item: MenuItem = builder.get_object("about_menu_item").unwrap();
// --- default properties
// Windows
main_window.set_title(&gettext("Blue Recorder"));
@ -96,6 +107,9 @@ fn main() {
&gdk::Screen::get_rgba_visual(&gdk::Screen::get_default().unwrap()).unwrap(),
));
stop_button.hide();
play_button.hide();
// Entries
filename_entry.set_placeholder_text(Some(&gettext("Default filename:")));
command_entry.set_placeholder_text(Some(&gettext("Default command:")));
@ -110,8 +124,7 @@ fn main() {
format_chooser_combobox.append(Some("avi"), &gettext("AVI (Audio Video Interleaved)"));
format_chooser_combobox.append(Some("mp4"), &gettext("MP4 (MPEG-4 Part 14)"));
format_chooser_combobox.append(Some("wmv"), &gettext("WMV (Windows Media Video)"));
// TODO: gif not work at this time, fix it!
// format_chooser_combobox.append(Some("gif"), &gettext("GIF (Graphics Interchange Format)"));
format_chooser_combobox.append(Some("gif"), &gettext("GIF (Graphics Interchange Format)"));
format_chooser_combobox.append(Some("nut"), &gettext("NUT (NUT Recording Format)"));
format_chooser_combobox.set_active(Some(0));
@ -287,21 +300,21 @@ fn main() {
});
// init record struct
let ffmpeg_record_interface: Rc<RefCell<ffmpeg_interface::Ffmpeg>> =
Rc::new(RefCell::new(ffmpeg_interface::Ffmpeg {
filename: (folder_chooser, filename_entry, format_chooser_combobox),
record_video: video_switch,
record_audio: audio_switch,
audio_id: audio_source_combobox,
record_mouse: mouse_switch,
follow_mouse: follow_mouse_switch,
record_frames: frames_spin,
record_delay: delay_spin,
command: command_entry,
process_id: None,
saved_filename: None,
unbound: None,
}));
let ffmpeg_record_interface: Rc<RefCell<Ffmpeg>> = Rc::new(RefCell::new(Ffmpeg {
filename: (folder_chooser, filename_entry, format_chooser_combobox),
record_video: video_switch,
record_audio: audio_switch,
audio_id: audio_source_combobox,
record_mouse: mouse_switch,
follow_mouse: follow_mouse_switch,
record_frames: frames_spin,
record_delay: delay_spin,
command: command_entry,
process_id: None,
saved_filename: None,
unbound: None,
progress_widget: ProgressWidget::new(&main_window),
}));
// App Indicator
let mut indicator_icon_path = {
@ -312,7 +325,12 @@ fn main() {
.join(Path::new("data/blue-recorder.png"));
if !indicator_icon_path.exists() {
indicator_icon_path = std::fs::canonicalize(Path::new("data/blue-recorder.png")).unwrap();
indicator_icon_path = std::fs::canonicalize(Path::new(
&std::env::var("DATA_DIR")
.unwrap_or(String::from("data/"))
.add("blue-recorder.png"),
))
.unwrap();
}
let indicator = Rc::new(RefCell::new(AppIndicator::new(
@ -331,16 +349,25 @@ fn main() {
// when indictor stop recording button clicked
let mut _ffmpeg_record_interface = ffmpeg_record_interface.clone();
let mut _indicator = indicator.clone();
let _stop_button = stop_button.clone();
let _play_button = play_button.clone();
let _record_button = record_button.clone();
indicator_stop_recording.connect_activate(move |_| {
_ffmpeg_record_interface.borrow_mut().clone().stop_record();
_indicator
.borrow_mut()
.set_status(AppIndicatorStatus::Passive);
_record_button.show();
_stop_button.hide();
_play_button.show();
});
let mut _ffmpeg_record_interface = ffmpeg_record_interface.clone();
let mut _area_capture = area_capture.clone();
let mut _indicator = indicator.clone();
let _stop_button = stop_button.clone();
let _record_button = record_button.clone();
record_button.connect_clicked(move |_| {
let _area_capture = _area_capture.borrow_mut().clone();
_ffmpeg_record_interface.borrow_mut().start_record(
@ -352,15 +379,25 @@ fn main() {
_indicator
.borrow_mut()
.set_status(AppIndicatorStatus::Active);
_record_button.hide();
_stop_button.show();
});
let mut _ffmpeg_record_interface = ffmpeg_record_interface.clone();
let mut _indicator = indicator.clone();
let _stop_button = stop_button.clone();
let _play_button = play_button.clone();
let _record_button = record_button.clone();
stop_button.connect_clicked(move |_| {
_ffmpeg_record_interface.borrow_mut().clone().stop_record();
_indicator
.borrow_mut()
.set_status(AppIndicatorStatus::Passive);
_record_button.show();
_stop_button.hide();
_play_button.show();
});
let mut _ffmpeg_record_interface = ffmpeg_record_interface.clone();