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"