File indexing completed on 2024-05-12 04:19:47

0001 /*
0002 Gwenview: an image viewer
0003 Copyright 2018 Friedrich W. H. Kossebau <kossebau@kde.org>
0004 
0005 This program is free software; you can redistribute it and/or
0006 modify it under the terms of the GNU General Public License
0007 as published by the Free Software Foundation; either version 2
0008 of the License, or (at your option) any later version.
0009 
0010 This program is distributed in the hope that it will be useful,
0011 but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013 GNU General Public License for more details.
0014 
0015 You should have received a copy of the GNU General Public License
0016 along with this program; if not, write to the Free Software
0017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
0018 */
0019 
0020 #include "mprismediaplayer2player.h"
0021 
0022 #include <config-gwenview.h>
0023 
0024 // lib
0025 #include "gwenview_lib_debug.h"
0026 #include <contextmanager.h>
0027 #include <document/documentfactory.h>
0028 #include <imagemetainfomodel.h>
0029 #include <mimetypeutils.h>
0030 #include <semanticinfo/semanticinfodirmodel.h>
0031 #include <semanticinfo/sorteddirmodel.h>
0032 #include <slideshow.h>
0033 // KF
0034 #include <KFileItem>
0035 #include <KLocalizedString>
0036 // Qt
0037 #include <QAction>
0038 #include <QDBusObjectPath>
0039 
0040 namespace Gwenview
0041 {
0042 static const double MAX_RATE = 1.0;
0043 static const double MIN_RATE = 1.0;
0044 
0045 MprisMediaPlayer2Player::MprisMediaPlayer2Player(const QString &objectDBusPath,
0046                                                  SlideShow *slideShow,
0047                                                  ContextManager *contextManager,
0048                                                  QAction *toggleSlideShowAction,
0049                                                  QAction *fullScreenAction,
0050                                                  QAction *previousAction,
0051                                                  QAction *nextAction,
0052                                                  QObject *parent)
0053     : DBusAbstractAdaptor(objectDBusPath, parent)
0054     , mSlideShow(slideShow)
0055     , mContextManager(contextManager)
0056     , mToggleSlideShowAction(toggleSlideShowAction)
0057     , mFullScreenAction(fullScreenAction)
0058     , mPreviousAction(previousAction)
0059     , mNextAction(nextAction)
0060     , mSlideShowEnabled(mToggleSlideShowAction->isEnabled())
0061     , mPreviousEnabled(mPreviousAction->isEnabled())
0062     , mNextEnabled(mNextAction->isEnabled())
0063 {
0064     updatePlaybackStatus();
0065 
0066     connect(mSlideShow, &SlideShow::stateChanged, this, &MprisMediaPlayer2Player::onSlideShowStateChanged);
0067     connect(mSlideShow, &SlideShow::intervalChanged, this, &MprisMediaPlayer2Player::onMetaInfoUpdated);
0068     connect(mContextManager, &ContextManager::currentUrlChanged, this, &MprisMediaPlayer2Player::onCurrentUrlChanged);
0069     connect(mSlideShow->randomAction(), &QAction::toggled, this, &MprisMediaPlayer2Player::onRandomActionToggled);
0070     connect(mToggleSlideShowAction, &QAction::changed, this, &MprisMediaPlayer2Player::onToggleSlideShowActionChanged);
0071     connect(mNextAction, &QAction::changed, this, &MprisMediaPlayer2Player::onNextActionChanged);
0072     connect(mPreviousAction, &QAction::changed, this, &MprisMediaPlayer2Player::onPreviousActionChanged);
0073 }
0074 
0075 MprisMediaPlayer2Player::~MprisMediaPlayer2Player() = default;
0076 
0077 bool MprisMediaPlayer2Player::updatePlaybackStatus()
0078 {
0079     const QString newStatus = mSlideShow->isRunning() ? QStringLiteral("Playing") : QStringLiteral("Stopped");
0080 
0081     const bool changed = (newStatus != mPlaybackStatus);
0082     if (changed) {
0083         mPlaybackStatus = newStatus;
0084     }
0085 
0086     return changed;
0087 }
0088 
0089 QString MprisMediaPlayer2Player::playbackStatus() const
0090 {
0091     return mPlaybackStatus;
0092 }
0093 
0094 bool MprisMediaPlayer2Player::canGoNext() const
0095 {
0096     return mNextEnabled;
0097 }
0098 
0099 void MprisMediaPlayer2Player::Next()
0100 {
0101     mNextAction->trigger();
0102 }
0103 
0104 bool MprisMediaPlayer2Player::canGoPrevious() const
0105 {
0106     return mPreviousEnabled;
0107 }
0108 
0109 void MprisMediaPlayer2Player::Previous()
0110 {
0111     mPreviousAction->trigger();
0112 }
0113 
0114 bool MprisMediaPlayer2Player::canPause() const
0115 {
0116     return mSlideShowEnabled;
0117 }
0118 
0119 void MprisMediaPlayer2Player::Pause()
0120 {
0121     mSlideShow->pause();
0122 }
0123 
0124 void MprisMediaPlayer2Player::PlayPause()
0125 {
0126     mToggleSlideShowAction->trigger();
0127 }
0128 
0129 void MprisMediaPlayer2Player::Stop()
0130 {
0131     if (mFullScreenAction->isChecked()) {
0132         mFullScreenAction->trigger();
0133     }
0134 }
0135 
0136 bool MprisMediaPlayer2Player::canPlay() const
0137 {
0138     return mSlideShowEnabled;
0139 }
0140 
0141 void MprisMediaPlayer2Player::Play()
0142 {
0143     if (mSlideShow->isRunning()) {
0144         return;
0145     }
0146     mToggleSlideShowAction->trigger();
0147 }
0148 
0149 double MprisMediaPlayer2Player::volume() const
0150 {
0151     return 0;
0152 }
0153 
0154 void MprisMediaPlayer2Player::setVolume(double volume)
0155 {
0156     Q_UNUSED(volume);
0157 }
0158 
0159 void MprisMediaPlayer2Player::setShuffle(bool isShuffle)
0160 {
0161     mSlideShow->randomAction()->setChecked(isShuffle);
0162 }
0163 
0164 QVariantMap MprisMediaPlayer2Player::metadata() const
0165 {
0166     return mMetaData;
0167 }
0168 
0169 qlonglong MprisMediaPlayer2Player::position() const
0170 {
0171     // milliseconds -> microseconds
0172     return mSlideShow->position() * 1000;
0173 }
0174 
0175 double MprisMediaPlayer2Player::rate() const
0176 {
0177     return 1.0;
0178 }
0179 
0180 void MprisMediaPlayer2Player::setRate(double newRate)
0181 {
0182     Q_UNUSED(newRate);
0183 }
0184 
0185 double MprisMediaPlayer2Player::minimumRate() const
0186 {
0187     return MIN_RATE;
0188 }
0189 
0190 double MprisMediaPlayer2Player::maximumRate() const
0191 {
0192     return MAX_RATE;
0193 }
0194 
0195 bool MprisMediaPlayer2Player::isShuffle() const
0196 {
0197     return mSlideShow->randomAction()->isChecked();
0198 }
0199 
0200 bool MprisMediaPlayer2Player::canSeek() const
0201 {
0202     return false;
0203 }
0204 
0205 bool MprisMediaPlayer2Player::canControl() const
0206 {
0207     return true;
0208 }
0209 
0210 void MprisMediaPlayer2Player::Seek(qlonglong offset)
0211 {
0212     Q_UNUSED(offset);
0213 }
0214 
0215 void MprisMediaPlayer2Player::SetPosition(const QDBusObjectPath &trackId, qlonglong pos)
0216 {
0217     Q_UNUSED(trackId);
0218     Q_UNUSED(pos);
0219 }
0220 
0221 void MprisMediaPlayer2Player::OpenUri(const QString &uri)
0222 {
0223     Q_UNUSED(uri);
0224 }
0225 
0226 void MprisMediaPlayer2Player::onSlideShowStateChanged()
0227 {
0228     if (!updatePlaybackStatus()) {
0229         return;
0230     }
0231 
0232     signalPropertyChange(QStringLiteral("Position"), position());
0233     signalPropertyChange(QStringLiteral("PlaybackStatus"), mPlaybackStatus);
0234 }
0235 
0236 void MprisMediaPlayer2Player::onCurrentUrlChanged(const QUrl &url)
0237 {
0238     if (url.isEmpty()) {
0239         mCurrentDocument = Document::Ptr();
0240     } else {
0241         mCurrentDocument = DocumentFactory::instance()->load(url);
0242         connect(mCurrentDocument.data(), &Document::metaInfoUpdated, this, &MprisMediaPlayer2Player::onMetaInfoUpdated);
0243     }
0244 
0245     onMetaInfoUpdated();
0246     signalPropertyChange(QStringLiteral("Position"), position());
0247 }
0248 
0249 void MprisMediaPlayer2Player::onMetaInfoUpdated()
0250 {
0251     QVariantMap updatedMetaData;
0252 
0253     if (mCurrentDocument) {
0254         const QUrl url = mCurrentDocument->url();
0255         ImageMetaInfoModel *metaInfoModel = mCurrentDocument->metaInfo();
0256 
0257         // We need some unique id mapping to urls. The index in the list is not reliable,
0258         // as images can be added/removed during a running slideshow
0259         // To allow some bidrectional mapping, convert the url to base64 to encode it for
0260         // matching the D-Bus object path spec
0261         const QString slideId = QString::fromLatin1(url.toString().toUtf8().toBase64(QByteArray::OmitTrailingEquals).replace('+', ""));
0262         const QDBusObjectPath trackId(QStringLiteral("/org/kde/gwenview/imagelist/") + slideId);
0263         updatedMetaData.insert(QStringLiteral("mpris:trackid"), QVariant::fromValue<QDBusObjectPath>(trackId));
0264 
0265         // TODO: for videos also get and report the length
0266         if (MimeTypeUtils::urlKind(url) != MimeTypeUtils::KIND_VIDEO) {
0267             // convert seconds in microseconds
0268             const auto duration = qlonglong(mSlideShow->interval() * 1000000);
0269             updatedMetaData.insert(QStringLiteral("mpris:length"), duration);
0270         }
0271 
0272         // TODO: update on metadata changes, given user can edit most of this data
0273 
0274         const QString name = metaInfoModel->getValueForKey(QStringLiteral("General.Name"));
0275         updatedMetaData.insert(QStringLiteral("xesam:title"), name);
0276         const QString comment = metaInfoModel->getValueForKey(QStringLiteral("General.Comment"));
0277         if (!comment.isEmpty()) {
0278             updatedMetaData.insert(QStringLiteral("xesam:comment"), comment);
0279         }
0280         updatedMetaData.insert(QStringLiteral("xesam:url"), url.toString());
0281         // slight bending of semantics :)
0282         const KFileItem folderItem(mContextManager->currentDirUrl());
0283         updatedMetaData.insert(QStringLiteral("xesam:album"), folderItem.text());
0284         // TODO: hook up with thumbnail cache and pass that as arturl
0285         // updatedMetaData.insert(QStringLiteral("mpris:artUrl"), url.toString());
0286 
0287 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
0288         const QModelIndex index = mContextManager->dirModel()->indexForUrl(url);
0289         if (index.isValid()) {
0290             const double rating = index.data(SemanticInfoDirModel::RatingRole).toInt() / 10.0;
0291             updatedMetaData.insert(QStringLiteral("xesam:userRating"), rating);
0292         }
0293 #endif
0294         // consider export of other metadata where mapping works
0295     }
0296 
0297     if (updatedMetaData != mMetaData) {
0298         mMetaData = updatedMetaData;
0299 
0300         signalPropertyChange(QStringLiteral("Metadata"), mMetaData);
0301     }
0302 }
0303 
0304 void MprisMediaPlayer2Player::onRandomActionToggled(bool checked)
0305 {
0306     signalPropertyChange(QStringLiteral("Shuffle"), checked);
0307 }
0308 
0309 void MprisMediaPlayer2Player::onToggleSlideShowActionChanged()
0310 {
0311     const bool isEnabled = mToggleSlideShowAction->isEnabled();
0312     if (mSlideShowEnabled == isEnabled) {
0313         return;
0314     }
0315 
0316     mSlideShowEnabled = isEnabled;
0317 
0318     const bool playbackStatusChanged = updatePlaybackStatus();
0319 
0320     signalPropertyChange(QStringLiteral("CanPlay"), mSlideShowEnabled);
0321     signalPropertyChange(QStringLiteral("CanPause"), mSlideShowEnabled);
0322     if (playbackStatusChanged) {
0323         signalPropertyChange(QStringLiteral("Position"), position());
0324         signalPropertyChange(QStringLiteral("PlaybackStatus"), mPlaybackStatus);
0325     }
0326 }
0327 
0328 void MprisMediaPlayer2Player::onNextActionChanged()
0329 {
0330     const bool isEnabled = mNextAction->isEnabled();
0331     if (mNextEnabled == isEnabled) {
0332         return;
0333     }
0334 
0335     mNextEnabled = isEnabled;
0336 
0337     signalPropertyChange(QStringLiteral("CanGoNext"), mNextEnabled);
0338 }
0339 
0340 void MprisMediaPlayer2Player::onPreviousActionChanged()
0341 {
0342     const bool isEnabled = mPreviousAction->isEnabled();
0343     if (mPreviousEnabled == isEnabled) {
0344         return;
0345     }
0346 
0347     mPreviousEnabled = isEnabled;
0348 
0349     signalPropertyChange(QStringLiteral("CanGoPrevious"), mPreviousEnabled);
0350 }
0351 
0352 }
0353 
0354 #include "moc_mprismediaplayer2player.cpp"