auto-switch mpris player on pause/stop to most recently playing session
This commit is contained in:
parent
195dd37be5
commit
a032e554d0
2 changed files with 142 additions and 4 deletions
|
|
@ -60,7 +60,7 @@ You poor thing. Here's what the Nix packaging does for you, manually:
|
||||||
1. Build [quickshell](https://quickshell.outfoxxed.me) from source. You'll need Qt 6.
|
1. Build [quickshell](https://quickshell.outfoxxed.me) from source. You'll need Qt 6.
|
||||||
2. Build `nova-stats` from `stats-daemon/` (`cargo build --release`). Put the binary in your PATH.
|
2. Build `nova-stats` from `stats-daemon/` (`cargo build --release`). Put the binary in your PATH.
|
||||||
3. Put `gdbus` (from glib) in your PATH. The lock screen needs it.
|
3. Put `gdbus` (from glib) in your PATH. The lock screen needs it.
|
||||||
4. Compile the overview shader: `qsb --qt6 -o shell/modules/hex_wave.frag.qsb shell/modules/hex_wave.frag`
|
4. Compile the shaders: `for f in shell/modules/*.frag; do qsb --qt6 -o "${f}.qsb" "$f"; done`
|
||||||
5. Install [Symbols Nerd Font](https://www.nerdfonts.com/).
|
5. Install [Symbols Nerd Font](https://www.nerdfonts.com/).
|
||||||
6. Create `~/.config/nova-shell/theme.json` and `~/.config/nova-shell/modules.json`. The Nix module generates these from your config. Without it, you write JSON by hand like a human. See the theme and module tables below for keys.
|
6. Create `~/.config/nova-shell/theme.json` and `~/.config/nova-shell/modules.json`. The Nix module generates these from your config. Without it, you write JSON by hand like a human. See the theme and module tables below for keys.
|
||||||
7. Run: `quickshell -p /path/to/nova-shell/shell/shell.qml`
|
7. Run: `quickshell -p /path/to/nova-shell/shell/shell.qml`
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,149 @@ QtObject {
|
||||||
readonly property MprisPlayer player: players[playerIdx] ?? players[0] ?? null
|
readonly property MprisPlayer player: players[playerIdx] ?? players[0] ?? null
|
||||||
readonly property bool playing: player?.playbackState === MprisPlaybackState.Playing
|
readonly property bool playing: player?.playbackState === MprisPlaybackState.Playing
|
||||||
|
|
||||||
// Reset index if current player disappears
|
// Track the identity of the selected player so we can find it after list changes
|
||||||
onPlayersChanged: if (playerIdx >= players.length)
|
property string _selectedIdentity: ""
|
||||||
playerIdx = 0
|
|
||||||
|
// Track which player played most recently (by identity)
|
||||||
|
property var _lastPlayedTime: ({})
|
||||||
|
|
||||||
|
onPlayerChanged: {
|
||||||
|
if (player)
|
||||||
|
_selectedIdentity = player.identity ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
onPlayersChanged: {
|
||||||
|
// Try to find the previously selected player by identity
|
||||||
|
if (_selectedIdentity) {
|
||||||
|
const idx = players.findIndex(p => (p.identity ?? "") === _selectedIdentity);
|
||||||
|
if (idx >= 0) {
|
||||||
|
playerIdx = idx;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selected player disappeared - switch to most recently playing
|
||||||
|
_switchToBest();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch playback state changes on all players
|
||||||
|
property Connections _mprisConn: Connections {
|
||||||
|
target: Mpris
|
||||||
|
|
||||||
|
function onPlayersChanged() {
|
||||||
|
// Re-evaluate when the global player list changes
|
||||||
|
Qt.callLater(_reconnectWatchers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property var _watchedPlayers: []
|
||||||
|
|
||||||
|
function _reconnectWatchers() {
|
||||||
|
// Clean up old watchers
|
||||||
|
for (const w of _watchedPlayers) {
|
||||||
|
if (w.conn)
|
||||||
|
w.conn.destroy();
|
||||||
|
}
|
||||||
|
_watchedPlayers = [];
|
||||||
|
|
||||||
|
// Watch each player's playback state
|
||||||
|
for (const p of players) {
|
||||||
|
const conn = _watcherComp.createObject(root, {
|
||||||
|
target: p
|
||||||
|
});
|
||||||
|
_watchedPlayers.push({
|
||||||
|
player: p,
|
||||||
|
conn: conn
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property Component _watcherComp: Component {
|
||||||
|
Connections {
|
||||||
|
function onPlaybackStateChanged() {
|
||||||
|
root._onPlaybackStateChanged(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _onPlaybackStateChanged(p) {
|
||||||
|
if (!p)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const identity = p.identity ?? "";
|
||||||
|
|
||||||
|
if (p.playbackState === MprisPlaybackState.Playing) {
|
||||||
|
// Record this as the most recently playing
|
||||||
|
const times = Object.assign({}, _lastPlayedTime);
|
||||||
|
times[identity] = Date.now();
|
||||||
|
_lastPlayedTime = times;
|
||||||
|
|
||||||
|
// If current player is not playing, switch to this one
|
||||||
|
if (player !== p && player?.playbackState !== MprisPlaybackState.Playing) {
|
||||||
|
const idx = players.indexOf(p);
|
||||||
|
if (idx >= 0) {
|
||||||
|
playerIdx = idx;
|
||||||
|
_selectedIdentity = identity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (p.playbackState === MprisPlaybackState.Paused && p === player) {
|
||||||
|
// Current player paused - switch to a playing one if available
|
||||||
|
_switchToPlaying();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _switchToPlaying() {
|
||||||
|
// Find a playing player, prefer most recently started
|
||||||
|
let best = -1;
|
||||||
|
let bestTime = 0;
|
||||||
|
for (let i = 0; i < players.length; i++) {
|
||||||
|
if (players[i].playbackState === MprisPlaybackState.Playing) {
|
||||||
|
const t = _lastPlayedTime[players[i].identity ?? ""] || 0;
|
||||||
|
if (best < 0 || t > bestTime) {
|
||||||
|
best = i;
|
||||||
|
bestTime = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (best >= 0) {
|
||||||
|
playerIdx = best;
|
||||||
|
_selectedIdentity = players[best].identity ?? "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _switchToBest() {
|
||||||
|
// Switch to playing player, or most recently played
|
||||||
|
let best = -1;
|
||||||
|
let bestTime = 0;
|
||||||
|
let bestPlaying = -1;
|
||||||
|
let bestPlayingTime = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < players.length; i++) {
|
||||||
|
const identity = players[i].identity ?? "";
|
||||||
|
const t = _lastPlayedTime[identity] || 0;
|
||||||
|
if (players[i].playbackState === MprisPlaybackState.Playing) {
|
||||||
|
if (bestPlaying < 0 || t > bestPlayingTime) {
|
||||||
|
bestPlaying = i;
|
||||||
|
bestPlayingTime = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (best < 0 || t > bestTime) {
|
||||||
|
best = i;
|
||||||
|
bestTime = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const chosen = bestPlaying >= 0 ? bestPlaying : (best >= 0 ? best : 0);
|
||||||
|
playerIdx = chosen;
|
||||||
|
if (players[chosen])
|
||||||
|
_selectedIdentity = players[chosen].identity ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
function switchPlayer(idx) {
|
function switchPlayer(idx) {
|
||||||
playerIdx = idx;
|
playerIdx = idx;
|
||||||
|
if (players[idx])
|
||||||
|
_selectedIdentity = players[idx].identity ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: Qt.callLater(_reconnectWatchers)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue