forked from quickshell/quickshell
service/mpris: add uniqueId property to detect track changes
Also emits all property changes after trackChanged
This commit is contained in:
parent
d9f66e63a3
commit
abc0201f6e
4 changed files with 104 additions and 34 deletions
|
@ -21,3 +21,22 @@ Qt::Edges Edges::toQt(Edges::Flags edges) { return Qt::Edges(edges.toInt()); }
|
||||||
bool Edges::isOpposing(Edges::Flags edges) {
|
bool Edges::isOpposing(Edges::Flags edges) {
|
||||||
return edges.testFlags(Edges::Top | Edges::Bottom) || edges.testFlags(Edges::Left | Edges::Right);
|
return edges.testFlags(Edges::Top | Edges::Bottom) || edges.testFlags(Edges::Left | Edges::Right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DropEmitter::DropEmitter(DropEmitter&& other) noexcept: object(other.object), signal(other.signal) {
|
||||||
|
other.object = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
DropEmitter& DropEmitter::operator=(DropEmitter&& other) noexcept {
|
||||||
|
this->object = other.object;
|
||||||
|
this->signal = other.signal;
|
||||||
|
other.object = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DropEmitter::~DropEmitter() { this->call(); }
|
||||||
|
|
||||||
|
void DropEmitter::call() {
|
||||||
|
if (!this->object) return;
|
||||||
|
this->signal(this->object);
|
||||||
|
this->object = nullptr;
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <qdebug.h>
|
#include <qdebug.h>
|
||||||
#include <qnamespace.h>
|
#include <qnamespace.h>
|
||||||
#include <qpoint.h>
|
#include <qpoint.h>
|
||||||
|
#include <qrect.h>
|
||||||
#include <qqmlintegration.h>
|
#include <qqmlintegration.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
@ -68,3 +69,28 @@ bool isOpposing(Flags edges);
|
||||||
}; // namespace Edges
|
}; // namespace Edges
|
||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Edges::Flags);
|
Q_DECLARE_OPERATORS_FOR_FLAGS(Edges::Flags);
|
||||||
|
|
||||||
|
// NOLINTBEGIN
|
||||||
|
#define DROP_EMIT(object, func) \
|
||||||
|
DropEmitter(object, static_cast<void (*)(typeof(object))>([](typeof(object) o) { o->func(); }))
|
||||||
|
// NOLINTEND
|
||||||
|
|
||||||
|
class DropEmitter {
|
||||||
|
public:
|
||||||
|
template <class O>
|
||||||
|
DropEmitter(O* object, void (*signal)(O*))
|
||||||
|
: object(object)
|
||||||
|
, signal(*reinterpret_cast<void (*)(void*)>(signal)) {} // NOLINT
|
||||||
|
|
||||||
|
DropEmitter() = default;
|
||||||
|
DropEmitter(DropEmitter&& other) noexcept;
|
||||||
|
DropEmitter& operator=(DropEmitter&& other) noexcept;
|
||||||
|
~DropEmitter();
|
||||||
|
Q_DISABLE_COPY(DropEmitter);
|
||||||
|
|
||||||
|
void call();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void* object = nullptr;
|
||||||
|
void (*signal)(void*) = nullptr;
|
||||||
|
};
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
#include "../../core/types.hpp"
|
||||||
#include "../../dbus/properties.hpp"
|
#include "../../dbus/properties.hpp"
|
||||||
#include "dbus_player.h"
|
#include "dbus_player.h"
|
||||||
#include "dbus_player_app.h"
|
#include "dbus_player_app.h"
|
||||||
|
@ -255,6 +256,7 @@ void MprisPlayer::setVolume(qreal volume) {
|
||||||
this->pVolume.write();
|
this->pVolume.write();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quint32 MprisPlayer::uniqueId() const { return this->mUniqueId; }
|
||||||
QVariantMap MprisPlayer::metadata() const { return this->pMetadata.get(); }
|
QVariantMap MprisPlayer::metadata() const { return this->pMetadata.get(); }
|
||||||
QString MprisPlayer::trackTitle() const { return this->mTrackTitle; }
|
QString MprisPlayer::trackTitle() const { return this->mTrackTitle; }
|
||||||
QString MprisPlayer::trackAlbum() const { return this->mTrackAlbum; }
|
QString MprisPlayer::trackAlbum() const { return this->mTrackAlbum; }
|
||||||
|
@ -271,10 +273,7 @@ void MprisPlayer::onMetadataChanged() {
|
||||||
length = lengthVariant.value<qlonglong>();
|
length = lengthVariant.value<qlonglong>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length != this->mLength) {
|
auto emitLengthChanged = this->setLength(length);
|
||||||
this->mLength = length;
|
|
||||||
emit this->lengthChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto trackChanged = false;
|
auto trackChanged = false;
|
||||||
|
|
||||||
|
@ -299,81 +298,94 @@ void MprisPlayer::onMetadataChanged() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DropEmitter emitTrackTitle;
|
||||||
auto trackTitle = this->pMetadata.get().value("xesam:title");
|
auto trackTitle = this->pMetadata.get().value("xesam:title");
|
||||||
if (trackTitle.isValid() && trackTitle.canConvert<QString>()) {
|
if (trackTitle.isValid() && trackTitle.canConvert<QString>()) {
|
||||||
this->setTrackTitle(trackTitle.toString());
|
emitTrackTitle = this->setTrackTitle(trackTitle.toString());
|
||||||
} else if (trackChanged) {
|
} else if (trackChanged) {
|
||||||
this->setTrackTitle("Unknown Track");
|
emitTrackTitle = this->setTrackTitle("Unknown Track");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DropEmitter emitTrackAlbum;
|
||||||
auto trackAlbum = this->pMetadata.get().value("xesam:album");
|
auto trackAlbum = this->pMetadata.get().value("xesam:album");
|
||||||
if (trackAlbum.isValid() && trackAlbum.canConvert<QString>()) {
|
if (trackAlbum.isValid() && trackAlbum.canConvert<QString>()) {
|
||||||
this->setTrackAlbum(trackAlbum.toString());
|
emitTrackAlbum = this->setTrackAlbum(trackAlbum.toString());
|
||||||
} else if (trackChanged) {
|
} else if (trackChanged) {
|
||||||
this->setTrackAlbum("Unknown Album");
|
emitTrackAlbum = this->setTrackAlbum("Unknown Album");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DropEmitter emitTrackAlbumArtist;
|
||||||
auto trackAlbumArtist = this->pMetadata.get().value("xesam:albumArtist");
|
auto trackAlbumArtist = this->pMetadata.get().value("xesam:albumArtist");
|
||||||
if (trackAlbumArtist.isValid() && trackAlbumArtist.canConvert<QString>()) {
|
if (trackAlbumArtist.isValid() && trackAlbumArtist.canConvert<QString>()) {
|
||||||
this->setTrackAlbumArtist(trackAlbumArtist.toString());
|
emitTrackAlbumArtist = this->setTrackAlbumArtist(trackAlbumArtist.toString());
|
||||||
} else if (trackChanged) {
|
} else if (trackChanged) {
|
||||||
this->setTrackAlbumArtist("Unknown Artist");
|
emitTrackAlbumArtist = this->setTrackAlbumArtist("Unknown Artist");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DropEmitter emitTrackArtists;
|
||||||
auto trackArtists = this->pMetadata.get().value("xesam:artist");
|
auto trackArtists = this->pMetadata.get().value("xesam:artist");
|
||||||
if (trackArtists.isValid() && trackArtists.canConvert<QVector<QString>>()) {
|
if (trackArtists.isValid() && trackArtists.canConvert<QVector<QString>>()) {
|
||||||
this->setTrackArtists(trackArtists.value<QVector<QString>>());
|
emitTrackArtists = this->setTrackArtists(trackArtists.value<QVector<QString>>());
|
||||||
} else if (trackChanged) {
|
} else if (trackChanged) {
|
||||||
this->setTrackArtists({});
|
emitTrackArtists = this->setTrackArtists({});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DropEmitter emitTrackArtUrl;
|
||||||
auto trackArtUrl = this->pMetadata.get().value("mpris:artUrl");
|
auto trackArtUrl = this->pMetadata.get().value("mpris:artUrl");
|
||||||
if (trackArtUrl.isValid() && trackArtUrl.canConvert<QString>()) {
|
if (trackArtUrl.isValid() && trackArtUrl.canConvert<QString>()) {
|
||||||
this->setTrackArtUrl(trackArtUrl.toString());
|
emitTrackArtUrl = this->setTrackArtUrl(trackArtUrl.toString());
|
||||||
} else if (trackChanged) {
|
} else if (trackChanged) {
|
||||||
this->setTrackArtUrl("");
|
emitTrackArtUrl = this->setTrackArtUrl("");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackChanged) {
|
if (trackChanged) {
|
||||||
|
this->mUniqueId++;
|
||||||
// Some players don't seem to send position updates or seeks on track change.
|
// Some players don't seem to send position updates or seeks on track change.
|
||||||
this->pPosition.update();
|
this->pPosition.update();
|
||||||
emit this->trackChanged();
|
emit this->trackChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MprisPlayer::setTrackTitle(QString title) {
|
DropEmitter MprisPlayer::setLength(qlonglong length) {
|
||||||
if (title == this->mTrackTitle) return;
|
if (length == this->mLength) return DropEmitter();
|
||||||
|
|
||||||
|
this->mLength = length;
|
||||||
|
return DROP_EMIT(this, lengthChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
DropEmitter MprisPlayer::setTrackTitle(QString title) {
|
||||||
|
if (title == this->mTrackTitle) return DropEmitter();
|
||||||
|
|
||||||
this->mTrackTitle = std::move(title);
|
this->mTrackTitle = std::move(title);
|
||||||
emit this->trackTitleChanged();
|
return DROP_EMIT(this, trackTitleChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MprisPlayer::setTrackAlbum(QString album) {
|
DropEmitter MprisPlayer::setTrackAlbum(QString album) {
|
||||||
if (album == this->mTrackAlbum) return;
|
if (album == this->mTrackAlbum) return DropEmitter();
|
||||||
|
|
||||||
this->mTrackAlbum = std::move(album);
|
this->mTrackAlbum = std::move(album);
|
||||||
emit this->trackAlbumChanged();
|
return DROP_EMIT(this, trackAlbumChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MprisPlayer::setTrackAlbumArtist(QString albumArtist) {
|
DropEmitter MprisPlayer::setTrackAlbumArtist(QString albumArtist) {
|
||||||
if (albumArtist == this->mTrackAlbumArtist) return;
|
if (albumArtist == this->mTrackAlbumArtist) return DropEmitter();
|
||||||
|
|
||||||
this->mTrackAlbumArtist = std::move(albumArtist);
|
this->mTrackAlbumArtist = std::move(albumArtist);
|
||||||
emit this->trackAlbumArtistChanged();
|
return DROP_EMIT(this, trackAlbumArtistChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MprisPlayer::setTrackArtists(QVector<QString> artists) {
|
DropEmitter MprisPlayer::setTrackArtists(QVector<QString> artists) {
|
||||||
if (artists == this->mTrackArtists) return;
|
if (artists == this->mTrackArtists) return DropEmitter();
|
||||||
|
|
||||||
this->mTrackArtists = std::move(artists);
|
this->mTrackArtists = std::move(artists);
|
||||||
emit this->trackArtistsChanged();
|
return DROP_EMIT(this, trackArtistsChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MprisPlayer::setTrackArtUrl(QString artUrl) {
|
DropEmitter MprisPlayer::setTrackArtUrl(QString artUrl) {
|
||||||
if (artUrl == this->mTrackArtUrl) return;
|
if (artUrl == this->mTrackArtUrl) return DropEmitter();
|
||||||
|
|
||||||
this->mTrackArtUrl = std::move(artUrl);
|
this->mTrackArtUrl = std::move(artUrl);
|
||||||
emit this->trackArtUrlChanged();
|
return DROP_EMIT(this, trackArtUrlChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
MprisPlaybackState::Enum MprisPlayer::playbackState() const { return this->mPlaybackState; }
|
MprisPlaybackState::Enum MprisPlayer::playbackState() const { return this->mPlaybackState; }
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
|
|
||||||
#include "../../core/doc.hpp"
|
#include "../../core/doc.hpp"
|
||||||
|
#include "../../core/types.hpp"
|
||||||
#include "../../dbus/properties.hpp"
|
#include "../../dbus/properties.hpp"
|
||||||
#include "dbus_player.h"
|
#include "dbus_player.h"
|
||||||
#include "dbus_player_app.h"
|
#include "dbus_player_app.h"
|
||||||
|
@ -128,6 +129,11 @@ class MprisPlayer: public QObject {
|
||||||
/// properties have extra logic to guard against bad players sending weird metadata, and should
|
/// properties have extra logic to guard against bad players sending weird metadata, and should
|
||||||
/// be used over grabbing the properties directly from the metadata.
|
/// be used over grabbing the properties directly from the metadata.
|
||||||
Q_PROPERTY(QVariantMap metadata READ metadata NOTIFY metadataChanged);
|
Q_PROPERTY(QVariantMap metadata READ metadata NOTIFY metadataChanged);
|
||||||
|
/// An opaque identifier for the current track unique within the current player.
|
||||||
|
///
|
||||||
|
/// > [!WARNING] This is NOT `mpris:trackid` as that is sometimes missing or nonunique
|
||||||
|
/// > in some players.
|
||||||
|
Q_PROPERTY(quint32 uniqueId READ uniqueId NOTIFY trackChanged);
|
||||||
/// The title of the current track, or "Unknown Track" if none was provided.
|
/// The title of the current track, or "Unknown Track" if none was provided.
|
||||||
Q_PROPERTY(QString trackTitle READ trackTitle NOTIFY trackTitleChanged);
|
Q_PROPERTY(QString trackTitle READ trackTitle NOTIFY trackTitleChanged);
|
||||||
/// The current track's album, or "Unknown Album" if none was provided.
|
/// The current track's album, or "Unknown Album" if none was provided.
|
||||||
|
@ -248,6 +254,7 @@ public:
|
||||||
[[nodiscard]] bool volumeSupported() const;
|
[[nodiscard]] bool volumeSupported() const;
|
||||||
void setVolume(qreal volume);
|
void setVolume(qreal volume);
|
||||||
|
|
||||||
|
[[nodiscard]] quint32 uniqueId() const;
|
||||||
[[nodiscard]] QVariantMap metadata() const;
|
[[nodiscard]] QVariantMap metadata() const;
|
||||||
[[nodiscard]] QString trackTitle() const;
|
[[nodiscard]] QString trackTitle() const;
|
||||||
[[nodiscard]] QString trackAlbum() const;
|
[[nodiscard]] QString trackAlbum() const;
|
||||||
|
@ -278,6 +285,10 @@ public:
|
||||||
[[nodiscard]] QList<QString> supportedMimeTypes() const;
|
[[nodiscard]] QList<QString> supportedMimeTypes() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
/// The track has changed.
|
||||||
|
///
|
||||||
|
/// All track info change signalss will fire immediately after if applicable,
|
||||||
|
/// but their values will be updated before the signal fires.
|
||||||
void trackChanged();
|
void trackChanged();
|
||||||
|
|
||||||
QSDOC_HIDE void ready();
|
QSDOC_HIDE void ready();
|
||||||
|
@ -327,11 +338,12 @@ private slots:
|
||||||
void onLoopStatusChanged();
|
void onLoopStatusChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setTrackTitle(QString title);
|
DropEmitter setLength(qlonglong length);
|
||||||
void setTrackAlbum(QString album);
|
DropEmitter setTrackTitle(QString title);
|
||||||
void setTrackAlbumArtist(QString albumArtist);
|
DropEmitter setTrackAlbum(QString album);
|
||||||
void setTrackArtists(QVector<QString> artists);
|
DropEmitter setTrackAlbumArtist(QString albumArtist);
|
||||||
void setTrackArtUrl(QString artUrl);
|
DropEmitter setTrackArtists(QVector<QString> artists);
|
||||||
|
DropEmitter setTrackArtUrl(QString artUrl);
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
dbus::DBusPropertyGroup appProperties;
|
dbus::DBusPropertyGroup appProperties;
|
||||||
|
@ -370,6 +382,7 @@ private:
|
||||||
|
|
||||||
DBusMprisPlayerApp* app = nullptr;
|
DBusMprisPlayerApp* app = nullptr;
|
||||||
DBusMprisPlayer* player = nullptr;
|
DBusMprisPlayer* player = nullptr;
|
||||||
|
quint32 mUniqueId = 0;
|
||||||
QString mTrackId;
|
QString mTrackId;
|
||||||
QString mTrackUrl;
|
QString mTrackUrl;
|
||||||
QString mTrackTitle;
|
QString mTrackTitle;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue