From 41677b58d971ab57a79514d972f10acb04797130 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 27 Dec 2015 21:55:19 +0100 Subject: initial music controller implementation --- musiccontroller.cpp | 277 +++++++++++++++++++++++++++++++++++++++++++++++++- musiccontroller.h | 59 ++++++++++- musiccontroller_p.h | 27 ++++- notificationmonitor.h | 2 + walltimemonitor.h | 2 + 5 files changed, 362 insertions(+), 5 deletions(-) diff --git a/musiccontroller.cpp b/musiccontroller.cpp index ba9fc39..638d440 100644 --- a/musiccontroller.cpp +++ b/musiccontroller.cpp @@ -16,21 +16,183 @@ * along with this program. If not, see . */ +#include +#include + #include "musiccontroller.h" #include "musiccontroller_p.h" namespace watchfish { +Q_LOGGING_CATEGORY(musicControllerCat, "watchfish-MusicController") + MusicControllerPrivate::MusicControllerPrivate(MusicController *q) - : q_ptr(q) + : manager(new MprisManager(this)), q_ptr(q) { + connect(manager, &MprisManager::currentServiceChanged, + this, &MusicControllerPrivate::handleCurrentServiceChanged); + connect(manager, &MprisManager::playbackStatusChanged, + this, &MusicControllerPrivate::handlePlaybackStatusChanged); + connect(manager, &MprisManager::metadataChanged, + this, &MusicControllerPrivate::handleMetadataChanged); + connect(manager, &MprisManager::shuffleChanged, + q, &MusicController::shuffleChanged); + connect(manager, &MprisManager::loopStatusChanged, + q, &MusicController::repeatChanged); } MusicControllerPrivate::~MusicControllerPrivate() { } +QString MusicControllerPrivate::stripAlbumArtComponent(const QString& component) +{ + static QRegExp rsb("\\[.*\\]"); + static QRegExp rfb("{.*}"); + static QRegExp rrb("\\(.*\\)"); + static QRegExp stripB("^[()_{}[]!@#$^&*+=|\\\\/\"'?<>~`\\s\\t]*"); + static QRegExp stripE("[()_{}[]!@#$^&*+=|\\\\/\"'?<>~`\\s\\t]*$"); + QString s(component); + + if (s.isEmpty()) { + return QString(" "); + } + + s = s.replace(rsb, ""); + s = s.replace(rfb, ""); + s = s.replace(rrb, ""); + s = s.replace(stripB, ""); + s = s.replace(stripE, ""); + s = s.replace(" ", " "); + s = s.replace("\t", " "); + + return s.toLower(); +} + +QString MusicControllerPrivate::findAlbumArt(const QString &artist, const QString &album) +{ + QDir dir(QDir::homePath() + "/.cache/media-art/"); + QByteArray first_hash = QCryptographicHash::hash(stripAlbumArtComponent(artist).toUtf8(), + QCryptographicHash::Md5).toHex(); + QByteArray second_hash = QCryptographicHash::hash(stripAlbumArtComponent(album).toUtf8(), + QCryptographicHash::Md5).toHex(); + QString file = QString("album-%1-%2.jpeg").arg(first_hash.constData()).arg(second_hash.constData()); + qCDebug(musicControllerCat()) << "checking for albumart in" << file; + if (dir.exists(file)) { + return dir.absoluteFilePath(file); + } + + // Now try with an empty artist name + first_hash = QCryptographicHash::hash(QString(" ").toUtf8(), QCryptographicHash::Md5).toHex(); + file = QString("album-%1-%2.jpeg").arg(first_hash.constData()).arg(second_hash.constData()); + qCDebug(musicControllerCat()) << "checking for albumart in" << file; + if (dir.exists(file)) { + return dir.absoluteFilePath(file); + } + + return QString(); +} + +void MusicControllerPrivate::updateStatus() +{ + Q_Q(MusicController); + QString service = manager->currentService(); + MusicController::Status newStatus; + + if (service.isEmpty()) { + newStatus = MusicController::StatusNoPlayer; + } else { + switch (manager->playbackStatus()) { + case Mpris::Playing: + newStatus = MusicController::StatusPlaying; + break; + case Mpris::Paused: + newStatus = MusicController::StatusPaused; + break; + default: + newStatus = MusicController::StatusStopped; + break; + } + } + + if (newStatus != curStatus) { + curStatus = newStatus; + emit q->statusChanged(); + } +} + +void MusicControllerPrivate::updateAlbumArt() +{ + Q_Q(MusicController); + QString newAlbumArt = findAlbumArt(curArtist, curAlbum); + if (newAlbumArt != curAlbumArt) { + curAlbumArt = newAlbumArt; + emit q->albumArtChanged(); + } +} + +void MusicControllerPrivate::updateMetadata() +{ + Q_Q(MusicController); + QVariantMap metadata = manager->metadata(); + bool checkAlbumArt = false; + + qCDebug(musicControllerCat()) << metadata; + + QString newArtist = metadata.value("xesam:artist").toString(), + newAlbum = metadata.value("xesam:album").toString(), + newTitle = metadata.value("xesam:title").toString(); + + if (newArtist != curArtist) { + curArtist = newArtist; + checkAlbumArt = true; + emit q->artistChanged(); + } + + if (newAlbum != curAlbum) { + curAlbum = newAlbum; + checkAlbumArt = true; + emit q->albumChanged(); + } + + if (newTitle != curTitle) { + curTitle = newTitle; + emit q->titleChanged(); + } + + if (checkAlbumArt) { + updateAlbumArt(); + } + + int newDuration = metadata.value("mpris:length").toULongLong() / 1000UL; + if (newDuration != curDuration) { + curDuration = newDuration; + emit q->durationChanged(); + } + + emit q->metadataChanged(); +} + +void MusicControllerPrivate::handleCurrentServiceChanged() +{ + Q_Q(MusicController); + qCDebug(musicControllerCat()) << manager->currentService(); + updateStatus(); + emit q->serviceChanged(); +} + +void MusicControllerPrivate::handlePlaybackStatusChanged() +{ + qCDebug(musicControllerCat()) << manager->playbackStatus(); + updateStatus(); +} + +void MusicControllerPrivate::handleMetadataChanged() +{ + updateMetadata(); +} + MusicController::MusicController(QObject *parent) : QObject(parent), d_ptr(new MusicControllerPrivate(this)) { @@ -41,4 +203,117 @@ MusicController::~MusicController() delete d_ptr; } +MusicController::Status MusicController::status() const +{ + Q_D(const MusicController); + return d->curStatus; +} + +QString MusicController::service() const +{ + Q_D(const MusicController); + return d->manager->currentService(); +} + +QVariantMap MusicController::metadata() const +{ + Q_D(const MusicController); + return d->manager->metadata(); +} + +QString MusicController::title() const +{ + Q_D(const MusicController); + return d->curTitle; +} + +QString MusicController::album() const +{ + Q_D(const MusicController); + return d->curAlbum; +} + +QString MusicController::artist() const +{ + Q_D(const MusicController); + return d->curArtist; +} + +QString MusicController::albumArt() const +{ + Q_D(const MusicController); + return d->curAlbumArt; +} + +int MusicController::duration() const +{ + Q_D(const MusicController); + return d->curDuration; +} + +MusicController::RepeatStatus MusicController::repeat() const +{ + Q_D(const MusicController); + switch (d->manager->loopStatus()) { + case Mpris::None: + default: + return RepeatNone; + case Mpris::Track: + return RepeatTrack; + case Mpris::Playlist: + return RepeatPlaylist; + } +} + +bool MusicController::shuffle() const +{ + Q_D(const MusicController); + return d->manager->shuffle(); +} + +int MusicController::volume() const +{ + return 100; // TODO volume +} + +void MusicController::play() +{ + Q_D(MusicController); + d->manager->play(); +} + +void MusicController::pause() +{ + Q_D(MusicController); + d->manager->pause(); +} + +void MusicController::playPause() +{ + Q_D(MusicController); + d->manager->playPause(); +} + +void MusicController::next() +{ + Q_D(MusicController); + d->manager->next(); +} + +void MusicController::previous() +{ + Q_D(MusicController); + d->manager->previous(); +} + +void MusicController::volumeUp() +{ + +} + +void MusicController::volumeDown() +{ + +} + } diff --git a/musiccontroller.h b/musiccontroller.h index 33f6810..acf5beb 100644 --- a/musiccontroller.h +++ b/musiccontroller.h @@ -19,22 +19,79 @@ #ifndef WATCHFISH_MUSICCONTROLLER_H #define WATCHFISH_MUSICCONTROLLER_H -#include +#include namespace watchfish { +Q_DECLARE_LOGGING_CATEGORY(musicControllerCat) + class MusicControllerPrivate; class MusicController : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(MusicController) + Q_ENUMS(Status) public: explicit MusicController(QObject *parent = 0); ~MusicController(); + enum Status { + StatusNoPlayer = 0, + StatusStopped, + StatusPaused, + StatusPlaying + }; + + enum RepeatStatus { + RepeatNone = 0, + RepeatTrack, + RepeatPlaylist + }; + + Status status() const; + QString service() const; + + QVariantMap metadata() const; + + QString title() const; + QString album() const; + QString artist() const; + + QString albumArt() const; + + int duration() const; + + RepeatStatus repeat() const; + bool shuffle() const; + + int volume() const; + +public slots: + void play(); + void pause(); + void playPause(); + void next(); + void previous(); + + void volumeUp(); + void volumeDown(); + +signals: + void statusChanged(); + void serviceChanged(); + void metadataChanged(); + void titleChanged(); + void albumChanged(); + void artistChanged(); + void albumArtChanged(); + void durationChanged(); + void repeatChanged(); + void shuffleChanged(); + void volumeChanged(); + private: MusicControllerPrivate * const d_ptr; }; diff --git a/musiccontroller_p.h b/musiccontroller_p.h index 5711e65..dd26ff0 100644 --- a/musiccontroller_p.h +++ b/musiccontroller_p.h @@ -1,15 +1,15 @@ #ifndef WATCHFISH_MUSICCONTROLLER_P_H #define WATCHFISH_MUSICCONTROLLER_P_H -#include -#include +#include +#include #include "musiccontroller.h" namespace watchfish { -class MusicControllerPrivate : public QObject, protected QDBusContext +class MusicControllerPrivate : public QObject { Q_OBJECT @@ -17,6 +17,27 @@ public: MusicControllerPrivate(MusicController *q); ~MusicControllerPrivate(); +public: + MprisManager *manager; + MusicController::Status curStatus; + QString curTitle; + QString curAlbum; + QString curArtist; + QString curAlbumArt; + int curDuration; + +private: + static QString stripAlbumArtComponent(const QString& component); + static QString findAlbumArt(const QString &artist, const QString &album); + void updateStatus(); + void updateAlbumArt(); + void updateMetadata(); + +private slots: + void handleCurrentServiceChanged(); + void handlePlaybackStatusChanged(); + void handleMetadataChanged(); + private: MusicController * const q_ptr; Q_DECLARE_PUBLIC(MusicController) diff --git a/notificationmonitor.h b/notificationmonitor.h index 03683ca..4aad942 100644 --- a/notificationmonitor.h +++ b/notificationmonitor.h @@ -28,6 +28,8 @@ namespace watchfish { +Q_DECLARE_LOGGING_CATEGORY(notificationMonitorCat) + class NotificationMonitorPrivate; class NotificationMonitor : public QObject diff --git a/walltimemonitor.h b/walltimemonitor.h index f2f6b95..8001b02 100644 --- a/walltimemonitor.h +++ b/walltimemonitor.h @@ -26,6 +26,8 @@ namespace watchfish { +Q_DECLARE_LOGGING_CATEGORY(walltimeMonitorCat) + class WallTimeMonitorPrivate; class WallTimeMonitor : public QObject -- cgit v1.2.3