├── contents ├── config │ ├── config.qml │ └── main.xml └── ui │ ├── OneLineLayout.qml │ ├── configGeneral.qml │ └── main.qml ├── metadata.json ├── license.md └── readme.md /contents/config/config.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQml 2.2 3 | 4 | import org.kde.plasma.configuration 2.0 5 | //import org.kde.plasma.calendar 2.0 as PlasmaCalendar 6 | 7 | ConfigModel { 8 | id: configModel 9 | 10 | ConfigCategory { 11 | name: i18n("General") 12 | icon: "configure" 13 | source: "configGeneral.qml" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /contents/ui/OneLineLayout.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Layouts 1.1 3 | import org.kde.plasma.core as PlasmaCore 4 | import org.kde.plasma.plasmoid 5 | 6 | Text { 7 | property int fontSize: { 8 | return (plasmoid.configuration.shouldUseDefaultThemeFontSize) 9 | ? PlasmaCore.Theme.defaultFont.pixelSize 10 | : plasmoid.configuration.configuredFontSize 11 | } 12 | 13 | text: oneLineTextContent 14 | color: PlasmaCore.Theme.textColor 15 | // horizontalAlignment: Text.AlignRight 16 | verticalAlignment: Text.AlignVCenter 17 | font.pixelSize: fontSize 18 | } 19 | -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "KPlugin": { 3 | "Authors": [ 4 | { 5 | "Email": "juno.ngx@gmail.com", 6 | "Name": "Juno Nguyen" 7 | } 8 | ], 9 | "Category": "Multimedia", 10 | "Description": "A minimal way to display the currently playing media info.", 11 | "Icon": "preferences-media-playback-amarok", 12 | "Id": "com.junongx.kde.currentlyplaying", 13 | "License": "MIT License", 14 | "Name": "Currently Playing", 15 | "ServiceTypes": [ 16 | "Plasma/Applet" 17 | ], 18 | "Version": "2.0", 19 | "Website": "https://github.com/JunoNgx/kde-currently-playing" 20 | }, 21 | "X-Plasma-API-Minimum-Version": "6.0" 22 | } 23 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Copyright 2022 Juno Nguyen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # KDE Currently Playing 2 | 3 | A minimal widget for the Linux Desktop Environment KDE Plasma, **Currently Playing** is quiet, undistracting, and straight-forward. It does one thing and one thing alone: display the title and artist of the media currently being played. 4 | 5 | The widget was made for personal use following my dissatisfaction with other tools of similar functions for KDE Plasma. A publishing plan is pending. 6 | 7 | ## Features 8 | 9 | - Display of current media using the data engine `mpris2` 10 | - Switch sequence of track title and artist 11 | - Customizable separator string between track title and artist 12 | - Font size settings 13 | 14 | ## Installation 15 | 16 | ### KDE Store 17 | 18 | The widget is available directly from [the KDE Store](https://store.kde.org/p/1821551), which is accessible from KDE GUI upon adding new widget. 19 | 20 | ### Local file 21 | 22 | Download [the release](https://github.com/JunoNgx/kde-currently-playing/releases), and choose this file upon choosing to add widget from local file. 23 | 24 | ### Manual installation 25 | 26 | Clone this repository, move it to `/home//.local/share/plasma`, logout, and re-login. `Currently Playing` will now appear in your widget list. 27 | 28 | ## Known bug 29 | 30 | ### Spotify 31 | 32 | Upon startup, Spotify does not properly send the track information to `mpris2` data engine, missing out on most info field except for `xesam:url`. This leads to the inability to display info until the track is changed. This appears to be a bug from the Spotify Linux client`. -------------------------------------------------------------------------------- /contents/config/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | true 17 | 18 | 19 | true 20 | 21 | 22 | 40 23 | 24 | 25 | | 26 | 27 | 28 | No media 29 | 30 | 31 | No artist 32 | 33 | 34 | 35 | true 36 | 37 | 38 | 18 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /contents/ui/configGeneral.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Controls 2.5 3 | import QtQuick.Layouts 1.12 4 | import org.kde.kirigami 2.4 as Kirigami 5 | 6 | Kirigami.FormLayout { 7 | id: page 8 | 9 | property alias cfg_shouldDisplayTitleOnly: shouldDisplayTitleOnly.checked 10 | property alias cfg_shouldDisplayTitleFirst: shouldDisplayTitleFirst.checked 11 | property alias cfg_shouldAddLeadingWhitespaceToSeparator: shouldAddLeadingWhitespaceToSeparator.checked 12 | property alias cfg_shouldAddTrailingWhitespaceToSeparator: shouldAddTrailingWhitespaceToSeparator.checked 13 | property alias cfg_characterLimit: characterLimit.text 14 | property alias cfg_separatorString: separatorString.text 15 | property alias cfg_noMediaString: noMediaString.text 16 | property alias cfg_noArtistString: noArtistString.text 17 | property alias cfg_shouldUseDefaultThemeFontSize: shouldUseDefaultThemeFontSize.checked 18 | property alias cfg_configuredFontSize: configuredFontSize.text 19 | 20 | CheckBox { 21 | id: shouldDisplayTitleOnly 22 | Kirigami.FormData.label: i18n("Display options:") 23 | text: i18n("Display title only") 24 | } 25 | 26 | CheckBox { 27 | id: shouldDisplayTitleFirst 28 | text: i18n("Display title first") 29 | } 30 | 31 | CheckBox { 32 | id: shouldAddLeadingWhitespaceToSeparator 33 | text: i18n("Add leading whitespace to separator string") 34 | } 35 | 36 | CheckBox { 37 | id: shouldAddTrailingWhitespaceToSeparator 38 | text: i18n("Add trailing whitespace to separator string") 39 | } 40 | 41 | TextField { 42 | id: characterLimit 43 | Kirigami.FormData.label: i18n("Character Limit:") 44 | placeholderText: i18n("") 45 | validator: IntValidator {bottom: 0; top: 9999} 46 | } 47 | 48 | TextField { 49 | id: separatorString 50 | Kirigami.FormData.label: i18n("Separator string:") 51 | placeholderText: i18n("") 52 | } 53 | 54 | TextField { 55 | id: noMediaString 56 | Kirigami.FormData.label: i18n("Text to show when not playing any media (or no info is available):") 57 | placeholderText: i18n("") 58 | } 59 | 60 | TextField { 61 | id: noArtistString 62 | Kirigami.FormData.label: i18n("Text to show when artist is not found from metadata:") 63 | placeholderText: i18n("") 64 | } 65 | 66 | CheckBox { 67 | id: shouldUseDefaultThemeFontSize 68 | text: i18n("Ignore the setting below and use theme default size") 69 | } 70 | 71 | TextField { 72 | id: configuredFontSize 73 | Kirigami.FormData.label: i18n("Custom font size:") 74 | placeholderText: i18n("") 75 | validator: IntValidator {bottom: 0; top: 9999} 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /contents/ui/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Layouts 1.1 3 | import org.kde.plasma.core as PlasmaCore 4 | import org.kde.plasma.plasmoid 5 | import org.kde.plasma.private.mpris as Mpris 6 | 7 | PlasmoidItem { 8 | id: root 9 | 10 | property var metadata: mpris2Model.currentPlayer ? mpris2Model.currentPlayer : undefined 11 | 12 | property string trackTitle: { 13 | if (!metadata) return "no metadata title found" 14 | if (metadata.track) return metadata.track 15 | // if (metadata["xesam:url"]) return metadata["xesam:url"].toString() 16 | return "" 17 | } 18 | 19 | property string artist: { 20 | if (!metadata) return "no metadata artist found" 21 | if (metadata.artist) return metadata.artist 22 | return "" 23 | } 24 | 25 | property string albumArt: { 26 | return metadata 27 | ? metadata.artUrl || "" 28 | : "" 29 | } 30 | 31 | property string separatorString: { 32 | let separatorStr = "" 33 | 34 | if (plasmoid.configuration.shouldAddLeadingWhitespaceToSeparator) 35 | separatorStr += " " 36 | 37 | separatorStr += plasmoid.configuration.separatorString 38 | 39 | if (plasmoid.configuration.shouldAddTrailingWhitespaceToSeparator) 40 | separatorStr += " " 41 | 42 | return separatorStr 43 | } 44 | 45 | property string oneLineTextContent: { 46 | if (!metadata) return plasmoid.configuration.noMediaString 47 | if (trackTitle == "" && artist == "no metadata artist found") return plasmoid.configuration.noArtistString 48 | if (!trackTitle && !artist) return "" 49 | 50 | if (plasmoid.configuration.shouldDisplayTitleOnly) 51 | return trackTitle 52 | 53 | let content 54 | if (plasmoid.configuration.shouldDisplayTitleFirst) { 55 | content = trackTitle 56 | + separatorString 57 | + artist 58 | } else { 59 | content = artist 60 | + separatorString 61 | + trackTitle 62 | } 63 | 64 | if (content.length > plasmoid.configuration.characterLimit) 65 | content = content.slice(0, plasmoid.configuration.characterLimit) 66 | 67 | return content 68 | } 69 | 70 | 71 | property var twoLineLayoutWidth: { 72 | return Math.max( 73 | artistContentComponent.contentWidth, 74 | trackContentComponent.contentWidth 75 | ) 76 | } 77 | 78 | property var twoLineLayoutHeight: { 79 | return artistContentComponent.contentHeight 80 | + trackContentComponent.contentHeight 81 | } 82 | 83 | Mpris.Mpris2Model { 84 | id: mpris2Model 85 | onDataChanged: { 86 | updateLayoutSize() 87 | } 88 | onCurrentPlayerChanged: { 89 | updateLayoutSize() 90 | } 91 | } 92 | 93 | function updateLayoutSize() { 94 | Layout.minimumWidth = oneLineLayout.contentWidth 95 | Layout.minimumHeight = oneLineLayout.contentHeight 96 | } 97 | 98 | preferredRepresentation: fullRepresentation 99 | fullRepresentation: OneLineLayout { 100 | id: oneLineLayout 101 | Layout.minimumWidth: oneLineLayout.contentWidth 102 | Layout.minimumHeight: oneLineLayout.contentHeight 103 | } 104 | } 105 | --------------------------------------------------------------------------------