mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-06 05:25:41 -05:00
add shell
This commit is contained in:
162
Services/MprisController.qml
Normal file
162
Services/MprisController.qml
Normal file
@@ -0,0 +1,162 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQml.Models
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.Mpris
|
||||
|
||||
/**
|
||||
* A service that provides easy access to the active Mpris player.
|
||||
*/
|
||||
Singleton {
|
||||
id: root
|
||||
property MprisPlayer trackedPlayer: null
|
||||
property MprisPlayer activePlayer: trackedPlayer ?? Mpris.players.values[0] ?? null
|
||||
signal trackChanged(reverse: bool)
|
||||
|
||||
property bool __reverse: false
|
||||
|
||||
property var activeTrack
|
||||
|
||||
Instantiator {
|
||||
model: Mpris.players
|
||||
|
||||
Connections {
|
||||
required property MprisPlayer modelData
|
||||
target: modelData
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("MPRIS Player connected:", modelData.identity)
|
||||
if (root.trackedPlayer == null || modelData.isPlaying) {
|
||||
root.trackedPlayer = modelData
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
if (root.trackedPlayer == null || !root.trackedPlayer.isPlaying) {
|
||||
for (const player of Mpris.players.values) {
|
||||
if (player.playbackState.isPlaying) {
|
||||
root.trackedPlayer = player
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (trackedPlayer == null && Mpris.players.values.length != 0) {
|
||||
trackedPlayer = Mpris.players.values[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onPlaybackStateChanged() {
|
||||
if (root.trackedPlayer !== modelData) root.trackedPlayer = modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: activePlayer
|
||||
|
||||
function onPostTrackChanged() {
|
||||
root.updateTrack()
|
||||
}
|
||||
|
||||
function onTrackArtUrlChanged() {
|
||||
if (root.activePlayer.uniqueId == root.activeTrack.uniqueId && root.activePlayer.trackArtUrl != root.activeTrack.artUrl) {
|
||||
const r = root.__reverse
|
||||
root.updateTrack()
|
||||
root.__reverse = r
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onActivePlayerChanged: this.updateTrack()
|
||||
|
||||
function updateTrack() {
|
||||
console.log(`MPRIS Track Update: ${this.activePlayer?.trackTitle ?? ""} : ${this.activePlayer?.trackArtist}`)
|
||||
this.activeTrack = {
|
||||
uniqueId: this.activePlayer?.uniqueId ?? 0,
|
||||
artUrl: this.activePlayer?.trackArtUrl ?? "",
|
||||
title: this.activePlayer?.trackTitle || "Unknown Title",
|
||||
artist: this.activePlayer?.trackArtist || "Unknown Artist",
|
||||
album: this.activePlayer?.trackAlbum || "Unknown Album",
|
||||
}
|
||||
|
||||
this.trackChanged(__reverse)
|
||||
this.__reverse = false
|
||||
}
|
||||
|
||||
property bool isPlaying: this.activePlayer && this.activePlayer.isPlaying
|
||||
property bool canTogglePlaying: this.activePlayer?.canTogglePlaying ?? false
|
||||
function togglePlaying() {
|
||||
if (this.canTogglePlaying) this.activePlayer.togglePlaying()
|
||||
}
|
||||
|
||||
property bool canGoPrevious: this.activePlayer?.canGoPrevious ?? false
|
||||
function previous() {
|
||||
if (this.canGoPrevious) {
|
||||
this.__reverse = true
|
||||
this.activePlayer.previous()
|
||||
}
|
||||
}
|
||||
|
||||
property bool canGoNext: this.activePlayer?.canGoNext ?? false
|
||||
function next() {
|
||||
if (this.canGoNext) {
|
||||
this.__reverse = false
|
||||
this.activePlayer.next()
|
||||
}
|
||||
}
|
||||
|
||||
property bool canChangeVolume: this.activePlayer && this.activePlayer.volumeSupported && this.activePlayer.canControl
|
||||
|
||||
property bool loopSupported: this.activePlayer && this.activePlayer.loopSupported && this.activePlayer.canControl
|
||||
property var loopState: this.activePlayer?.loopState ?? MprisLoopState.None
|
||||
function setLoopState(loopState) {
|
||||
if (this.loopSupported) {
|
||||
this.activePlayer.loopState = loopState
|
||||
}
|
||||
}
|
||||
|
||||
property bool shuffleSupported: this.activePlayer && this.activePlayer.shuffleSupported && this.activePlayer.canControl
|
||||
property bool hasShuffle: this.activePlayer?.shuffle ?? false
|
||||
function setShuffle(shuffle) {
|
||||
if (this.shuffleSupported) {
|
||||
this.activePlayer.shuffle = shuffle
|
||||
}
|
||||
}
|
||||
|
||||
function setActivePlayer(player) {
|
||||
const targetPlayer = player ?? Mpris.players[0]
|
||||
console.log(`[Mpris] Active player ${targetPlayer} << ${activePlayer}`)
|
||||
|
||||
if (targetPlayer && this.activePlayer) {
|
||||
this.__reverse = Mpris.players.indexOf(targetPlayer) < Mpris.players.indexOf(this.activePlayer)
|
||||
} else {
|
||||
this.__reverse = false
|
||||
}
|
||||
|
||||
this.trackedPlayer = targetPlayer
|
||||
}
|
||||
|
||||
// Debug timer
|
||||
Timer {
|
||||
interval: 3000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
console.log(`[MprisController] Players: ${Mpris.players.length}, Active: ${activePlayer?.identity || 'none'}, Playing: ${isPlaying}`)
|
||||
if (activePlayer) {
|
||||
console.log(` Track: ${activePlayer.trackTitle || 'Unknown'} by ${activePlayer.trackArtist || 'Unknown'}`)
|
||||
console.log(` State: ${activePlayer.playbackState}`)
|
||||
} else if (Mpris.players.length === 0) {
|
||||
console.log(" No MPRIS players detected. Try:")
|
||||
console.log(" - mpv --script-opts=mpris-title='{{media-title}}' file.mp3")
|
||||
console.log(" - firefox/chromium (YouTube, Spotify Web)")
|
||||
console.log(" - vlc file.mp3")
|
||||
console.log(" Check available players: busctl --user list | grep mpris")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
85
Services/OSDetectionService.qml
Normal file
85
Services/OSDetectionService.qml
Normal file
@@ -0,0 +1,85 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
QtObject {
|
||||
id: osService
|
||||
|
||||
property string osLogo: ""
|
||||
property string osName: ""
|
||||
|
||||
Process {
|
||||
id: osDetector
|
||||
command: ["lsb_release", "-i", "-s"]
|
||||
running: true
|
||||
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
onRead: (data) => {
|
||||
if (data.trim()) {
|
||||
let osId = data.trim().toLowerCase()
|
||||
console.log("Detected OS:", osId)
|
||||
setOSInfo(osId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
osDetectorFallback.running = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: osDetectorFallback
|
||||
command: ["sh", "-c", "cat /etc/os-release | grep '^ID=' | cut -d'=' -f2 | tr -d '\"'"]
|
||||
running: false
|
||||
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
onRead: (data) => {
|
||||
if (data.trim()) {
|
||||
let osId = data.trim().toLowerCase()
|
||||
console.log("Detected OS (fallback):", osId)
|
||||
setOSInfo(osId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
osService.osLogo = ""
|
||||
osService.osName = "Linux"
|
||||
console.log("OS detection failed, using generic icon")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setOSInfo(osId) {
|
||||
if (osId.includes("arch")) {
|
||||
osService.osLogo = "\uf303"
|
||||
osService.osName = "Arch Linux"
|
||||
} else if (osId.includes("ubuntu")) {
|
||||
osService.osLogo = "\uf31b"
|
||||
osService.osName = "Ubuntu"
|
||||
} else if (osId.includes("fedora")) {
|
||||
osService.osLogo = "\uf30a"
|
||||
osService.osName = "Fedora"
|
||||
} else if (osId.includes("debian")) {
|
||||
osService.osLogo = "\uf306"
|
||||
osService.osName = "Debian"
|
||||
} else if (osId.includes("opensuse")) {
|
||||
osService.osLogo = "\uef6d"
|
||||
osService.osName = "openSUSE"
|
||||
} else if (osId.includes("manjaro")) {
|
||||
osService.osLogo = "\uf312"
|
||||
osService.osName = "Manjaro"
|
||||
} else {
|
||||
osService.osLogo = "\uf033"
|
||||
osService.osName = "Linux"
|
||||
}
|
||||
}
|
||||
}
|
||||
78
Services/WeatherService.qml
Normal file
78
Services/WeatherService.qml
Normal file
@@ -0,0 +1,78 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
QtObject {
|
||||
id: weatherService
|
||||
|
||||
property var weather: ({
|
||||
available: false,
|
||||
temp: 0,
|
||||
tempF: 0,
|
||||
city: "",
|
||||
wCode: "113",
|
||||
humidity: 0,
|
||||
wind: "",
|
||||
sunrise: "06:00",
|
||||
sunset: "18:00",
|
||||
uv: 0,
|
||||
pressure: 0
|
||||
})
|
||||
|
||||
Process {
|
||||
id: weatherFetcher
|
||||
command: ["bash", "-c", "curl -s 'wttr.in/?format=j1' | jq '{current: .current_condition[0], location: .nearest_area[0], astronomy: .weather[0].astronomy[0]}'"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim() && text.trim().startsWith("{")) {
|
||||
try {
|
||||
let parsedData = JSON.parse(text.trim())
|
||||
if (parsedData.current && parsedData.location) {
|
||||
weatherService.weather = {
|
||||
available: true,
|
||||
temp: parseInt(parsedData.current.temp_C || 0),
|
||||
tempF: parseInt(parsedData.current.temp_F || 0),
|
||||
city: parsedData.location.areaName[0]?.value || "Unknown",
|
||||
wCode: parsedData.current.weatherCode || "113",
|
||||
humidity: parseInt(parsedData.current.humidity || 0),
|
||||
wind: (parsedData.current.windspeedKmph || 0) + " km/h",
|
||||
sunrise: parsedData.astronomy?.sunrise || "06:00",
|
||||
sunset: parsedData.astronomy?.sunset || "18:00",
|
||||
uv: parseInt(parsedData.current.uvIndex || 0),
|
||||
pressure: parseInt(parsedData.current.pressure || 0)
|
||||
}
|
||||
console.log("Weather updated:", weatherService.weather.city, weatherService.weather.temp + "°C")
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse weather data:", e.message)
|
||||
weatherService.weather.available = false
|
||||
}
|
||||
} else {
|
||||
console.warn("No valid weather data received")
|
||||
weatherService.weather.available = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Weather fetch failed with exit code:", exitCode)
|
||||
weatherService.weather.available = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 600000 // 10 minutes
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
weatherFetcher.running = true
|
||||
}
|
||||
}
|
||||
}
|
||||
1
Services/qmldir
Normal file
1
Services/qmldir
Normal file
@@ -0,0 +1 @@
|
||||
singleton MprisController 1.0 MprisController.qml
|
||||
Reference in New Issue
Block a user