feat: added widgets capture, music visualiser, bluetooth, notes
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
|
||||
Item {
|
||||
id: service
|
||||
|
||||
property var daemon: null
|
||||
|
||||
readonly property var pluginData: (daemon && daemon.pluginData) ? daemon.pluginData : ({})
|
||||
readonly property string saveDirectory: String(pluginData.captureDirectory || "~/Videos/GameBar").trim()
|
||||
readonly property int fps: Math.max(10, pluginData.captureFps || 60)
|
||||
readonly property int replaySeconds: Math.max(5, pluginData.captureReplaySeconds || 30)
|
||||
readonly property string audioSource: String(pluginData.captureAudio || "default_output").trim()
|
||||
readonly property string quality: String(pluginData.captureQuality || "very_high").trim()
|
||||
|
||||
// "" | "recording" | "replay"
|
||||
property string mode: ""
|
||||
property string targetMonitor: ""
|
||||
property string lastFile: ""
|
||||
property string lastError: ""
|
||||
property double startedAt: 0
|
||||
property int elapsedSeconds: 0
|
||||
|
||||
readonly property bool recording: mode === "recording"
|
||||
readonly property bool replayArmed: mode === "replay"
|
||||
readonly property bool busy: mode !== ""
|
||||
|
||||
signal notice(string message)
|
||||
|
||||
function expandedDirectory() {
|
||||
const home = Quickshell.env("HOME") || "";
|
||||
if (service.saveDirectory.startsWith("~/"))
|
||||
return home + service.saveDirectory.substring(1);
|
||||
|
||||
return service.saveDirectory;
|
||||
}
|
||||
|
||||
function timestampedFile() {
|
||||
const now = new Date();
|
||||
const stamp = Qt.formatDateTime(now, "yyyy-MM-dd_HH-mm-ss");
|
||||
return service.expandedDirectory() + "/GameBar_" + stamp + ".mp4";
|
||||
}
|
||||
|
||||
function baseArgs(monitor) {
|
||||
return ["-w", monitor, "-f", String(service.fps), "-a", service.audioSource, "-q", service.quality, "-cursor", "no"];
|
||||
}
|
||||
|
||||
function startRecording(monitor) {
|
||||
if (service.busy)
|
||||
return;
|
||||
|
||||
service.targetMonitor = monitor || "focused";
|
||||
service.lastFile = service.timestampedFile();
|
||||
service.lastError = "";
|
||||
recorder.command = ["sh", "-c", 'mkdir -p "$1" && exec gpu-screen-recorder "${@:2}"', "gameBar", service.expandedDirectory()].concat(service.baseArgs(service.targetMonitor)).concat(["-o", service.lastFile]);
|
||||
recorder.running = true;
|
||||
service.mode = "recording";
|
||||
service.startedAt = Date.now();
|
||||
service.elapsedSeconds = 0;
|
||||
service.notice("Recording started");
|
||||
}
|
||||
|
||||
function startReplay(monitor) {
|
||||
if (service.busy)
|
||||
return;
|
||||
|
||||
service.targetMonitor = monitor || "focused";
|
||||
service.lastError = "";
|
||||
recorder.command = ["sh", "-c", 'mkdir -p "$1" && exec gpu-screen-recorder "${@:2}"', "gameBar", service.expandedDirectory()].concat(service.baseArgs(service.targetMonitor)).concat(["-c", "mp4", "-r", String(service.replaySeconds), "-o", service.expandedDirectory()]);
|
||||
recorder.running = true;
|
||||
service.mode = "replay";
|
||||
service.startedAt = Date.now();
|
||||
service.elapsedSeconds = 0;
|
||||
service.notice("Replay buffer armed (" + service.replaySeconds + "s)");
|
||||
}
|
||||
|
||||
// SIGINT stops and saves a recording; in replay mode it just stops.
|
||||
function stop() {
|
||||
if (!service.busy)
|
||||
return;
|
||||
|
||||
recorder.signal(2);
|
||||
service.notice(service.recording ? "Recording saved" : "Replay buffer stopped");
|
||||
}
|
||||
|
||||
// SIGUSR1 writes the last <replaySeconds> to disk, replay mode only.
|
||||
function saveReplay() {
|
||||
if (!service.replayArmed)
|
||||
return;
|
||||
|
||||
recorder.signal(10);
|
||||
service.notice("Replay saved");
|
||||
}
|
||||
|
||||
function toggleRecording(monitor) {
|
||||
if (service.recording)
|
||||
service.stop();
|
||||
else if (!service.busy)
|
||||
service.startRecording(monitor);
|
||||
}
|
||||
|
||||
function toggleReplay(monitor) {
|
||||
if (service.replayArmed)
|
||||
service.stop();
|
||||
else if (!service.busy)
|
||||
service.startReplay(monitor);
|
||||
}
|
||||
|
||||
function screenshot(mode) {
|
||||
// Delegate to the quickCapture plugin when it is installed.
|
||||
Quickshell.execDetached(["dms", "ipc", "call", "quickCapture", "screenshot", mode || "region", "edit"]);
|
||||
}
|
||||
|
||||
Process {
|
||||
id: recorder
|
||||
|
||||
running: false
|
||||
|
||||
stderr: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const raw = String(text || "").trim();
|
||||
if (raw !== "")
|
||||
service.lastError = raw.split("\n").pop();
|
||||
}
|
||||
}
|
||||
|
||||
onExited: exitCode => {
|
||||
service.mode = "";
|
||||
service.elapsedSeconds = 0;
|
||||
// A clean exit still prints encoder chatter on stderr; only keep it
|
||||
// around when the recorder actually failed.
|
||||
if (exitCode === 0 || exitCode === 130) {
|
||||
service.lastError = "";
|
||||
return;
|
||||
}
|
||||
|
||||
if (service.lastError !== "")
|
||||
service.notice("Recorder: " + service.lastError);
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
running: service.busy
|
||||
interval: 1000
|
||||
repeat: true
|
||||
onTriggered: service.elapsedSeconds = Math.floor((Date.now() - service.startedAt) / 1000)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user