Merge pull request #22634 from CastagnaIT/webvtt_overllaped_segment
[xbmc.git] / xbmc / PlayListPlayer.cpp
blobb86e1cfbdfea8b223b8815aff3127689377079b1
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "PlayListPlayer.h"
11 #include "FileItem.h"
12 #include "GUIUserMessages.h"
13 #include "PartyModeManager.h"
14 #include "ServiceBroker.h"
15 #include "URL.h"
16 #include "application/Application.h"
17 #include "application/ApplicationComponents.h"
18 #include "application/ApplicationPlayer.h"
19 #include "application/ApplicationPowerHandling.h"
20 #include "dialogs/GUIDialogKaiToast.h"
21 #include "filesystem/PluginDirectory.h"
22 #include "filesystem/VideoDatabaseFile.h"
23 #include "guilib/GUIComponent.h"
24 #include "guilib/GUIWindowManager.h"
25 #include "guilib/LocalizeStrings.h"
26 #include "input/actions/Action.h"
27 #include "input/actions/ActionIDs.h"
28 #include "interfaces/AnnouncementManager.h"
29 #include "messaging/ApplicationMessenger.h"
30 #include "messaging/helpers/DialogOKHelper.h"
31 #include "music/tags/MusicInfoTag.h"
32 #include "playlists/PlayList.h"
33 #include "settings/AdvancedSettings.h"
34 #include "settings/SettingsComponent.h"
35 #include "utils/StringUtils.h"
36 #include "utils/URIUtils.h"
37 #include "utils/Variant.h"
38 #include "utils/log.h"
39 #include "video/VideoDatabase.h"
41 using namespace PLAYLIST;
42 using namespace KODI::MESSAGING;
44 CPlayListPlayer::CPlayListPlayer(void)
46 m_PlaylistMusic = new CPlayList(TYPE_MUSIC);
47 m_PlaylistVideo = new CPlayList(TYPE_VIDEO);
48 m_PlaylistEmpty = new CPlayList;
49 m_iCurrentSong = -1;
50 m_bPlayedFirstFile = false;
51 m_bPlaybackStarted = false;
52 m_iFailedSongs = 0;
53 m_failedSongsStart = std::chrono::steady_clock::now();
56 CPlayListPlayer::~CPlayListPlayer(void)
58 Clear();
59 delete m_PlaylistMusic;
60 delete m_PlaylistVideo;
61 delete m_PlaylistEmpty;
64 bool CPlayListPlayer::OnAction(const CAction &action)
66 if (action.GetID() == ACTION_PREV_ITEM && !IsSingleItemNonRepeatPlaylist())
68 PlayPrevious();
69 return true;
71 else if (action.GetID() == ACTION_NEXT_ITEM && !IsSingleItemNonRepeatPlaylist())
73 PlayNext();
74 return true;
76 else
77 return false;
80 bool CPlayListPlayer::OnMessage(CGUIMessage &message)
82 switch (message.GetMessage())
84 case GUI_MSG_NOTIFY_ALL:
85 if (message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem())
87 // update the items in our playlist(s) if necessary
88 for (Id playlistId : {TYPE_MUSIC, TYPE_VIDEO})
90 CPlayList& playlist = GetPlaylist(playlistId);
91 CFileItemPtr item = std::static_pointer_cast<CFileItem>(message.GetItem());
92 playlist.UpdateItem(item.get());
95 break;
96 case GUI_MSG_PLAYBACK_STOPPED:
98 if (m_iCurrentPlayList != TYPE_NONE && m_bPlaybackStarted)
100 CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, m_iCurrentPlayList, m_iCurrentSong);
101 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
102 Reset();
103 m_iCurrentPlayList = TYPE_NONE;
104 return true;
107 break;
108 case GUI_MSG_PLAYBACK_STARTED:
110 m_bPlaybackStarted = true;
112 break;
115 return false;
118 int CPlayListPlayer::GetNextSong(int offset) const
120 if (m_iCurrentPlayList == TYPE_NONE)
121 return -1;
123 const CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
124 if (playlist.size() <= 0)
125 return -1;
127 int song = m_iCurrentSong;
129 // party mode
130 if (g_partyModeManager.IsEnabled() && GetCurrentPlaylist() == TYPE_MUSIC)
131 return song + offset;
133 // wrap around in the case of repeating
134 if (RepeatedOne(m_iCurrentPlayList))
135 return song;
137 song += offset;
138 if (song >= playlist.size() && Repeated(m_iCurrentPlayList))
139 song %= playlist.size();
141 return song;
144 int CPlayListPlayer::GetNextSong()
146 if (m_iCurrentPlayList == TYPE_NONE)
147 return -1;
148 CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
149 if (playlist.size() <= 0)
150 return -1;
151 int iSong = m_iCurrentSong;
153 // party mode
154 if (g_partyModeManager.IsEnabled() && GetCurrentPlaylist() == TYPE_MUSIC)
155 return iSong + 1;
157 // if repeat one, keep playing the current song if its valid
158 if (RepeatedOne(m_iCurrentPlayList))
160 // otherwise immediately abort playback
161 if (m_iCurrentSong >= 0 && m_iCurrentSong < playlist.size() && playlist[m_iCurrentSong]->GetProperty("unplayable").asBoolean())
163 CLog::Log(LOGERROR, "Playlist Player: RepeatOne stuck on unplayable item: {}, path [{}]",
164 m_iCurrentSong, playlist[m_iCurrentSong]->GetPath());
165 CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, m_iCurrentPlayList, m_iCurrentSong);
166 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
167 Reset();
168 m_iCurrentPlayList = TYPE_NONE;
169 return -1;
171 return iSong;
174 // if we've gone beyond the playlist and repeat all is enabled,
175 // then we clear played status and wrap around
176 iSong++;
177 if (iSong >= playlist.size() && Repeated(m_iCurrentPlayList))
178 iSong = 0;
180 return iSong;
183 bool CPlayListPlayer::PlayNext(int offset, bool bAutoPlay)
185 int iSong = GetNextSong(offset);
186 const CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
188 if ((iSong < 0) || (iSong >= playlist.size()) || (playlist.GetPlayable() <= 0))
190 if(!bAutoPlay)
191 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559), g_localizeStrings.Get(34201));
193 CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, m_iCurrentPlayList, m_iCurrentSong);
194 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
195 Reset();
196 m_iCurrentPlayList = TYPE_NONE;
197 return false;
200 return Play(iSong, "", false);
203 bool CPlayListPlayer::PlayPrevious()
205 if (m_iCurrentPlayList == TYPE_NONE)
206 return false;
208 const CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
209 int iSong = m_iCurrentSong;
211 if (!RepeatedOne(m_iCurrentPlayList))
212 iSong--;
214 if (iSong < 0 && Repeated(m_iCurrentPlayList))
215 iSong = playlist.size() - 1;
217 if (iSong < 0 || playlist.size() <= 0)
219 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559), g_localizeStrings.Get(34202));
220 return false;
223 return Play(iSong, "", false, true);
226 bool CPlayListPlayer::IsSingleItemNonRepeatPlaylist() const
228 const CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
229 return (playlist.size() <= 1 && !RepeatedOne(m_iCurrentPlayList) && !Repeated(m_iCurrentPlayList));
232 bool CPlayListPlayer::Play()
234 if (m_iCurrentPlayList == TYPE_NONE)
235 return false;
237 const CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
238 if (playlist.size() <= 0)
239 return false;
241 return Play(0, "");
244 bool CPlayListPlayer::PlaySongId(int songId)
246 if (m_iCurrentPlayList == TYPE_NONE)
247 return false;
249 CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
250 if (playlist.size() <= 0)
251 return Play();
253 for (int i = 0; i < playlist.size(); i++)
255 if (playlist[i]->HasMusicInfoTag() && playlist[i]->GetMusicInfoTag()->GetDatabaseId() == songId)
256 return Play(i, "");
258 return Play();
261 bool CPlayListPlayer::Play(const CFileItemPtr& pItem, const std::string& player)
263 Id playlistId;
264 bool isVideo{pItem->IsVideo()};
265 bool isAudio{pItem->IsAudio()};
267 if (isAudio && !isVideo)
268 playlistId = TYPE_MUSIC;
269 else if (isVideo && !isAudio)
270 playlistId = TYPE_VIDEO;
271 else if (pItem->HasProperty("playlist_type_hint"))
273 // There are two main cases that can fall here:
274 // - If an extension is set on both audio / video extension lists example .strm
275 // see GetFileExtensionProvider() -> GetVideoExtensions() / GetAudioExtensions()
276 // When you play the .strm containing single path, cause that
277 // IsVideo() and IsAudio() methods both return true
279 // - When you play a playlist (e.g. .m3u / .strm) containing multiple paths,
280 // and the path played is generic (e.g.without extension) and have no properties
281 // to detect the media type, IsVideo() / IsAudio() both return false
283 // for these cases the type is unknown so we rely on the hint
284 playlistId = pItem->GetProperty("playlist_type_hint").asInteger32(TYPE_NONE);
286 else
288 CLog::LogF(LOGWARNING, "ListItem type must be audio or video type. The type can be specified "
289 "by using ListItem::getVideoInfoTag or ListItem::getMusicInfoTag, in "
290 "the case of playlist entries by adding #KODIPROP mimetype value.");
291 return false;
294 ClearPlaylist(playlistId);
295 Reset();
296 SetCurrentPlaylist(playlistId);
297 Add(playlistId, pItem);
299 return Play(0, player);
302 bool CPlayListPlayer::Play(int iSong,
303 const std::string& player,
304 bool bAutoPlay /* = false */,
305 bool bPlayPrevious /* = false */)
307 if (m_iCurrentPlayList == TYPE_NONE)
308 return false;
310 CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
311 if (playlist.size() <= 0)
312 return false;
313 if (iSong < 0)
314 iSong = 0;
315 if (iSong >= playlist.size())
316 iSong = playlist.size() - 1;
318 // check if the item itself is a playlist, and can be expanded
319 // only allow a few levels, this could end up in a loop
320 // if they refer to each other in a loop
321 for (int i=0; i<5; i++)
323 if(!playlist.Expand(iSong))
324 break;
327 m_iCurrentSong = iSong;
328 CFileItemPtr item = playlist[m_iCurrentSong];
329 if (item->IsVideoDb() && !item->HasVideoInfoTag())
330 *(item->GetVideoInfoTag()) = XFILE::CVideoDatabaseFile::GetVideoTag(CURL(item->GetDynPath()));
332 playlist.SetPlayed(true);
334 m_bPlaybackStarted = false;
336 const auto playAttempt = std::chrono::steady_clock::now();
337 bool ret = g_application.PlayFile(*item, player, bAutoPlay);
338 if (!ret)
340 CLog::Log(LOGERROR, "Playlist Player: skipping unplayable item: {}, path [{}]", m_iCurrentSong,
341 CURL::GetRedacted(item->GetDynPath()));
342 playlist.SetUnPlayable(m_iCurrentSong);
344 // abort on 100 failed CONSECUTIVE songs
345 if (!m_iFailedSongs)
346 m_failedSongsStart = playAttempt;
347 m_iFailedSongs++;
348 const std::shared_ptr<CAdvancedSettings> advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
350 auto now = std::chrono::steady_clock::now();
351 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_failedSongsStart);
353 if ((m_iFailedSongs >= advancedSettings->m_playlistRetries &&
354 advancedSettings->m_playlistRetries >= 0) ||
355 ((duration.count() >=
356 static_cast<unsigned int>(advancedSettings->m_playlistTimeout) * 1000) &&
357 advancedSettings->m_playlistTimeout))
359 CLog::Log(LOGDEBUG,"Playlist Player: one or more items failed to play... aborting playback");
361 // open error dialog
362 HELPERS::ShowOKDialogText(CVariant{16026}, CVariant{16027});
364 CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, m_iCurrentPlayList, m_iCurrentSong);
365 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
366 Reset();
367 GetPlaylist(m_iCurrentPlayList).Clear();
368 m_iCurrentPlayList = TYPE_NONE;
369 m_iFailedSongs = 0;
370 m_failedSongsStart = std::chrono::steady_clock::now();
371 return false;
374 // how many playable items are in the playlist?
375 if (playlist.GetPlayable() > 0)
377 return bPlayPrevious ? PlayPrevious() : PlayNext();
379 // none? then abort playback
380 else
382 CLog::Log(LOGDEBUG,"Playlist Player: no more playable items... aborting playback");
383 CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, m_iCurrentPlayList, m_iCurrentSong);
384 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
385 Reset();
386 m_iCurrentPlayList = TYPE_NONE;
387 return false;
391 // reset the start offset of this item
392 if (item->GetStartOffset() == STARTOFFSET_RESUME)
393 item->SetStartOffset(0);
395 //! @todo - move the above failure logic and the below success logic
396 //! to callbacks instead so we don't rely on the return value
397 //! of PlayFile()
399 // consecutive error counter so reset if the current item is playing
400 m_iFailedSongs = 0;
401 m_failedSongsStart = std::chrono::steady_clock::now();
402 m_bPlayedFirstFile = true;
403 return true;
406 void CPlayListPlayer::SetCurrentSong(int iSong)
408 if (iSong >= -1 && iSong < GetPlaylist(m_iCurrentPlayList).size())
409 m_iCurrentSong = iSong;
412 int CPlayListPlayer::GetCurrentSong() const
414 return m_iCurrentSong;
417 Id CPlayListPlayer::GetCurrentPlaylist() const
419 return m_iCurrentPlayList;
422 void CPlayListPlayer::SetCurrentPlaylist(Id playlistId)
424 if (playlistId == m_iCurrentPlayList)
425 return;
427 // changing the current playlist while party mode is on
428 // disables party mode
429 if (g_partyModeManager.IsEnabled())
430 g_partyModeManager.Disable();
432 m_iCurrentPlayList = playlistId;
433 m_bPlayedFirstFile = false;
436 void CPlayListPlayer::ClearPlaylist(Id playlistId)
438 // clear our applications playlist file
439 g_application.m_strPlayListFile.clear();
441 CPlayList& playlist = GetPlaylist(playlistId);
442 playlist.Clear();
444 // its likely that the playlist changed
445 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
446 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
449 CPlayList& CPlayListPlayer::GetPlaylist(Id playlistId)
451 switch (playlistId)
453 case TYPE_MUSIC:
454 return *m_PlaylistMusic;
455 break;
456 case TYPE_VIDEO:
457 return *m_PlaylistVideo;
458 break;
459 default:
460 m_PlaylistEmpty->Clear();
461 return *m_PlaylistEmpty;
462 break;
466 const CPlayList& CPlayListPlayer::GetPlaylist(Id playlistId) const
468 switch (playlistId)
470 case TYPE_MUSIC:
471 return *m_PlaylistMusic;
472 break;
473 case TYPE_VIDEO:
474 return *m_PlaylistVideo;
475 break;
476 default:
477 // NOTE: This playlist may not be empty if the caller of the non-const version alters it!
478 return *m_PlaylistEmpty;
479 break;
483 int CPlayListPlayer::RemoveDVDItems()
485 int nRemovedM = m_PlaylistMusic->RemoveDVDItems();
486 int nRemovedV = m_PlaylistVideo->RemoveDVDItems();
488 return nRemovedM + nRemovedV;
491 void CPlayListPlayer::Reset()
493 m_iCurrentSong = -1;
494 m_bPlayedFirstFile = false;
495 m_bPlaybackStarted = false;
497 // its likely that the playlist changed
498 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
499 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
502 bool CPlayListPlayer::HasPlayedFirstFile() const
504 return m_bPlayedFirstFile;
507 bool CPlayListPlayer::Repeated(Id playlistId) const
509 const auto repStatePos = m_repeatState.find(playlistId);
510 if (repStatePos != m_repeatState.end())
511 return repStatePos->second == RepeatState::ALL;
512 return false;
515 bool CPlayListPlayer::RepeatedOne(Id playlistId) const
517 const auto repStatePos = m_repeatState.find(playlistId);
518 if (repStatePos != m_repeatState.end())
519 return (repStatePos->second == RepeatState::ONE);
520 return false;
523 void CPlayListPlayer::SetShuffle(Id playlistId, bool bYesNo, bool bNotify /* = false */)
525 if (playlistId != TYPE_MUSIC && playlistId != TYPE_VIDEO)
526 return;
528 // disable shuffle in party mode
529 if (g_partyModeManager.IsEnabled() && playlistId == TYPE_MUSIC)
530 return;
532 // do we even need to do anything?
533 if (bYesNo != IsShuffled(playlistId))
535 // save the order value of the current song so we can use it find its new location later
536 int iOrder = -1;
537 CPlayList& playlist = GetPlaylist(playlistId);
538 if (m_iCurrentSong >= 0 && m_iCurrentSong < playlist.size())
539 iOrder = playlist[m_iCurrentSong]->m_iprogramCount;
541 // shuffle or unshuffle as necessary
542 if (bYesNo)
543 playlist.Shuffle();
544 else
545 playlist.UnShuffle();
547 if (bNotify)
549 std::string shuffleStr =
550 StringUtils::Format("{}: {}", g_localizeStrings.Get(191),
551 g_localizeStrings.Get(bYesNo ? 593 : 591)); // Shuffle: All/Off
552 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559), shuffleStr);
555 // find the previous order value and fix the current song marker
556 if (iOrder >= 0)
558 int iIndex = playlist.FindOrder(iOrder);
559 if (iIndex >= 0)
560 m_iCurrentSong = iIndex;
561 // if iIndex < 0, something unexpected happened
562 // so dont do anything
566 // its likely that the playlist changed
567 if (CServiceBroker::GetGUI() != nullptr)
569 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
570 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
573 AnnouncePropertyChanged(playlistId, "shuffled", IsShuffled(playlistId));
576 bool CPlayListPlayer::IsShuffled(Id playlistId) const
578 // even if shuffled, party mode says its not
579 if (g_partyModeManager.IsEnabled() && playlistId == TYPE_MUSIC)
580 return false;
582 if (playlistId == TYPE_MUSIC || playlistId == TYPE_VIDEO)
583 return GetPlaylist(playlistId).IsShuffled();
585 return false;
588 void CPlayListPlayer::SetRepeat(Id playlistId, RepeatState state, bool bNotify /* = false */)
590 if (playlistId != TYPE_MUSIC && playlistId != TYPE_VIDEO)
591 return;
593 // disable repeat in party mode
594 if (g_partyModeManager.IsEnabled() && playlistId == TYPE_MUSIC)
595 state = RepeatState::NONE;
597 // notify the user if there was a change in the repeat state
598 if (m_repeatState[playlistId] != state && bNotify)
600 int iLocalizedString;
601 if (state == RepeatState::NONE)
602 iLocalizedString = 595; // Repeat: Off
603 else if (state == RepeatState::ONE)
604 iLocalizedString = 596; // Repeat: One
605 else
606 iLocalizedString = 597; // Repeat: All
607 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559), g_localizeStrings.Get(iLocalizedString));
610 m_repeatState[playlistId] = state;
612 CVariant data;
613 switch (state)
615 case RepeatState::ONE:
616 data = "one";
617 break;
618 case RepeatState::ALL:
619 data = "all";
620 break;
621 default:
622 data = "off";
623 break;
626 // its likely that the playlist changed
627 if (CServiceBroker::GetGUI() != nullptr)
629 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
630 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
633 AnnouncePropertyChanged(playlistId, "repeat", data);
636 RepeatState CPlayListPlayer::GetRepeat(Id playlistId) const
638 const auto repStatePos = m_repeatState.find(playlistId);
639 if (repStatePos != m_repeatState.end())
640 return repStatePos->second;
641 return RepeatState::NONE;
644 void CPlayListPlayer::ReShuffle(Id playlistId, int iPosition)
646 // playlist has not played yet so shuffle the entire list
647 // (this only really works for new video playlists)
648 if (!GetPlaylist(playlistId).WasPlayed())
650 GetPlaylist(playlistId).Shuffle();
652 // we're trying to shuffle new items into the currently playing playlist
653 // so we shuffle starting at two positions below the current item
654 else if (playlistId == m_iCurrentPlayList)
656 const auto& components = CServiceBroker::GetAppComponents();
657 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
658 if ((appPlayer->IsPlayingAudio() && playlistId == TYPE_MUSIC) ||
659 (appPlayer->IsPlayingVideo() && playlistId == TYPE_VIDEO))
661 GetPlaylist(playlistId).Shuffle(m_iCurrentSong + 2);
664 // otherwise, shuffle from the passed position
665 // which is the position of the first new item added
666 else
668 GetPlaylist(playlistId).Shuffle(iPosition);
672 void CPlayListPlayer::Add(Id playlistId, const CPlayList& playlist)
674 if (playlistId != TYPE_MUSIC && playlistId != TYPE_VIDEO)
675 return;
676 CPlayList& list = GetPlaylist(playlistId);
677 int iSize = list.size();
678 list.Add(playlist);
679 if (list.IsShuffled())
680 ReShuffle(playlistId, iSize);
683 void CPlayListPlayer::Add(Id playlistId, const CFileItemPtr& pItem)
685 if (playlistId != TYPE_MUSIC && playlistId != TYPE_VIDEO)
686 return;
687 CPlayList& list = GetPlaylist(playlistId);
688 int iSize = list.size();
689 list.Add(pItem);
690 if (list.IsShuffled())
691 ReShuffle(playlistId, iSize);
694 void CPlayListPlayer::Add(Id playlistId, const CFileItemList& items)
696 if (playlistId != TYPE_MUSIC && playlistId != TYPE_VIDEO)
697 return;
698 CPlayList& list = GetPlaylist(playlistId);
699 int iSize = list.size();
700 list.Add(items);
701 if (list.IsShuffled())
702 ReShuffle(playlistId, iSize);
704 // its likely that the playlist changed
705 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
706 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
709 void CPlayListPlayer::Insert(Id playlistId, const CPlayList& playlist, int iIndex)
711 if (playlistId != TYPE_MUSIC && playlistId != TYPE_VIDEO)
712 return;
713 CPlayList& list = GetPlaylist(playlistId);
714 int iSize = list.size();
715 list.Insert(playlist, iIndex);
716 if (list.IsShuffled())
717 ReShuffle(playlistId, iSize);
718 else if (m_iCurrentPlayList == playlistId && m_iCurrentSong >= iIndex)
719 m_iCurrentSong++;
722 void CPlayListPlayer::Insert(Id playlistId, const CFileItemPtr& pItem, int iIndex)
724 if (playlistId != TYPE_MUSIC && playlistId != TYPE_VIDEO)
725 return;
726 CPlayList& list = GetPlaylist(playlistId);
727 int iSize = list.size();
728 list.Insert(pItem, iIndex);
729 if (list.IsShuffled())
730 ReShuffle(playlistId, iSize);
731 else if (m_iCurrentPlayList == playlistId && m_iCurrentSong >= iIndex)
732 m_iCurrentSong++;
735 void CPlayListPlayer::Insert(Id playlistId, const CFileItemList& items, int iIndex)
737 if (playlistId != TYPE_MUSIC && playlistId != TYPE_VIDEO)
738 return;
739 CPlayList& list = GetPlaylist(playlistId);
740 int iSize = list.size();
741 list.Insert(items, iIndex);
742 if (list.IsShuffled())
743 ReShuffle(playlistId, iSize);
744 else if (m_iCurrentPlayList == playlistId && m_iCurrentSong >= iIndex)
745 m_iCurrentSong++;
747 // its likely that the playlist changed
748 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
749 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
752 void CPlayListPlayer::Remove(Id playlistId, int iPosition)
754 if (playlistId != TYPE_MUSIC && playlistId != TYPE_VIDEO)
755 return;
756 CPlayList& list = GetPlaylist(playlistId);
757 list.Remove(iPosition);
758 if (m_iCurrentPlayList == playlistId && m_iCurrentSong >= iPosition)
759 m_iCurrentSong--;
761 // its likely that the playlist changed
762 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
763 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
766 void CPlayListPlayer::Clear()
768 if (m_PlaylistMusic)
769 m_PlaylistMusic->Clear();
770 if (m_PlaylistVideo)
771 m_PlaylistVideo->Clear();
772 if (m_PlaylistEmpty)
773 m_PlaylistEmpty->Clear();
776 void CPlayListPlayer::Swap(Id playlistId, int indexItem1, int indexItem2)
778 if (playlistId != TYPE_MUSIC && playlistId != TYPE_VIDEO)
779 return;
781 CPlayList& list = GetPlaylist(playlistId);
782 if (list.Swap(indexItem1, indexItem2) && playlistId == m_iCurrentPlayList)
784 if (m_iCurrentSong == indexItem1)
785 m_iCurrentSong = indexItem2;
786 else if (m_iCurrentSong == indexItem2)
787 m_iCurrentSong = indexItem1;
790 // its likely that the playlist changed
791 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
792 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
795 void CPlayListPlayer::AnnouncePropertyChanged(Id playlistId,
796 const std::string& strProperty,
797 const CVariant& value)
799 const auto& components = CServiceBroker::GetAppComponents();
800 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
802 if (strProperty.empty() || value.isNull() ||
803 (playlistId == TYPE_VIDEO && !appPlayer->IsPlayingVideo()) ||
804 (playlistId == TYPE_MUSIC && !appPlayer->IsPlayingAudio()))
805 return;
807 CVariant data;
808 data["player"]["playerid"] = playlistId;
809 data["property"][strProperty] = value;
810 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnPropertyChanged",
811 data);
814 int PLAYLIST::CPlayListPlayer::GetMessageMask()
816 return TMSG_MASK_PLAYLISTPLAYER;
819 void PLAYLIST::CPlayListPlayer::OnApplicationMessage(KODI::MESSAGING::ThreadMessage* pMsg)
821 auto& components = CServiceBroker::GetAppComponents();
822 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
824 auto wakeScreensaver = []() {
825 auto& components = CServiceBroker::GetAppComponents();
826 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
827 appPower->ResetScreenSaver();
828 appPower->WakeUpScreenSaverAndDPMS();
831 switch (pMsg->dwMessage)
833 case TMSG_PLAYLISTPLAYER_PLAY:
834 if (pMsg->param1 != -1)
835 Play(pMsg->param1, "");
836 else
837 Play();
838 break;
840 case TMSG_PLAYLISTPLAYER_PLAY_SONG_ID:
841 if (pMsg->param1 != -1)
843 bool *result = (bool*)pMsg->lpVoid;
844 *result = PlaySongId(pMsg->param1);
846 else
847 Play();
848 break;
850 case TMSG_PLAYLISTPLAYER_NEXT:
851 PlayNext();
852 break;
854 case TMSG_PLAYLISTPLAYER_PREV:
855 PlayPrevious();
856 break;
858 case TMSG_PLAYLISTPLAYER_ADD:
859 if (pMsg->lpVoid)
861 CFileItemList *list = static_cast<CFileItemList*>(pMsg->lpVoid);
863 Add(pMsg->param1, (*list));
864 delete list;
866 break;
868 case TMSG_PLAYLISTPLAYER_INSERT:
869 if (pMsg->lpVoid)
871 CFileItemList *list = static_cast<CFileItemList*>(pMsg->lpVoid);
872 Insert(pMsg->param1, (*list), pMsg->param2);
873 delete list;
875 break;
877 case TMSG_PLAYLISTPLAYER_REMOVE:
878 if (pMsg->param1 != -1)
879 Remove(pMsg->param1, pMsg->param2);
880 break;
882 case TMSG_PLAYLISTPLAYER_CLEAR:
883 ClearPlaylist(pMsg->param1);
884 break;
886 case TMSG_PLAYLISTPLAYER_SHUFFLE:
887 SetShuffle(pMsg->param1, pMsg->param2 > 0);
888 break;
890 case TMSG_PLAYLISTPLAYER_REPEAT:
891 SetRepeat(pMsg->param1, static_cast<RepeatState>(pMsg->param2));
892 break;
894 case TMSG_PLAYLISTPLAYER_GET_ITEMS:
895 if (pMsg->lpVoid)
897 PLAYLIST::CPlayList playlist = GetPlaylist(pMsg->param1);
898 CFileItemList *list = static_cast<CFileItemList*>(pMsg->lpVoid);
900 for (int i = 0; i < playlist.size(); i++)
901 list->Add(std::make_shared<CFileItem>(*playlist[i]));
903 break;
905 case TMSG_PLAYLISTPLAYER_SWAP:
906 if (pMsg->lpVoid)
908 auto indexes = static_cast<std::vector<int>*>(pMsg->lpVoid);
909 if (indexes->size() == 2)
910 Swap(pMsg->param1, indexes->at(0), indexes->at(1));
911 delete indexes;
913 break;
915 case TMSG_MEDIA_PLAY:
917 wakeScreensaver();
919 // first check if we were called from the PlayFile() function
920 if (pMsg->lpVoid && pMsg->param2 == 0)
922 // Discard the current playlist, if TMSG_MEDIA_PLAY gets posted with just a single item.
923 // Otherwise items may fail to play, when started while a playlist is playing.
924 Reset();
926 CFileItem *item = static_cast<CFileItem*>(pMsg->lpVoid);
927 g_application.PlayFile(*item, "", pMsg->param1 != 0);
928 delete item;
929 return;
932 //g_application.StopPlaying();
933 // play file
934 if (pMsg->lpVoid)
936 CFileItemList *list = static_cast<CFileItemList*>(pMsg->lpVoid);
938 if (list->Size() > 0)
940 Id playlistId = TYPE_MUSIC;
941 for (int i = 0; i < list->Size(); i++)
943 if ((*list)[i]->IsVideo())
945 playlistId = TYPE_VIDEO;
946 break;
950 ClearPlaylist(playlistId);
951 SetCurrentPlaylist(playlistId);
952 if (list->Size() == 1 && !(*list)[0]->IsPlayList())
954 CFileItemPtr item = (*list)[0];
955 // if the item is a plugin we need to resolve the URL to ensure the infotags are filled.
956 if (URIUtils::HasPluginPath(*item) &&
957 !XFILE::CPluginDirectory::GetResolvedPluginResult(*item))
959 return;
961 if (item->IsAudio() || item->IsVideo())
962 Play(item, pMsg->strParam);
963 else
964 g_application.PlayMedia(*item, pMsg->strParam, playlistId);
966 else
968 // Handle "shuffled" option if present
969 if (list->HasProperty("shuffled") && list->GetProperty("shuffled").isBoolean())
970 SetShuffle(playlistId, list->GetProperty("shuffled").asBoolean(), false);
971 // Handle "repeat" option if present
972 if (list->HasProperty("repeat") && list->GetProperty("repeat").isInteger())
973 SetRepeat(playlistId, static_cast<RepeatState>(list->GetProperty("repeat").asInteger()),
974 false);
976 Add(playlistId, (*list));
977 Play(pMsg->param1, pMsg->strParam);
981 delete list;
983 else if (pMsg->param1 == TYPE_MUSIC || pMsg->param1 == TYPE_VIDEO)
985 if (GetCurrentPlaylist() != pMsg->param1)
986 SetCurrentPlaylist(pMsg->param1);
988 CServiceBroker::GetAppMessenger()->SendMsg(TMSG_PLAYLISTPLAYER_PLAY, pMsg->param2);
991 break;
993 case TMSG_MEDIA_RESTART:
994 g_application.Restart(true);
995 break;
997 case TMSG_MEDIA_STOP:
999 // restore to previous window if needed
1000 bool stopSlideshow = true;
1001 bool stopVideo = true;
1002 bool stopMusic = true;
1004 Id playlistId = pMsg->param1;
1005 if (playlistId != TYPE_NONE)
1007 stopSlideshow = (playlistId == TYPE_PICTURE);
1008 stopVideo = (playlistId == TYPE_VIDEO);
1009 stopMusic = (playlistId == TYPE_MUSIC);
1012 if ((stopSlideshow && CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW) ||
1013 (stopVideo && CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO) ||
1014 (stopVideo && CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_GAME) ||
1015 (stopMusic && CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VISUALISATION))
1016 CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow();
1018 wakeScreensaver();
1020 // stop playing file
1021 if (appPlayer->IsPlaying())
1022 g_application.StopPlaying();
1024 break;
1026 case TMSG_MEDIA_PAUSE:
1027 if (appPlayer->HasPlayer())
1029 wakeScreensaver();
1030 appPlayer->Pause();
1032 break;
1034 case TMSG_MEDIA_UNPAUSE:
1035 if (appPlayer->IsPausedPlayback())
1037 wakeScreensaver();
1038 appPlayer->Pause();
1040 break;
1042 case TMSG_MEDIA_PAUSE_IF_PLAYING:
1043 if (appPlayer->IsPlaying() && !appPlayer->IsPaused())
1045 wakeScreensaver();
1046 appPlayer->Pause();
1048 break;
1050 case TMSG_MEDIA_SEEK_TIME:
1052 if (appPlayer->IsPlaying() || appPlayer->IsPaused())
1053 appPlayer->SeekTime(pMsg->param3);
1055 break;
1057 default:
1058 break;