// Reworked the PlayerController a bit pragma Singleton pragma ComponentBehavior: Bound import QtQml.Models import QtQuick import Quickshell import Quickshell.Io import Quickshell.Services.Mpris // import qs Singleton { id: root; property Mpris mpris: Mpris; property MprisPlayer activePlayer: findSpotify(); signal trackChanged(reverse: bool); property bool __reverse: false; property var activeTrack; property string activeTrackPositionFormated: "0:00"; property string activeTrackLengthFormated: "0:00"; PersistentProperties { id: persist reloadableId: "MusicWidget"; property string lastActivePlayerIdentify: ""; onReloaded: { root.updateActiveTrackPosition() } onLoaded: { root.updateActiveTrackPosition() } } Instantiator { model: Mpris.players; Connections { required property MprisPlayer modelData; target: modelData; Component.onCompleted: { 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() { console.log("Playback state has been changed") // if (root.trackedPlayer !== modelData) root.trackedPlayer = modelData; } } } Connections { target: activePlayer function onPostTrackChanged() { root.updateTrack(); } function onTrackArtUrlChanged() { console.log("arturl:", activePlayer.trackArtUrl) //root.updateTrack(); if (root.activePlayer.uniqueId == root.activeTrack.uniqueId && root.activePlayer.trackArtUrl != root.activeTrack.artUrl) { // cantata likes to send cover updates *BEFORE* updating the track info. // as such, art url changes shouldn't be able to break the reverse animation const r = root.__reverse; root.updateTrack(); root.__reverse = r; } } } onActivePlayerChanged: this.updateTrack(); function setPlayerByIdentity(id: string) { for (var i = 0; i < 9; i++) { console.log(i) } mpris.players } function updateTrack() { 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: var) { 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: bool) { if (this.shuffleSupported) { this.activePlayer.shuffle = shuffle; } } function setActivePlayer(player: MprisPlayer) { const targetPlayer = player; console.log(`setactive: ${targetPlayer} from ${activePlayer}`) persist.lastActivePlayerIdentify = targetPlayer.identity this.activePlayer = player; } Timer { // only emit the signal when the position is actually changing. running: root.activePlayer.playbackState == MprisPlaybackState.Playing // Make sure the position updates at least once per second. interval: 1000 repeat: true // emit the positionChanged signal every second. onTriggered: root.updateActiveTrackPosition() } function updateActiveTrackPosition() { activeTrackPositionFormated = Math.floor(root.activePlayer.position / 60) + ":" + ("0" + Math.floor(root.activePlayer.position % 60)).slice(-2) activeTrackLengthFormated = Math.floor(root.activePlayer.length / 60) + ":" + ("0" + Math.floor(root.activePlayer.length % 60)).slice(-2) } function update() { updateActiveTrackPosition() } function trackPositionFormatter(position: double): string { return Math.floor(position / 60) + ":" + ("0" + Math.floor(position % 60)).slice(-2) } function findSpotify(): MprisPlayer { const players = Mpris.players for (const player of players.values) { if (player.identity == "Spotify") { return player } } return undefined console.log(""); } IpcHandler { target: "mpris" function pauseAll(): void { for (const player of Mpris.players.values) { if (player.canPause) player.pause(); } } function playPause(): void { root.togglePlaying(); } function previous(): void { root.previous(); } function next(): void { root.next(); } } }