1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-09 15:05:39 -05:00
Files
DankMaterialShell/Services/PrivacyService.qml
2025-08-07 08:59:12 -04:00

125 lines
4.2 KiB
QML

pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Services.Pipewire
Singleton {
id: root
readonly property bool microphoneActive: {
if (!Pipewire.ready || !Pipewire.nodes?.values) return false
for (let i = 0; i < Pipewire.nodes.values.length; i++) {
const node = Pipewire.nodes.values[i]
if (!node) continue
if ((node.type & PwNodeType.AudioInStream) === PwNodeType.AudioInStream) {
if (!looksLikeSystemVirtualMic(node)) {
console.log(node.audio)
if (node.audio && node.audio.muted) {
return false
}
return true
}
}
}
return false
}
PwObjectTracker {
objects: Pipewire.nodes.values
}
readonly property bool cameraActive: {
if (!Pipewire.ready || !Pipewire.nodes?.values) return false
for (let i = 0; i < Pipewire.nodes.values.length; i++) {
const node = Pipewire.nodes.values[i]
if (!node || !node.ready) continue
if (node.properties && node.properties["media.class"] === "Stream/Input/Video") {
if (node.properties["stream.is-live"] === "true") {
return true
}
}
}
return false
}
readonly property bool screensharingActive: {
if (!Pipewire.ready || !Pipewire.nodes?.values) return false
for (let i = 0; i < Pipewire.nodes.values.length; i++) {
const node = Pipewire.nodes.values[i]
if (!node || !node.ready) continue
if ((node.type & PwNodeType.VideoSource) === PwNodeType.VideoSource) {
if (looksLikeScreencast(node)) {
return true
}
}
if (node.properties && node.properties["media.class"] === "Stream/Input/Audio") {
const mediaName = (node.properties["media.name"] || "").toLowerCase()
const appName = (node.properties["application.name"] || "").toLowerCase()
if (mediaName.includes("desktop") || appName.includes("screen") || appName === "obs") {
if (node.properties["stream.is-live"] === "true") {
if (node.audio && node.audio.muted) {
return false
}
return true
}
}
}
}
return false
}
readonly property bool anyPrivacyActive: microphoneActive || cameraActive || screensharingActive
function looksLikeSystemVirtualMic(node) {
if (!node) return false
const name = (node.name || "").toLowerCase()
const mediaName = (node.properties && node.properties["media.name"] || "").toLowerCase()
const appName = (node.properties && node.properties["application.name"] || "").toLowerCase()
const combined = name + " " + mediaName + " " + appName
return /cava|monitor|system/.test(combined)
}
function looksLikeScreencast(node) {
if (!node) return false
const appName = (node.properties && node.properties["application.name"] || "").toLowerCase()
const nodeName = (node.name || "").toLowerCase()
const combined = appName + " " + nodeName
return /xdg-desktop-portal|xdpw|screencast|screen|gnome shell|kwin|obs/.test(combined)
}
function getMicrophoneStatus() {
return microphoneActive ? "active" : "inactive";
}
function getCameraStatus() {
return cameraActive ? "active" : "inactive";
}
function getScreensharingStatus() {
return screensharingActive ? "active" : "inactive";
}
function getPrivacySummary() {
const active = [];
if (microphoneActive) active.push("microphone");
if (cameraActive) active.push("camera");
if (screensharingActive) active.push("screensharing");
return active.length > 0 ?
"Privacy active: " + active.join(", ") :
"No privacy concerns detected";
}
}