File indexing completed on 2024-05-19 05:38:37
0001 /* 0002 SPDX-FileCopyrightText: 2012 Alex Merry <alex.merry@kdemail.net> 0003 SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com> 0004 0005 SPDX-License-Identifier: LGPL-2.1-or-later 0006 */ 0007 0008 #include "playercontainer.h" 0009 0010 #include <QDBusConnection> 0011 #include <QDBusObjectPath> 0012 #include <QDBusReply> 0013 #include <QVariantMap> 0014 0015 #include <KDesktopFile> 0016 0017 #include "dbusproperties.h" 0018 #include "libkmpris_debug.h" 0019 #include "mprisplayer.h" 0020 #include "mprisroot.h" 0021 0022 AbstractPlayerContainer::AbstractPlayerContainer(QObject *parent) 0023 : QObject(parent) 0024 { 0025 } 0026 0027 AbstractPlayerContainer::~AbstractPlayerContainer() 0028 { 0029 } 0030 0031 bool AbstractPlayerContainer::canControl() const 0032 { 0033 return m_canControl.value(); 0034 } 0035 0036 bool AbstractPlayerContainer::canGoNext() const 0037 { 0038 return m_effectiveCanGoNext.value(); 0039 } 0040 0041 bool AbstractPlayerContainer::canGoPrevious() const 0042 { 0043 return m_effectiveCanGoPrevious.value(); 0044 } 0045 0046 bool AbstractPlayerContainer::canPause() const 0047 { 0048 return m_effectiveCanPause.value(); 0049 } 0050 0051 bool AbstractPlayerContainer::canPlay() const 0052 { 0053 return m_effectiveCanPlay.value(); 0054 } 0055 0056 bool AbstractPlayerContainer::canStop() const 0057 { 0058 return m_effectiveCanStop.value(); 0059 } 0060 0061 bool AbstractPlayerContainer::canSeek() const 0062 { 0063 return m_effectiveCanSeek.value(); 0064 } 0065 0066 LoopStatus::Status AbstractPlayerContainer::loopStatus() const 0067 { 0068 return m_loopStatus.value(); 0069 } 0070 0071 double AbstractPlayerContainer::maximumRate() const 0072 { 0073 return m_maximumRate.value(); 0074 } 0075 0076 double AbstractPlayerContainer::minimumRate() const 0077 { 0078 return m_minimumRate.value(); 0079 } 0080 0081 PlaybackStatus::Status AbstractPlayerContainer::playbackStatus() const 0082 { 0083 return m_playbackStatus.value(); 0084 } 0085 0086 qlonglong AbstractPlayerContainer::position() const 0087 { 0088 return m_position.value(); 0089 } 0090 0091 double AbstractPlayerContainer::rate() const 0092 { 0093 return m_rate.value(); 0094 } 0095 0096 ShuffleStatus::Status AbstractPlayerContainer::shuffle() const 0097 { 0098 return m_shuffle.value(); 0099 } 0100 0101 double AbstractPlayerContainer::volume() const 0102 { 0103 return m_volume.value(); 0104 } 0105 0106 QString AbstractPlayerContainer::track() const 0107 { 0108 return m_track.value(); 0109 } 0110 0111 QString AbstractPlayerContainer::artist() const 0112 { 0113 return m_artist.value(); 0114 } 0115 0116 QString AbstractPlayerContainer::artUrl() const 0117 { 0118 return m_artUrl.value(); 0119 } 0120 0121 QString AbstractPlayerContainer::album() const 0122 { 0123 return m_album.value(); 0124 } 0125 0126 double AbstractPlayerContainer::length() const 0127 { 0128 return m_length; 0129 } 0130 0131 unsigned AbstractPlayerContainer::instancePid() const 0132 { 0133 return m_instancePid; 0134 } 0135 0136 unsigned AbstractPlayerContainer::kdePid() const 0137 { 0138 return m_kdePid.value(); 0139 } 0140 0141 bool AbstractPlayerContainer::canQuit() const 0142 { 0143 return m_canQuit.value(); 0144 } 0145 0146 bool AbstractPlayerContainer::canRaise() const 0147 { 0148 return m_canRaise.value(); 0149 } 0150 0151 bool AbstractPlayerContainer::canSetFullscreen() const 0152 { 0153 return m_canSetFullscreen.value(); 0154 } 0155 0156 QString AbstractPlayerContainer::desktopEntry() const 0157 { 0158 return m_desktopEntry; 0159 } 0160 0161 bool AbstractPlayerContainer::fullscreen() const 0162 { 0163 return m_fullscreen.value(); 0164 } 0165 0166 PlayerContainer::PlayerContainer(const QString &busAddress, QObject *parent) 0167 : AbstractPlayerContainer(parent) 0168 , m_dbusAddress(busAddress) 0169 , m_propsIface(new OrgFreedesktopDBusPropertiesInterface(busAddress, MPRIS2_PATH, QDBusConnection::sessionBus(), this)) 0170 , m_playerIface(new OrgMprisMediaPlayer2PlayerInterface(busAddress, MPRIS2_PATH, QDBusConnection::sessionBus(), this)) 0171 , m_rootIface(new OrgMprisMediaPlayer2Interface(busAddress, MPRIS2_PATH, QDBusConnection::sessionBus(), this)) 0172 { 0173 Q_ASSERT(busAddress.startsWith(MPRIS2_PREFIX)); 0174 0175 // MPRIS specifies, that in case a player supports several instances, each additional 0176 // instance after the first one is supposed to append ".instance<pid>" at the end of 0177 // its dbus address. So instances of media players, which implement this correctly 0178 // can have one D-Bus connection per instance and can be identified by their pids. 0179 if (QDBusReply<unsigned> pidReply = QDBusConnection::sessionBus().interface()->servicePid(busAddress); pidReply.isValid()) { 0180 m_instancePid = pidReply.value(); 0181 } 0182 0183 initBindings(); 0184 refresh(); 0185 } 0186 0187 PlayerContainer::~PlayerContainer() 0188 { 0189 } 0190 0191 void PlayerContainer::setLoopStatus(LoopStatus::Status value) 0192 { 0193 if (m_loopStatus == value) { 0194 return; 0195 } 0196 0197 QVariant result; 0198 switch (value) { 0199 case LoopStatus::None: 0200 result = QStringLiteral("None"); 0201 break; 0202 case LoopStatus::Playlist: 0203 result = QStringLiteral("Playlist"); 0204 break; 0205 case LoopStatus::Track: 0206 result = QStringLiteral("Track"); 0207 break; 0208 default: 0209 Q_UNREACHABLE(); 0210 } 0211 m_propsIface->Set(OrgMprisMediaPlayer2PlayerInterface::staticInterfaceName(), QStringLiteral("LoopStatus"), QDBusVariant(result)); 0212 // Emit changed signals in onPropertiesChanged 0213 } 0214 0215 void PlayerContainer::setPosition(qlonglong value) 0216 { 0217 if (m_position == value) { 0218 return; 0219 } 0220 0221 m_playerIface->SetPosition(QDBusObjectPath(m_trackId.value()), value); 0222 } 0223 0224 void PlayerContainer::setRate(double value) 0225 { 0226 if (m_rate == value) { 0227 return; 0228 } 0229 0230 m_propsIface->Set(OrgMprisMediaPlayer2PlayerInterface::staticInterfaceName(), QStringLiteral("Rate"), QDBusVariant(QVariant(value))); 0231 // Emit changed signals in onPropertiesChanged 0232 } 0233 0234 void PlayerContainer::setShuffle(ShuffleStatus::Status value) 0235 { 0236 if (m_shuffle == value) { 0237 return; 0238 } 0239 0240 m_propsIface->Set(OrgMprisMediaPlayer2PlayerInterface::staticInterfaceName(), 0241 QStringLiteral("Shuffle"), 0242 QDBusVariant(QVariant(value == ShuffleStatus::On))); 0243 // Emit changed signals in onPropertiesChanged 0244 } 0245 0246 void PlayerContainer::setVolume(double value) 0247 { 0248 if (m_volume == value) { 0249 return; 0250 } 0251 0252 m_propsIface->Set(OrgMprisMediaPlayer2PlayerInterface::staticInterfaceName(), QStringLiteral("Volume"), QDBusVariant(QVariant(value))); 0253 // Emit changed signals in onPropertiesChanged 0254 } 0255 0256 void PlayerContainer::Next() 0257 { 0258 Q_ASSERT_X(m_canGoNext.value(), Q_FUNC_INFO, qUtf8Printable(identity())); 0259 if (!m_canGoNext.value()) { 0260 return; 0261 } 0262 m_playerIface->Next(); 0263 updatePosition(); 0264 } 0265 0266 void PlayerContainer::OpenUri(const QString &Uri) 0267 { 0268 m_playerIface->OpenUri(Uri); 0269 } 0270 0271 void PlayerContainer::Pause() 0272 { 0273 Q_ASSERT_X(m_canPause.value(), Q_FUNC_INFO, qUtf8Printable(identity())); 0274 if (!m_canPause.value()) { 0275 return; 0276 } 0277 m_playerIface->Pause(); 0278 } 0279 0280 void PlayerContainer::Play() 0281 { 0282 Q_ASSERT_X(m_canPlay.value(), Q_FUNC_INFO, qUtf8Printable(identity())); 0283 if (!m_canPlay.value()) { 0284 return; 0285 } 0286 m_playerIface->Play(); 0287 } 0288 0289 void PlayerContainer::PlayPause() 0290 { 0291 Q_ASSERT_X(m_canPlay.value(), Q_FUNC_INFO, qUtf8Printable(identity())); 0292 Q_ASSERT_X(m_canPause.value(), Q_FUNC_INFO, qUtf8Printable(identity())); 0293 if (!m_canPlay.value() || !m_canPause.value()) { 0294 return; 0295 } 0296 m_playerIface->PlayPause(); 0297 } 0298 0299 void PlayerContainer::Previous() 0300 { 0301 Q_ASSERT_X(m_canGoPrevious.value(), Q_FUNC_INFO, qUtf8Printable(identity())); 0302 if (!m_canGoPrevious.value()) { 0303 return; 0304 } 0305 m_playerIface->Previous(); 0306 updatePosition(); 0307 } 0308 0309 void PlayerContainer::Seek(qlonglong Offset) 0310 { 0311 Q_ASSERT_X(m_canSeek.value(), Q_FUNC_INFO, qUtf8Printable(identity())); 0312 if (!m_canSeek.value()) { 0313 return; 0314 } 0315 m_playerIface->Seek(Offset); 0316 } 0317 0318 void PlayerContainer::Stop() 0319 { 0320 Q_ASSERT_X(m_canStop.value(), Q_FUNC_INFO, qUtf8Printable(identity())); 0321 if (!m_canStop.value()) { 0322 return; 0323 } 0324 m_playerIface->Stop(); 0325 } 0326 0327 void PlayerContainer::setFullscreen(bool value) 0328 { 0329 if (m_fullscreen == value) { 0330 return; 0331 } 0332 0333 m_propsIface->Set(OrgMprisMediaPlayer2Interface::staticInterfaceName(), QStringLiteral("Fullscreen"), QDBusVariant(QVariant(value))); 0334 // Emit changed signals in onPropertiesChanged 0335 } 0336 0337 void PlayerContainer::setPlaybackStatus(PlaybackStatus::Status value) 0338 { 0339 if (m_playbackStatus == value) { 0340 return; 0341 } 0342 0343 switch (value) { 0344 case PlaybackStatus::Playing: 0345 Play(); 0346 break; 0347 case PlaybackStatus::Paused: 0348 Pause(); 0349 break; 0350 case PlaybackStatus::Stopped: 0351 Stop(); 0352 break; 0353 default: 0354 #ifdef __cpp_lib_unreachable 0355 std::unreachable(); 0356 #else 0357 Q_UNREACHABLE(); 0358 #endif 0359 } 0360 0361 // Emit changed signals in onPropertiesChanged 0362 } 0363 0364 bool AbstractPlayerContainer::hasTrackList() const 0365 { 0366 return m_hasTrackList; 0367 } 0368 0369 QString AbstractPlayerContainer::identity() const 0370 { 0371 return m_identity; 0372 } 0373 0374 QStringList AbstractPlayerContainer::supportedMimeTypes() const 0375 { 0376 return m_supportedMimeTypes; 0377 } 0378 0379 QStringList AbstractPlayerContainer::supportedUriSchemes() const 0380 { 0381 return m_supportedUriSchemes; 0382 } 0383 0384 void PlayerContainer::Quit() 0385 { 0386 Q_ASSERT_X(m_canQuit.value(), Q_FUNC_INFO, qUtf8Printable(identity())); 0387 if (!m_canQuit.value()) { 0388 return; 0389 } 0390 m_rootIface->Quit(); 0391 } 0392 void PlayerContainer::Raise() 0393 { 0394 Q_ASSERT_X(m_canRaise.value(), Q_FUNC_INFO, qUtf8Printable(identity())); 0395 if (!m_canRaise.value()) { 0396 return; 0397 } 0398 m_rootIface->Raise(); 0399 } 0400 0401 QString AbstractPlayerContainer::iconName() const 0402 { 0403 return m_iconName; 0404 } 0405 0406 void PlayerContainer::refresh() 0407 { 0408 // despite these calls being async, we should never update values in the 0409 // wrong order (eg: a stale GetAll response overwriting a more recent value 0410 // from a PropertiesChanged signal) due to D-Bus message ordering guarantees. 0411 0412 QDBusPendingCall async = m_propsIface->GetAll(OrgMprisMediaPlayer2Interface::staticInterfaceName()); 0413 auto watcher = new QDBusPendingCallWatcher(async, this); 0414 connect(watcher, &QDBusPendingCallWatcher::finished, this, &PlayerContainer::onGetPropsFinished); 0415 ++m_fetchesPending; 0416 0417 async = m_propsIface->GetAll(OrgMprisMediaPlayer2PlayerInterface::staticInterfaceName()); 0418 watcher = new QDBusPendingCallWatcher(async, this); 0419 connect(watcher, &QDBusPendingCallWatcher::finished, this, &PlayerContainer::onGetPropsFinished); 0420 ++m_fetchesPending; 0421 } 0422 0423 void PlayerContainer::updatePosition() 0424 { 0425 QDBusPendingCall call = m_propsIface->Get(OrgMprisMediaPlayer2PlayerInterface::staticInterfaceName(), QStringLiteral("Position")); 0426 auto watcher = new QDBusPendingCallWatcher(call, this); 0427 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { 0428 QDBusPendingReply<QVariant> propsReply = *watcher; 0429 watcher->deleteLater(); 0430 if (!propsReply.isValid() && propsReply.error().type() != QDBusError::NotSupported) { 0431 qCDebug(MPRIS2) << m_dbusAddress << "does not implement" << OrgFreedesktopDBusPropertiesInterface::staticInterfaceName() 0432 << "correctly. Error message was" << propsReply.error().name() << propsReply.error().message(); 0433 return; 0434 } 0435 0436 m_position = propsReply.value().toLongLong(); 0437 }); 0438 } 0439 0440 void PlayerContainer::changeVolume(double delta, bool showOSD) 0441 { 0442 // Not relying on property/setProperty to avoid doing blocking DBus calls 0443 const double newVolume = qBound(0.0, m_volume + delta, std::max(m_volume.value(), 1.0)); 0444 const double oldVolume = m_volume; 0445 0446 // Update the container value right away so when calling this method in quick succession 0447 // (mouse wheeling the tray icon) next call already gets the new value 0448 m_volume = newVolume; 0449 0450 QDBusPendingCall call = m_propsIface->Set(m_playerIface->interface(), QStringLiteral("Volume"), QDBusVariant(newVolume)); 0451 auto watcher = new QDBusPendingCallWatcher(call, this); 0452 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, oldVolume, showOSD](QDBusPendingCallWatcher *watcher) { 0453 QDBusPendingReply<void> reply = *watcher; 0454 watcher->deleteLater(); 0455 if (!reply.isValid()) { 0456 m_volume = oldVolume; 0457 return; 0458 } 0459 0460 if (showOSD) { 0461 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.plasmashell"), 0462 QStringLiteral("/org/kde/osdService"), 0463 QStringLiteral("org.kde.osdService"), 0464 QStringLiteral("mediaPlayerVolumeChanged")); 0465 0466 msg.setArguments({qRound(m_volume * 100), m_identity, m_iconName}); 0467 0468 QDBusConnection::sessionBus().asyncCall(msg); 0469 } 0470 }); 0471 } 0472 0473 void PlayerContainer::initBindings() 0474 { 0475 // Since the bindings are already used in QML, move them to C++ for better efficiency and consistency 0476 m_effectiveCanGoNext.setBinding([this] { 0477 return m_canControl.value() && m_canGoNext.value(); 0478 }); 0479 m_effectiveCanGoPrevious.setBinding([this] { 0480 return m_canControl.value() && m_canGoPrevious.value(); 0481 }); 0482 m_effectiveCanPlay.setBinding([this] { 0483 return m_canControl.value() && m_canPlay.value(); 0484 }); 0485 m_effectiveCanPause.setBinding([this] { 0486 return m_canControl.value() && m_canPause.value(); 0487 }); 0488 m_effectiveCanStop.setBinding([this] { 0489 return m_canControl.value() && m_canStop.value(); 0490 }); 0491 m_effectiveCanSeek.setBinding([this] { 0492 return m_canControl.value() && m_canSeek.value(); 0493 }); 0494 0495 // Fake canStop property 0496 m_canStop.setBinding([this] { 0497 return m_canControl.value() && m_playbackStatus.value() > PlaybackStatus::Stopped; 0498 }); 0499 0500 // Metadata 0501 m_track.setBinding([this] { 0502 if (!m_xesamTitle.value().isEmpty()) { 0503 return m_xesamTitle.value(); 0504 } 0505 const QStringView xesamUrl{m_xesamUrl.value()}; 0506 if (xesamUrl.isEmpty()) { 0507 return QString(); 0508 } 0509 if (int lastSlashPos = xesamUrl.lastIndexOf(QLatin1Char('/')); lastSlashPos < 0 || lastSlashPos == xesamUrl.size() - 1) { 0510 return QString(); 0511 } else { 0512 const QStringView lastUrlPart = xesamUrl.sliced(lastSlashPos + 1); 0513 return QUrl::fromEncoded(lastUrlPart.toLatin1()).toString(); 0514 } 0515 }); 0516 m_artist.setBinding([this] { 0517 if (!m_xesamArtist.value().empty()) { 0518 return m_xesamArtist.value().join(QLatin1String(", ")); 0519 } 0520 if (!m_xesamAlbumArtist.value().empty()) { 0521 return m_xesamAlbumArtist.value().join(QLatin1String(", ")); 0522 } 0523 return QString(); 0524 }); 0525 m_album.setBinding([this] { 0526 if (!m_xesamAlbum.value().isEmpty()) { 0527 return m_xesamAlbum.value(); 0528 } 0529 const QStringView xesamUrl{m_xesamUrl.value()}; 0530 if (!xesamUrl.startsWith(QLatin1String("file:///"))) { 0531 return QString(); 0532 } 0533 const QList<QStringView> urlParts = xesamUrl.split(QLatin1Char('/')); 0534 if (urlParts.size() < 3) { 0535 return QString(); 0536 } 0537 // if we play a local file without title and artist, show its containing folder instead 0538 if (auto lastFolderPathIt = std::next(urlParts.crbegin()); !lastFolderPathIt->isEmpty()) { 0539 return QUrl::fromEncoded(lastFolderPathIt->toLatin1()).toString(); 0540 } 0541 return QString(); 0542 }); 0543 0544 auto callback = [this] { 0545 updatePosition(); 0546 }; 0547 m_rateNotifier = m_rate.addNotifier(callback); 0548 m_playbackStatusNotifier = m_playbackStatus.addNotifier(callback); 0549 } 0550 0551 void PlayerContainer::updateFromMap(const QVariantMap &map) 0552 { 0553 auto updateSingleProperty = [this]<typename T>(T &property, const QVariant &value, QMetaType::Type expectedType, void (PlayerContainer::*signal)()) { 0554 if (value.metaType().id() != expectedType) { 0555 qCWarning(MPRIS2) << m_dbusAddress << "exports" << value.metaType() << "but it should be" << QMetaType(expectedType); 0556 } 0557 if (T newProperty = value.value<T>(); property != newProperty) { 0558 property = newProperty; 0559 Q_EMIT(this->*signal)(); 0560 } 0561 }; 0562 0563 QString oldTrackId; 0564 0565 for (auto it = map.cbegin(); it != map.cend(); it = std::next(it)) { 0566 const QString &propName = it.key(); 0567 0568 if (propName == QLatin1String("Identity")) { 0569 updateSingleProperty(m_identity, it.value(), QMetaType::QString, &PlayerContainer::identityChanged); 0570 } else if (propName == QLatin1String("DesktopEntry")) { 0571 m_iconName = KDesktopFile(it.value().toString() + QLatin1String(".desktop")).readIcon(); 0572 if (m_iconName.isEmpty()) { 0573 m_iconName = QStringLiteral("emblem-music-symbolic"); 0574 } 0575 updateSingleProperty(m_desktopEntry, it.value(), QMetaType::QString, &PlayerContainer::desktopEntryChanged); 0576 } else if (propName == QLatin1String("SupportedUriSchemes")) { 0577 updateSingleProperty(m_supportedUriSchemes, it.value(), QMetaType::QStringList, &PlayerContainer::supportedUriSchemesChanged); 0578 } else if (propName == QLatin1String("SupportedMimeTypes")) { 0579 updateSingleProperty(m_supportedMimeTypes, it.value(), QMetaType::QStringList, &PlayerContainer::supportedMimeTypesChanged); 0580 } else if (propName == QLatin1String("Fullscreen")) { 0581 m_fullscreen = it->toBool(); 0582 } else if (propName == QLatin1String("HasTrackList")) { 0583 m_hasTrackList = it->toBool(); 0584 } else if (propName == QLatin1String("PlaybackStatus")) { 0585 if (const QString newValue = it->toString(); newValue == QLatin1String("Stopped")) { 0586 m_playbackStatus = PlaybackStatus::Stopped; 0587 } else if (newValue == QLatin1String("Paused")) { 0588 m_playbackStatus = PlaybackStatus::Paused; 0589 } else if (newValue == QLatin1String("Playing")) { 0590 m_playbackStatus = PlaybackStatus::Playing; 0591 } else { 0592 m_playbackStatus = PlaybackStatus::Unknown; 0593 } 0594 } else if (propName == QLatin1String("LoopStatus")) { 0595 if (const QString newValue = it.value().toString(); newValue == QLatin1String("Playlist")) { 0596 m_loopStatus = LoopStatus::Playlist; 0597 } else if (newValue == QLatin1String("Track")) { 0598 m_loopStatus = LoopStatus::Track; 0599 } else { 0600 m_loopStatus = LoopStatus::None; 0601 } 0602 } else if (propName == QLatin1String("Shuffle")) { 0603 m_shuffle = it->toBool() ? ShuffleStatus::On : ShuffleStatus::Off; 0604 } else if (propName == QLatin1String("Rate")) { 0605 m_rate = it->toDouble(); 0606 } else if (propName == QLatin1String("MinimumRate")) { 0607 m_minimumRate = it->toDouble(); 0608 } else if (propName == QLatin1String("MaximumRate")) { 0609 m_maximumRate = it->toDouble(); 0610 } else if (propName == QLatin1String("Volume")) { 0611 m_volume = it->toDouble(); 0612 } else if (propName == QLatin1String("Position")) { 0613 m_position = it->toLongLong(); 0614 } else if (propName == QLatin1String("Metadata")) { 0615 oldTrackId = m_trackId.value(); 0616 QDBusArgument arg = it->value<QDBusArgument>(); 0617 if (arg.currentType() != QDBusArgument::MapType || arg.currentSignature() != QLatin1String("a{sv}")) { 0618 continue; 0619 } 0620 0621 QVariantMap map; 0622 arg >> map; 0623 0624 m_trackId = map[QStringLiteral("mpris:trackid")].value<QDBusObjectPath>().path(); 0625 m_xesamTitle = map[QStringLiteral("xesam:title")].toString(); 0626 m_xesamUrl = map[QStringLiteral("xesam:url")].toString(); 0627 m_xesamArtist = map[QStringLiteral("xesam:artist")].toStringList(); 0628 m_xesamAlbumArtist = map[QStringLiteral("xesam:albumArtist")].toStringList(); 0629 m_xesamAlbum = map[QStringLiteral("xesam:album")].toString(); 0630 m_artUrl = map[QStringLiteral("mpris:artUrl")].toString(); 0631 m_length = map[QStringLiteral("mpris:length")].toDouble(); 0632 m_kdePid = map[QStringLiteral("kde:pid")].toUInt(); 0633 } 0634 // we give out CanControl, as this may completely 0635 // change the UI of the widget 0636 else if (propName == QLatin1String("CanControl")) { 0637 m_canControl = it->toBool(); 0638 } else if (propName == QLatin1String("CanSeek")) { 0639 m_canSeek = it->toBool(); 0640 } else if (propName == QLatin1String("CanGoNext")) { 0641 m_canGoNext = it->toBool(); 0642 } else if (propName == QLatin1String("CanGoPrevious")) { 0643 m_canGoPrevious = it->toBool(); 0644 } else if (propName == QLatin1String("CanRaise")) { 0645 m_canRaise = it->toBool(); 0646 } else if (propName == QLatin1String("CanSetFullscreen")) { 0647 m_canSetFullscreen = it->toBool(); 0648 } else if (propName == QLatin1String("CanQuit")) { 0649 m_canQuit = it->toBool(); 0650 } else if (propName == QLatin1String("CanPlay")) { 0651 m_canPlay = it->toBool(); 0652 } else if (propName == QLatin1String("CanPause")) { 0653 m_canPause = it->toBool(); 0654 } 0655 } 0656 0657 if (map.contains(QStringLiteral("Position"))) { 0658 return; 0659 } 0660 0661 if (m_position != 0.0 && (m_playbackStatus == PlaybackStatus::Stopped || (!oldTrackId.isEmpty() && m_trackId.value() != oldTrackId))) { 0662 // assume the position has reset to 0, since this is really the 0663 // only sensible value for a stopped track 0664 updatePosition(); 0665 } 0666 } 0667 0668 void PlayerContainer::onGetPropsFinished(QDBusPendingCallWatcher *watcher) 0669 { 0670 QDBusPendingReply<QVariantMap> propsReply = *watcher; 0671 watcher->deleteLater(); 0672 0673 if (m_fetchesPending < 1) { 0674 // we already failed 0675 Q_EMIT initialFetchFailed(this); 0676 return; 0677 } 0678 0679 if (propsReply.isError()) { 0680 qCDebug(MPRIS2) << m_dbusAddress << "does not implement" << OrgFreedesktopDBusPropertiesInterface::staticInterfaceName() << "correctly" 0681 << "Error message was" << propsReply.error().name() << propsReply.error().message(); 0682 m_fetchesPending = 0; 0683 Q_EMIT initialFetchFailed(this); 0684 return; 0685 } 0686 0687 updateFromMap(propsReply.value()); 0688 0689 if (--m_fetchesPending == 0) { 0690 Q_EMIT initialFetchFinished(this); 0691 0692 connect(m_propsIface, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged, this, &PlayerContainer::onPropertiesChanged); 0693 connect(m_playerIface, &OrgMprisMediaPlayer2PlayerInterface::Seeked, this, &PlayerContainer::onSeeked); 0694 } 0695 } 0696 0697 void PlayerContainer::onPropertiesChanged(const QString &, const QVariantMap &changedProperties, const QStringList &invalidatedProperties) 0698 { 0699 if (!invalidatedProperties.empty()) { 0700 disconnect(m_propsIface, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged, this, &PlayerContainer::onPropertiesChanged); 0701 disconnect(m_playerIface, &OrgMprisMediaPlayer2PlayerInterface::Seeked, this, &PlayerContainer::onSeeked); 0702 refresh(); 0703 } else { 0704 updateFromMap(changedProperties); 0705 } 0706 } 0707 0708 void PlayerContainer::onSeeked(qlonglong position) 0709 { 0710 m_position = position; 0711 } 0712 0713 #include "moc_playercontainer.cpp"