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.
591 lines
13 KiB
C++
591 lines
13 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.h"
|
|
|
|
#include "mprisplayer_p.h"
|
|
|
|
#include <qqmlinfo.h>
|
|
|
|
#include <QDBusConnection>
|
|
#include <QDBusMessage>
|
|
#include <QDBusPendingCall>
|
|
#include <QDBusPendingCallWatcher>
|
|
#include <QDBusReply>
|
|
|
|
static const QString serviceNamePrefix = QStringLiteral("org.mpris.MediaPlayer2.");
|
|
static const QString mprisObjectPath = QStringLiteral("/org/mpris/MediaPlayer2");
|
|
static const QString dBusPropertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties");
|
|
static const QString dBusPropertiesChangedSignal = QStringLiteral("PropertiesChanged");
|
|
|
|
static inline QDBusConnection getDBusConnection()
|
|
{
|
|
#ifdef USE_SYSTEM_DBUS
|
|
return QDBusConnection::systemBus();
|
|
#else
|
|
return QDBusConnection::sessionBus();
|
|
#endif
|
|
}
|
|
|
|
|
|
MprisPlayer::MprisPlayer(QObject *parent)
|
|
: QObject(parent)
|
|
, QDBusContext()
|
|
, m_mprisRootAdaptor(new MprisRootAdaptor(this))
|
|
, m_mprisPlayerAdaptor(new MprisPlayerAdaptor(this))
|
|
, m_canQuit(false)
|
|
, m_canRaise(false)
|
|
, m_canSetFullscreen(false)
|
|
, m_fullscreen(false)
|
|
, m_hasTrackList(false)
|
|
, m_canControl(false)
|
|
, m_canGoNext(false)
|
|
, m_canGoPrevious(false)
|
|
, m_canPause(false)
|
|
, m_canPlay(false)
|
|
, m_canSeek(false)
|
|
, m_loopStatus(Mpris::None)
|
|
, m_maximumRate(1)
|
|
, m_minimumRate(1)
|
|
, m_playbackStatus(Mpris::Stopped)
|
|
, m_position(0)
|
|
, m_rate(1)
|
|
, m_shuffle(false)
|
|
, m_volume(0)
|
|
{
|
|
QDBusConnection connection = getDBusConnection();
|
|
|
|
if (!connection.isConnected()) {
|
|
qmlInfo(this) << "Failed attempting to connect to DBus";
|
|
} else if (!connection.registerObject(mprisObjectPath, this)) {
|
|
qmlInfo(this) << "Failed attempting to register object path. Already registered?";
|
|
}
|
|
}
|
|
|
|
MprisPlayer::~MprisPlayer()
|
|
{
|
|
unregisterService();
|
|
}
|
|
|
|
QString MprisPlayer::serviceName() const
|
|
{
|
|
return m_serviceName;
|
|
}
|
|
|
|
void MprisPlayer::setServiceName(const QString &serviceName)
|
|
{
|
|
if (m_serviceName == serviceName) {
|
|
return;
|
|
}
|
|
|
|
unregisterService();
|
|
m_serviceName = serviceName;
|
|
registerService();
|
|
|
|
Q_EMIT serviceNameChanged();
|
|
}
|
|
|
|
|
|
// Mpris2 Root Interface
|
|
bool MprisPlayer::canQuit() const
|
|
{
|
|
return m_canQuit;
|
|
}
|
|
|
|
void MprisPlayer::setCanQuit(bool canQuit)
|
|
{
|
|
if (m_canQuit == canQuit) {
|
|
return;
|
|
}
|
|
|
|
m_canQuit = canQuit;
|
|
Q_EMIT canQuitChanged();
|
|
}
|
|
|
|
bool MprisPlayer::canRaise() const
|
|
{
|
|
return m_canRaise;
|
|
}
|
|
|
|
void MprisPlayer::setCanRaise(bool canRaise)
|
|
{
|
|
if (m_canRaise == canRaise) {
|
|
return;
|
|
}
|
|
|
|
m_canRaise = canRaise;
|
|
Q_EMIT canRaiseChanged();
|
|
}
|
|
|
|
bool MprisPlayer::canSetFullscreen() const
|
|
{
|
|
return m_canSetFullscreen;
|
|
}
|
|
|
|
void MprisPlayer::setCanSetFullscreen(bool canSetFullscreen)
|
|
{
|
|
if (m_canSetFullscreen == canSetFullscreen) {
|
|
return;
|
|
}
|
|
|
|
m_canSetFullscreen = canSetFullscreen;
|
|
Q_EMIT canSetFullscreenChanged();
|
|
}
|
|
|
|
QString MprisPlayer::desktopEntry() const
|
|
{
|
|
return m_desktopEntry;
|
|
}
|
|
|
|
void MprisPlayer::setDesktopEntry(const QString &desktopEntry)
|
|
{
|
|
if (m_desktopEntry == desktopEntry) {
|
|
return;
|
|
}
|
|
|
|
m_desktopEntry = desktopEntry;
|
|
Q_EMIT desktopEntryChanged();
|
|
}
|
|
|
|
bool MprisPlayer::fullscreen() const
|
|
{
|
|
return m_fullscreen;
|
|
}
|
|
|
|
void MprisPlayer::setFullscreen(bool fullscreen)
|
|
{
|
|
if (m_fullscreen == fullscreen) {
|
|
return;
|
|
}
|
|
|
|
m_fullscreen = fullscreen;
|
|
Q_EMIT fullscreenChanged();
|
|
}
|
|
|
|
bool MprisPlayer::hasTrackList() const
|
|
{
|
|
return m_hasTrackList;
|
|
}
|
|
|
|
void MprisPlayer::setHasTrackList(bool hasTrackList)
|
|
{
|
|
if (m_hasTrackList == hasTrackList) {
|
|
return;
|
|
}
|
|
|
|
m_hasTrackList = hasTrackList;
|
|
Q_EMIT hasTrackListChanged();
|
|
}
|
|
|
|
QString MprisPlayer::identity() const
|
|
{
|
|
return m_identity;
|
|
}
|
|
|
|
void MprisPlayer::setIdentity(const QString &identity)
|
|
{
|
|
if (m_identity == identity) {
|
|
return;
|
|
}
|
|
|
|
m_identity = identity;
|
|
Q_EMIT identityChanged();
|
|
}
|
|
|
|
QStringList MprisPlayer::supportedUriSchemes() const
|
|
{
|
|
return m_supportedUriSchemes;
|
|
}
|
|
|
|
void MprisPlayer::setSupportedUriSchemes(const QStringList &supportedUriSchemes)
|
|
{
|
|
if (m_supportedUriSchemes == supportedUriSchemes) {
|
|
return;
|
|
}
|
|
|
|
m_supportedUriSchemes = supportedUriSchemes;
|
|
Q_EMIT supportedUriSchemesChanged();
|
|
}
|
|
|
|
QStringList MprisPlayer::supportedMimeTypes() const
|
|
{
|
|
return m_supportedMimeTypes;
|
|
}
|
|
|
|
void MprisPlayer::setSupportedMimeTypes(const QStringList &supportedMimeTypes)
|
|
{
|
|
if (m_supportedMimeTypes == supportedMimeTypes) {
|
|
return;
|
|
}
|
|
|
|
m_supportedMimeTypes = supportedMimeTypes;
|
|
Q_EMIT supportedMimeTypesChanged();
|
|
}
|
|
|
|
// Mpris2 Player Interface
|
|
bool MprisPlayer::canControl() const
|
|
{
|
|
return m_canControl;
|
|
}
|
|
|
|
void MprisPlayer::setCanControl(bool canControl)
|
|
{
|
|
if (m_canControl == canControl) {
|
|
return;
|
|
}
|
|
|
|
m_canControl = canControl;
|
|
Q_EMIT canControlChanged();
|
|
}
|
|
|
|
bool MprisPlayer::canGoNext() const
|
|
{
|
|
return m_canGoNext;
|
|
}
|
|
|
|
void MprisPlayer::setCanGoNext(bool canGoNext)
|
|
{
|
|
if (m_canGoNext == canGoNext) {
|
|
return;
|
|
}
|
|
|
|
m_canGoNext = canGoNext;
|
|
Q_EMIT canGoNextChanged();
|
|
}
|
|
|
|
bool MprisPlayer::canGoPrevious() const
|
|
{
|
|
return m_canGoPrevious;
|
|
}
|
|
|
|
void MprisPlayer::setCanGoPrevious(bool canGoPrevious)
|
|
{
|
|
if (m_canGoPrevious == canGoPrevious) {
|
|
return;
|
|
}
|
|
|
|
m_canGoPrevious = canGoPrevious;
|
|
Q_EMIT canGoPreviousChanged();
|
|
}
|
|
|
|
bool MprisPlayer::canPause() const
|
|
{
|
|
return m_canPause;
|
|
}
|
|
|
|
void MprisPlayer::setCanPause(bool canPause)
|
|
{
|
|
if (m_canPause == canPause) {
|
|
return;
|
|
}
|
|
|
|
m_canPause = canPause;
|
|
Q_EMIT canPauseChanged();
|
|
}
|
|
|
|
bool MprisPlayer::canPlay() const
|
|
{
|
|
return m_canPlay;
|
|
}
|
|
|
|
void MprisPlayer::setCanPlay(bool canPlay)
|
|
{
|
|
if (m_canPlay == canPlay) {
|
|
return;
|
|
}
|
|
|
|
m_canPlay = canPlay;
|
|
Q_EMIT canPlayChanged();
|
|
}
|
|
|
|
bool MprisPlayer::canSeek() const
|
|
{
|
|
return m_canSeek;
|
|
}
|
|
|
|
void MprisPlayer::setCanSeek(bool canSeek)
|
|
{
|
|
if (m_canSeek == canSeek) {
|
|
return;
|
|
}
|
|
|
|
m_canSeek = canSeek;
|
|
Q_EMIT canSeekChanged();
|
|
}
|
|
|
|
Mpris::LoopStatus MprisPlayer::loopStatus() const
|
|
{
|
|
return m_loopStatus;
|
|
}
|
|
|
|
void MprisPlayer::setLoopStatus(Mpris::LoopStatus loopStatus)
|
|
{
|
|
if (m_loopStatus == loopStatus) {
|
|
return;
|
|
}
|
|
|
|
m_loopStatus = loopStatus;
|
|
Q_EMIT loopStatusChanged();
|
|
}
|
|
|
|
double MprisPlayer::maximumRate() const
|
|
{
|
|
return m_maximumRate;
|
|
}
|
|
|
|
void MprisPlayer::setMaximumRate(double maximumRate)
|
|
{
|
|
if (m_maximumRate == maximumRate) {
|
|
return;
|
|
}
|
|
|
|
m_maximumRate = maximumRate;
|
|
Q_EMIT maximumRateChanged();
|
|
}
|
|
|
|
QVariantMap MprisPlayer::metadata() const
|
|
{
|
|
return m_typedMetadata;
|
|
}
|
|
|
|
void MprisPlayer::setMetadata(const QVariantMap &metadata)
|
|
{
|
|
if (m_metadata == metadata) {
|
|
return;
|
|
}
|
|
|
|
m_metadata = metadata;
|
|
m_typedMetadata = typeMetadata(metadata);
|
|
Q_EMIT metadataChanged();
|
|
}
|
|
|
|
double MprisPlayer::minimumRate() const
|
|
{
|
|
return m_minimumRate;
|
|
}
|
|
|
|
void MprisPlayer::setMinimumRate(double minimumRate)
|
|
{
|
|
if (m_minimumRate == minimumRate) {
|
|
return;
|
|
}
|
|
|
|
m_minimumRate = minimumRate;
|
|
Q_EMIT minimumRateChanged();
|
|
}
|
|
|
|
Mpris::PlaybackStatus MprisPlayer::playbackStatus() const
|
|
{
|
|
return m_playbackStatus;
|
|
}
|
|
|
|
void MprisPlayer::setPlaybackStatus(Mpris::PlaybackStatus playbackStatus)
|
|
{
|
|
if (m_playbackStatus == playbackStatus) {
|
|
return;
|
|
}
|
|
|
|
m_playbackStatus = playbackStatus;
|
|
Q_EMIT playbackStatusChanged();
|
|
}
|
|
|
|
qlonglong MprisPlayer::position() const
|
|
{
|
|
return m_position;
|
|
}
|
|
|
|
void MprisPlayer::setPosition(qlonglong position)
|
|
{
|
|
if (m_position == position) {
|
|
return;
|
|
}
|
|
|
|
m_position = position;
|
|
Q_EMIT positionChanged();
|
|
}
|
|
|
|
double MprisPlayer::rate() const
|
|
{
|
|
return m_rate;
|
|
}
|
|
|
|
void MprisPlayer::setRate(double rate)
|
|
{
|
|
if (m_rate == rate) {
|
|
return;
|
|
}
|
|
|
|
m_rate = rate;
|
|
Q_EMIT rateChanged();
|
|
}
|
|
|
|
bool MprisPlayer::shuffle() const
|
|
{
|
|
return m_shuffle;
|
|
}
|
|
|
|
void MprisPlayer::setShuffle(bool shuffle)
|
|
{
|
|
if (m_shuffle == shuffle) {
|
|
return;
|
|
}
|
|
|
|
m_shuffle= shuffle;
|
|
Q_EMIT shuffleChanged();
|
|
}
|
|
|
|
double MprisPlayer::volume() const
|
|
{
|
|
return m_volume;
|
|
}
|
|
|
|
void MprisPlayer::setVolume(double volume)
|
|
{
|
|
if (m_volume == volume) {
|
|
return;
|
|
}
|
|
|
|
m_volume = volume;
|
|
Q_EMIT volumeChanged();
|
|
}
|
|
|
|
|
|
// Private
|
|
|
|
QVariantMap MprisPlayer::typeMetadata(const QVariantMap &aMetadata)
|
|
{
|
|
QVariantMap metadata;
|
|
QVariantMap::const_iterator i = aMetadata.constBegin();
|
|
while (i != aMetadata.constEnd()) {
|
|
switch (Mpris::enumerationFromString<Mpris::Metadata>(i.key())) {
|
|
case Mpris::TrackId:
|
|
metadata.insert(i.key(), QVariant::fromValue(QDBusObjectPath(i.value().toString())));
|
|
break;
|
|
case Mpris::Length:
|
|
metadata.insert(i.key(), QVariant::fromValue(i.value().toLongLong()));
|
|
break;
|
|
case Mpris::ArtUrl:
|
|
case Mpris::Url:
|
|
metadata.insert(i.key(), QVariant::fromValue(i.value().toUrl().toString()));
|
|
break;
|
|
case Mpris::Album:
|
|
case Mpris::AsText:
|
|
case Mpris::Title:
|
|
metadata.insert(i.key(), QVariant::fromValue(i.value().toString()));
|
|
break;
|
|
case Mpris::AlbumArtist:
|
|
case Mpris::Artist:
|
|
case Mpris::Comment:
|
|
case Mpris::Composer:
|
|
case Mpris::Genre:
|
|
case Mpris::Lyricist:
|
|
metadata.insert(i.key(), QVariant::fromValue(i.value().toStringList()));
|
|
break;
|
|
case Mpris::AudioBPM:
|
|
case Mpris::DiscNumber:
|
|
case Mpris::TrackNumber:
|
|
case Mpris::UseCount:
|
|
metadata.insert(i.key(), QVariant::fromValue(i.value().toInt()));
|
|
break;
|
|
case Mpris::AutoRating:
|
|
case Mpris::UserRating:
|
|
metadata.insert(i.key(), QVariant::fromValue(i.value().toFloat()));
|
|
break;
|
|
case Mpris::ContentCreated:
|
|
case Mpris::FirstUsed:
|
|
case Mpris::LastUsed:
|
|
metadata.insert(i.key(), QVariant::fromValue(i.value().toDate().toString(Qt::ISODate)));
|
|
break;
|
|
case Mpris::InvalidMetadata:
|
|
// Passing with the original type and hoping the user used
|
|
// a type supported by DBus
|
|
metadata.insert(i.key(), i.value());
|
|
break;
|
|
default:
|
|
// Nothing to do
|
|
break;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
|
|
return metadata;
|
|
}
|
|
|
|
void MprisPlayer::registerService()
|
|
{
|
|
if (m_serviceName.isEmpty()) {
|
|
qmlInfo(this) << "Failed to register service: empty service name";
|
|
return;
|
|
}
|
|
|
|
QDBusConnection connection = getDBusConnection();
|
|
|
|
if (!connection.isConnected()) {
|
|
qmlInfo(this) << "Failed attempting to connect to DBus";
|
|
return;
|
|
}
|
|
|
|
if (!connection.registerService(QString(serviceNamePrefix).append(m_serviceName))) {
|
|
qmlInfo(this) << "Failed attempting to register service: " << m_serviceName << " Already taken?";
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void MprisPlayer::unregisterService()
|
|
{
|
|
if (!m_serviceName.isEmpty()) {
|
|
QDBusConnection connection = getDBusConnection();
|
|
connection.unregisterService(QString(serviceNamePrefix).append(m_serviceName));
|
|
}
|
|
}
|
|
|
|
void MprisPlayer::notifyPropertiesChanged(const QString& interfaceName, const QVariantMap &changedProperties, const QStringList &invalidatedProperties) const
|
|
{
|
|
if (m_serviceName.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
QDBusConnection connection = getDBusConnection();
|
|
|
|
if (!connection.isConnected()) {
|
|
qmlInfo(this) << "Failed attempting to connect to DBus";
|
|
return;
|
|
}
|
|
|
|
QDBusMessage message = QDBusMessage::createSignal(mprisObjectPath,
|
|
dBusPropertiesInterface,
|
|
dBusPropertiesChangedSignal);
|
|
|
|
QList<QVariant> arguments;
|
|
arguments << QVariant(interfaceName) << QVariant(changedProperties) << QVariant(invalidatedProperties);
|
|
message.setArguments(arguments);
|
|
|
|
if (!connection.send(message)) {
|
|
qmlInfo(this) << "Failed to send DBus property notification signal";
|
|
}
|
|
}
|