1 /*
2 SPDX-FileCopyrightText: 2016 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr>
3
4 SPDX-License-Identifier: LGPL-3.0-or-later
5 */
6
7 #include "manageaudioplayer.h"
8
9 #include "mediaplaylist.h"
10
11 #include "elisa_settings.h"
12
13 #include "playerLogging.h"
14
15 #include <QTimer>
16 #include <QDateTime>
17
ManageAudioPlayer(QObject * parent)18 ManageAudioPlayer::ManageAudioPlayer(QObject *parent) : QObject(parent)
19 {
20
21 }
22
currentTrack() const23 QPersistentModelIndex ManageAudioPlayer::currentTrack() const
24 {
25 return mCurrentTrack;
26 }
27
playListModel() const28 QAbstractItemModel *ManageAudioPlayer::playListModel() const
29 {
30 return mPlayListModel;
31 }
32
urlRole() const33 int ManageAudioPlayer::urlRole() const
34 {
35 return mUrlRole;
36 }
37
isPlayingRole() const38 int ManageAudioPlayer::isPlayingRole() const
39 {
40 return mIsPlayingRole;
41 }
42
playerSource() const43 QUrl ManageAudioPlayer::playerSource() const
44 {
45 if (!mCurrentTrack.isValid()) {
46 return QUrl();
47 }
48
49 return mCurrentTrack.data(mUrlRole).toUrl();
50 }
51
playerStatus() const52 QMediaPlayer::MediaStatus ManageAudioPlayer::playerStatus() const
53 {
54 return mPlayerStatus;
55 }
56
playerPlaybackState() const57 QMediaPlayer::State ManageAudioPlayer::playerPlaybackState() const
58 {
59 return mPlayerPlaybackState;
60 }
61
playerError() const62 QMediaPlayer::Error ManageAudioPlayer::playerError() const
63 {
64 return mPlayerError;
65 }
66
audioDuration() const67 qint64 ManageAudioPlayer::audioDuration() const
68 {
69 return mAudioDuration;
70 }
71
playerIsSeekable() const72 bool ManageAudioPlayer::playerIsSeekable() const
73 {
74 return mPlayerIsSeekable;
75 }
76
playerPosition() const77 qint64 ManageAudioPlayer::playerPosition() const
78 {
79 return mPlayerPosition;
80 }
81
playControlPosition() const82 qint64 ManageAudioPlayer::playControlPosition() const
83 {
84 return mPlayerPosition;
85 }
86
persistentState() const87 QVariantMap ManageAudioPlayer::persistentState() const
88 {
89 auto persistentStateValue = QVariantMap();
90
91 persistentStateValue[QStringLiteral("isPlaying")] = mPlayingState;
92
93 persistentStateValue[QStringLiteral("playerPosition")] = mPlayerPosition;
94 persistentStateValue[QStringLiteral("playerDuration")] = mAudioDuration;
95
96 if (mCurrentTrack.isValid()) {
97 persistentStateValue[QStringLiteral("audioPlayerCurrentTitle")] = mCurrentTrack.data(mTitleRole);
98 persistentStateValue[QStringLiteral("audioPlayerCurrentArtistName")] = mCurrentTrack.data(mArtistNameRole);
99 persistentStateValue[QStringLiteral("audioPlayerCurrentAlbumName")] = mCurrentTrack.data(mAlbumNameRole);
100 } else {
101 persistentStateValue[QStringLiteral("audioPlayerCurrentTitle")] = {};
102 persistentStateValue[QStringLiteral("audioPlayerCurrentArtistName")] = {};
103 persistentStateValue[QStringLiteral("audioPlayerCurrentAlbumName")] = {};
104 }
105
106 return persistentStateValue;
107 }
108
playListPosition() const109 int ManageAudioPlayer::playListPosition() const
110 {
111 if (mCurrentTrack.isValid()) {
112 return mCurrentTrack.row();
113 }
114
115 return 0;
116 }
117
titleRole() const118 int ManageAudioPlayer::titleRole() const
119 {
120 return mTitleRole;
121 }
122
artistNameRole() const123 int ManageAudioPlayer::artistNameRole() const
124 {
125 return mArtistNameRole;
126 }
127
albumNameRole() const128 int ManageAudioPlayer::albumNameRole() const
129 {
130 return mAlbumNameRole;
131 }
132
setCurrentTrack(const QPersistentModelIndex & currentTrack)133 void ManageAudioPlayer::setCurrentTrack(const QPersistentModelIndex ¤tTrack)
134 {
135 mOldCurrentTrack = mCurrentTrack;
136
137 mCurrentTrack = currentTrack;
138
139 if (mCurrentTrack.isValid()) {
140 restorePreviousState();
141 }
142
143 mPlayerError = QMediaPlayer::NoError;
144
145 if (mOldCurrentTrack != mCurrentTrack || mPlayingState) {
146 Q_EMIT currentTrackChanged();
147 }
148
149 switch (mPlayerPlaybackState) {
150 case QMediaPlayer::StoppedState:
151 Q_EMIT playerSourceChanged(mCurrentTrack.data(mUrlRole).toUrl());
152 break;
153 case QMediaPlayer::PlayingState:
154 case QMediaPlayer::PausedState:
155 triggerStop();
156 if (mPlayingState && !mCurrentTrack.isValid()) {
157 mPlayingState = false;
158 }
159 mSkippingCurrentTrack = true;
160 break;
161 }
162 }
163
saveForUndoClearPlaylist()164 void ManageAudioPlayer::saveForUndoClearPlaylist(){
165 mUndoPlayingState = mPlayingState;
166
167 mUndoPlayerPosition = mPlayerPosition;
168 Q_EMIT saveUndoPositionInAudioWrapper(mUndoPlayerPosition);
169 }
170
restoreForUndoClearPlaylist()171 void ManageAudioPlayer::restoreForUndoClearPlaylist(){
172 mPlayerPosition = mUndoPlayerPosition;
173 Q_EMIT seek(mPlayerPosition);
174
175 mPlayingState = mUndoPlayingState;
176 Q_EMIT restoreUndoPositionInAudioWrapper();
177 }
178
setPlayListModel(QAbstractItemModel * aPlayListModel)179 void ManageAudioPlayer::setPlayListModel(QAbstractItemModel *aPlayListModel)
180 {
181 if (mPlayListModel == aPlayListModel) {
182 return;
183 }
184
185 if (mPlayListModel) {
186 disconnect(mPlayListModel, &QAbstractItemModel::dataChanged, this, &ManageAudioPlayer::tracksDataChanged);
187 }
188
189 mPlayListModel = aPlayListModel;
190
191 if (mPlayListModel) {
192 connect(mPlayListModel, &QAbstractItemModel::dataChanged, this, &ManageAudioPlayer::tracksDataChanged);
193 }
194
195 Q_EMIT playListModelChanged();
196 }
197
setUrlRole(int value)198 void ManageAudioPlayer::setUrlRole(int value)
199 {
200 mUrlRole = value;
201 Q_EMIT urlRoleChanged();
202 notifyPlayerSourceProperty();
203 restorePreviousState();
204 }
205
setIsPlayingRole(int value)206 void ManageAudioPlayer::setIsPlayingRole(int value)
207 {
208 if (mIsPlayingRole == value) {
209 return;
210 }
211
212 mIsPlayingRole = value;
213 Q_EMIT isPlayingRoleChanged();
214 }
215
setPlayerStatus(QMediaPlayer::MediaStatus playerStatus)216 void ManageAudioPlayer::setPlayerStatus(QMediaPlayer::MediaStatus playerStatus)
217 {
218 if (mPlayerStatus == playerStatus) {
219 return;
220 }
221
222 mPlayerStatus = playerStatus;
223 Q_EMIT playerStatusChanged();
224
225 switch (mPlayerStatus) {
226 case QMediaPlayer::NoMedia:
227 break;
228 case QMediaPlayer::LoadingMedia:
229 break;
230 case QMediaPlayer::LoadedMedia:
231 if (mPlayingState) {
232 triggerPlay();
233 }
234 break;
235 case QMediaPlayer::BufferingMedia:
236 break;
237 case QMediaPlayer::StalledMedia:
238 break;
239 case QMediaPlayer::BufferedMedia:
240 break;
241 case QMediaPlayer::EndOfMedia:
242 break;
243 case QMediaPlayer::InvalidMedia:
244 triggerSkipNextTrack();
245 break;
246 case QMediaPlayer::UnknownMediaStatus:
247 break;
248 }
249 }
250
setPlayerPlaybackState(QMediaPlayer::State playerPlaybackState)251 void ManageAudioPlayer::setPlayerPlaybackState(QMediaPlayer::State playerPlaybackState)
252 {
253 if (mPlayerPlaybackState == playerPlaybackState) {
254 return;
255 }
256
257 mPlayerPlaybackState = playerPlaybackState;
258 Q_EMIT playerPlaybackStateChanged();
259
260 if (!mSkippingCurrentTrack) {
261 switch(mPlayerPlaybackState) {
262 case QMediaPlayer::StoppedState:
263 if (mPlayerStatus == QMediaPlayer::EndOfMedia || mPlayerStatus == QMediaPlayer::InvalidMedia) {
264 triggerSkipNextTrack();
265 }
266 if (mPlayListModel && mCurrentTrack.isValid()) {
267 mPlayListModel->setData(mCurrentTrack, MediaPlayList::NotPlaying, mIsPlayingRole);
268 }
269 break;
270 case QMediaPlayer::PlayingState:
271 if (mPlayListModel && mCurrentTrack.isValid()) {
272 mPlayListModel->setData(mCurrentTrack, MediaPlayList::IsPlaying, mIsPlayingRole);
273 Q_EMIT startedPlayingTrack(mCurrentTrack.data(mUrlRole).toUrl(), QDateTime::currentDateTime());
274 }
275 break;
276 case QMediaPlayer::PausedState:
277 if (mPlayListModel && mCurrentTrack.isValid()) {
278 mPlayListModel->setData(mCurrentTrack, MediaPlayList::IsPaused, mIsPlayingRole);
279 }
280 break;
281 }
282 } else {
283 switch(mPlayerPlaybackState) {
284 case QMediaPlayer::StoppedState:
285 notifyPlayerSourceProperty();
286 mSkippingCurrentTrack = false;
287 if (mPlayListModel && mOldCurrentTrack.isValid()) {
288 mPlayListModel->setData(mOldCurrentTrack, MediaPlayList::NotPlaying, mIsPlayingRole);
289 }
290 break;
291 case QMediaPlayer::PlayingState:
292 if (mPlayListModel && mCurrentTrack.isValid()) {
293 mPlayListModel->setData(mCurrentTrack, MediaPlayList::IsPlaying, mIsPlayingRole);
294 Q_EMIT startedPlayingTrack(mCurrentTrack.data(mUrlRole).toUrl(), QDateTime::currentDateTime());
295 }
296 break;
297 case QMediaPlayer::PausedState:
298 if (mPlayListModel && mCurrentTrack.isValid()) {
299 mPlayListModel->setData(mCurrentTrack, MediaPlayList::IsPaused, mIsPlayingRole);
300 }
301 break;
302 }
303 }
304 }
305
setPlayerError(QMediaPlayer::Error playerError)306 void ManageAudioPlayer::setPlayerError(QMediaPlayer::Error playerError)
307 {
308 if (mPlayerError == playerError) {
309 return;
310 }
311
312 mPlayerError = playerError;
313 Q_EMIT playerErrorChanged();
314
315 if (mPlayerError != QMediaPlayer::NoError) {
316 auto currentSource = playerSource();
317
318 Q_EMIT sourceInError(currentSource, mPlayerError);
319
320 if (currentSource.isLocalFile()) {
321 Q_EMIT displayTrackError(currentSource.toLocalFile());
322 } else {
323 Q_EMIT displayTrackError(currentSource.toString());
324 }
325 }
326 }
327
ensurePause()328 void ManageAudioPlayer::ensurePause()
329 {
330 if (mPlayingState) {
331 mPlayingState = false;
332 triggerPause();
333 }
334 }
335
ensurePlay()336 void ManageAudioPlayer::ensurePlay()
337 {
338 if (!mPlayingState) {
339 mPlayingState = true;
340 triggerPlay();
341 }
342 }
343
stop()344 void ManageAudioPlayer::stop()
345 {
346 mPlayingState = false;
347 triggerStop();
348 }
349
playPause()350 void ManageAudioPlayer::playPause()
351 {
352 mPlayingState = !mPlayingState;
353
354 switch (mPlayerStatus) {
355 case QMediaPlayer::LoadedMedia:
356 case QMediaPlayer::BufferingMedia:
357 case QMediaPlayer::BufferedMedia:
358 case QMediaPlayer::LoadingMedia:
359 if (mPlayingState) {
360 triggerPlay();
361 } else {
362 triggerPause();
363 }
364 break;
365 case QMediaPlayer::EndOfMedia:
366 if (mPlayerPlaybackState == QMediaPlayer::PlayingState && !mPlayingState) {
367 triggerPause();
368 } else if (mPlayerPlaybackState == QMediaPlayer::PausedState && mPlayingState) {
369 triggerPlay();
370 }
371 break;
372 case QMediaPlayer::NoMedia:
373 case QMediaPlayer::StalledMedia:
374 case QMediaPlayer::InvalidMedia:
375 case QMediaPlayer::UnknownMediaStatus:
376 break;
377 }
378 }
379
setAudioDuration(qint64 audioDuration)380 void ManageAudioPlayer::setAudioDuration(qint64 audioDuration)
381 {
382 if (mAudioDuration == audioDuration) {
383 return;
384 }
385
386 mAudioDuration = audioDuration;
387 Q_EMIT audioDurationChanged();
388 }
389
setPlayerIsSeekable(bool playerIsSeekable)390 void ManageAudioPlayer::setPlayerIsSeekable(bool playerIsSeekable)
391 {
392 if (mPlayerIsSeekable == playerIsSeekable) {
393 return;
394 }
395
396 mPlayerIsSeekable = playerIsSeekable;
397 Q_EMIT playerIsSeekableChanged();
398 }
399
setPlayerPosition(qint64 playerPosition)400 void ManageAudioPlayer::setPlayerPosition(qint64 playerPosition)
401 {
402 if (mPlayerPosition == playerPosition) {
403 return;
404 }
405
406 mPlayerPosition = playerPosition;
407 Q_EMIT playerPositionChanged();
408 QTimer::singleShot(0, this, [this]() {Q_EMIT playControlPositionChanged();});
409 }
410
setCurrentPlayingForRadios(const QString & title,const QString & nowPlaying)411 void ManageAudioPlayer::setCurrentPlayingForRadios(const QString &title, const QString &nowPlaying)
412 {
413 if (mPlayListModel && mCurrentTrack.isValid()) {
414 Q_EMIT updateData(mCurrentTrack, title, MediaPlayList::TitleRole);
415 Q_EMIT updateData(mCurrentTrack, nowPlaying, MediaPlayList::ArtistRole);
416 }
417 }
418
setPlayControlPosition(int playerPosition)419 void ManageAudioPlayer::setPlayControlPosition(int playerPosition)
420 {
421 qCDebug(orgKdeElisaPlayer()) << "ManageAudioPlayer::setPlayControlPosition" << playerPosition;
422 Q_EMIT seek(playerPosition);
423 }
424
setPersistentState(const QVariantMap & persistentStateValue)425 void ManageAudioPlayer::setPersistentState(const QVariantMap &persistentStateValue)
426 {
427 if (mPersistentState == persistentStateValue) {
428 return;
429 }
430
431 mPersistentState = persistentStateValue;
432
433 Q_EMIT persistentStateChanged();
434
435 if (mCurrentTrack.isValid()) {
436 restorePreviousState();
437 }
438 }
439
playerSeek(int position)440 void ManageAudioPlayer::playerSeek(int position)
441 {
442 qCDebug(orgKdeElisaPlayer()) << "ManageAudioPlayer::playerSeek" << position;
443 Q_EMIT seek(position);
444 }
445
playListFinished()446 void ManageAudioPlayer::playListFinished()
447 {
448 mPlayingState = false;
449 }
450
tracksDataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight,const QVector<int> & roles)451 void ManageAudioPlayer::tracksDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
452 {
453 if (!mCurrentTrack.isValid()) {
454 return;
455 }
456
457 if (mCurrentTrack.row() > bottomRight.row() || mCurrentTrack.row() < topLeft.row()) {
458 return;
459 }
460
461 if (mCurrentTrack.column() > bottomRight.column() || mCurrentTrack.column() < topLeft.column()) {
462 return;
463 }
464
465 if (roles.isEmpty()) {
466 notifyPlayerSourceProperty();
467 restorePreviousState();
468 } else {
469 for(auto oneRole : roles) {
470 if (oneRole == mUrlRole) {
471 notifyPlayerSourceProperty();
472 restorePreviousState();
473 }
474 }
475 }
476 }
477
setTitleRole(int titleRole)478 void ManageAudioPlayer::setTitleRole(int titleRole)
479 {
480 if (mTitleRole == titleRole) {
481 return;
482 }
483
484 mTitleRole = titleRole;
485 Q_EMIT titleRoleChanged();
486
487 if (mCurrentTrack.isValid()) {
488 restorePreviousState();
489 }
490 }
491
setArtistNameRole(int artistNameRole)492 void ManageAudioPlayer::setArtistNameRole(int artistNameRole)
493 {
494 if (mArtistNameRole == artistNameRole) {
495 return;
496 }
497
498 mArtistNameRole = artistNameRole;
499 Q_EMIT artistNameRoleChanged();
500
501 if (mCurrentTrack.isValid()) {
502 restorePreviousState();
503 }
504 }
505
setAlbumNameRole(int albumNameRole)506 void ManageAudioPlayer::setAlbumNameRole(int albumNameRole)
507 {
508 if (mAlbumNameRole == albumNameRole) {
509 return;
510 }
511
512 mAlbumNameRole = albumNameRole;
513 Q_EMIT albumNameRoleChanged();
514
515 if (mCurrentTrack.isValid()) {
516 restorePreviousState();
517 }
518 }
519
notifyPlayerSourceProperty()520 void ManageAudioPlayer::notifyPlayerSourceProperty()
521 {
522 auto newUrlValue = mCurrentTrack.data(mUrlRole);
523 if (mSkippingCurrentTrack || mOldPlayerSource != newUrlValue) {
524 Q_EMIT playerSourceChanged(mCurrentTrack.data(mUrlRole).toUrl());
525
526 mOldPlayerSource = newUrlValue;
527 }
528 }
529
triggerPlay()530 void ManageAudioPlayer::triggerPlay()
531 {
532 QTimer::singleShot(0, this, [this]() {Q_EMIT playerPlay();});
533 }
534
triggerPause()535 void ManageAudioPlayer::triggerPause()
536 {
537 QTimer::singleShot(0, this, [this]() {Q_EMIT playerPause();});
538 }
539
triggerStop()540 void ManageAudioPlayer::triggerStop()
541 {
542 QTimer::singleShot(0, this, [this]() {Q_EMIT playerStop();});
543 }
544
triggerSkipNextTrack()545 void ManageAudioPlayer::triggerSkipNextTrack()
546 {
547 QTimer::singleShot(0, this, [this]() {Q_EMIT skipNextTrack();});
548 }
549
restorePreviousState()550 void ManageAudioPlayer::restorePreviousState()
551 {
552 if (mPersistentState.isEmpty()) {
553 return;
554 }
555
556 auto itTitle = mPersistentState.find(QStringLiteral("audioPlayerCurrentTitle"));
557 auto itArtistName = mPersistentState.find(QStringLiteral("audioPlayerCurrentArtistName"));
558 auto itAlbumName = mPersistentState.find(QStringLiteral("audioPlayerCurrentAlbumName"));
559
560 if (itTitle == mPersistentState.end() || itArtistName == mPersistentState.end() ||
561 itAlbumName == mPersistentState.end()) {
562 return;
563 }
564
565 if (*itTitle != mCurrentTrack.data(mTitleRole) ||
566 (itArtistName->isValid() && *itArtistName != mCurrentTrack.data(mArtistNameRole)) ||
567 (itAlbumName->isValid() && *itAlbumName != mCurrentTrack.data(mAlbumNameRole))) {
568 if (mCurrentTrack.isValid() && mCurrentTrack.data(mTitleRole).isValid() && mCurrentTrack.data(mArtistNameRole).isValid() &&
569 mCurrentTrack.data(mAlbumNameRole).isValid()) {
570 mPersistentState.clear();
571 }
572
573 return;
574 }
575
576 if (!mCurrentTrack.data(mUrlRole).toUrl().isValid()) {
577 return;
578 }
579
580 auto isPlaying = mPersistentState.find(QStringLiteral("isPlaying"));
581 auto currentConfiguration = Elisa::ElisaConfiguration::self();
582 bool autoPlay = currentConfiguration->playAtStartup();
583 if (autoPlay && isPlaying != mPersistentState.end() && mPlayingState != isPlaying->toBool()) {
584 mPlayingState = isPlaying->toBool();
585 }
586
587 auto playerPosition = mPersistentState.find(QStringLiteral("playerPosition"));
588 if (playerPosition != mPersistentState.end()) {
589 setPlayerPosition(playerPosition->toLongLong());
590 Q_EMIT seek(mPlayerPosition);
591 }
592 auto playerDuration = mPersistentState.find(QStringLiteral("playerDuration"));
593 if (playerDuration != mPersistentState.end()) {
594 setAudioDuration(playerDuration->toInt());
595 }
596
597 mPersistentState.clear();
598 }
599
600
601 #include "moc_manageaudioplayer.cpp"
602