✨ ags(scripts/recording): properly run recorder process
This commit is contained in:
+83
-29
@@ -1,37 +1,41 @@
|
|||||||
import { execAsync, GLib, GObject, property, register, signal } from "astal";
|
import { execAsync, Gio, GLib, GObject } from "astal";
|
||||||
import { Connectable } from "astal/binding";
|
import { property, register, signal } from "astal/gobject";
|
||||||
import { Gdk } from "astal/gtk3";
|
import { Gdk } from "astal/gtk3";
|
||||||
import { getDateTime } from "./time";
|
import { getDateTime } from "./time";
|
||||||
import { getUserDirs } from "./utils";
|
import { makeDirectory } from "./utils";
|
||||||
|
|
||||||
@register({ GTypeName: "ScreenRecording" })
|
export { Recording };
|
||||||
class Recording extends GObject.Object implements Connectable {
|
|
||||||
|
|
||||||
|
@register({ GTypeName: "Recording" })
|
||||||
|
class Recording extends GObject.Object {
|
||||||
private static instance: Recording;
|
private static instance: Recording;
|
||||||
|
|
||||||
@signal()
|
@signal()
|
||||||
declare started: () => void;
|
declare started: () => void;
|
||||||
@signal(String)
|
@signal()
|
||||||
declare stopped: (outputFile: string) => void;
|
declare stopped: () => void;
|
||||||
@signal(String)
|
|
||||||
declare outputChanged: (newPath: string) => void;
|
|
||||||
|
|
||||||
#recording: boolean = false;
|
#recording: boolean = false;
|
||||||
#path: string = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_VIDEOS) || `${GLib.get_home_dir()}/Recordings`;
|
#path: string = "~/Recordings";
|
||||||
|
|
||||||
/** Default extension: mp4(h264) */
|
/** Default extension: mp4(h264) */
|
||||||
#extension: string = "mp4";
|
#extension: string = "mp4";
|
||||||
#recordAudio: boolean = false;
|
#recordAudio: boolean = false;
|
||||||
#monitor: (number|null) = null;
|
|
||||||
#area: (Gdk.Rectangle|null) = null;
|
#area: (Gdk.Rectangle|null) = null;
|
||||||
#pid: (number|null) = null;
|
#startedAt: (GLib.DateTime|null) = null;
|
||||||
|
#process: (Gio.Subprocess|null) = null;
|
||||||
|
#output: (string|null) = null;
|
||||||
|
|
||||||
|
@property()
|
||||||
|
/** GLib.DateTime of when recording started */
|
||||||
|
public get startedAt() { return this.#startedAt; }
|
||||||
|
|
||||||
@property(Boolean)
|
@property(Boolean)
|
||||||
public get recording() { return this.#recording; }
|
public get recording() { return this.#recording; }
|
||||||
private set recording(newValue: boolean) {
|
private set recording(newValue: boolean) {
|
||||||
(!newValue && this.recording) ?
|
(!newValue && this.#recording) ?
|
||||||
this.stopRecording()
|
this.stopRecording()
|
||||||
: this.startRecording(this.#monitor || 0, this.#area || undefined);
|
: this.startRecording(this.#area || undefined);
|
||||||
|
|
||||||
this.#recording = newValue;
|
this.#recording = newValue;
|
||||||
this.notify("recording");
|
this.notify("recording");
|
||||||
@@ -40,6 +44,8 @@ class Recording extends GObject.Object implements Connectable {
|
|||||||
@property(String)
|
@property(String)
|
||||||
public get path() { return this.#path; }
|
public get path() { return this.#path; }
|
||||||
public set path(newPath: string) {
|
public set path(newPath: string) {
|
||||||
|
if(this.recording) return;
|
||||||
|
|
||||||
this.#path = newPath;
|
this.#path = newPath;
|
||||||
this.notify("path");
|
this.notify("path");
|
||||||
}
|
}
|
||||||
@@ -47,19 +53,28 @@ class Recording extends GObject.Object implements Connectable {
|
|||||||
@property(String)
|
@property(String)
|
||||||
public get extension() { return this.#extension; }
|
public get extension() { return this.#extension; }
|
||||||
public set extension(newExt: string) {
|
public set extension(newExt: string) {
|
||||||
|
if(this.recording) return;
|
||||||
|
|
||||||
this.#extension = newExt;
|
this.#extension = newExt;
|
||||||
this.notify("extension");
|
this.notify("extension");
|
||||||
}
|
}
|
||||||
|
|
||||||
@property(Boolean)
|
/** Recording output file name. %NULL if screen is not being recorded */
|
||||||
|
public get output() { return this.#output; }
|
||||||
|
|
||||||
|
/** Currently unsupported property */
|
||||||
public get recordAudio() { return this.#recordAudio; }
|
public get recordAudio() { return this.#recordAudio; }
|
||||||
public set recordAudio(newValue: boolean) {
|
public set recordAudio(newValue: boolean) {
|
||||||
|
if(this.recording) return;
|
||||||
|
|
||||||
this.#recordAudio = newValue;
|
this.#recordAudio = newValue;
|
||||||
this.notify("record-audio");
|
this.notify("record-audio");
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
const videosDir = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_VIDEOS);
|
||||||
|
if(videosDir) this.#path = `${videosDir}/Recordings`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getDefault() {
|
public static getDefault() {
|
||||||
@@ -69,28 +84,67 @@ class Recording extends GObject.Object implements Connectable {
|
|||||||
return this.instance;
|
return this.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public startRecording(monitor?: number, area?: Gdk.Rectangle) {
|
public startRecording(area?: Gdk.Rectangle) {
|
||||||
if(this.#recording)
|
if(this.recording)
|
||||||
throw new Error("Screen Recording is already running!");
|
throw new Error("Screen Recording is already running!");
|
||||||
|
|
||||||
const output = `${getDateTime().get().format("%Y-%m-%d-%H%M%S")}_rec.${this.extension || "mp4"}`;
|
this.#output = `${getDateTime().get().format("%Y-%m-%d-%H%M%S")}_rec.${this.extension || "mp4"}`;
|
||||||
this.#recording = true;
|
this.#recording = true;
|
||||||
|
this.notify("recording");
|
||||||
this.emit("started");
|
this.emit("started");
|
||||||
execAsync([
|
makeDirectory(this.path);
|
||||||
`sh ${ GLib.get_user_config_dir()}/ags/scripts/sh/recording.sh`,
|
|
||||||
`${ area ? `-g ${area?.x || 0},${area?.y || 0} ${area?.width || 1}x${area?.height || 1}` : "" }`,
|
|
||||||
`-f ${output}`
|
|
||||||
]).then(async (stdout: string) => {
|
|
||||||
const pid: number = Number.parseInt(
|
|
||||||
(await execAsync(`echo ${stdout} | head -n 1`)).split(':')[1]);
|
|
||||||
|
|
||||||
this.#pid = pid;
|
const cancellable = Gio.Cancellable.new();
|
||||||
|
cancellable.cancel = () => {};
|
||||||
|
|
||||||
|
const areaString = `${area?.x ?? 0},${area?.y ?? 0} ${area?.width ?? 1}x${area?.height ?? 1}`;
|
||||||
|
|
||||||
|
this.#process = Gio.Subprocess.new([
|
||||||
|
"wf-recorder",
|
||||||
|
...(area ? [ `-g`, areaString ] : []),
|
||||||
|
"-f",
|
||||||
|
`${this.path}/${this.output!}`
|
||||||
|
], Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_PIPE);
|
||||||
|
|
||||||
|
this.#process.wait_async(cancellable, () => {
|
||||||
|
this.stopRecording();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.#startedAt = getDateTime().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public stopRecording() {
|
public stopRecording() {
|
||||||
|
if(!this.#process) return;
|
||||||
|
|
||||||
}
|
!this.#process.get_if_exited() && execAsync([
|
||||||
}
|
"kill", "-s", "SIGTERM", this.#process.get_identifier()!
|
||||||
|
]);
|
||||||
|
|
||||||
export { Recording };
|
const path = this.#path;
|
||||||
|
const output = this.#output;
|
||||||
|
|
||||||
|
this.#process = null;
|
||||||
|
this.#recording = false;
|
||||||
|
this.#startedAt = null;
|
||||||
|
this.#output = null;
|
||||||
|
this.notify("recording");
|
||||||
|
this.emit("stopped");
|
||||||
|
|
||||||
|
execAsync([
|
||||||
|
"notify-send", "-A", "View",
|
||||||
|
"-A", "Open", "-a", "Screen Recording",
|
||||||
|
"Screen recording saved",
|
||||||
|
`Saved as ${path}/${output}`
|
||||||
|
]).then((stdout: string) => {
|
||||||
|
stdout = stdout.replaceAll('\n', "").trim();
|
||||||
|
if(stdout.length > 1 || stdout === "") return;
|
||||||
|
|
||||||
|
const selected = Number.parseInt(stdout);
|
||||||
|
if(selected === 0)
|
||||||
|
execAsync(["nautilus", "-s", output!, path]);
|
||||||
|
|
||||||
|
if(selected === 1)
|
||||||
|
execAsync(["xdg-open", `${path}/${output}`]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user