File indexing completed on 2024-05-05 04:50:35
0001 /*************************************************************************** 0002 SPDX-FileCopyrightText: 2014 (c) Sujith Haridasan <sujith.haridasan@kdemail.net> 0003 SPDX-FileCopyrightText: 2014 (c) Ashish Madeti <ashishmadeti@gmail.com> 0004 SPDX-FileCopyrightText: 2016 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr> 0005 0006 SPDX-License-Identifier: GPL-3.0-or-later 0007 ***************************************************************************/ 0008 0009 #include "config-upnp-qt.h" 0010 0011 #include "mediaplayer2player.h" 0012 #include "mpris2.h" 0013 0014 #include "mediaplaylistproxymodel.h" 0015 #include "manageaudioplayer.h" 0016 #include "managemediaplayercontrol.h" 0017 #include "manageheaderbar.h" 0018 #include "audiowrapper.h" 0019 #include <kfilemetadata/extractorcollection.h> 0020 0021 #if KFFileMetaData_FOUND 0022 #include <KFileMetaData/ExtractorCollection> 0023 #include <KFileMetaData/Extractor> 0024 #include <KFileMetaData/SimpleExtractionResult> 0025 #include <KFileMetaData/EmbeddedImageData> 0026 #include <QMimeDatabase> 0027 #endif 0028 0029 #include <QCryptographicHash> 0030 #include <QStringList> 0031 #include <QDBusConnection> 0032 0033 0034 static const double MAX_RATE = 1.0; 0035 static const double MIN_RATE = 1.0; 0036 0037 MediaPlayer2Player::MediaPlayer2Player(MediaPlayListProxyModel *playListControler, ManageAudioPlayer *manageAudioPlayer, 0038 ManageMediaPlayerControl *manageMediaPlayerControl, ManageHeaderBar *manageHeaderBar, 0039 AudioWrapper *audioPlayer, bool showProgressOnTaskBar, QObject* parent) 0040 : QDBusAbstractAdaptor(parent), m_playListControler(playListControler), m_manageAudioPlayer(manageAudioPlayer), 0041 m_manageMediaPlayerControl(manageMediaPlayerControl), m_manageHeaderBar(manageHeaderBar), m_audioPlayer(audioPlayer), 0042 mProgressIndicatorSignal(QDBusMessage::createSignal(QStringLiteral("/org/kde/elisa"), 0043 QStringLiteral("com.canonical.Unity.LauncherEntry"), 0044 QStringLiteral("Update"))), 0045 mShowProgressOnTaskBar(showProgressOnTaskBar) 0046 { 0047 if (!m_playListControler) { 0048 return; 0049 } 0050 0051 connect(m_manageAudioPlayer, &ManageAudioPlayer::playerSourceChanged, 0052 this, &MediaPlayer2Player::playerSourceChanged, Qt::QueuedConnection); 0053 connect(m_manageMediaPlayerControl, &ManageMediaPlayerControl::playControlEnabledChanged, 0054 this, &MediaPlayer2Player::playControlEnabledChanged); 0055 connect(m_manageMediaPlayerControl, &ManageMediaPlayerControl::skipBackwardControlEnabledChanged, 0056 this, &MediaPlayer2Player::skipBackwardControlEnabledChanged); 0057 connect(m_manageMediaPlayerControl, &ManageMediaPlayerControl::skipForwardControlEnabledChanged, 0058 this, &MediaPlayer2Player::skipForwardControlEnabledChanged); 0059 connect(m_manageAudioPlayer, &ManageAudioPlayer::playerPlaybackStateChanged, 0060 this, &MediaPlayer2Player::playerPlaybackStateChanged); 0061 connect(m_manageAudioPlayer, &ManageAudioPlayer::playerIsSeekableChanged, 0062 this, &MediaPlayer2Player::playerIsSeekableChanged); 0063 connect(m_manageAudioPlayer, &ManageAudioPlayer::playerPositionChanged, 0064 this, &MediaPlayer2Player::audioPositionChanged); 0065 connect(m_manageAudioPlayer, &ManageAudioPlayer::seek, 0066 this, &MediaPlayer2Player::playerSeeked); 0067 connect(m_manageAudioPlayer, &ManageAudioPlayer::audioDurationChanged, 0068 this, &MediaPlayer2Player::audioDurationChanged); 0069 connect(m_audioPlayer, &AudioWrapper::volumeChanged, 0070 this, &MediaPlayer2Player::playerVolumeChanged); 0071 connect(m_playListControler, &MediaPlayListProxyModel::shufflePlayListChanged, 0072 this, &MediaPlayer2Player::shufflePlayListChanged); 0073 connect(m_playListControler, &MediaPlayListProxyModel::repeatModeChanged, 0074 this, &MediaPlayer2Player::repeatModeChanged); 0075 0076 m_volume = m_audioPlayer->volume() / 100; 0077 m_canPlay = m_manageMediaPlayerControl->playControlEnabled(); 0078 signalPropertiesChange(QStringLiteral("Volume"), Volume()); 0079 0080 m_mediaPlayerPresent = 1; 0081 } 0082 0083 MediaPlayer2Player::~MediaPlayer2Player() 0084 = default; 0085 0086 QString MediaPlayer2Player::PlaybackStatus() const 0087 { 0088 QString result; 0089 0090 if (!m_playListControler) { 0091 result = QStringLiteral("Stopped"); 0092 return result; 0093 } 0094 0095 if (m_manageAudioPlayer->playerPlaybackState() == QMediaPlayer::StoppedState) { 0096 result = QStringLiteral("Stopped"); 0097 } else if (m_manageAudioPlayer->playerPlaybackState() == QMediaPlayer::PlayingState) { 0098 result = QStringLiteral("Playing"); 0099 } else { 0100 result = QStringLiteral("Paused"); 0101 } 0102 0103 if (mShowProgressOnTaskBar) { 0104 QVariantMap parameters; 0105 0106 if (m_manageAudioPlayer->playerPlaybackState() == QMediaPlayer::StoppedState || m_audioPlayer->duration() == 0) { 0107 parameters.insert(QStringLiteral("progress-visible"), false); 0108 parameters.insert(QStringLiteral("progress"), 0); 0109 } else { 0110 parameters.insert(QStringLiteral("progress-visible"), true); 0111 parameters.insert(QStringLiteral("progress"), qRound(static_cast<double>(m_position / m_audioPlayer->duration())) / 1000.0); 0112 } 0113 0114 mProgressIndicatorSignal.setArguments({QStringLiteral("application://org.kde.elisa.desktop"), parameters}); 0115 0116 QDBusConnection::sessionBus().send(mProgressIndicatorSignal); 0117 } 0118 0119 return result; 0120 } 0121 0122 bool MediaPlayer2Player::CanGoNext() const 0123 { 0124 return m_canGoNext; 0125 } 0126 0127 void MediaPlayer2Player::Next() 0128 { 0129 Q_EMIT next(); 0130 0131 if (m_playListControler) { 0132 m_playListControler->skipNextTrack(ElisaUtils::SkipReason::Manual); 0133 } 0134 } 0135 0136 bool MediaPlayer2Player::CanGoPrevious() const 0137 { 0138 return m_canGoPrevious; 0139 } 0140 0141 void MediaPlayer2Player::Previous() 0142 { 0143 Q_EMIT previous(); 0144 0145 if (m_playListControler) { 0146 m_playListControler->skipPreviousTrack(m_audioPlayer->position()); 0147 } 0148 } 0149 0150 bool MediaPlayer2Player::CanPause() const 0151 { 0152 return m_canPlay; 0153 } 0154 0155 void MediaPlayer2Player::Pause() 0156 { 0157 if (m_playListControler) { 0158 m_manageAudioPlayer->ensurePause(); 0159 } 0160 } 0161 0162 void MediaPlayer2Player::PlayPause() 0163 { 0164 Q_EMIT playPause(); 0165 0166 if (m_playListControler) { 0167 m_manageAudioPlayer->playPause(); 0168 } 0169 } 0170 0171 void MediaPlayer2Player::Stop() 0172 { 0173 Q_EMIT stop(); 0174 0175 if (m_playListControler) { 0176 m_manageAudioPlayer->stop(); 0177 } 0178 } 0179 0180 bool MediaPlayer2Player::CanPlay() const 0181 { 0182 return m_canPlay; 0183 } 0184 0185 void MediaPlayer2Player::Play() 0186 { 0187 if (m_playListControler) { 0188 m_manageAudioPlayer->ensurePlay(); 0189 } 0190 } 0191 0192 double MediaPlayer2Player::Volume() const 0193 { 0194 return m_volume; 0195 } 0196 0197 void MediaPlayer2Player::setVolume(double volume) 0198 { 0199 m_volume= qBound(0.0, volume, 1.0); 0200 Q_EMIT volumeChanged(m_volume); 0201 0202 m_audioPlayer->setVolume(100 * m_volume); 0203 0204 signalPropertiesChange(QStringLiteral("Volume"), Volume()); 0205 } 0206 0207 QVariantMap MediaPlayer2Player::Metadata() const 0208 { 0209 return m_metadata; 0210 } 0211 0212 qlonglong MediaPlayer2Player::Position() const 0213 { 0214 return m_position; 0215 } 0216 0217 void MediaPlayer2Player::setPropertyPosition(int newPositionInMs) 0218 { 0219 m_position = qlonglong(newPositionInMs) * 1000; 0220 0221 /* only send new progress when it has advanced more than 1 % 0222 * to limit DBus traffic 0223 */ 0224 const auto incrementalProgress = static_cast<double>(newPositionInMs - mPreviousProgressPosition) / m_audioPlayer->duration(); 0225 if (mShowProgressOnTaskBar && (incrementalProgress > 0.01 || incrementalProgress < 0)) 0226 { 0227 mPreviousProgressPosition = newPositionInMs; 0228 QVariantMap parameters; 0229 parameters.insert(QStringLiteral("progress-visible"), true); 0230 parameters.insert(QStringLiteral("progress"), static_cast<double>(newPositionInMs) / m_audioPlayer->duration()); 0231 0232 mProgressIndicatorSignal.setArguments({QStringLiteral("application://org.kde.elisa.desktop"), parameters}); 0233 0234 QDBusConnection::sessionBus().send(mProgressIndicatorSignal); 0235 } 0236 } 0237 0238 double MediaPlayer2Player::Rate() const 0239 { 0240 return m_rate; 0241 } 0242 0243 void MediaPlayer2Player::setRate(double newRate) 0244 { 0245 if (newRate <= 0.0001 && newRate >= -0.0001) { 0246 Pause(); 0247 } else { 0248 m_rate = qBound(MinimumRate(), newRate, MaximumRate()); 0249 Q_EMIT rateChanged(m_rate); 0250 0251 signalPropertiesChange(QStringLiteral("Rate"), Rate()); 0252 } 0253 } 0254 0255 double MediaPlayer2Player::MinimumRate() const 0256 { 0257 return MIN_RATE; 0258 } 0259 0260 double MediaPlayer2Player::MaximumRate() const 0261 { 0262 return MAX_RATE; 0263 } 0264 0265 bool MediaPlayer2Player::CanSeek() const 0266 { 0267 return m_playerIsSeekableChanged; 0268 } 0269 0270 bool MediaPlayer2Player::CanControl() const 0271 { 0272 return true; 0273 } 0274 0275 void MediaPlayer2Player::Seek(qlonglong Offset) 0276 { 0277 if (mediaPlayerPresent()) { 0278 auto offset = (m_position + Offset) / 1000; 0279 m_manageAudioPlayer->playerSeek(int(offset)); 0280 } 0281 } 0282 0283 void MediaPlayer2Player::SetPosition(const QDBusObjectPath &trackId, qlonglong pos) 0284 { 0285 if (trackId.path() == m_currentTrackId) { 0286 m_manageAudioPlayer->playerSeek(int(pos / 1000)); 0287 } 0288 } 0289 0290 void MediaPlayer2Player::OpenUri(const QString &uri) 0291 { 0292 Q_UNUSED(uri); 0293 } 0294 0295 void MediaPlayer2Player::playerSourceChanged() 0296 { 0297 if (!m_playListControler) { 0298 return; 0299 } 0300 0301 setCurrentTrack(m_manageAudioPlayer->playListPosition()); 0302 } 0303 0304 void MediaPlayer2Player::playControlEnabledChanged() 0305 { 0306 if (!m_playListControler) { 0307 return; 0308 } 0309 0310 m_canPlay = m_manageMediaPlayerControl->playControlEnabled(); 0311 0312 signalPropertiesChange(QStringLiteral("CanPause"), CanPause()); 0313 signalPropertiesChange(QStringLiteral("CanPlay"), CanPlay()); 0314 0315 Q_EMIT canPauseChanged(); 0316 Q_EMIT canPlayChanged(); 0317 } 0318 0319 void MediaPlayer2Player::skipBackwardControlEnabledChanged() 0320 { 0321 if (!m_playListControler) { 0322 return; 0323 } 0324 0325 m_canGoPrevious = m_manageMediaPlayerControl->skipBackwardControlEnabled(); 0326 0327 signalPropertiesChange(QStringLiteral("CanGoPrevious"), CanGoPrevious()); 0328 Q_EMIT canGoPreviousChanged(); 0329 } 0330 0331 void MediaPlayer2Player::skipForwardControlEnabledChanged() 0332 { 0333 if (!m_playListControler) { 0334 return; 0335 } 0336 0337 m_canGoNext = m_manageMediaPlayerControl->skipForwardControlEnabled(); 0338 0339 signalPropertiesChange(QStringLiteral("CanGoNext"), CanGoNext()); 0340 Q_EMIT canGoNextChanged(); 0341 } 0342 0343 void MediaPlayer2Player::playerPlaybackStateChanged() 0344 { 0345 signalPropertiesChange(QStringLiteral("PlaybackStatus"), PlaybackStatus()); 0346 Q_EMIT playbackStatusChanged(); 0347 0348 playerIsSeekableChanged(); 0349 } 0350 0351 void MediaPlayer2Player::playerIsSeekableChanged() 0352 { 0353 m_playerIsSeekableChanged = m_manageAudioPlayer->playerIsSeekable(); 0354 0355 signalPropertiesChange(QStringLiteral("CanSeek"), CanSeek()); 0356 Q_EMIT canSeekChanged(); 0357 } 0358 0359 void MediaPlayer2Player::audioPositionChanged() 0360 { 0361 setPropertyPosition(static_cast<int>(m_manageAudioPlayer->playerPosition())); 0362 } 0363 0364 void MediaPlayer2Player::playerSeeked(qint64 position) 0365 { 0366 Q_EMIT Seeked(position * 1000); 0367 } 0368 0369 void MediaPlayer2Player::audioDurationChanged() 0370 { 0371 m_metadata = getMetadataOfCurrentTrack(); 0372 signalPropertiesChange(QStringLiteral("Metadata"), Metadata()); 0373 0374 skipBackwardControlEnabledChanged(); 0375 skipForwardControlEnabledChanged(); 0376 playerPlaybackStateChanged(); 0377 playerIsSeekableChanged(); 0378 setPropertyPosition(static_cast<int>(m_manageAudioPlayer->playerPosition())); 0379 } 0380 0381 void MediaPlayer2Player::playerVolumeChanged() 0382 { 0383 setVolume(m_audioPlayer->volume() / 100.0); 0384 } 0385 0386 int MediaPlayer2Player::currentTrack() const 0387 { 0388 return m_manageAudioPlayer->playListPosition(); 0389 } 0390 0391 void MediaPlayer2Player::setCurrentTrack(int newTrackPosition) 0392 { 0393 m_currentTrack = m_manageAudioPlayer->playerSource().toString(); 0394 m_currentTrackId = QDBusObjectPath(QLatin1String("/org/kde/elisa/playlist/") + QString::number(newTrackPosition)).path(); 0395 0396 Q_EMIT currentTrackChanged(); 0397 0398 m_metadata = getMetadataOfCurrentTrack(); 0399 signalPropertiesChange(QStringLiteral("Metadata"), Metadata()); 0400 } 0401 0402 QVariantMap MediaPlayer2Player::getMetadataOfCurrentTrack() 0403 { 0404 auto result = QVariantMap(); 0405 0406 if (m_currentTrackId.isEmpty()) { 0407 return {}; 0408 } 0409 0410 result[QStringLiteral("mpris:trackid")] = QVariant::fromValue<QDBusObjectPath>(QDBusObjectPath(m_currentTrackId)); 0411 result[QStringLiteral("mpris:length")] = qlonglong(m_manageAudioPlayer->audioDuration()) * 1000; 0412 //convert milli-seconds into micro-seconds 0413 if (!m_manageHeaderBar->title().isNull() && !m_manageHeaderBar->title().toString().isEmpty()) { 0414 result[QStringLiteral("xesam:title")] = m_manageHeaderBar->title(); 0415 } 0416 result[QStringLiteral("xesam:url")] = m_manageAudioPlayer->playerSource().toString(); 0417 if (!m_manageHeaderBar->album().isNull() && !m_manageHeaderBar->album().toString().isEmpty()) { 0418 result[QStringLiteral("xesam:album")] = m_manageHeaderBar->album(); 0419 } 0420 if (!m_manageHeaderBar->artist().isNull() && !m_manageHeaderBar->artist().toString().isEmpty()) { 0421 result[QStringLiteral("xesam:artist")] = QStringList{m_manageHeaderBar->artist().toString()}; 0422 } 0423 if (!m_manageHeaderBar->image().isEmpty() && !m_manageHeaderBar->image().toString().isEmpty()) { 0424 if (m_manageHeaderBar->image().scheme() == QStringLiteral("image")) { 0425 // adding a special case for image:// URLs that are only valid because Elisa installs a special handler for them 0426 // converting those URL to data URLs with embedded image data 0427 #if KFFileMetaData_FOUND 0428 0429 const auto &mimeType = QMimeDatabase().mimeTypeForFile(m_manageHeaderBar->image().toString().mid(14)).name(); 0430 KFileMetaData::ExtractorCollection extractorCollection; 0431 auto extractors = extractorCollection.fetchExtractors(mimeType); 0432 0433 QMap<KFileMetaData::EmbeddedImageData::ImageType, QByteArray> imageData; 0434 for (const auto &extractor : extractors) { 0435 KFileMetaData::SimpleExtractionResult result(m_manageHeaderBar->image().toString().mid(14), mimeType, KFileMetaData::ExtractionResult::ExtractImageData); 0436 extractor->extract(&result); 0437 if (!result.imageData().isEmpty()) { 0438 imageData = result.imageData(); 0439 } 0440 } 0441 0442 if (imageData.contains(KFileMetaData::EmbeddedImageData::FrontCover)) { 0443 result[QStringLiteral("mpris:artUrl")] = QString{QStringLiteral("data:image/png;base64,") + QLatin1String{imageData[KFileMetaData::EmbeddedImageData::FrontCover].toBase64()}}; 0444 } 0445 #endif 0446 } else { 0447 result[QStringLiteral("mpris:artUrl")] = m_manageHeaderBar->image().toString(); 0448 } 0449 } 0450 0451 return result; 0452 } 0453 0454 int MediaPlayer2Player::mediaPlayerPresent() const 0455 { 0456 return m_mediaPlayerPresent; 0457 } 0458 0459 bool MediaPlayer2Player::showProgressOnTaskBar() const 0460 { 0461 return mShowProgressOnTaskBar; 0462 } 0463 0464 void MediaPlayer2Player::setShowProgressOnTaskBar(bool value) 0465 { 0466 mShowProgressOnTaskBar = value; 0467 0468 QVariantMap parameters; 0469 0470 if (!mShowProgressOnTaskBar || m_manageAudioPlayer->playerPlaybackState() == QMediaPlayer::StoppedState || m_audioPlayer->duration() == 0) { 0471 parameters.insert(QStringLiteral("progress-visible"), false); 0472 parameters.insert(QStringLiteral("progress"), 0); 0473 } else { 0474 parameters.insert(QStringLiteral("progress-visible"), true); 0475 parameters.insert(QStringLiteral("progress"), qRound(static_cast<double>(m_position / m_audioPlayer->duration())) / 1000.0); 0476 } 0477 0478 mProgressIndicatorSignal.setArguments({QStringLiteral("application://org.kde.elisa.desktop"), parameters}); 0479 0480 QDBusConnection::sessionBus().send(mProgressIndicatorSignal); 0481 } 0482 0483 void MediaPlayer2Player::setMediaPlayerPresent(int status) 0484 { 0485 if (m_mediaPlayerPresent != status) { 0486 m_mediaPlayerPresent = status; 0487 Q_EMIT mediaPlayerPresentChanged(); 0488 0489 signalPropertiesChange(QStringLiteral("CanGoNext"), CanGoNext()); 0490 signalPropertiesChange(QStringLiteral("CanGoPrevious"), CanGoPrevious()); 0491 signalPropertiesChange(QStringLiteral("CanPause"), CanPause()); 0492 signalPropertiesChange(QStringLiteral("CanPlay"), CanPlay()); 0493 Q_EMIT canGoNextChanged(); 0494 Q_EMIT canGoPreviousChanged(); 0495 Q_EMIT canPauseChanged(); 0496 Q_EMIT canPlayChanged(); 0497 } 0498 } 0499 0500 void MediaPlayer2Player::setShuffle(bool shuffle) 0501 { 0502 if (m_playListControler) { 0503 m_playListControler->setShufflePlayList(shuffle); 0504 signalPropertiesChange(QStringLiteral("Shuffle"), Shuffle()); 0505 } 0506 } 0507 0508 bool MediaPlayer2Player::Shuffle() const 0509 { 0510 if (m_playListControler) { 0511 return m_playListControler->shufflePlayList(); 0512 } 0513 0514 return false; 0515 } 0516 0517 void MediaPlayer2Player::shufflePlayListChanged() 0518 { 0519 signalPropertiesChange(QStringLiteral("Shuffle"), Shuffle()); 0520 } 0521 0522 void MediaPlayer2Player::setLoopStatus(const QString& loopStatus) 0523 { 0524 MediaPlayListProxyModel::Repeat repeatMode; 0525 0526 if (m_playListControler) { 0527 if (loopStatus == QStringLiteral("Playlist")) { 0528 repeatMode = MediaPlayListProxyModel::Playlist; 0529 } else if (loopStatus == QStringLiteral("Track")) { 0530 repeatMode = MediaPlayListProxyModel::One; 0531 } else { 0532 repeatMode = MediaPlayListProxyModel::None; 0533 } 0534 0535 m_playListControler->setRepeatMode(repeatMode); 0536 signalPropertiesChange(QStringLiteral("LoopStatus"), LoopStatus()); 0537 } 0538 } 0539 0540 QString MediaPlayer2Player::LoopStatus() const 0541 { 0542 if (m_playListControler) { 0543 const auto repeatMode = m_playListControler->repeatMode(); 0544 switch (repeatMode) { 0545 case MediaPlayListProxyModel::Playlist: 0546 return QStringLiteral("Playlist"); 0547 case MediaPlayListProxyModel::One: 0548 return QStringLiteral("Track"); 0549 case MediaPlayListProxyModel::None: 0550 default: 0551 return QStringLiteral("None"); 0552 } 0553 } 0554 0555 return QStringLiteral("None"); 0556 } 0557 0558 void MediaPlayer2Player::repeatModeChanged() 0559 { 0560 signalPropertiesChange(QStringLiteral("LoopStatus"), LoopStatus()); 0561 } 0562 0563 void MediaPlayer2Player::signalPropertiesChange(const QString &property, const QVariant &value) 0564 { 0565 QVariantMap properties; 0566 properties[property] = value; 0567 const int ifaceIndex = metaObject()->indexOfClassInfo("D-Bus Interface"); 0568 QDBusMessage msg = QDBusMessage::createSignal(QStringLiteral("/org/mpris/MediaPlayer2"), 0569 QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("PropertiesChanged")); 0570 0571 msg << QLatin1String(metaObject()->classInfo(ifaceIndex).value()); 0572 msg << properties; 0573 msg << QStringList(); 0574 0575 QDBusConnection::sessionBus().send(msg); 0576 } 0577 0578 #include "moc_mediaplayer2player.cpp"