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 &currentTrack)
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