widgets/mediacontrols: Port to kmpris

This commit is contained in:
Devin Lin 2023-09-26 22:41:52 -07:00
parent ee803b400a
commit ae8a278ab6
2 changed files with 73 additions and 121 deletions

View file

@ -1,25 +1,17 @@
/*
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
* SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
// SPDX-FileCopyrightText: 2021-2023 Devin Lin <devin@kde.org>
// SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick
import org.kde.plasma.plasma5support 2.0 as P5Support
import org.kde.plasma.private.mpris as Mpris
P5Support.DataSource {
id: mpris2Source
QtObject {
property var mpris2Model: Mpris.Mpris2Model {}
engine: "mpris2"
connectedSources: sources
readonly property string multiplexSource: "@multiplex"
property var mprisSourcesModel: []
readonly property bool hasPlayer: sources.length > 1
function startOperation(src, op) {
var service = serviceForSource(src)
@ -27,85 +19,16 @@ P5Support.DataSource {
return service.startOperationCall(operation)
}
function goPrevious(source) {
startOperation(source, "Previous");
function setIndex(index) {
mpris2Model.currentIndex = index;
}
function goNext(source) {
startOperation(source, "Next");
function goPrevious() {
mpris2Model.currentPlayer.Previous();
}
function playPause(source) {
startOperation(source, "PlayPause");
function goNext() {
mpris2Model.currentPlayer.Next();
}
function isPlaying(source) {
return data[source] ? data[source].PlaybackStatus === "Playing" : false;
function playPause() {
mpris2Model.currentPlayer.PlayPause();
}
function canControl(source) {
return data[source] ? data[source].CanControl : false;
}
function canGoBack(source) {
return data[source] ? data[source].CanGoPrevious : false;
}
function canGoNext(source) {
return data[source] ? data[source].CanGoNext : false;
}
function track(source) {
if (!data[source]) {
return "";
}
const xesamTitle = data[source].Metadata["xesam:title"]
if (xesamTitle) {
return xesamTitle
}
// if no track title is given, print out the file name
const xesamUrl = data[source].Metadata["xesam:url"] ? data[source].Metadata["xesam:url"].toString() : ""
if (!xesamUrl) {
return ""
}
const lastSlashPos = xesamUrl.lastIndexOf('/')
if (lastSlashPos < 0) {
return ""
}
const lastUrlPart = xesamUrl.substring(lastSlashPos + 1)
return decodeURIComponent(lastUrlPart)
}
function artist(source) {
return data[source] ? data[source].Metadata["xesam:artist"] || "" : "";
}
function albumArt(source) {
return data[source] ? data[source].Metadata["mpris:artUrl"] || "" : "";
}
function updateMprisSourcesModel() {
let model = [];
let sources = mpris2Source.sources;
for (let i = 0; i < sources.length; ++i) {
let source = sources[i];
if (source === mpris2Source.multiplexSource) {
continue;
}
const playerData = mpris2Source.data[source];
// source data is removed before its name is removed from the list
if (!playerData) {
continue;
}
model.push({
'application': playerData["Identity"],
'source': source,
'desktopEntry': playerData["DesktopEntry"]
});
}
mprisSourcesModel = model;
}
Component.onCompleted: {
mpris2Source.serviceForSource("@multiplex").enableGlobalShortcuts()
updateMprisSourcesModel()
}
onSourceAdded: updateMprisSourcesModel()
onSourceRemoved: updateMprisSourcesModel();
}

View file

@ -1,9 +1,6 @@
/*
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
* SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
// SPDX-FileCopyrightText: 2021-2023 Devin Lin <devin@kde.org>
// SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick
import QtQuick.Layouts
@ -14,6 +11,8 @@ import org.kde.kirigami as Kirigami
import org.kde.plasma.private.mobileshell.state 1.0 as MobileShellState
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.private.mpris as Mpris
import "../../components" as Components
/**
@ -21,10 +20,10 @@ import "../../components" as Components
*/
Item {
id: root
visible: mpris2Source.hasPlayer
visible: sourceRepeater.count > 0
readonly property real padding: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing
readonly property real contentHeight: Kirigami.Units.gridUnit * 2 + Kirigami.Units.smallSpacing
readonly property real padding: Kirigami.Units.gridUnit
readonly property real contentHeight: Kirigami.Units.gridUnit * 2
implicitHeight: visible ? padding * 2 + contentHeight : 0
MediaControlsSource {
@ -36,7 +35,7 @@ Item {
z: 1
visible: view.count > 1
spacing: Kirigami.Units.smallSpacing
anchors.bottomMargin: Kirigami.Units.smallSpacing * 2
anchors.bottomMargin: Kirigami.Units.smallSpacing
anchors.bottom: view.bottom
anchors.horizontalCenter: parent.horizontalCenter
@ -59,20 +58,39 @@ Item {
anchors.fill: parent
Repeater {
model: mpris2Source.mprisSourcesModel
id: sourceRepeater
model: mpris2Source.mpris2Model
delegate: Loader {
active: modelData
id: delegate
// NOTE: model is PlayerContainer from KMpris in plasma-workspace
asynchronous: true
function getTrackName() {
console.log('track name: ' + model.title);
if (model.title) {
return model.title;
}
// if no track title is given, print out the file name
if (!model.url) {
return "";
}
const lastSlashPos = model.url.lastIndexOf('/')
if (lastSlashPos < 0) {
return ""
}
const lastUrlPart = model.url.substring(lastSlashPos + 1);
return decodeURIComponent(lastUrlPart);
}
sourceComponent: MouseArea {
id: mouseArea
implicitHeight: playerItem.implicitHeight
implicitWidth: playerItem.implicitWidth
onClicked: {
Components.AppLaunch.launchOrActivateApp(modelData.desktopEntry + ".desktop");
Components.AppLaunch.launchOrActivateApp(model.desktopEntry + ".desktop");
MobileShellState.ShellDBusClient.closeActionDrawer();
}
@ -80,15 +98,13 @@ Item {
id: playerItem
anchors.fill: parent
property string source: modelData.source
padding: root.padding
implicitHeight: root.contentHeight + root.padding * 2
implicitWidth: root.width
background: BlurredBackground {
darken: mouseArea.pressed
imageSource: mpris2Source.albumArt(playerItem.source)
imageSource: model.artUrl
}
contentItem: Item {
@ -102,7 +118,7 @@ Item {
height: parent.height
spacing: 0
enabled: mpris2Source.canControl(playerItem.source)
enabled: model.canControl
Image {
id: albumArt
@ -110,7 +126,7 @@ Item {
Layout.fillHeight: true
asynchronous: true
fillMode: Image.PreserveAspectFit
source: mpris2Source.albumArt(playerItem.source)
source: model.artUrl
sourceSize.height: height
visible: status === Image.Loading || status === Image.Ready
}
@ -120,20 +136,24 @@ Item {
Layout.fillWidth: true
spacing: Kirigami.Units.smallSpacing
// media track name text
Components.MarqueeLabel {
id: trackLabel
Layout.fillWidth: true
inputText: mpris2Source.track(playerItem.source) || i18n("No media playing")
inputText: model.track || i18n("No media playing");
textFormat: Text.PlainText
font.pointSize: Kirigami.Theme.defaultFont.pointSize
color: "white"
}
// media artist name text
Components.MarqueeLabel {
id: artistLabel
Layout.fillWidth: true
// if no artist is given, show player name instead
inputText: mpris2Source.artist(playerItem.source) || modelData.application || ""
inputText: model.artist || model.identity || ""
textFormat: Text.PlainText
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.9
opacity: 0.9
@ -145,12 +165,15 @@ Item {
Layout.fillHeight: true
Layout.preferredWidth: height
enabled: mpris2Source.canGoBack(playerItem.source)
enabled: model.canGoPrevious
icon.name: LayoutMirroring.enabled ? "media-skip-forward" : "media-skip-backward"
icon.width: Kirigami.Units.iconSizes.small
icon.height: Kirigami.Units.iconSizes.small
onClicked: mpris2Source.goPrevious(playerItem.source)
visible: mpris2Source.canGoBack(playerItem.source) || mpris2Source.canGoNext(playerItem.source)
onClicked: {
mpris2Source.setIndex(model.index);
mpris2Source.goPrevious();
}
visible: model.canGoPrevious || model.canGoNext
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Previous track")
}
@ -158,10 +181,13 @@ Item {
Layout.fillHeight: true
Layout.preferredWidth: height
icon.name: mpris2Source.isPlaying(playerItem.source) ? "media-playback-pause" : "media-playback-start"
icon.name: (model.playbackStatus === Mpris.PlaybackStatus.Playing) ? "media-playback-pause" : "media-playback-start"
icon.width: Kirigami.Units.iconSizes.small
icon.height: Kirigami.Units.iconSizes.small
onClicked: mpris2Source.playPause(playerItem.source)
onClicked: {
mpris2Source.setIndex(model.index);
mpris2Source.playPause();
}
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Play or Pause media")
}
@ -169,12 +195,15 @@ Item {
Layout.fillHeight: true
Layout.preferredWidth: height
enabled: mpris2Source.canGoBack(playerItem.source)
enabled: model.canGoNext
icon.name: LayoutMirroring.enabled ? "media-skip-backward" : "media-skip-forward"
icon.width: Kirigami.Units.iconSizes.small
icon.height: Kirigami.Units.iconSizes.small
onClicked: mpris2Source.goNext(playerItem.source)
visible: mpris2Source.canGoBack(playerItem.source) || mpris2Source.canGoNext(playerItem.source)
onClicked: {
mpris2Source.setIndex(model.index);
mpris2Source.goNext();
}
visible: model.canGoPrevious || model.canGoNext
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Next track")
}
}