You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libcutefish/mpris/mprisplayeradaptor.cpp

645 lines
20 KiB
C++

// -*- c++ -*-
/*!
*
* Copyright (C) 2015 Jolla Ltd.
*
* Contact: Valerio Valerio <valerio.valerio@jolla.com>
* Author: Andres Gomez <andres.gomez@jolla.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mprisplayer_p.h"
#include "mprisplayer.h"
#include <qqmlinfo.h>
#include <QtCore/QMetaObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QDebug>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
/*
* Implementation of adaptor class MprisPlayerAdaptor
*/
static const QString mprisPlayerInterface = QStringLiteral("org.mpris.MediaPlayer2.Player");
MprisPlayerAdaptor::MprisPlayerAdaptor(MprisPlayer *parent)
: QDBusAbstractAdaptor(parent)
{
// constructor
setAutoRelaySignals(false);
connect(parent, SIGNAL(canControlChanged()), this, SLOT(onCanControlChanged()));
connect(parent, SIGNAL(canGoNextChanged()), this, SLOT(onCanGoNextChanged()));
connect(parent, SIGNAL(canGoPreviousChanged()), this, SLOT(onCanGoPreviousChanged()));
connect(parent, SIGNAL(canPauseChanged()), this, SLOT(onCanPauseChanged()));
connect(parent, SIGNAL(canPlayChanged()), this, SLOT(onCanPlayChanged()));
connect(parent, SIGNAL(canSeekChanged()), this, SLOT(onCanSeekChanged()));
connect(parent, SIGNAL(loopStatusChanged()), this, SLOT(onLoopStatusChanged()));
connect(parent, SIGNAL(maximumRateChanged()), this, SLOT(onMaximumRateChanged()));
connect(parent, SIGNAL(metadataChanged()), this, SLOT(onMetadataChanged()));
connect(parent, SIGNAL(minimumRateChanged()), this, SLOT(onMinimumRateChanged()));
connect(parent, SIGNAL(playbackStatusChanged()), this, SLOT(onPlaybackStatusChanged()));
// PositionChanged signal is not forwarded through DBus ...
connect(parent, SIGNAL(rateChanged()), this, SLOT(onRateChanged()));
connect(parent, SIGNAL(shuffleChanged()), this, SLOT(onShuffleChanged()));
connect(parent, SIGNAL(volumeChanged()), this, SLOT(onVolumeChanged()));
connect(parent, SIGNAL(seeked(qlonglong)), this, SIGNAL(Seeked(qlonglong)));
}
MprisPlayerAdaptor::~MprisPlayerAdaptor()
{
// destructor
}
bool MprisPlayerAdaptor::canControl() const
{
return static_cast<MprisPlayer *>(parent())->canControl();
}
bool MprisPlayerAdaptor::canGoNext() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
return player->canControl() && player->canGoNext();
}
bool MprisPlayerAdaptor::canGoPrevious() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
return player->canControl() && player->canGoPrevious();
}
bool MprisPlayerAdaptor::canPause() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
return player->canControl() && player->canPause();
}
bool MprisPlayerAdaptor::canPlay() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
return player->canControl() && player->canPlay();
}
bool MprisPlayerAdaptor::canSeek() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
return player->canControl() && player->canSeek();
}
QString MprisPlayerAdaptor::loopStatus() const
{
return Mpris::enumerationToString(static_cast<MprisPlayer *>(parent())->loopStatus());
}
void MprisPlayerAdaptor::setLoopStatus(const QString &value)
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (player->canControl()) {
Q_EMIT player->loopStatusRequested(Mpris::enumerationFromString<Mpris::LoopStatus>(value));
return;
}
// We cannot send an error reply in a property setter so we just
// complain here
qDebug() << Q_FUNC_INFO << "Requested to modify the loop status but it is not supported.";
}
double MprisPlayerAdaptor::maximumRate() const
{
return static_cast<MprisPlayer *>(parent())->maximumRate();
}
QVariantMap MprisPlayerAdaptor::metadata() const
{
return static_cast<MprisPlayer *>(parent())->metadata();
}
double MprisPlayerAdaptor::minimumRate() const
{
return static_cast<MprisPlayer *>(parent())->minimumRate();
}
QString MprisPlayerAdaptor::playbackStatus() const
{
return Mpris::enumerationToString(static_cast<MprisPlayer *>(parent())->playbackStatus());
}
qlonglong MprisPlayerAdaptor::position() const
{
return static_cast<MprisPlayer *>(parent())->position();
}
double MprisPlayerAdaptor::rate() const
{
return static_cast<MprisPlayer *>(parent())->rate();
}
void MprisPlayerAdaptor::setRate(double value)
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
QString errorMessage;
if (!player->canControl()) {
errorMessage = QStringLiteral("Requested to modify the rate but it is not supported.");
} else if (player->minimumRate() > value) {
errorMessage = QStringLiteral("Requested to modify the rate under its minimum allowed value.");
} else if (player->maximumRate() < value) {
errorMessage = QStringLiteral("Requested to modify the rate over its maximum allowed value.");
}
if (errorMessage.isEmpty()) {
if (value == 0) {
Q_EMIT player->pauseRequested();
} else {
Q_EMIT player->rateRequested(value);
}
return;
}
// We cannot send an error reply in a property setter so we just
// complain here
qDebug() << Q_FUNC_INFO << errorMessage;
}
bool MprisPlayerAdaptor::shuffle() const
{
return static_cast<MprisPlayer *>(parent())->shuffle();
}
void MprisPlayerAdaptor::setShuffle(bool value)
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (player->canControl()) {
Q_EMIT player->shuffleRequested(value);
return;
}
// We cannot send an error reply in a property setter so we just
// complain here
qDebug() << Q_FUNC_INFO << "Requested to modify the shuffle but it is not supported.";
}
double MprisPlayerAdaptor::volume() const
{
return static_cast<MprisPlayer *>(parent())->volume();
}
void MprisPlayerAdaptor::setVolume(double value)
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (player->canControl()) {
Q_EMIT player->volumeRequested(value < 0 ? 0 : value);
return;
}
// We cannot send an error reply in a property setter so we just
// complain here
qDebug() << Q_FUNC_INFO << "Requested to modify the volume but it is not supported.";
}
void MprisPlayerAdaptor::Next()
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (!player->canControl()) {
player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to move to next track but it is not supported."));
return;
}
if (!player->canGoNext()) {
return;
}
Q_EMIT player->nextRequested();
}
void MprisPlayerAdaptor::OpenUri(const QString &Uri)
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (!player->canControl()) {
player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to open an url but it is not supported."));
return;
}
QUrl url(Uri, QUrl::StrictMode);
if (!url.isValid()) {
player->sendErrorReply(QDBusError::InvalidArgs, QStringLiteral("Wanted to open an url but the url is invalid."));
}
if (!player->supportedUriSchemes().contains(url.scheme())) {
player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to open an url but the scheme is not supported."));
}
QMimeDatabase db;
QMimeType mime;
if (url.isLocalFile()) {
mime = db.mimeTypeForFile(url.toLocalFile());
} else {
mime = db.mimeTypeForFile(url.fileName(), QMimeDatabase::MatchExtension);
}
QStringList mimeNames = mime.aliases();
mimeNames.prepend(mime.name());
for (int i = 0; i < mimeNames.size(); i++) {
if (player->supportedMimeTypes().contains(mimeNames[i])) {
Q_EMIT player->openUriRequested(url);
return;
}
}
player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to open an url but the mime type is not supported."));
}
void MprisPlayerAdaptor::Pause()
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (!player->canControl()) {
player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to pause but it is not supported."));
return;
}
if (!player->canPause()) {
return;
}
switch (player->playbackStatus()) {
case Mpris::Playing:
case Mpris::Stopped:
Q_EMIT player->pauseRequested();
break;
case Mpris::Paused:
default:
// Nothing to do
break;
}
}
void MprisPlayerAdaptor::Play()
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (!player->canControl()) {
player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to play but it is not supported."));
return;
}
if (!player->canPlay()) {
return;
}
switch (player->playbackStatus()) {
case Mpris::Stopped:
case Mpris::Paused:
Q_EMIT player->playRequested();
break;
case Mpris::Playing:
default:
// Nothing to do
break;
}
}
void MprisPlayerAdaptor::PlayPause()
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (!player->canControl()) {
player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to play or pause but it is not supported."));
return;
}
switch (player->playbackStatus()) {
case Mpris::Playing:
if (!player->canPause()) {
player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to pause but it is not supported."));
return;
}
Q_EMIT player->pauseRequested();
break;
case Mpris::Stopped:
case Mpris::Paused:
if (!player->canPlay()) {
player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to play but it is not supported."));
return;
}
Q_EMIT player->playRequested();
break;
default:
// Nothing to do
break;
}
}
void MprisPlayerAdaptor::Previous()
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (!player->canControl()) {
player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to move to previous track but it is not supported."));
return;
}
if (!player->canGoPrevious()) {
return;
}
Q_EMIT player->previousRequested();
}
void MprisPlayerAdaptor::Seek(qlonglong Offset)
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (!player->canControl()) {
player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to seek but it is not supported."));
return;
}
if (!player->canSeek() || Offset == 0) {
return;
}
if (Offset < 0) {
Q_EMIT player->seekRequested(Offset);
return;
}
QVariantMap metadata = player->metadata();
QVariant length = metadata[Mpris::metadataToString(Mpris::Length)];
if (length.isValid() && (player->position() + Offset) > length.toLongLong()) {
Q_EMIT player->nextRequested();
return;
}
Q_EMIT player->seekRequested(Offset);
}
void MprisPlayerAdaptor::SetPosition(const QDBusObjectPath &TrackId, qlonglong Position)
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (!player->canControl()) {
player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to move to position but it is not supported."));
return;
}
if (!player->canSeek()) {
return;
}
QVariantMap metadata = player->metadata();
QVariant trackId = metadata[Mpris::metadataToString(Mpris::TrackId)];
QVariant length = metadata[Mpris::metadataToString(Mpris::Length)];
if (!trackId.isValid() || !length.isValid()) {
return;
}
if (trackId.value<QDBusObjectPath>() != TrackId) {
player->sendErrorReply(QDBusError::InvalidArgs, QStringLiteral("Wanted to move to position but the TrackId is not the current one."));
return;
}
if (Position > length.toLongLong()) {
player->sendErrorReply(QDBusError::InvalidArgs, QStringLiteral("Wanted to move to position but the position is off range."));
return;
}
Q_EMIT player->setPositionRequested(TrackId, Position);
}
void MprisPlayerAdaptor::Stop()
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (!player->canControl()) {
player->sendErrorReply(QDBusError::NotSupported, QStringLiteral("Wanted to stop but it is not supported."));
return;
}
switch (player->playbackStatus()) {
case Mpris::Playing:
case Mpris::Paused:
Q_EMIT player->stopRequested();
break;
case Mpris::Stopped:
default:
// Nothing to do
break;
}
}
// Private
void MprisPlayerAdaptor::onCanControlChanged() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
// canControlChanged signal is not forwarded through DBus, but
// we can notify about the rest of "Can*" properties
QVariantMap changedProperties;
if (player->canGoNext()) {
changedProperties[QStringLiteral("CanGoNext")] = QVariant(player->canControl());
}
if (player->canGoPrevious()) {
changedProperties[QStringLiteral("CanGoPrevious")] = QVariant(player->canControl());
}
if (player->canPause()) {
changedProperties[QStringLiteral("CanPause")] = QVariant(player->canControl());
}
if (player->canPlay()) {
changedProperties[QStringLiteral("CanPlay")] = QVariant(player->canControl());
}
if (player->canSeek()) {
changedProperties[QStringLiteral("CanSeek")] = QVariant(player->canControl());
}
if (changedProperties.isEmpty()) {
return;
}
player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList());
}
void MprisPlayerAdaptor::onCanGoNextChanged() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (!player->canControl()) {
return;
}
QVariantMap changedProperties;
changedProperties[QStringLiteral("CanGoNext")] = QVariant(player->canGoNext());
player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList());
}
void MprisPlayerAdaptor::onCanGoPreviousChanged() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (!player->canControl()) {
return;
}
QVariantMap changedProperties;
changedProperties[QStringLiteral("CanGoPrevious")] = QVariant(player->canGoPrevious());
player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList());
}
void MprisPlayerAdaptor::onCanPauseChanged() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (!player->canControl()) {
return;
}
QVariantMap changedProperties;
changedProperties[QStringLiteral("CanPause")] = QVariant(player->canPause());
player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList());
}
void MprisPlayerAdaptor::onCanPlayChanged() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (!player->canControl()) {
return;
}
QVariantMap changedProperties;
changedProperties[QStringLiteral("CanPlay")] = QVariant(player->canPlay());
player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList());
}
void MprisPlayerAdaptor::onCanSeekChanged() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (!player->canControl()) {
return;
}
QVariantMap changedProperties;
changedProperties[QStringLiteral("CanSeek")] = QVariant(player->canSeek());
player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList());
}
void MprisPlayerAdaptor::onLoopStatusChanged() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
QVariantMap changedProperties;
changedProperties[QStringLiteral("LoopStatus")] = QVariant(Mpris::enumerationToString(player->loopStatus()));
player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList());
}
void MprisPlayerAdaptor::onMaximumRateChanged() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (player->maximumRate() < 1) {
qmlInfo(this) << "Maximum rate should be equal or above 1";
return;
}
QVariantMap changedProperties;
changedProperties[QStringLiteral("MaximumRate")] = QVariant(player->maximumRate());
player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList());
}
void MprisPlayerAdaptor::onMetadataChanged() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
QVariantMap changedProperties;
changedProperties[QStringLiteral("Metadata")] = QVariant(player->metadata());
player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList());
}
void MprisPlayerAdaptor::onMinimumRateChanged() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (player->minimumRate() > 1) {
qmlInfo(this) << "Minimum rate should be equal or less than 1";
return;
}
QVariantMap changedProperties;
changedProperties[QStringLiteral("MinimumRate")] = QVariant(player->minimumRate() < 0 ? 0 : player->minimumRate());
player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList());
}
void MprisPlayerAdaptor::onPlaybackStatusChanged() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
QVariantMap changedProperties;
changedProperties[QStringLiteral("PlaybackStatus")] = QVariant(Mpris::enumerationToString(player->playbackStatus()));
player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList());
}
void MprisPlayerAdaptor::onRateChanged() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
if (player->rate() <= 0 || player->rate() < player->minimumRate() || player->rate() > player->maximumRate()) {
qmlInfo(this) << "Rate should never be negative or out of the minimum and maximum limits";
return;
}
QVariantMap changedProperties;
changedProperties[QStringLiteral("Rate")] = QVariant(player->rate());
player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList());
}
void MprisPlayerAdaptor::onShuffleChanged() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
QVariantMap changedProperties;
changedProperties[QStringLiteral("Shuffle")] = QVariant(player->shuffle());
player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList());
}
void MprisPlayerAdaptor::onVolumeChanged() const
{
MprisPlayer * const player = static_cast<MprisPlayer *>(parent());
QVariantMap changedProperties;
changedProperties[QStringLiteral("Volume")] = QVariant(player->volume() < 0 ? 0 : player->volume());
player->notifyPropertiesChanged(mprisPlayerInterface, changedProperties, QStringList());
}