1 #include <cmath>
2 #include "manager.h"
3 #include "mainwindow.h"
4 #include "mpvwidget.h"
5 #include "helpers.h"
6 
7 using namespace Helpers;
8 
9 
PlaybackManager(QObject * parent)10 PlaybackManager::PlaybackManager(QObject *parent) :
11     QObject(parent)
12 {
13 }
14 
setMpvObject(MpvObject * mpvObject,bool makeConnections)15 void PlaybackManager::setMpvObject(MpvObject *mpvObject, bool makeConnections)
16 {
17     mpvObject_ = mpvObject;
18 
19     if (makeConnections) {
20         connect(mpvObject, &MpvObject::playTimeChanged,
21                 this, &PlaybackManager::mpvw_playTimeChanged);
22         connect(mpvObject, &MpvObject::playLengthChanged,
23                 this, &PlaybackManager::mpvw_playLengthChanged);
24         connect(mpvObject, &MpvObject::seekableChanged,
25                 this, &PlaybackManager::mpvw_seekableChanged);
26         connect(mpvObject, &MpvObject::playbackLoading,
27                 this, &PlaybackManager::mpvw_playbackLoading);
28         connect(mpvObject, &MpvObject::playbackStarted,
29                 this, &PlaybackManager::mpvw_playbackStarted);
30         connect(mpvObject, &MpvObject::pausedChanged,
31                 this, &PlaybackManager::mpvw_pausedChanged);
32         connect(mpvObject, &MpvObject::playbackIdling,
33                 this, &PlaybackManager::mpvw_playbackIdling);
34         connect(mpvObject, &MpvObject::mediaTitleChanged,
35                 this, &PlaybackManager::mpvw_mediaTitleChanged);
36         connect(mpvObject, &MpvObject::chapterDataChanged,
37                 this, &PlaybackManager::mpvw_chapterDataChanged);
38         connect(mpvObject, &MpvObject::chaptersChanged,
39                 this, &PlaybackManager::mpvw_chaptersChanged);
40         connect(mpvObject, &MpvObject::tracksChanged,
41                 this, &PlaybackManager::mpvw_tracksChanged);
42         connect(mpvObject, &MpvObject::videoSizeChanged,
43                 this, &PlaybackManager::mpvw_videoSizeChanged);
44         connect(mpvObject, &MpvObject::fpsChanged,
45                 this, &PlaybackManager::mpvw_fpsChanged);
46         connect(mpvObject, &MpvObject::avsyncChanged,
47                 this, &PlaybackManager::mpvw_avsyncChanged);
48         connect(mpvObject, &MpvObject::displayFramedropsChanged,
49                 this, &PlaybackManager::mpvw_displayFramedropsChanged);
50         connect(mpvObject, &MpvObject::decoderFramedropsChanged,
51                 this, &PlaybackManager::mpvw_decoderFramedropsChanged);
52         connect(mpvObject, &MpvObject::metaDataChanged,
53                 this, &PlaybackManager::mpvw_metadataChanged);
54         connect(mpvObject, &MpvObject::playlistChanged,
55                 this, &PlaybackManager::mpvw_playlistChanged);
56         connect(mpvObject, &MpvObject::audioBitrateChanged,
57                 this, &PlaybackManager::mpvw_audioBitrateChanged);
58         connect(mpvObject, &MpvObject::videoBitrateChanged,
59                 this, &PlaybackManager::mpvw_videoBitrateChanged);
60 
61         connect(this, &PlaybackManager::hasNoVideo,
62                 mpvObject, &MpvObject::setDrawLogo);
63     }
64 }
65 
setPlaylistWindow(PlaylistWindow * playlistWindow)66 void PlaybackManager::setPlaylistWindow(PlaylistWindow *playlistWindow)
67 {
68     playlistWindow_ = playlistWindow;
69     connect(playlistWindow, &PlaylistWindow::itemDesired,
70             this, &PlaybackManager::playItem);
71     connect(this, &PlaybackManager::nowPlayingChanged,
72             playlistWindow, &PlaylistWindow::changePlaylistSelection);
73 }
74 
nowPlaying()75 QUrl PlaybackManager::nowPlaying()
76 {
77     return nowPlaying_;
78 }
79 
playbackState()80 PlaybackManager::PlaybackState PlaybackManager::playbackState()
81 {
82     return playbackState_;
83 }
84 
openSeveralFiles(QList<QUrl> what,bool important)85 void PlaybackManager::openSeveralFiles(QList<QUrl> what, bool important)
86 {
87     if (important) {
88         playlistWindow_->setCurrentPlaylist(QUuid());
89         playlistWindow_->clearPlaylist(QUuid());
90     }
91     bool playAfterAdd = (playlistWindow_->isCurrentPlaylistEmpty()
92                          && (important || nowPlayingItem == QUuid()))
93                         || !playlistWindow_->isVisible();
94     auto info = playlistWindow_->addToCurrentPlaylist(what);
95     if (playAfterAdd && !info.second.isNull()) {
96         QUrl urlToPlay = playlistWindow_->getUrlOf(info.first, info.second);
97         startPlayWithUuid(urlToPlay, info.first, info.second, false);
98     }
99 }
100 
openFile(QUrl what,QUrl with)101 void PlaybackManager::openFile(QUrl what, QUrl with)
102 {
103     auto info = playlistWindow_->urlToQuickPlaylist(what);
104     if (!info.second.isNull()) {
105         QUrl urlToPlay = playlistWindow_->getUrlOf(info.first, info.second);
106         startPlayWithUuid(urlToPlay, info.first, info.second, false, with);
107     }
108 }
109 
playDiscFiles(QUrl where)110 void PlaybackManager::playDiscFiles(QUrl where)
111 {
112     if (playbackState_ != StoppedState) {
113         playbackState_ = StoppedState;
114         emit stateChanged(playbackState_);
115         mpvObject_->stopPlayback();
116     }
117     mpvStartTime = -1.0;
118     mpvObject_->discFilesOpen(where.toLocalFile());
119     mpvObject_->setPaused(false);
120     playbackStartState = PlayingState;
121     nowPlayingItem = QUuid();
122     nowPlayingList = QUuid();
123     emit nowPlayingChanged(where, QUuid(), QUuid());
124 }
125 
playStream(QUrl stream)126 void PlaybackManager::playStream(QUrl stream)
127 {
128     openFile(stream);
129 }
130 
playItem(QUuid playlist,QUuid item)131 void PlaybackManager::playItem(QUuid playlist, QUuid item)
132 {
133     auto url = playlistWindow_->getUrlOf(playlist, item);
134     startPlayWithUuid(url, playlist, item, false);
135 }
136 
playDevice(QUrl device)137 void PlaybackManager::playDevice(QUrl device)
138 {
139     Q_UNUSED(device)
140     //FIXME: detect dvb dongles, and use a channel map (or make one?)
141 }
142 
loadSubtitle(QUrl with)143 void PlaybackManager::loadSubtitle(QUrl with)
144 {
145     QString f = with.isLocalFile() ? with.toLocalFile()
146                                    : with.fromPercentEncoding(with.toEncoded());
147     mpvObject_->addSubFile(f);
148 }
149 
playPlayer()150 void PlaybackManager::playPlayer()
151 {
152     unpausePlayer();
153     if (playbackState_ == StoppedState) {
154         startPlayer();
155     }
156 }
157 
startPlayer()158 void PlaybackManager::startPlayer()
159 {
160     if (!playlistWindow_->playActiveItem())
161         playlistWindow_->playCurrentItem();
162 }
163 
playPausePlayer()164 void PlaybackManager::playPausePlayer()
165 {
166     switch (playbackState_) {
167     case PausedState:
168         unpausePlayer();
169         break;
170     case StoppedState:
171         startPlayer();
172         break;
173     default:
174         pausePlayer();
175     }
176 }
177 
pausePlayer()178 void PlaybackManager::pausePlayer()
179 {
180     if (playbackState_ == PlayingState)
181         mpvObject_->setPaused(true);
182 }
183 
unpausePlayer()184 void PlaybackManager::unpausePlayer()
185 {
186     if (playbackState_ == PausedState)
187         mpvObject_->setPaused(false);
188 }
189 
stopPlayer()190 void PlaybackManager::stopPlayer()
191 {
192     nowPlayingItem = QUuid();
193     mpvObject_->stopPlayback();
194 }
195 
stepBackward()196 void PlaybackManager::stepBackward()
197 {
198     mpvObject_->stepBackward();
199 }
200 
stepForward()201 void PlaybackManager::stepForward()
202 {
203     mpvObject_->stepForward();
204 }
205 
navigateToNextChapter()206 void PlaybackManager::navigateToNextChapter()
207 {
208     int64_t nextChapter = mpvObject_->chapter() + 1;
209     if (nextChapter >= numChapters)
210         playNext();
211     else
212         navigateToChapter(nextChapter);
213 }
214 
navigateToPrevChapter()215 void PlaybackManager::navigateToPrevChapter()
216 {
217     int64_t chapter = mpvObject_->chapter();
218     if (chapter > 0)
219         navigateToChapter(std::max(int64_t(0), chapter - 1));
220     else
221         playPrev();
222 }
223 
playNext()224 void PlaybackManager::playNext()
225 {
226     if (folderFallback && playlistWindow_->isPlaylistSingularFile(nowPlayingList)) {
227         playNextFile();
228     } else {
229         playNextTrack();
230     }
231 }
232 
playPrev()233 void PlaybackManager::playPrev()
234 {
235     if (folderFallback && playlistWindow_->isPlaylistSingularFile(nowPlayingList)) {
236         playPrevFile();
237     } else {
238         playPrevTrack();
239     }
240 }
241 
repeatThisFile()242 void PlaybackManager::repeatThisFile()
243 {
244     startPlayWithUuid(nowPlaying_, nowPlayingList, nowPlayingItem, true);
245 }
246 
deltaExtraPlaytimes(int delta)247 void PlaybackManager::deltaExtraPlaytimes(int delta)
248 {
249     playlistWindow_->deltaExtraPlayTimes(nowPlayingList, nowPlayingItem, delta);
250 }
251 
navigateToChapter(int64_t chapter)252 void PlaybackManager::navigateToChapter(int64_t chapter)
253 {
254     if (!mpvObject_->setChapter(chapter)) {
255         // Out-of-bounds chapter navigation request. i.e. unseekable chapter
256         // from either past-the-end or invalid.  So stop playback and continue
257         // on the next via the playback finished slot.
258         mpvObject_->setPaused(false);
259         mpvObject_->stopPlayback();
260     }
261 }
262 
navigateToTime(double time)263 void PlaybackManager::navigateToTime(double time)
264 {
265     if (playbackState_ == WaitingState || playbackState_ == StoppedState)
266         mpvStartTime = time;
267     else
268         mpvObject_->setTime(time);
269 }
270 
speedUp()271 void PlaybackManager::speedUp()
272 {
273     double speed = speedStepAdditive ? mpvSpeed + speedStep - 1.0
274                                      : mpvSpeed * speedStep;
275     setPlaybackSpeed(std::min(8.0, speed));
276 }
277 
speedDown()278 void PlaybackManager::speedDown()
279 {
280     double speed = speedStepAdditive ? mpvSpeed - speedStep + 1.0
281                                      : mpvSpeed / speedStep;
282     setPlaybackSpeed(std::max(0.125, speed));
283 }
284 
speedReset()285 void PlaybackManager::speedReset()
286 {
287     setPlaybackSpeed(1.0);
288 }
289 
relativeSeek(bool forwards,bool isSmall)290 void PlaybackManager::relativeSeek(bool forwards, bool isSmall)
291 {
292     mpvObject_->seek((forwards ? 1.0 : -1.0) *
293                      (isSmall ? stepTimeSmall : stepTimeLarge), isSmall);
294 }
295 
setPlaybackSpeed(double speed)296 void PlaybackManager::setPlaybackSpeed(double speed)
297 {
298     mpvSpeed = speed;
299     mpvObject_->setSpeed(speed);
300     mpvObject_->showMessage(tr("Speed: %1%").arg(speed*100));
301 }
302 
setSpeedStep(double step)303 void PlaybackManager::setSpeedStep(double step)
304 {
305     speedStep = step;
306 }
307 
setSpeedStepAdditive(bool isAdditive)308 void PlaybackManager::setSpeedStepAdditive(bool isAdditive)
309 {
310     speedStepAdditive = isAdditive;
311 }
312 
setStepTimeLarge(int largeMsec)313 void PlaybackManager::setStepTimeLarge(int largeMsec)
314 {
315     stepTimeLarge = largeMsec / 1000.0;
316 }
317 
setStepTimeSmall(int smallMsec)318 void PlaybackManager::setStepTimeSmall(int smallMsec)
319 {
320     stepTimeSmall = smallMsec / 1000.0;
321 }
322 
findSecondById(QList<QPair<int64_t,QString>> list,int64_t id)323 static QString findSecondById(QList<QPair<int64_t,QString>> list, int64_t id) {
324     // this *should* return the string at id-1
325     for (int i = 0; i < list.count(); ++i)
326         if (list.value(i).first == id)
327             return list.value(i).second;
328     return QString();
329 }
330 
setAudioTrack(int64_t id)331 void PlaybackManager::setAudioTrack(int64_t id)
332 {
333     audioListSelected = findSecondById(audioList, id);
334     mpvObject_->setAudioTrack(id);
335 }
336 
setSubtitleTrack(int64_t id)337 void PlaybackManager::setSubtitleTrack(int64_t id)
338 {
339     subtitleListSelected = findSecondById(subtitleList, id);
340     subtitleTrackSelected = id;
341     updateSubtitleTrack();
342 }
343 
setVideoTrack(int64_t id)344 void PlaybackManager::setVideoTrack(int64_t id)
345 {
346     videoListSelected = findSecondById(videoList, id);
347     mpvObject_->setVideoTrack(id);
348 }
349 
setSubtitleEnabled(bool enabled)350 void PlaybackManager::setSubtitleEnabled(bool enabled)
351 {
352     subtitleEnabled = enabled;
353     updateSubtitleTrack();
354 }
355 
selectNextSubtitle()356 void PlaybackManager::selectNextSubtitle()
357 {
358     if (subtitleList.isEmpty())
359         return;
360     int64_t nextSubs = subtitleTrackSelected + 1;
361     if (nextSubs >= subtitleList.count())
362         nextSubs = 0;
363     setSubtitleTrack(nextSubs);
364 }
365 
selectPrevSubtitle()366 void PlaybackManager::selectPrevSubtitle()
367 {
368     if (subtitleList.isEmpty())
369         return;
370     int64_t previousSubs = subtitleTrackSelected - 1;
371     if (previousSubs < 0)
372         previousSubs = subtitleList.count() - 1;
373     setSubtitleTrack(previousSubs);
374 }
375 
setVolume(int64_t volume)376 void PlaybackManager::setVolume(int64_t volume)
377 {
378     static int64_t lastVol = -1;
379     if (lastVol == volume)
380         return;
381     lastVol = volume;
382     mpvObject_->setVolume(volume);
383     mpvObject_->showMessage(tr("Volume: %1%").arg(volume));
384 }
385 
setMute(bool muted)386 void PlaybackManager::setMute(bool muted)
387 {
388     mpvObject_->setMute(muted);
389     mpvObject_->showMessage(muted ? tr("Mute: on") : tr("Mute: off"));
390 }
391 
setAfterPlaybackOnce(AfterPlayback mode)392 void PlaybackManager::setAfterPlaybackOnce(AfterPlayback mode)
393 {
394     afterPlaybackOnce = mode;
395 }
396 
setAfterPlaybackAlways(AfterPlayback mode)397 void PlaybackManager::setAfterPlaybackAlways(AfterPlayback mode)
398 {
399     afterPlaybackAlways = mode;
400 }
401 
setSubtitlesPreferDefaultForced(bool forced)402 void PlaybackManager::setSubtitlesPreferDefaultForced(bool forced)
403 {
404     subtitlesPreferDefaultForced = forced;
405 }
406 
setSubtitlesPreferExternal(bool external)407 void PlaybackManager::setSubtitlesPreferExternal(bool external)
408 {
409     subtitlesPreferExternal = external;
410 }
411 
setSubtitlesIgnoreEmbedded(bool ignore)412 void PlaybackManager::setSubtitlesIgnoreEmbedded(bool ignore)
413 {
414     subtitlesIgnoreEmbedded = ignore;
415 }
416 
setPlaybackPlayTimes(int times)417 void PlaybackManager::setPlaybackPlayTimes(int times)
418 {
419     this->playbackPlayTimes = times > 1 ? times : 1;
420     this->playbackStartPaused = times < 1;
421 }
422 
setPlaybackForever(bool yes)423 void PlaybackManager::setPlaybackForever(bool yes)
424 {
425     this->playbackForever = yes;
426 }
427 
setFolderFallback(bool yes)428 void PlaybackManager::setFolderFallback(bool yes)
429 {
430     folderFallback = yes;
431 }
432 
sendCurrentTrackInfo()433 void PlaybackManager::sendCurrentTrackInfo()
434 {
435     QUrl url(playlistWindow_->getUrlOf(nowPlayingList, nowPlayingItem));
436     emit currentTrackInfo({url, nowPlayingList, nowPlayingItem,
437                            nowPlayingTitle, mpvLength, mpvTime});
438 }
439 
startPlayWithUuid(QUrl what,QUuid playlistUuid,QUuid itemUuid,bool isRepeating,QUrl with)440 void PlaybackManager::startPlayWithUuid(QUrl what, QUuid playlistUuid,
441                                         QUuid itemUuid, bool isRepeating,
442                                         QUrl with)
443 {
444     if (playbackState_ == WaitingState || what.isEmpty())
445         return;
446     emit stateChanged(playbackState_ = WaitingState);
447 
448     mpvStartTime = -1.0;
449     nowPlaying_ = what;
450     mpvObject_->fileOpen(what.isLocalFile() ? what.toLocalFile()
451                                             : what.fromPercentEncoding(what.toEncoded()));
452     mpvObject_->setSubFile(with.toString());
453     mpvObject_->setPaused(playbackStartPaused);
454     playbackStartState = playbackStartPaused ? PausedState : PlayingState;
455     nowPlayingList = playlistUuid;
456     nowPlayingItem = itemUuid;
457 
458     if (!isRepeating && playbackPlayTimes > 1
459             && playlistWindow_->extraPlayTimes(playlistUuid, itemUuid) <= 0) {
460         // On first play, when playing more than once, and when the extra
461         // play times has not been set, set the extra play times to the one
462         // configured in the settings dialog.
463         playlistWindow_->setExtraPlayTimes(playlistUuid, itemUuid, playbackPlayTimes - 1);
464     }
465     emit nowPlayingChanged(nowPlaying_, nowPlayingList, nowPlayingItem);
466 }
467 
selectDesiredTracks()468 void PlaybackManager::selectDesiredTracks()
469 {
470     // search current tracks by mangled string of no id and no spaces
471     auto mangle = [](QString s) {
472         return QStringList(s.split(' ').mid(1)).join("");
473     };
474     auto findIdBySecond = [&](QList<QPair<int64_t,QString>> list,
475                               QString needle) -> int64_t {
476         if (list.isEmpty() || (needle = mangle(needle)).isEmpty())
477             return -1;
478         for (int i = 0; i < list.count(); i++) {
479             if (mangle(list[i].second) == needle) {
480                 return list[i].first;
481             }
482         }
483         return -1;
484     };
485     auto findSubIdByPreference = [&](void) -> int64_t {
486         if (subtitlesPreferExternal) {
487             for (auto it = subtitleListData.constBegin();
488                  it != subtitleListData.constEnd(); it++) {
489                 if (it.value().value("external").toBool())
490                     return it.key();
491             }
492         }
493         if (subtitlesPreferDefaultForced) {
494             for (auto it = subtitleListData.constBegin();
495                  it != subtitleListData.constEnd(); it++)
496                 if (it.value().value("forced").toBool()
497                     || it.value().value("default").toBool())
498                     return it.key();
499         }
500         return -1;
501     };
502     int64_t videoId = findIdBySecond(videoList, videoListSelected);
503     int64_t audioId = findIdBySecond(audioList, audioListSelected);
504     int64_t subsId = findSubIdByPreference();
505     if (subsId < 0) subsId = findIdBySecond(subtitleList, subtitleListSelected);
506 
507     // Set detected tracks; if no preferred track from a list could be found,
508     // clear user selection
509     if (videoId >= 0)
510         setVideoTrack(videoId);
511     else if (!videoList.isEmpty())
512         videoListSelected.clear();
513     if (audioId >= 0)
514         setAudioTrack(audioId);
515     else if (!audioList.isEmpty())
516         audioListSelected.clear();
517     if (subsId >= 0)
518         setSubtitleTrack(subsId);
519     else if (!subtitleList.isEmpty()) {
520         subtitleListSelected.clear();
521         setSubtitleTrack(1);
522     }
523 }
524 
updateSubtitleTrack()525 void PlaybackManager::updateSubtitleTrack()
526 {
527     emit subtitlesVisibile(subtitleEnabled && subtitleTrackSelected != 0);
528     mpvObject_->setSubtitleTrack(subtitleEnabled ? subtitleTrackSelected : 0);
529 }
530 
checkAfterPlayback(bool playlistMode)531 void PlaybackManager::checkAfterPlayback(bool playlistMode)
532 {
533     Helpers::AfterPlayback action = afterPlaybackOnce;
534     if (afterPlaybackOnce == Helpers::DoNothingAfter)
535         action = afterPlaybackAlways;
536 
537     afterPlaybackOnce = Helpers::DoNothingAfter;
538     emit afterPlaybackReset();
539 
540     switch (action) {
541     case Helpers::ExitAfter:
542         emit instanceShouldClose();
543         break;
544     case Helpers::StandByAfter:
545         emit systemShouldStandby();
546         break;
547     case Helpers::HibernateAfter:
548         emit systemShouldHibernate();
549         break;
550     case Helpers::ShutdownAfter:
551         emit systemShouldShutdown();
552         break;
553     case Helpers::LogOffAfter:
554         emit systemShouldLogOff();
555         break;
556     case Helpers::LockAfter:
557         emit systemShouldLock();
558         break;
559     case Helpers::RepeatAfter:
560         if (playlistMode)
561             repeatThisFile();
562         break;
563     case Helpers::PlayNextAfter:
564         if (!playNextFileUrl(nowPlaying_))
565             playNextTrack();
566         break;
567     case Helpers::DoNothingAfter:
568         if (playlistMode)
569             playNext();
570     }
571 }
572 
playNextTrack()573 void PlaybackManager::playNextTrack()
574 {
575     QPair<QUuid, QUuid> next;
576     next = playlistWindow_->getItemAfter(nowPlayingList, nowPlayingItem);
577     QUrl url = playlistWindow_->getUrlOf(next.first, next.second);
578     if (url.isEmpty()) {
579         playHalt();
580         return;
581     }
582     startPlayWithUuid(url, next.first, next.second, false);
583 }
584 
playPrevTrack()585 void PlaybackManager::playPrevTrack()
586 {
587     QUuid uuid = playlistWindow_->getItemBefore(nowPlayingList, nowPlayingItem);
588     QUrl url = playlistWindow_->getUrlOf(nowPlayingList, uuid);
589     if (url.isEmpty()) {
590         playHalt();
591         return;
592     }
593     startPlayWithUuid(url, nowPlayingList, uuid, false);
594 }
595 
playNextFileUrl(QUrl url,int delta)596 bool PlaybackManager::playNextFileUrl(QUrl url, int delta)
597 {
598     QFileInfo info;
599     QDir dir;
600     QStringList files;
601     int index;
602     QString nextFile;
603 
604 
605     if (url.isEmpty())
606         return false;
607     info = QFileInfo(url.toLocalFile());
608     if (!info.exists())
609         return false;
610     dir = info.dir();
611     files = dir.entryList(QDir::Files, QDir::Name);
612     index = files.indexOf(info.fileName());
613     if (index == -1)
614         return false;
615     do {
616         index += delta;
617         if (index < 0 || index >= files.count())
618             return false;
619         nextFile = dir.filePath(files.value(index));
620         url = QUrl::fromLocalFile(nextFile);
621     } while (!Helpers::urlSurvivesFilter(url));
622     playlistWindow_->replaceItem(nowPlayingList, nowPlayingItem, { url });
623     startPlayWithUuid(url, nowPlayingList, nowPlayingItem, false);
624     return true;
625 }
626 
playNextFile(int delta)627 void PlaybackManager::playNextFile(int delta)
628 {
629     QUrl url = playlistWindow_->getUrlOfFirst(nowPlayingList);
630     if (!playNextFileUrl(url, delta))
631         playHalt();
632 }
633 
playPrevFile()634 void PlaybackManager::playPrevFile()
635 {
636     playNextFile(-1);
637 }
638 
playHalt()639 void PlaybackManager::playHalt()
640 {
641     mpvObject_->stopPlayback();
642     nowPlaying_.clear();
643     nowPlayingItem = QUuid();
644     playbackState_ = StoppedState;
645     emit stateChanged(playbackState_);
646 }
647 
mpvw_playTimeChanged(double time)648 void PlaybackManager::mpvw_playTimeChanged(double time)
649 {
650     // in case the duration property is not available, update the play length
651     // to indicate that the time is in fact available.
652     if (mpvLength < time)
653         mpvLength = time;
654     mpvTime = time;
655     emit timeChanged(time, mpvLength);
656 }
657 
mpvw_playLengthChanged(double length)658 void PlaybackManager::mpvw_playLengthChanged(double length)
659 {
660     mpvLength = length;
661 }
662 
mpvw_seekableChanged(bool yes)663 void PlaybackManager::mpvw_seekableChanged(bool yes)
664 {
665     if (yes && mpvStartTime > 0) {
666         mpvObject_->setTimeSync(mpvStartTime);
667         mpvStartTime = -1;
668     }
669 }
670 
mpvw_playbackLoading()671 void PlaybackManager::mpvw_playbackLoading()
672 {
673     playbackState_ = BufferingState;
674     emit stateChanged(playbackState_);
675 }
676 
mpvw_playbackStarted()677 void PlaybackManager::mpvw_playbackStarted()
678 {
679     playbackState_ = playbackStartState;
680     emit stateChanged(playbackState_);
681     emit playerSettingsRequested();
682 }
683 
mpvw_pausedChanged(bool yes)684 void PlaybackManager::mpvw_pausedChanged(bool yes)
685 {
686     if (playbackState_ == StoppedState)
687         return;
688 
689     playbackState_ = yes ? PausedState : PlayingState;
690     emit stateChanged(playbackState_);
691 }
692 
mpvw_playbackIdling()693 void PlaybackManager::mpvw_playbackIdling()
694 {
695     if (nowPlayingItem.isNull()) {
696         nowPlaying_.clear();
697         playbackState_ = StoppedState;
698         emit stateChanged(playbackState_);
699         checkAfterPlayback(false);
700         return;
701     }
702 
703     int extraTimes = playlistWindow_->extraPlayTimes(nowPlayingList, nowPlayingItem);
704     playlistWindow_->setExtraPlayTimes(nowPlayingList, nowPlayingItem, extraTimes - 1);
705 
706     bool isRepeating = playbackForever || extraTimes > 0;
707     if (isRepeating)
708         repeatThisFile();
709     else
710         checkAfterPlayback(true);
711 }
712 
mpvw_mediaTitleChanged(QString title)713 void PlaybackManager::mpvw_mediaTitleChanged(QString title)
714 {
715     nowPlayingTitle = title;
716     emit titleChanged(title);
717 }
718 
mpvw_chapterDataChanged(QVariantMap metadata)719 void PlaybackManager::mpvw_chapterDataChanged(QVariantMap metadata)
720 {
721     emit chapterTitleChanged(metadata.value("title").toString());
722 }
723 
mpvw_chaptersChanged(QVariantList chapters)724 void PlaybackManager::mpvw_chaptersChanged(QVariantList chapters)
725 {
726     QList<QPair<double,QString>> list;
727     for (QVariant &v : chapters) {
728         QMap<QString, QVariant> node = v.toMap();
729         QString text = QString("[%1] - %2").arg(
730                 toDateFormat(node["time"].toDouble()),
731                 node["title"].toString());
732         QPair<double,QString> item(node["time"].toDouble(), text);
733         list.append(item);
734     }
735     numChapters = list.count();
736     emit chaptersAvailable(list);
737 }
738 
mpvw_tracksChanged(QVariantList tracks)739 void PlaybackManager::mpvw_tracksChanged(QVariantList tracks)
740 {
741     videoList.clear();
742     audioList.clear();
743     subtitleList.clear();
744     subtitleListData.clear();
745     QPair<int64_t,QString> item;
746 
747     auto str = [](QVariantMap map, QString key) {
748         return map[key].toString();
749     };
750     auto formatter = [&str](QVariantMap track) {
751         QString output;
752         output.append(QString("%1: ").arg(str(track,"id")));
753         if (track.contains("codec"))
754             output.append(QString("[%1] ").arg(str(track,"codec")));
755         if (track.contains("lang"))
756             output.append(QString("%1 ").arg(str(track,"lang")));
757         if (track.contains("title"))
758             output.append(QString("- %1 ").arg(str(track,"title")));
759         return output;
760     };
761 
762     for (QVariant &track : tracks) {
763         QVariantMap t = track.toMap();
764         item.first = t["id"].toLongLong();
765         item.second = formatter(t);
766         if (str(t,"type") == "video") {
767             videoList.append(item);
768         } else if (str(t,"type") == "audio") {
769             audioList.append(item);
770         } else if (str(t,"type") == "sub") {
771             if (!subtitlesIgnoreEmbedded || t.value("external").toBool()) {
772                 subtitleList.append(item);
773                 subtitleListData.insert(item.first, t);
774             }
775         }
776     }
777     if (!subtitleList.isEmpty()) {
778         subtitleList.append({0, tr("0: None")});
779     }
780 
781     emit videoTracksAvailable(videoList);
782     emit audioTracksAvailable(audioList);
783     emit subtitleTracksAvailable(subtitleList);
784 
785     selectDesiredTracks();
786 
787     emit hasNoVideo(videoList.empty());
788     emit hasNoAudio(audioList.empty());
789     emit hasNoSubtitles(subtitleList.empty());
790 }
791 
mpvw_videoSizeChanged(QSize size)792 void PlaybackManager::mpvw_videoSizeChanged(QSize size)
793 {
794     emit videoSizeChanged(size);
795 }
796 
mpvw_fpsChanged(double fps)797 void PlaybackManager::mpvw_fpsChanged(double fps)
798 {
799     emit fpsChanged(fps);
800 }
801 
mpvw_avsyncChanged(double sync)802 void PlaybackManager::mpvw_avsyncChanged(double sync)
803 {
804     emit avsyncChanged(sync);
805 }
806 
mpvw_displayFramedropsChanged(int64_t count)807 void PlaybackManager::mpvw_displayFramedropsChanged(int64_t count)
808 {
809     emit displayFramedropsChanged(count);
810 }
811 
mpvw_decoderFramedropsChanged(int64_t count)812 void PlaybackManager::mpvw_decoderFramedropsChanged(int64_t count)
813 {
814     emit decoderFramedropsChanged(count);
815 }
816 
mpvw_audioBitrateChanged(double bitrate)817 void PlaybackManager::mpvw_audioBitrateChanged(double bitrate)
818 {
819     emit audioBitrateChanged(bitrate);
820 }
821 
mpvw_videoBitrateChanged(double bitrate)822 void PlaybackManager::mpvw_videoBitrateChanged(double bitrate)
823 {
824     emit videoBitrateChanged(bitrate);
825 }
826 
mpvw_metadataChanged(QVariantMap metadata)827 void PlaybackManager::mpvw_metadataChanged(QVariantMap metadata)
828 {
829     playlistWindow_->setMetadata(nowPlayingList, nowPlayingItem, metadata);
830 }
831 
mpvw_playlistChanged(const QVariantList & playlist)832 void PlaybackManager::mpvw_playlistChanged(const QVariantList &playlist)
833 {
834     // replace current item with whatever we got, and trigger its playback
835     QList<QUrl> urls;
836     for (auto i : playlist)
837         urls.append(QUrl::fromUserInput(i.toMap()["filename"].toString()));
838     playlistWindow_->replaceItem(nowPlayingList, nowPlayingItem, urls);
839     playItem(nowPlayingList, nowPlayingItem);
840 }
841