ScreenRecorder Service + Reconnected the utility button
This commit is contained in:
parent
c1f22d5e87
commit
e7588b29d9
10 changed files with 142 additions and 51 deletions
|
|
@ -18,14 +18,20 @@ NBox {
|
|||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
// Record
|
||||
// Screen Recorder
|
||||
NIconButton {
|
||||
icon: "videocam"
|
||||
showFilled: ScreenRecorder.isRecording
|
||||
onClicked: {
|
||||
ScreenRecorder.toggleRecording()
|
||||
}
|
||||
}
|
||||
|
||||
// Wallpaper
|
||||
NIconButton {
|
||||
icon: "image"
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ NBox {
|
|||
|
||||
readonly property real scaling: Scaling.scale(screen)
|
||||
readonly property bool weatherReady: (Location.data.weather !== null)
|
||||
// TBC weatherReady is not turning to false when we reset weather...
|
||||
|
||||
// TBC weatherReady is not turning to false when we reset weather...
|
||||
Layout.fillWidth: true
|
||||
// Height driven by content
|
||||
implicitHeight: content.implicitHeight + Style.marginLarge * 2 * scaling
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ Singleton {
|
|||
vol = 0
|
||||
}
|
||||
root._volume = vol
|
||||
|
||||
}
|
||||
|
||||
function onMutedChanged() {
|
||||
|
|
|
|||
64
Services/ScreenRecorder.qml
Normal file
64
Services/ScreenRecorder.qml
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Services
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property var settings: Settings.data.screenRecorder
|
||||
property bool isRecording: false
|
||||
property string outputPath: ""
|
||||
|
||||
// Start or Stop recording
|
||||
function toggleRecording() {
|
||||
isRecording ? stopRecording() : startRecording()
|
||||
}
|
||||
|
||||
// Start screen recording using Quickshell.execDetached
|
||||
function startRecording() {
|
||||
if (isRecording) {
|
||||
return
|
||||
}
|
||||
isRecording = true
|
||||
|
||||
var filename = Time.getFormattedTimestamp() + ".mp4"
|
||||
var videoDir = settings.directory
|
||||
if (videoDir && !videoDir.endsWith("/")) {
|
||||
videoDir += "/"
|
||||
}
|
||||
outputPath = videoDir + filename
|
||||
var command = "gpu-screen-recorder -w portal" + " -f " + settings.frameRate + " -ac " + settings.audioCodec
|
||||
+ " -k " + settings.videoCodec + " -a " + settings.audioSource + " -q " + settings.quality
|
||||
+ " -cursor " + (settings.showCursor ? "yes" : "no") + " -cr " + settings.colorRange + " -o " + outputPath
|
||||
|
||||
//console.log("[ScreenRecorder]", command)
|
||||
Quickshell.execDetached(["sh", "-c", command])
|
||||
console.log("[ScreenRecorder] Started recording")
|
||||
}
|
||||
|
||||
// Stop recording using Quickshell.execDetached
|
||||
function stopRecording() {
|
||||
if (!isRecording) {
|
||||
return
|
||||
}
|
||||
|
||||
Quickshell.execDetached(["sh", "-c", "pkill -SIGINT -f 'gpu-screen-recorder.*portal'"])
|
||||
console.log("[ScreenRecorder] Finished recording:", outputPath)
|
||||
|
||||
// Just in case, force kill after 3 seconds
|
||||
killTimer.running = true
|
||||
isRecording = false
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: killTimer
|
||||
interval: 3000
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
Quickshell.execDetached(["sh", "-c", "pkill -9 -f 'gpu-screen-recorder.*portal' 2>/dev/null || true"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -105,9 +105,8 @@ Singleton {
|
|||
property string videoCodec: "h264"
|
||||
property string quality: "very_high"
|
||||
property string colorRange: "limited"
|
||||
property bool showCursor: true
|
||||
// New: optional audio source selection (default: system output)
|
||||
property string audioSource: "default_output"
|
||||
property bool showCursor: true
|
||||
}
|
||||
|
||||
// wallpaper
|
||||
|
|
|
|||
|
|
@ -43,9 +43,29 @@ Singleton {
|
|||
return Math.floor(Date.now() / 1000)
|
||||
}
|
||||
|
||||
// Format an easy to read approximate duration ex: 4h32m
|
||||
// Used to display the time remaining on the Battery widget
|
||||
function formatVagueHumanReadableDuration(totalSeconds) {
|
||||
|
||||
/**
|
||||
* Formats a Date object into a YYYYMMDD-HHMMSS string.
|
||||
* @param {Date} [date=new Date()] - The date to format. Defaults to the current date and time.
|
||||
* @returns {string} The formatted date string.
|
||||
*/
|
||||
function getFormattedTimestamp(date = new Date()) {
|
||||
const year = date.getFullYear()
|
||||
|
||||
// getMonth() is zero-based, so we add 1
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
|
||||
return `${year}${month}${day}-${hours}${minutes}${seconds}`
|
||||
}
|
||||
|
||||
// Format an easy to read approximate duration ex: 4h32m
|
||||
// Used to display the time remaining on the Battery widget
|
||||
function formatVagueHumanReadableDuration(totalSeconds) {
|
||||
const hours = Math.floor(totalSeconds / 3600)
|
||||
const minutes = Math.floor((totalSeconds - (hours * 3600)) / 60)
|
||||
const seconds = totalSeconds - (hours * 3600) - (minutes * 60)
|
||||
|
|
@ -61,13 +81,13 @@ Singleton {
|
|||
str += seconds.toString() + "s"
|
||||
}
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
Timer {
|
||||
interval: 1000
|
||||
repeat: true
|
||||
running: true
|
||||
|
||||
onTriggered: root.date = new Date()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ Singleton {
|
|||
|
||||
changeWallpaperProcess.running = true
|
||||
} else {
|
||||
|
||||
// Fallback: update the settings directly for non-SWWW mode
|
||||
//console.log("[WP] Not using Swww, setting wallpaper directly")
|
||||
}
|
||||
|
|
@ -159,6 +160,7 @@ Singleton {
|
|||
running: false
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
|
||||
// console.log(this.text)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ Rectangle {
|
|||
property string icon
|
||||
property string tooltipText
|
||||
property bool showBorder: true
|
||||
property bool showFilled: false
|
||||
property bool enabled: true
|
||||
property bool hovering: false
|
||||
property real fontPointSize: Style.fontSizeMedium
|
||||
|
|
@ -25,7 +26,7 @@ Rectangle {
|
|||
implicitWidth: size
|
||||
implicitHeight: size
|
||||
|
||||
color: root.hovering ? Colors.accentPrimary : "transparent"
|
||||
color: (root.hovering || showFilled) ? Colors.accentPrimary : "transparent"
|
||||
radius: width * 0.5
|
||||
border.color: showBorder ? Colors.accentPrimary : "transparent"
|
||||
border.width: Math.max(1, Style.borderThin * scaling)
|
||||
|
|
@ -41,7 +42,7 @@ Rectangle {
|
|||
font.variableAxes: {
|
||||
"wght": (Font.Normal + Font.Bold) / 2.0
|
||||
}
|
||||
color: root.hovering ? Colors.onAccent : showBorder ? Colors.accentPrimary : Colors.textPrimary
|
||||
color: (root.hovering || showFilled) ? Colors.onAccent : showBorder ? Colors.accentPrimary : Colors.textPrimary
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
opacity: root.enabled ? Style.opacityFull : Style.opacityMedium
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue