TTKMusicPlayer  4.2.0.0
TTKMusicPlayer imitates Kugou UI, the music player uses of qmmp core library based on Qt for windows and linux
musicmprisplayer.cpp
Go to the documentation of this file.
1 #include "musicmprisplayer.h"
2 #include "musicapplication.h"
3 #include "musicplayer.h"
4 #include "musicplaylist.h"
5 #include "ttktime.h"
6 
7 #include <QBuffer>
8 #include <QDBusMessage>
9 #include <qmmp/soundcore.h>
10 #include <qmmp/metadatamanager.h>
11 #include <qmmp/abstractengine.h>
12 
14  : QObject(parent)
15 {
16  QDBusConnection connection = QDBusConnection::sessionBus();
17  //MPRISv2.0
18  m_root = new MusicMPRISPlayerRoot(this);
19  m_player = new MusicMPRISPlayerCore(this);
20  //
21  connection.registerService("org.mpris.MediaPlayer2." TTK_APP_NAME);
22  connection.registerObject("/org/mpris/MediaPlayer2", this);
23 }
24 
26 {
27  QDBusConnection::sessionBus().unregisterService("org.mpris.MediaPlayer2." TTK_APP_NAME);
28  delete m_root;
29  delete m_player;
30 }
31 
33 {
35 }
36 
37 
39  : QDBusAbstractAdaptor(parent)
40 {
41 
42 }
43 
45 {
46  return true;
47 }
48 
50 {
51  return true;
52 }
53 
55 {
56  return TTK_APP_NAME;
57 }
58 
60 {
61  return false;
62 }
63 
65 {
66  return TTK_APP_NAME;
67 }
68 
70 {
71  QStringList mimeTypes;
72  mimeTypes << Decoder::contentTypes();
73  mimeTypes << AbstractEngine::contentTypes();
74  mimeTypes.removeDuplicates();
75  return mimeTypes;
76 }
77 
79 {
80  QStringList protocols = MetaDataManager::instance()->protocols();
81  if(!protocols.contains("file")) //append file if needed
82  {
83  protocols.append("file");
84  }
85  return protocols;
86 }
87 
89 {
91 }
92 
94 {
96 }
97 
98 
100  : QDBusAbstractAdaptor(parent)
101 {
102  TTK::initRandom();
103 
104  m_prevTrack = 0;
105  m_prevPos = 0;
108 
109  connect(m_core, SIGNAL(trackInfoChanged()), SLOT(trackInfoChanged()));
110  connect(m_core, SIGNAL(stateChanged(Qmmp::State)), SLOT(stateChanged()));
111  connect(m_core, SIGNAL(volumeChanged(int)), SLOT(volumeChanged()));
112  connect(m_core, SIGNAL(elapsedChanged(qint64)), SLOT(elapsedChanged(qint64)));
114 
115  updateTrackID();
116  syncProperties();
117 }
118 
120 {
121  return true;
122 }
123 
125 {
126  return !m_application->m_playlist->isEmpty();
127 }
128 
130 {
131  return !m_application->m_playlist->isEmpty();
132 }
133 
135 {
136  return !m_application->m_playlist->isEmpty();
137 }
138 
140 {
141  return !m_application->m_playlist->isEmpty();
142 }
143 
145 {
146  return m_core->duration() > 0;
147 }
148 
150 {
151  switch(m_application->playMode())
152  {
153  case TTK::PlayMode::OneLoop: return "Track";
154  case TTK::PlayMode::ListLoop: return "Playlist";
155  default: return "None";
156  }
157 }
158 
159 void MusicMPRISPlayerCore::setLoopStatus(const QString &value) noexcept
160 {
161  if(value == "Track")
162  {
163  m_application->playOneLoop();
164  }
165  else if(value == "Playlist")
166  {
167  m_application->playlistLoop();
168  }
169  else
170  {
171  m_application->playOrder();
172  }
173 }
174 
176 {
177  return 1.0;
178 }
179 
181 {
182  if(m_core->path().isEmpty())
183  {
184  return QVariantMap();
185  }
186 
187  QVariantMap map;
188  TrackInfo info = m_core->trackInfo();
189  map["mpris:length"] = qMax(m_core->duration() * TTK_DN_S2MS, qint64(0));
190 
191  const QString &coverPath = MetaDataManager::instance()->getCoverPath(info.path());
192  if(!coverPath.isEmpty())
193  {
194  map["mpris:artUrl"] = QUrl::fromLocalFile(coverPath).toString();
195  }
196  else
197  {
198  const QImage &coverImage = MetaDataManager::instance()->getCover(info.path());
199  if(!coverImage.isNull())
200  {
201  QByteArray tmp;
202  QBuffer buffer(&tmp);
203  if(buffer.open(QIODevice::WriteOnly))
204  {
205  coverImage.save(&buffer, "JPEG");
206  map["mpris:artUrl"] = QString("data:image/jpeg;base64,%1").arg(QString::fromLatin1(tmp.toBase64()));
207  }
208  }
209  }
210 
211  if(!info.value(Qmmp::ALBUM).isEmpty())
212  {
213  map["xesam:album"] = info.value(Qmmp::ALBUM);
214  }
215 
216  if(!info.value(Qmmp::ARTIST).isEmpty())
217  {
218  map["xesam:artist"] = QStringList(info.value(Qmmp::ARTIST));
219  }
220 
221  if(!info.value(Qmmp::ALBUMARTIST).isEmpty())
222  {
223  map["xesam:albumArtist"] = QStringList(info.value(Qmmp::ALBUMARTIST));
224  }
225 
226  if(!info.value(Qmmp::COMMENT).isEmpty())
227  {
228  map["xesam:comment"] = QStringList(info.value(Qmmp::COMMENT));
229  }
230 
231  if(!info.value(Qmmp::COMPOSER).isEmpty())
232  {
233  map["xesam:composer"] = QStringList(info.value(Qmmp::COMPOSER));
234  }
235 
236  if(!info.value(Qmmp::DISCNUMBER).isEmpty())
237  {
238  map["xesam:discNumber"] = info.value(Qmmp::DISCNUMBER).toInt();
239  }
240 
241  if(!info.value(Qmmp::GENRE).isEmpty())
242  {
243  map["xesam:genre"] = QStringList(info.value(Qmmp::GENRE));
244  }
245 
246  if(!info.value(Qmmp::TITLE).isEmpty())
247  {
248  map["xesam:title"] = info.value(Qmmp::TITLE);
249  }
250 
251  if(!info.value(Qmmp::TRACK).isEmpty())
252  {
253  map["xesam:trackNumber"] = info.value(Qmmp::TRACK).toInt();
254  }
255 
256  if(!info.value(Qmmp::YEAR).isEmpty())
257  {
258  map["xesam:contentCreated"] = info.value(Qmmp::YEAR);
259  }
260 
261  map["mpris:trackid"] = QVariant::fromValue<QDBusObjectPath>(m_trackID);
262  map["xesam:url"] = info.path().startsWith(TTK_SEPARATOR) ? QUrl::fromLocalFile(info.path()).toString() : info.path();
263  return map;
264 }
265 
267 {
268  return 1.0;
269 }
270 
272 {
273  if(m_core->state() == Qmmp::Playing)
274  {
275  return "Playing";
276  }
277  else if(m_core->state() == Qmmp::Paused)
278  {
279  return "Paused";
280  }
281  return "Stopped";
282 }
283 
285 {
286  return qMax(m_core->elapsed() * TTK_DN_S2MS, qint64(0));
287 }
288 
290 {
291  return 1.0;
292 }
293 
294 void MusicMPRISPlayerCore::setRate(double value) noexcept
295 {
296  Q_UNUSED(value)
297 }
298 
300 {
302 }
303 
304 void MusicMPRISPlayerCore::setShuffle(bool value) noexcept
305 {
306  m_application->m_playlist->setPlaybackMode(value ? TTK::PlayMode::Random : TTK::PlayMode::Order);
307 }
308 
310 {
311  return m_application->m_player->volume();
312 }
313 
315 {
317 }
318 
320 {
322 }
323 
325 {
327 }
328 
329 void MusicMPRISPlayerCore::OpenUri(const QString &uri)
330 {
331  QString path = uri;
332  if(uri.startsWith("file://"))
333  {
334  path = QUrl(uri).toLocalFile();
335  if(!QFile::exists(path))
336  {
337  return; //error
338  }
339  }
340 
341  m_application->importSongsByOutside(path, true);
342 }
343 
345 {
347 }
348 
350 {
352 }
353 
355 {
357 }
358 
360 {
362 }
363 
365 {
366  m_core->seek(qMax(qint64(0), m_core->elapsed() + offset / 1000));
367 }
368 
369 void MusicMPRISPlayerCore::SetPosition(const QDBusObjectPath &trackId, qlonglong position)
370 {
371  if(m_trackID == trackId)
372  {
373  m_core->seek(position / 1000);
374  }
375  else
376  {
377  TTK_WARN_STREAM("SetPosition called with a invalid trackId");
378  }
379 }
380 
382 {
383  updateTrackID();
384  sendProperties();
385 }
386 
388 {
389  if(m_core->state() == Qmmp::Playing)
390  {
391  updateTrackID();
392  m_prevPos = 0;
393  }
394  sendProperties();
395 }
396 
398 {
399  sendProperties();
400 }
401 
403 {
404  if(abs(elapsed - m_prevPos) > 2 * TTK_DN_S2MS)
405  {
406  Q_EMIT Seeked(elapsed * TTK_DN_S2MS);
407  }
408  m_prevPos = elapsed;
409 }
410 
412 {
413  sendProperties();
414 }
415 
417 {
419  {
420  m_trackID = QDBusObjectPath(QString("%1/Track/%2").arg("/org/qmmp/MediaPlayer2").arg(TTK::random()));
422  }
423 }
424 
426 {
427  m_properties["CanGoNext"] = canGoNext();
428  m_properties["CanGoPrevious"] = canGoPrevious();
429  m_properties["CanPause"] = canPause();
430  m_properties["CanPlay"] = canPlay();
431  m_properties["CanSeek"] = canSeek();
432  m_properties["LoopStatus"] = loopStatus();
433  m_properties["MaximumRate"] = maximumRate();
434  m_properties["MinimumRate"] = minimumRate();
435  m_properties["PlaybackStatus"] = playbackStatus();
436  m_properties["Rate"] = rate();
437  m_properties["Shuffle"] = shuffle();
438  m_properties["Volume"] = volume();
439  m_properties["Metadata"] = metadata();
440 }
441 
443 {
444  TTKVariantMap prevProps = m_properties;
445  syncProperties();
446 
447  QVariantMap map;
448  for(auto it = m_properties.constBegin(); it != m_properties.constEnd(); ++it)
449  {
450  if(it.value() != prevProps.value(it.key()))
451  {
452  map.insert(it.key(), it.value());
453  }
454  }
455 
456  if(map.isEmpty())
457  {
458  return;
459  }
460 
461  QDBusMessage msg = QDBusMessage::createSignal("/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "PropertiesChanged");
462  msg << "org.mpris.MediaPlayer2.Player";
463  msg << map;
464  msg << QStringList();
465  QDBusConnection::sessionBus().send(msg);
466 }
bool shuffle() const noexcept
The class of the app mpris player object.
QStringList supportedUriSchemes() const
static QStringList contentTypes()
bool canControl() const noexcept
qint64 elapsed() const
void OpenUri(const QString &uri)
double minimumRate() const noexcept
QImage getCover(const QString &url) const
void seek(qint64 time)
static MusicApplication * instance()
MusicMPRISPlayerCore * m_player
MusicApplication * m_application
bool hasTrackList() const
QDBusObjectPath m_trackID
static SoundCore * instance()
const TrackInfo & trackInfo() const
QString getCoverPath(const QString &url) const
const QString & path() const
QStringList protocols() const
#define TTK_APP_NAME
Definition: ttkobject.h:25
bool isEmpty() const noexcept
double rate() const noexcept
bool canPause() const noexcept
qint64 duration() const
TTK_MODULE_EXPORT void initRandom()
Definition: ttktime.cpp:7
MusicPlayer * m_player
bool canSeek() const noexcept
bool canPlay() const noexcept
void setLoopStatus(const QString &value) noexcept
MusicMPRISPlayer(QObject *parent=nullptr)
TTK::PlayMode playMode() const noexcept
MusicMPRISPlayerCore(QObject *parent=nullptr)
void setVolume(int volume)
Definition: musicplayer.cpp:70
bool canGoPrevious() const noexcept
TTK_MODULE_EXPORT int random(int value=RAND_MAX)
Definition: ttktime.cpp:14
#define TTK_WARN_STREAM(msg)
Definition: ttklogger.h:75
voidpf uLong offset
Definition: ioapi.h:142
QStringList supportedMimeTypes() const
QString path() const
void elapsedChanged(qint64 elapsed)
The TrackInfo class stores metadata and other information about track.
Definition: trackinfo.h:32
PlayMode
Definition: musicobject.h:185
QMap< QString, QVariant > TTKVariantMap
Definition: ttkqtglobal.h:203
MusicPlaylist * m_playlist
void Seek(qlonglong offset)
qlonglong position() const
#define TTK_SEPARATOR
Definition: ttkglobal.h:269
void Seeked(qlonglong position)
State
Definition: qmmp.h:89
MusicMPRISPlayerRoot(QObject *parent=nullptr)
QVariantMap metadata() const
double maximumRate() const noexcept
void SetPosition(const QDBusObjectPath &trackId, qlonglong position)
void importSongsByOutside(const QString &path, bool play)
QString desktopEntry() const
TTK_MODULE_EXPORT QString toString(Record type) noexcept
MusicMPRISPlayerRoot * m_root
void setShuffle(bool value) noexcept
#define TTK_DN_S2MS
Definition: ttkglobal.h:355
const QString value(Qmmp::MetaData key) const
bool canGoNext() const noexcept
int currentIndex() const noexcept
void setVolume(double value)
void setRate(double value) noexcept
QString loopStatus() const noexcept
static QStringList contentTypes()
int volume() const
Definition: musicplayer.cpp:65
#define const
Definition: zconf.h:233
The class of the app mpris root object.
static MetaDataManager * instance()
TTKVariantMap m_properties
QString playbackStatus() const noexcept
Qmmp::State state() const
QString identity() const