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 }