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"