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 {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
// Record
|
// Screen Recorder
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "videocam"
|
icon: "videocam"
|
||||||
|
showFilled: ScreenRecorder.isRecording
|
||||||
|
onClicked: {
|
||||||
|
ScreenRecorder.toggleRecording()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wallpaper
|
// Wallpaper
|
||||||
NIconButton {
|
NIconButton {
|
||||||
icon: "image"
|
icon: "image"
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ NBox {
|
||||||
|
|
||||||
readonly property real scaling: Scaling.scale(screen)
|
readonly property real scaling: Scaling.scale(screen)
|
||||||
readonly property bool weatherReady: (Location.data.weather !== null)
|
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
|
Layout.fillWidth: true
|
||||||
// Height driven by content
|
// Height driven by content
|
||||||
implicitHeight: content.implicitHeight + Style.marginLarge * 2 * scaling
|
implicitHeight: content.implicitHeight + Style.marginLarge * 2 * scaling
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,6 @@ Singleton {
|
||||||
vol = 0
|
vol = 0
|
||||||
}
|
}
|
||||||
root._volume = vol
|
root._volume = vol
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMutedChanged() {
|
function onMutedChanged() {
|
||||||
|
|
|
||||||
|
|
@ -53,30 +53,30 @@ Singleton {
|
||||||
// Default theme colors
|
// Default theme colors
|
||||||
QtObject {
|
QtObject {
|
||||||
id: defaultTheme
|
id: defaultTheme
|
||||||
|
|
||||||
property color backgroundPrimary: "#191724"
|
property color backgroundPrimary: "#191724"
|
||||||
property color backgroundSecondary: "#1f1d2e"
|
property color backgroundSecondary: "#1f1d2e"
|
||||||
property color backgroundTertiary: "#26233a"
|
property color backgroundTertiary: "#26233a"
|
||||||
|
|
||||||
property color surface: "#1f1d2e"
|
property color surface: "#1f1d2e"
|
||||||
property color surfaceVariant: "#37354c"
|
property color surfaceVariant: "#37354c"
|
||||||
|
|
||||||
property color textPrimary: "#e0def4"
|
property color textPrimary: "#e0def4"
|
||||||
property color textSecondary: "#908caa"
|
property color textSecondary: "#908caa"
|
||||||
property color textDisabled: "#6e6a86"
|
property color textDisabled: "#6e6a86"
|
||||||
|
|
||||||
property color accentPrimary: "#ebbcba"
|
property color accentPrimary: "#ebbcba"
|
||||||
property color accentSecondary: "#31748f"
|
property color accentSecondary: "#31748f"
|
||||||
property color accentTertiary: "#9ccfd8"
|
property color accentTertiary: "#9ccfd8"
|
||||||
|
|
||||||
property color error: "#eb6f92"
|
property color error: "#eb6f92"
|
||||||
property color warning: "#f6c177"
|
property color warning: "#f6c177"
|
||||||
|
|
||||||
property color hover: "#c4a7e7"
|
property color hover: "#c4a7e7"
|
||||||
|
|
||||||
property color onAccent: "#191724"
|
property color onAccent: "#191724"
|
||||||
property color outline: "#44415a"
|
property color outline: "#44415a"
|
||||||
|
|
||||||
property color shadow: "#191724"
|
property color shadow: "#191724"
|
||||||
property color overlay: "#191724"
|
property color overlay: "#191724"
|
||||||
}
|
}
|
||||||
|
|
@ -84,30 +84,30 @@ Singleton {
|
||||||
// Wallust theme colors (loaded from Theme.json)
|
// Wallust theme colors (loaded from Theme.json)
|
||||||
QtObject {
|
QtObject {
|
||||||
id: wallustTheme
|
id: wallustTheme
|
||||||
|
|
||||||
property color backgroundPrimary: wallustData.backgroundPrimary
|
property color backgroundPrimary: wallustData.backgroundPrimary
|
||||||
property color backgroundSecondary: wallustData.backgroundSecondary
|
property color backgroundSecondary: wallustData.backgroundSecondary
|
||||||
property color backgroundTertiary: wallustData.backgroundTertiary
|
property color backgroundTertiary: wallustData.backgroundTertiary
|
||||||
|
|
||||||
property color surface: wallustData.surface
|
property color surface: wallustData.surface
|
||||||
property color surfaceVariant: wallustData.surfaceVariant
|
property color surfaceVariant: wallustData.surfaceVariant
|
||||||
|
|
||||||
property color textPrimary: wallustData.textPrimary
|
property color textPrimary: wallustData.textPrimary
|
||||||
property color textSecondary: wallustData.textSecondary
|
property color textSecondary: wallustData.textSecondary
|
||||||
property color textDisabled: wallustData.textDisabled
|
property color textDisabled: wallustData.textDisabled
|
||||||
|
|
||||||
property color accentPrimary: wallustData.accentPrimary
|
property color accentPrimary: wallustData.accentPrimary
|
||||||
property color accentSecondary: wallustData.accentSecondary
|
property color accentSecondary: wallustData.accentSecondary
|
||||||
property color accentTertiary: wallustData.accentTertiary
|
property color accentTertiary: wallustData.accentTertiary
|
||||||
|
|
||||||
property color error: wallustData.error
|
property color error: wallustData.error
|
||||||
property color warning: wallustData.warning
|
property color warning: wallustData.warning
|
||||||
|
|
||||||
property color hover: wallustData.hover
|
property color hover: wallustData.hover
|
||||||
|
|
||||||
property color onAccent: wallustData.onAccent
|
property color onAccent: wallustData.onAccent
|
||||||
property color outline: wallustData.outline
|
property color outline: wallustData.outline
|
||||||
|
|
||||||
property color shadow: wallustData.shadow
|
property color shadow: wallustData.shadow
|
||||||
property color overlay: wallustData.overlay
|
property color overlay: wallustData.overlay
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ Singleton {
|
||||||
|
|
||||||
// --------------------------------
|
// --------------------------------
|
||||||
function loadFromCache() {
|
function loadFromCache() {
|
||||||
const now = Time.timestamp
|
const now = Time.timestamp
|
||||||
if (!data.timestamp || (now >= data.timestamp + githubUpdateFrequency)) {
|
if (!data.timestamp || (now >= data.timestamp + githubUpdateFrequency)) {
|
||||||
console.log("[GitHub] Cache expired or missing, fetching new data from GitHub...")
|
console.log("[GitHub] Cache expired or missing, fetching new data from GitHub...")
|
||||||
fetchFromGitHub()
|
fetchFromGitHub()
|
||||||
|
|
@ -79,7 +79,7 @@ Singleton {
|
||||||
|
|
||||||
// --------------------------------
|
// --------------------------------
|
||||||
function saveData() {
|
function saveData() {
|
||||||
data.timestamp = Time.timestamp
|
data.timestamp = Time.timestamp
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
// Access the FileView's writeAdapter method
|
// Access the FileView's writeAdapter method
|
||||||
var fileView = root.children.find(child => child.objectName === "githubDataFileView")
|
var fileView = root.children.find(child => child.objectName === "githubDataFileView")
|
||||||
|
|
|
||||||
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 videoCodec: "h264"
|
||||||
property string quality: "very_high"
|
property string quality: "very_high"
|
||||||
property string colorRange: "limited"
|
property string colorRange: "limited"
|
||||||
property bool showCursor: true
|
|
||||||
// New: optional audio source selection (default: system output)
|
|
||||||
property string audioSource: "default_output"
|
property string audioSource: "default_output"
|
||||||
|
property bool showCursor: true
|
||||||
}
|
}
|
||||||
|
|
||||||
// wallpaper
|
// wallpaper
|
||||||
|
|
|
||||||
|
|
@ -43,31 +43,51 @@ Singleton {
|
||||||
return Math.floor(Date.now() / 1000)
|
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) {
|
|
||||||
const hours = Math.floor(totalSeconds / 3600)
|
|
||||||
const minutes = Math.floor((totalSeconds - (hours * 3600)) / 60)
|
|
||||||
const seconds = totalSeconds - (hours * 3600) - (minutes * 60)
|
|
||||||
|
|
||||||
var str = ""
|
/**
|
||||||
if (hours) {
|
* Formats a Date object into a YYYYMMDD-HHMMSS string.
|
||||||
str += hours.toString() + "h"
|
* @param {Date} [date=new Date()] - The date to format. Defaults to the current date and time.
|
||||||
}
|
* @returns {string} The formatted date string.
|
||||||
if (minutes) {
|
*/
|
||||||
str += minutes.toString() + "m"
|
function getFormattedTimestamp(date = new Date()) {
|
||||||
}
|
const year = date.getFullYear()
|
||||||
if (!hours && !minutes) {
|
|
||||||
str += seconds.toString() + "s"
|
|
||||||
}
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
// getMonth() is zero-based, so we add 1
|
||||||
interval: 1000
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
repeat: true
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
running: true
|
|
||||||
|
|
||||||
onTriggered: root.date = new Date()
|
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)
|
||||||
|
|
||||||
|
var str = ""
|
||||||
|
if (hours) {
|
||||||
|
str += hours.toString() + "h"
|
||||||
|
}
|
||||||
|
if (minutes) {
|
||||||
|
str += minutes.toString() + "m"
|
||||||
|
}
|
||||||
|
if (!hours && !minutes) {
|
||||||
|
str += seconds.toString() + "s"
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 1000
|
||||||
|
repeat: true
|
||||||
|
running: true
|
||||||
|
|
||||||
|
onTriggered: root.date = new Date()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,9 +50,10 @@ Singleton {
|
||||||
} else {
|
} else {
|
||||||
transitionType = Settings.data.wallpaper.swww.transitionType
|
transitionType = Settings.data.wallpaper.swww.transitionType
|
||||||
}
|
}
|
||||||
|
|
||||||
changeWallpaperProcess.running = true
|
changeWallpaperProcess.running = true
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Fallback: update the settings directly for non-SWWW mode
|
// Fallback: update the settings directly for non-SWWW mode
|
||||||
//console.log("[WP] Not using Swww, setting wallpaper directly")
|
//console.log("[WP] Not using Swww, setting wallpaper directly")
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +141,7 @@ Singleton {
|
||||||
running: false
|
running: false
|
||||||
|
|
||||||
onStarted: {
|
onStarted: {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onExited: function (exitCode, exitStatus) {
|
onExited: function (exitCode, exitStatus) {
|
||||||
|
|
@ -159,6 +160,7 @@ Singleton {
|
||||||
running: false
|
running: false
|
||||||
stdout: StdioCollector {
|
stdout: StdioCollector {
|
||||||
onStreamFinished: {
|
onStreamFinished: {
|
||||||
|
|
||||||
// console.log(this.text)
|
// console.log(this.text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ Rectangle {
|
||||||
property string icon
|
property string icon
|
||||||
property string tooltipText
|
property string tooltipText
|
||||||
property bool showBorder: true
|
property bool showBorder: true
|
||||||
|
property bool showFilled: false
|
||||||
property bool enabled: true
|
property bool enabled: true
|
||||||
property bool hovering: false
|
property bool hovering: false
|
||||||
property real fontPointSize: Style.fontSizeMedium
|
property real fontPointSize: Style.fontSizeMedium
|
||||||
|
|
@ -25,7 +26,7 @@ Rectangle {
|
||||||
implicitWidth: size
|
implicitWidth: size
|
||||||
implicitHeight: size
|
implicitHeight: size
|
||||||
|
|
||||||
color: root.hovering ? Colors.accentPrimary : "transparent"
|
color: (root.hovering || showFilled) ? Colors.accentPrimary : "transparent"
|
||||||
radius: width * 0.5
|
radius: width * 0.5
|
||||||
border.color: showBorder ? Colors.accentPrimary : "transparent"
|
border.color: showBorder ? Colors.accentPrimary : "transparent"
|
||||||
border.width: Math.max(1, Style.borderThin * scaling)
|
border.width: Math.max(1, Style.borderThin * scaling)
|
||||||
|
|
@ -41,7 +42,7 @@ Rectangle {
|
||||||
font.variableAxes: {
|
font.variableAxes: {
|
||||||
"wght": (Font.Normal + Font.Bold) / 2.0
|
"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
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
opacity: root.enabled ? Style.opacityFull : Style.opacityMedium
|
opacity: root.enabled ? Style.opacityFull : Style.opacityMedium
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue