File indexing completed on 2025-02-23 04:35:17

0001 // SPDX-FileCopyrightText: 2014 Sujith Haridasan <sujith.haridasan@kdemail.net>
0002 // SPDX-FileCopyrightText: 2014 Ashish Madeti <ashishmadeti@gmail.com>
0003 // SPDX-FileCopyrightText: 2016 Matthieu Gallien <matthieu_gallien@yahoo.fr>
0004 // SPDX-FileCopyrightText: 2022-2023 Bart De Vries <bart@mogwai.be>
0005 // SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
0006 // SPDX-License-Identifier: GPL-3.0-or-later
0007 
0008 #include "mediaplayer2player.h"
0009 
0010 #include "mpris2logging.h"
0011 #include "videocontroller.h"
0012 
0013 #include <QCryptographicHash>
0014 #include <QDBusConnection>
0015 #include <QDBusMessage>
0016 #include <QDebug>
0017 #include <QMetaClassInfo>
0018 #include <QStringList>
0019 #include <QTimer>
0020 
0021 MediaPlayer2Player::MediaPlayer2Player(VideoController *audioPlayer, QObject *parent)
0022     : QDBusAbstractAdaptor(parent)
0023     , m_audioPlayer(audioPlayer)
0024     , mProgressIndicatorSignal(
0025           QDBusMessage::createSignal(QStringLiteral("/org/kde/kmediasession"), QStringLiteral("com.canonical.Unity.LauncherEntry"), QStringLiteral("Update")))
0026 {
0027     qCDebug(Mpris2Log) << "MediaPlayer2Player::MediaPlayer2Player()";
0028 
0029     connect(m_audioPlayer, &VideoController::currentVideoChanged, this, &MediaPlayer2Player::setSource);
0030     connect(m_audioPlayer, &VideoController::currentVideoChanged, this, &MediaPlayer2Player::playerMetaDataChanged);
0031     connect(m_audioPlayer, &VideoController::playbackStateChanged, this, &MediaPlayer2Player::playerPlaybackStateChanged);
0032     connect(m_audioPlayer, &VideoController::durationChanged, this, &MediaPlayer2Player::audioDurationChanged);
0033     connect(m_audioPlayer, &VideoController::positionChanged, this, &MediaPlayer2Player::audioPositionChanged);
0034     connect(m_audioPlayer->videoQueue(), &VideoQueue::queueChanged, this, &MediaPlayer2Player::playerCanGoNextChanged);
0035     connect(m_audioPlayer->videoQueue(), &VideoQueue::queueChanged, this, &MediaPlayer2Player::playerCanGoPreviousChanged);
0036 
0037     if (m_audioPlayer->hasVideo()) {
0038         setSource();
0039     }
0040 }
0041 
0042 MediaPlayer2Player::~MediaPlayer2Player()
0043 {
0044     qCDebug(Mpris2Log) << "MediaPlayer2Player::~MediaPlayer2Player()";
0045 }
0046 
0047 QString MediaPlayer2Player::PlaybackStatus() const
0048 {
0049     qCDebug(Mpris2Log) << "MediaPlayer2Player::PlaybackStatus()";
0050     QString result;
0051 
0052     if (!m_audioPlayer->hasVideo()) {
0053         return QStringLiteral("Stopped");
0054     }
0055 
0056     if (m_audioPlayer->stopped()) {
0057         result = QStringLiteral("Stopped");
0058     } else if (!m_audioPlayer->paused()) {
0059         result = QStringLiteral("Playing");
0060     } else {
0061         result = QStringLiteral("Paused");
0062     }
0063 
0064     return result;
0065 }
0066 
0067 bool MediaPlayer2Player::CanGoNext() const
0068 {
0069     qCDebug(Mpris2Log) << "MediaPlayer2Player::CanGoNext()";
0070     if (m_audioPlayer) {
0071         return m_audioPlayer->videoQueue()->canGoNext();
0072     } else {
0073         return false;
0074     }
0075 }
0076 
0077 void MediaPlayer2Player::Next()
0078 {
0079     qCDebug(Mpris2Log) << "MediaPlayer2Player::Next()";
0080     m_audioPlayer->next();
0081 }
0082 
0083 bool MediaPlayer2Player::CanGoPrevious() const
0084 {
0085     qCDebug(Mpris2Log) << "MediaPlayer2Player::CanGoPrevious()";
0086     if (m_audioPlayer) {
0087         return m_audioPlayer->videoQueue()->canGoPrevious();
0088     } else {
0089         return false;
0090     }
0091 }
0092 
0093 void MediaPlayer2Player::Previous()
0094 {
0095     qCDebug(Mpris2Log) << "MediaPlayer2Player::Previous()";
0096     m_audioPlayer->previous();
0097 }
0098 
0099 bool MediaPlayer2Player::CanPause() const
0100 {
0101     qCDebug(Mpris2Log) << "MediaPlayer2Player::CanPause()";
0102     return true;
0103 }
0104 
0105 void MediaPlayer2Player::Pause()
0106 {
0107     qCDebug(Mpris2Log) << "MediaPlayer2Player::Pause()";
0108     if (m_audioPlayer)
0109         m_audioPlayer->pause();
0110 }
0111 
0112 void MediaPlayer2Player::PlayPause()
0113 {
0114     qCDebug(Mpris2Log) << "MediaPlayer2Player::PlayPause()";
0115     m_audioPlayer->togglePlaying();
0116 }
0117 
0118 void MediaPlayer2Player::Stop()
0119 {
0120     qCDebug(Mpris2Log) << "MediaPlayer2Player::Stop()";
0121     m_audioPlayer->stop();
0122 }
0123 
0124 bool MediaPlayer2Player::CanPlay() const
0125 {
0126     qCDebug(Mpris2Log) << "MediaPlayer2Player::CanPlay()";
0127     return true;
0128 }
0129 
0130 void MediaPlayer2Player::Play()
0131 {
0132     qCDebug(Mpris2Log) << "MediaPlayer2Player::Play()";
0133     if (m_audioPlayer)
0134         m_audioPlayer->play();
0135 }
0136 
0137 double MediaPlayer2Player::Volume() const
0138 {
0139     qCDebug(Mpris2Log) << "MediaPlayer2Player::Volume()";
0140     return m_volume;
0141 }
0142 
0143 void MediaPlayer2Player::setVolume(double volume)
0144 {
0145     qCDebug(Mpris2Log) << "MediaPlayer2Player::setVolume(" << volume << ")";
0146     if (m_audioPlayer) {
0147         m_volume = qBound(0.0, volume, 1.0);
0148         Q_EMIT volumeChanged(m_volume);
0149 
0150         // TODO: app volume not implemented
0151 
0152         signalPropertiesChange(QStringLiteral("Volume"), Volume());
0153     }
0154 }
0155 
0156 QVariantMap MediaPlayer2Player::Metadata() const
0157 {
0158     qCDebug(Mpris2Log) << "MediaPlayer2Player::Metadata()";
0159     return m_metadata;
0160 }
0161 
0162 qlonglong MediaPlayer2Player::Position() const
0163 {
0164     qCDebug(Mpris2Log) << "MediaPlayer2Player::Position()";
0165     if (m_audioPlayer->hasVideo()) {
0166         return qlonglong(m_audioPlayer->position()) * 1000000;
0167     } else {
0168         return 0.0;
0169     }
0170 }
0171 
0172 double MediaPlayer2Player::Rate() const
0173 {
0174     qCDebug(Mpris2Log) << "MediaPlayer2Player::Rate()";
0175     return 1.0;
0176 }
0177 
0178 double MediaPlayer2Player::MinimumRate() const
0179 {
0180     qCDebug(Mpris2Log) << "MediaPlayer2Player::MinimumRate()";
0181     return 1.0;
0182 }
0183 
0184 double MediaPlayer2Player::MaximumRate() const
0185 {
0186     qCDebug(Mpris2Log) << "MediaPlayer2Player::MaximumRate()";
0187     return 1.0;
0188 }
0189 
0190 void MediaPlayer2Player::setRate(double newRate)
0191 {
0192     qCDebug(Mpris2Log) << "MediaPlayer2Player::setRate(" << newRate << ")";
0193     if (newRate <= 0.0001 && newRate >= -0.0001) {
0194         Pause();
0195     }
0196 }
0197 
0198 bool MediaPlayer2Player::CanSeek() const
0199 {
0200     qCDebug(Mpris2Log) << "MediaPlayer2Player::CanSeek()";
0201     return true;
0202 }
0203 
0204 bool MediaPlayer2Player::CanControl() const
0205 {
0206     qCDebug(Mpris2Log) << "MediaPlayer2Player::CanControl()";
0207     return true;
0208 }
0209 
0210 void MediaPlayer2Player::Seek(qlonglong Offset)
0211 {
0212     qCDebug(Mpris2Log) << "MediaPlayer2Player::Seek(" << Offset << ")";
0213     if (m_audioPlayer) {
0214         auto offset = (m_position + Offset) / 1000000;
0215         m_audioPlayer->setPosition(int(offset));
0216     }
0217 }
0218 
0219 void MediaPlayer2Player::SetPosition(const QDBusObjectPath &trackId, qlonglong pos)
0220 {
0221     qCDebug(Mpris2Log) << "MediaPlayer2Player::SetPosition(" << pos << ")";
0222     if (m_audioPlayer) {
0223         if (m_audioPlayer->currentVideo() != nullptr) {
0224             if (trackId.path() == m_currentTrackId) {
0225                 m_audioPlayer->setPosition(int(pos / 1000000));
0226             }
0227         }
0228     }
0229 }
0230 
0231 void MediaPlayer2Player::OpenUri(const QString &uri)
0232 {
0233     qCDebug(Mpris2Log) << "MediaPlayer2Player::OpenUri(" << uri << ")";
0234     Q_UNUSED(uri)
0235 }
0236 
0237 void MediaPlayer2Player::playerPlaybackStateChanged()
0238 {
0239     qCDebug(Mpris2Log) << "MediaPlayer2Player::playerPlaybackStateChanged()";
0240     signalPropertiesChange(QStringLiteral("PlaybackStatus"), PlaybackStatus());
0241     Q_EMIT playbackStatusChanged();
0242 }
0243 
0244 void MediaPlayer2Player::playerPlaybackRateChanged()
0245 {
0246     qCDebug(Mpris2Log) << "MediaPlayer2Player::playerPlaybackRateChanged()";
0247     signalPropertiesChange(QStringLiteral("Rate"), Rate());
0248     Q_EMIT rateChanged(Rate());
0249 }
0250 
0251 void MediaPlayer2Player::playerSeeked(qint64 position)
0252 {
0253     qCDebug(Mpris2Log) << "MediaPlayer2Player::playerSeeked(" << position << ")";
0254     Q_EMIT Seeked(position * 1000000);
0255 }
0256 
0257 void MediaPlayer2Player::audioPositionChanged()
0258 {
0259     qCDebug(Mpris2Log) << "MediaPlayer2Player::audioPositionChanged()";
0260     // for progress indicator on taskbar
0261     if (m_audioPlayer)
0262         setPropertyPosition(static_cast<int>(m_audioPlayer->position()));
0263 
0264     // Occasionally send updated position through MPRIS to make sure that
0265     // audio position is still correct if playing without seeking for a long
0266     // time.  This will also guarantee correct playback position if the MPRIS
0267     // client does not support non-standard playback rates
0268     qlonglong position = Position();
0269     if (abs(position - m_lastSentPosition) > 10000000) { // every 10 seconds
0270         m_lastSentPosition = position;
0271         Q_EMIT Seeked(position);
0272     }
0273 }
0274 
0275 void MediaPlayer2Player::audioDurationChanged()
0276 {
0277     qCDebug(Mpris2Log) << "MediaPlayer2Player::audioDurationChanged()";
0278     // We reset all metadata in case the audioDuration changed
0279     // This is done because duration is not yet available when setEntry is
0280     // called (this is before the QMediaPlayer has read the new track
0281     if (m_audioPlayer) {
0282         qCDebug(Mpris2Log) << "Signal change of audio duration through MPRIS2" << m_audioPlayer->duration();
0283         if (m_audioPlayer->currentVideo() != nullptr) {
0284             m_metadata = getMetadataOfCurrentTrack();
0285             signalPropertiesChange(QStringLiteral("Metadata"), Metadata());
0286             signalPropertiesChange(QStringLiteral("CanPause"), CanPause());
0287             signalPropertiesChange(QStringLiteral("CanPlay"), CanPlay());
0288         }
0289 
0290         // for progress indicator on taskbar
0291         setPropertyPosition(static_cast<int>(m_audioPlayer->duration()));
0292     }
0293 }
0294 
0295 void MediaPlayer2Player::playerVolumeChanged()
0296 {
0297     qCDebug(Mpris2Log) << "MediaPlayer2Player::playerVolumeChanged()";
0298     // TODO: app volume not implemented
0299 }
0300 
0301 void MediaPlayer2Player::playerCanPlayChanged()
0302 {
0303     qCDebug(Mpris2Log) << "MediaPlayer2Player::playerCanPlayChanged()";
0304     signalPropertiesChange(QStringLiteral("CanPlay"), CanPlay());
0305 }
0306 
0307 void MediaPlayer2Player::playerCanPauseChanged()
0308 {
0309     qCDebug(Mpris2Log) << "MediaPlayer2Player::playerCanPauseChanged()";
0310     signalPropertiesChange(QStringLiteral("CanPause"), CanPause());
0311 }
0312 
0313 void MediaPlayer2Player::playerCanGoNextChanged()
0314 {
0315     qCDebug(Mpris2Log) << "MediaPlayer2Player::playerCanGoNextChanged()";
0316     signalPropertiesChange(QStringLiteral("CanGoNext"), CanGoNext());
0317 }
0318 
0319 void MediaPlayer2Player::playerCanGoPreviousChanged()
0320 {
0321     qCDebug(Mpris2Log) << "MediaPlayer2Player::playerCanGoPreviousChanged()";
0322     signalPropertiesChange(QStringLiteral("CanGoPrevious"), CanGoNext());
0323 }
0324 
0325 void MediaPlayer2Player::playerCanSeekChanged()
0326 {
0327     qCDebug(Mpris2Log) << "MediaPlayer2Player::playerCanSeekChanged()";
0328     signalPropertiesChange(QStringLiteral("CanSeek"), CanSeek());
0329 }
0330 
0331 void MediaPlayer2Player::playerMetaDataChanged()
0332 {
0333     qCDebug(Mpris2Log) << "MediaPlayer2Player::playerMetaDataChanged()";
0334     m_metadata = getMetadataOfCurrentTrack();
0335     signalPropertiesChange(QStringLiteral("Metadata"), Metadata());
0336 }
0337 
0338 void MediaPlayer2Player::setSource()
0339 {
0340     auto video = m_audioPlayer->currentVideo();
0341     if (video == nullptr)
0342         return;
0343 
0344     qCDebug(Mpris2Log) << "MediaPlayer2Player::setSource(" << video->videoId() << ")";
0345 
0346     if (m_audioPlayer) {
0347         int queuenr = 0; // TODO: figure out smart way to handle this
0348         QString desktopName = QStringLiteral("/org.kde.plasmatube");
0349         desktopName.replace(QStringLiteral("."), QStringLiteral("/"));
0350         m_currentTrackId = QDBusObjectPath(desktopName + QLatin1String("/playlist/") + QString::number(queuenr)).path();
0351 
0352         m_metadata = getMetadataOfCurrentTrack();
0353         signalPropertiesChange(QStringLiteral("Metadata"), Metadata());
0354     }
0355 }
0356 
0357 QVariantMap MediaPlayer2Player::getMetadataOfCurrentTrack()
0358 {
0359     qCDebug(Mpris2Log) << "MediaPlayer2Player::getMetadataOfCurrentTrack()";
0360     auto result = QVariantMap();
0361 
0362     if (m_currentTrackId.isEmpty()) {
0363         return {};
0364     }
0365 
0366     if (!m_audioPlayer->hasVideo()) {
0367         return {};
0368     }
0369 
0370     result[QStringLiteral("mpris:trackid")] = QVariant::fromValue<QDBusObjectPath>(QDBusObjectPath(m_currentTrackId));
0371     result[QStringLiteral("mpris:length")] = qlonglong(m_audioPlayer->duration()) * 1000000; // TODO: what is this magic number?
0372     if (!m_audioPlayer->currentVideo()->title().isEmpty()) {
0373         result[QStringLiteral("xesam:title")] = m_audioPlayer->currentVideo()->title();
0374     }
0375     if (!m_audioPlayer->currentVideo()->author().isEmpty()) {
0376         result[QStringLiteral("xesam:artist")] = m_audioPlayer->currentVideo()->author();
0377         result[QStringLiteral("xesam:album")] = m_audioPlayer->currentVideo()->author();
0378     }
0379     if (!m_audioPlayer->currentVideo()->thumbnailUrl(QStringLiteral("medium")).isEmpty()) {
0380         result[QStringLiteral("mpris:artUrl")] = m_audioPlayer->currentVideo()->thumbnailUrl(QStringLiteral("medium")).toString();
0381     }
0382 
0383     return result;
0384 }
0385 
0386 void MediaPlayer2Player::setPropertyPosition(int newPositionInMs)
0387 {
0388     qCDebug(Mpris2Log) << "MediaPlayer2Player::setPropertyPosition(" << newPositionInMs << ")";
0389     // only needed for progressbar on taskbar (?)
0390     m_position = qlonglong(newPositionInMs) * 1000000;
0391 }
0392 
0393 void MediaPlayer2Player::signalPropertiesChange(const QString &property, const QVariant &value)
0394 {
0395     qCDebug(Mpris2Log) << "MediaPlayer2Player::signalPropertiesChange(" << property << value << ")";
0396     // qDebug() << "mpris signal property change" << property << value;
0397     QVariantMap properties;
0398     properties[property] = value;
0399     const int ifaceIndex = metaObject()->indexOfClassInfo("D-Bus Interface");
0400     QDBusMessage msg = QDBusMessage::createSignal(QStringLiteral("/org/mpris/MediaPlayer2"),
0401                                                   QStringLiteral("org.freedesktop.DBus.Properties"),
0402                                                   QStringLiteral("PropertiesChanged"));
0403 
0404     msg << QLatin1String(metaObject()->classInfo(ifaceIndex).value());
0405     msg << properties;
0406     msg << QStringList();
0407 
0408     QDBusConnection::sessionBus().send(msg);
0409 }