qs: add replay functionality (yet disabled)
This commit is contained in:
@@ -10,6 +10,10 @@ Item {
|
||||
RecordService.startOrStop();
|
||||
}
|
||||
|
||||
function saveReplay() {
|
||||
RecordService.stopReplay();
|
||||
}
|
||||
|
||||
target: "recording"
|
||||
}
|
||||
|
||||
|
||||
@@ -7,18 +7,33 @@ import qs.Utils
|
||||
pragma Singleton
|
||||
|
||||
Singleton {
|
||||
// Connections {
|
||||
// function onSinkChanged() {
|
||||
// if (!isReplayInitStarted && AudioService.sink) {
|
||||
// Logger.i("RecordService", "Audio sink available, starting replay buffer.");
|
||||
// startReplay();
|
||||
// isReplayInitStarted = true;
|
||||
// }
|
||||
// }
|
||||
// target: AudioService
|
||||
// }
|
||||
|
||||
readonly property string recordingDir: Paths.recordingDir
|
||||
property bool isRecording: false
|
||||
property bool isReplayInitStarted: false
|
||||
property bool isReplayStarted: false
|
||||
property bool isStopping: false
|
||||
property bool isReplayStopping: false
|
||||
readonly property string codec: "libx264"
|
||||
readonly property string container: "mkv"
|
||||
readonly property string pixelFormat: "yuv420p"
|
||||
property string recordingDisplay: ""
|
||||
readonly property int replayDuration: 15
|
||||
readonly property int framerate: 60
|
||||
readonly property var codecParams: Object.freeze(["preset=ultrafast", "crf=15", "tune=zerolatency", "color_range=tv"])
|
||||
readonly property var filterArgs: ""
|
||||
|
||||
function getFilename() {
|
||||
function getFilename(prefix = "recording") {
|
||||
var d = new Date();
|
||||
var year = d.getFullYear();
|
||||
var month = ("0" + (d.getMonth() + 1)).slice(-2);
|
||||
@@ -26,7 +41,7 @@ Singleton {
|
||||
var hours = ("0" + d.getHours()).slice(-2);
|
||||
var minutes = ("0" + d.getMinutes()).slice(-2);
|
||||
var seconds = ("0" + d.getSeconds()).slice(-2);
|
||||
return "recording_" + year + "-" + month + "-" + day + "_" + hours + "." + minutes + "." + seconds + "." + container;
|
||||
return prefix + "_" + year + "-" + month + "-" + day + "_" + hours + "." + minutes + "." + seconds + "." + container;
|
||||
}
|
||||
|
||||
function getAudioSink() {
|
||||
@@ -37,6 +52,15 @@ Singleton {
|
||||
return Niri.focusedOutput || null;
|
||||
}
|
||||
|
||||
function getPrimaryVideoSource() {
|
||||
for (const screen of Quickshell.screens) {
|
||||
if (screen.name === "DP-1")
|
||||
return screen.name;
|
||||
|
||||
}
|
||||
return Quickshell.screens.length ? Quickshell.screens[0].name : null;
|
||||
}
|
||||
|
||||
function startOrStop() {
|
||||
if (isRecording)
|
||||
stop();
|
||||
@@ -76,7 +100,7 @@ Singleton {
|
||||
Logger.e("RecordService", "No audio sink available.");
|
||||
return ;
|
||||
}
|
||||
recordProcess.filePath = recordingDir + getFilename();
|
||||
recordProcess.filePath = recordingDir + getFilename("recording");
|
||||
recordProcess.command = ["wf-recorder", "--audio=" + audioSink, "-o", source, "--codec", codec, "--pixel-format", pixelFormat, "--framerate", framerate.toString(), "-f", recordProcess.filePath];
|
||||
for (const param of codecParams) {
|
||||
recordProcess.command.push("-p");
|
||||
@@ -87,39 +111,78 @@ Singleton {
|
||||
recordProcess.command.push(filterArgs);
|
||||
}
|
||||
Logger.i("RecordService", "Starting recording with command: " + recordProcess.command.join(" "));
|
||||
recordProcess.onErrorExit = function() {
|
||||
Logger.e("RecordService", "Recording process exited with an error.");
|
||||
SendNotification.show("Recording failed", "An error occurred while trying to record the screen.");
|
||||
};
|
||||
recordProcess.onNormalExit = function() {
|
||||
Logger.i("RecordService", "Recording stopped, file saved to: " + recordProcess.filePath);
|
||||
SendNotification.show("Recording stopped", recordProcess.filePath);
|
||||
};
|
||||
recordProcess.running = true;
|
||||
SendNotification.show("Recording started", "Recording to " + recordProcess.filePath);
|
||||
}
|
||||
|
||||
function startReplay() {
|
||||
if (isReplayStarted || isReplayStopping) {
|
||||
Logger.w("RecordService", "Replay buffer already active, cannot start.");
|
||||
return ;
|
||||
}
|
||||
isReplayStarted = true;
|
||||
const source = getPrimaryVideoSource();
|
||||
if (!source) {
|
||||
SendNotification.show("Replay buffer failed", "Could not determine which display to record from.");
|
||||
Logger.e("RecordService", "No recording source available for replay buffer.");
|
||||
return ;
|
||||
}
|
||||
const audioSink = getAudioSink();
|
||||
if (!audioSink) {
|
||||
SendNotification.show("Replay buffer failed", "No audio sink available to record from.");
|
||||
Logger.e("RecordService", "No audio sink available for replay buffer.");
|
||||
return ;
|
||||
}
|
||||
replayProcess.filePath = recordingDir + getFilename("replay");
|
||||
replayProcess.command = ["wf-recorder", "--history", replayDuration, "--audio=" + audioSink, "-o", source, "--codec", codec, "--pixel-format", pixelFormat, "--framerate", framerate.toString(), "-f", replayProcess.filePath];
|
||||
for (const param of codecParams) {
|
||||
replayProcess.command.push("-p");
|
||||
replayProcess.command.push(param);
|
||||
}
|
||||
if (filterArgs !== "") {
|
||||
replayProcess.command.push("-F");
|
||||
replayProcess.command.push(filterArgs);
|
||||
}
|
||||
Logger.i("RecordService", "Starting replay buffer with command: " + replayProcess.command.join(" "));
|
||||
replayProcess.running = true;
|
||||
}
|
||||
|
||||
function stopReplay() {
|
||||
if (!isReplayStarted) {
|
||||
Logger.w("RecordService", "Replay buffer not active, cannot stop.");
|
||||
return ;
|
||||
}
|
||||
if (isReplayStopping) {
|
||||
Logger.w("RecordService", "Already stopping replay buffer, please wait.");
|
||||
return ;
|
||||
}
|
||||
isReplayStopping = true;
|
||||
replayStopTimeout.restart();
|
||||
replayProcess.signal(10); // SIGUSR1
|
||||
}
|
||||
|
||||
Component.onDestruction: function() {
|
||||
if (recordProcess.running)
|
||||
recordProcess.running = false;
|
||||
|
||||
if (replayProcess.running)
|
||||
replayProcess.signal(2);
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: recordProcess
|
||||
|
||||
property string filePath: ""
|
||||
property var onNormalExit: null
|
||||
property var onErrorExit: null
|
||||
|
||||
running: false
|
||||
onExited: function(exitCode, exitStatus) {
|
||||
if (exitCode === 0) {
|
||||
Logger.i("RecordService", "Recording stopped successfully.");
|
||||
if (onNormalExit) {
|
||||
onNormalExit();
|
||||
onNormalExit = null;
|
||||
}
|
||||
SendNotification.show("Recording stopped", "File saved to: " + filePath);
|
||||
} else {
|
||||
Logger.e("RecordService", "Recording process exited with error code: " + exitCode);
|
||||
if (onErrorExit) {
|
||||
onErrorExit();
|
||||
onErrorExit = null;
|
||||
}
|
||||
SendNotification.show("Recording failed", "An error occurred while trying to record the screen.");
|
||||
}
|
||||
isRecording = false;
|
||||
isStopping = false;
|
||||
@@ -127,4 +190,50 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: replayProcess
|
||||
|
||||
property string filePath: ""
|
||||
|
||||
running: false
|
||||
onExited: function(exitCode, exitStatus) {
|
||||
replayStopTimeout.stop();
|
||||
if (exitCode === 0) {
|
||||
Logger.i("RecordService", "Replay buffer saved successfully.");
|
||||
SendNotification.show("Replay saved", "File saved to: " + filePath);
|
||||
} else {
|
||||
Logger.e("RecordService", "Replay process exited with error code: " + exitCode);
|
||||
SendNotification.show("Replay failed", "An error occurred while trying to record the replay buffer.");
|
||||
}
|
||||
isReplayStarted = false;
|
||||
isReplayStopping = false;
|
||||
replayRestartTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: replayRestartTimer
|
||||
|
||||
interval: 1000
|
||||
repeat: false
|
||||
onTriggered: function() {
|
||||
if (!isReplayStarted && !isReplayStopping)
|
||||
startReplay();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: replayStopTimeout
|
||||
|
||||
interval: 5000
|
||||
repeat: false
|
||||
onTriggered: function() {
|
||||
if (isReplayStopping) {
|
||||
Logger.w("RecordService", "Replay buffer did not stop in time, killing process.");
|
||||
replayProcess.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user