File indexing completed on 2025-02-23 04:34:22

0001 /**
0002  * \file mprisinterface.h
0003  * MPRIS D-Bus interface for audio player.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 09-Dec-2016
0008  *
0009  * Copyright (C) 2016-2024  Urs Fleisch
0010  *
0011  * This file is part of Kid3.
0012  *
0013  * Kid3 is free software; you can redistribute it and/or modify
0014  * it under the terms of the GNU General Public License as published by
0015  * the Free Software Foundation; either version 2 of the License, or
0016  * (at your option) any later version.
0017  *
0018  * Kid3 is distributed in the hope that it will be useful,
0019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0021  * GNU General Public License for more details.
0022  *
0023  * You should have received a copy of the GNU General Public License
0024  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0025  */
0026 
0027 #pragma once
0028 
0029 #include <QObject>
0030 #include "config.h"
0031 
0032 #ifdef HAVE_QTDBUS
0033 
0034 #include <QDBusAbstractAdaptor>
0035 #include <QStringList>
0036 
0037 class QDBusObjectPath;
0038 class QTemporaryFile;
0039 class AudioPlayer;
0040 
0041 /**
0042  * MPRIS D-Bus Interface MediaPlayer2.
0043  * See https://specifications.freedesktop.org/mpris-spec/2.2/
0044  */
0045 class MprisInterface : public QDBusAbstractAdaptor {
0046   Q_OBJECT
0047   Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2")
0048   /** false, Quit() is not supported. */
0049   Q_PROPERTY(bool CanQuit READ canQuit CONSTANT)
0050   /** false, not full screen. */
0051   Q_PROPERTY(bool Fullscreen READ fullscreen CONSTANT)
0052   /** false, Fullscreen is not supported. */
0053   Q_PROPERTY(bool CanSetFullscreen READ canSetFullscreen CONSTANT)
0054   /** false, Raise() is not supported. */
0055   Q_PROPERTY(bool CanRaise READ canRaise CONSTANT)
0056   /** false, org.mpris.MediaPlayer2.TrackList interface is not implemented. */
0057   Q_PROPERTY(bool HasTrackList READ hasTrackList CONSTANT)
0058   /** Media player identification "Kid3". */
0059   Q_PROPERTY(QString Identity READ identity CONSTANT)
0060   /** Base name of desktop file "kid3". */
0061   Q_PROPERTY(QString DesktopEntry READ desktopEntry CONSTANT)
0062   /** URI schemes supported by the media player, ["file"]. */
0063   Q_PROPERTY(QStringList SupportedUriSchemes READ supportedUriSchemes CONSTANT)
0064   /** MIME types supported by the media player. */
0065   Q_PROPERTY(QStringList SupportedMimeTypes READ supportedMimeTypes CONSTANT)
0066 
0067 public:
0068   /**
0069    * Constructor.
0070    *
0071    * @param player parent audio player
0072    */
0073   explicit MprisInterface(AudioPlayer* player);
0074 
0075   /**
0076    * Destructor.
0077    */
0078   ~MprisInterface() override = default;
0079 
0080 public slots:
0081   /**
0082    * Bring media player to front, not implemented.
0083    */
0084   void Raise() {}
0085 
0086   /**
0087    * Terminate media player, not implemented.
0088    */
0089   void Quit() {}
0090 
0091 private:
0092   bool canQuit() const { return false; }
0093   bool fullscreen() const { return false; }
0094   bool canSetFullscreen() const { return false; }
0095   bool canRaise() const { return false; }
0096   bool hasTrackList() const { return false; }
0097   QString identity() const;
0098   QString desktopEntry() const;
0099   QStringList supportedUriSchemes() const;
0100   QStringList supportedMimeTypes() const;
0101 
0102   AudioPlayer* m_audioPlayer;
0103 };
0104 
0105 
0106 /**
0107  * MPRIS D-Bus Interface MediaPlayer2.Player.
0108  * See https://specifications.freedesktop.org/mpris-spec/2.2/
0109  */
0110 class MprisPlayerInterface : public QDBusAbstractAdaptor {
0111   Q_OBJECT
0112   Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2.Player")
0113   /** Playback status, "Playing", "Paused" or "Stopped". */
0114   Q_PROPERTY(QString PlaybackStatus READ playbackStatus)
0115   /** Loop status, "None". */
0116   Q_PROPERTY(QString LoopStatus READ loopStatus CONSTANT)
0117   /** Playback rate 1.0. */
0118   Q_PROPERTY(double Rate READ rate CONSTANT)
0119   /** false, shuffle is not implemented. */
0120   Q_PROPERTY(bool Shuffle READ shuffle CONSTANT)
0121   /** Map with metadata. */
0122   Q_PROPERTY(QVariantMap Metadata READ metadata)
0123   /** Current volume, value between 0.0 and 1.0. */
0124   Q_PROPERTY(double Volume READ volume WRITE setVolume)
0125   /** Current track position in microseconds. */
0126   Q_PROPERTY(qlonglong Position READ position)
0127   /** Minimum playback rate 1.0. */
0128   Q_PROPERTY(double MinimumRate READ minimumRate CONSTANT)
0129   /** Maximum playback rate 1.0. */
0130   Q_PROPERTY(double MaximumRate READ maximumRate CONSTANT)
0131   /** true if there is a next track. */
0132   Q_PROPERTY(bool CanGoNext READ canGoNext)
0133   /** true if there is a previous track. */
0134   Q_PROPERTY(bool CanGoPrevious READ canGoPrevious)
0135   /** true if there is a current track. */
0136   Q_PROPERTY(bool CanPlay READ canPlay)
0137   /** true if there is a current track. */
0138   Q_PROPERTY(bool CanPause READ canPause)
0139   /** true, Seek() and SetPosition() are implemented. */
0140   Q_PROPERTY(bool CanSeek READ canSeek)
0141   /** true, media player can be controlled. */
0142   Q_PROPERTY(bool CanControl READ canControl CONSTANT)
0143 
0144 public:
0145   /**
0146    * Constructor.
0147    *
0148    * @param player parent audio player
0149    */
0150   explicit MprisPlayerInterface(AudioPlayer* player);
0151 
0152   /**
0153    * Destructor.
0154    */
0155   ~MprisPlayerInterface() override;
0156 
0157 public slots:
0158   /**
0159    * Skip to next track in tracklist.
0160    */
0161   void Next();
0162 
0163   /**
0164    * Skip to previous track in tracklist.
0165    */
0166   void Previous();
0167 
0168   /**
0169    * Pause playback.
0170    */
0171   void Pause();
0172 
0173   /**
0174    * If playback is paused, resume playback, if playback is stopped,
0175    * start playback.
0176    */
0177   void PlayPause();
0178 
0179   /**
0180    * Stop playback.
0181    */
0182   void Stop();
0183 
0184   /**
0185    * Start or resume playback.
0186    */
0187   void Play();
0188 
0189   /**
0190    * Seek forward in the current track by the specified number of microseconds.
0191    *
0192    * A negative value seeks back. If this would mean seeking back further than
0193    * the start of the track, the position is set to 0. If the value passed in
0194    * would mean seeking beyond the end of the track, acts like a call to Next().
0195    *
0196    * @param offsetUs microseconds to seek forward
0197    */
0198   void Seek(qlonglong offsetUs);
0199 
0200   /**
0201    * Set the current track position in microseconds.
0202    *
0203    * If @a positionUs is less than 0, do nothing. If @a positionUs is greater
0204    * than the track length, do nothing.
0205    *
0206    * @param trackId the currently playing track's identifier
0207    * @param positionUs track position in microseconds
0208    */
0209   void SetPosition(const QDBusObjectPath& trackId, qlonglong positionUs);
0210 
0211   /**
0212    * Open file.
0213    * @param uri URL of track to load
0214    */
0215   void OpenUri(const QString& uri);
0216 
0217 signals:
0218   /**
0219    * Indicates that the track position has changed in a way that is
0220    * inconsistant with the current playing state.
0221    * @param positionUs new position in microseconds
0222    */
0223   void Seeked(qlonglong positionUs);
0224 
0225 private slots:
0226   void onStateChanged();
0227   void onTrackChanged(const QString& filePath, bool hasPrevious, bool hasNext);
0228   void onVolumeChanged();
0229   void onFileCountChanged(int count);
0230   void onCurrentPositionChanged(qint64 position);
0231 
0232 private:
0233   QString playbackStatus() const;
0234   QString loopStatus() const { return QLatin1String("None"); }
0235   double rate() const { return 1.0; }
0236   bool shuffle() const { return false; }
0237   QVariantMap metadata() const;
0238   double volume() const;
0239   void setVolume(double volume);
0240   qlonglong position() const;
0241   double minimumRate() const { return 1.0; }
0242   double maximumRate() const { return 1.0; }
0243   bool canGoNext() const;
0244   bool canGoPrevious() const;
0245   bool canPlay() const;
0246   bool canPause() const;
0247   bool canSeek() const { return true; }
0248   bool canControl() const { return true; }
0249 
0250   void sendPropertiesChangedSignal(const QString& name, const QVariant& value);
0251   QDBusObjectPath getCurrentTrackId() const;
0252   QString findCoverArtInDirectory(const QString& dirPath) const;
0253 
0254   AudioPlayer* m_audioPlayer;
0255   QString m_status;
0256   bool m_hasPrevious;
0257   bool m_hasNext;
0258   bool m_hasFiles;
0259   mutable QTemporaryFile* m_tempCoverArtFile;
0260   mutable QString m_coverArtDirName;
0261   mutable QString m_coverArtFileName;
0262 };
0263 
0264 #else
0265 
0266 // Just to suppress moc "No relevant classes found" warning.
0267 class MprisInterface : public QObject {
0268   Q_OBJECT
0269 };
0270 
0271 #endif