[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / PlayListPlayer.cpp
blob346727ca55b7ecd6c4754fda23314c2ebe4341b7
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 "FileItemList.h"
13 #include "GUIUserMessages.h"
14 #include "PartyModeManager.h"
15 #include "ServiceBroker.h"
16 #include "URL.h"
17 #include "application/Application.h"
18 #include "application/ApplicationComponents.h"
19 #include "application/ApplicationPlayer.h"
20 #include "application/ApplicationPowerHandling.h"
21 #include "dialogs/GUIDialogKaiToast.h"
22 #include "filesystem/PluginDirectory.h"
23 #include "filesystem/VideoDatabaseFile.h"
24 #include "guilib/GUIComponent.h"
25 #include "guilib/GUIWindowManager.h"
26 #include "guilib/LocalizeStrings.h"
27 #include "input/actions/Action.h"
28 #include "input/actions/ActionIDs.h"
29 #include "interfaces/AnnouncementManager.h"
30 #include "messaging/ApplicationMessenger.h"
31 #include "messaging/helpers/DialogOKHelper.h"
32 #include "music/MusicFileItemClassify.h"
33 #include "music/tags/MusicInfoTag.h"
34 #include "playlists/PlayList.h"
35 #include "settings/AdvancedSettings.h"
36 #include "settings/SettingsComponent.h"
37 #include "utils/StringUtils.h"
38 #include "utils/URIUtils.h"
39 #include "utils/Variant.h"
40 #include "utils/log.h"
41 #include "video/VideoDatabase.h"
42 #include "video/VideoFileItemClassify.h"
44 using namespace KODI::MESSAGING;
45 using namespace KODI::VIDEO;
47 namespace KODI::PLAYLIST
50 CPlayListPlayer::CPlayListPlayer(void)
52 m_PlaylistMusic = new CPlayList(Id::TYPE_MUSIC);
53 m_PlaylistVideo = new CPlayList(Id::TYPE_VIDEO);
54 m_PlaylistEmpty = new CPlayList;
55 m_iCurrentSong = -1;
56 m_bPlayedFirstFile = false;
57 m_bPlaybackStarted = false;
58 m_iFailedSongs = 0;
59 m_failedSongsStart = std::chrono::steady_clock::now();
62 CPlayListPlayer::~CPlayListPlayer(void)
64 Clear();
65 delete m_PlaylistMusic;
66 delete m_PlaylistVideo;
67 delete m_PlaylistEmpty;
70 bool CPlayListPlayer::OnAction(const CAction &action)
72 if (action.GetID() == ACTION_PREV_ITEM && !IsSingleItemNonRepeatPlaylist())
74 PlayPrevious();
75 return true;
77 else if (action.GetID() == ACTION_NEXT_ITEM && !IsSingleItemNonRepeatPlaylist())
79 PlayNext();
80 return true;
82 else
83 return false;
86 bool CPlayListPlayer::OnMessage(CGUIMessage &message)
88 switch (message.GetMessage())
90 case GUI_MSG_NOTIFY_ALL:
91 if (message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem())
93 // update the items in our playlist(s) if necessary
94 for (Id playlistId : {Id::TYPE_MUSIC, Id::TYPE_VIDEO})
96 CPlayList& playlist = GetPlaylist(playlistId);
97 CFileItemPtr item = std::static_pointer_cast<CFileItem>(message.GetItem());
98 playlist.UpdateItem(item.get());
101 break;
102 case GUI_MSG_PLAYBACK_STOPPED:
104 if (m_iCurrentPlayList != Id::TYPE_NONE && m_bPlaybackStarted)
106 CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, static_cast<int>(m_iCurrentPlayList),
107 m_iCurrentSong);
108 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
109 Reset();
110 m_iCurrentPlayList = Id::TYPE_NONE;
111 return true;
114 break;
115 case GUI_MSG_PLAYBACK_STARTED:
117 m_bPlaybackStarted = true;
119 break;
122 return false;
125 int CPlayListPlayer::GetNextItemIdx(int offset) const
127 if (m_iCurrentPlayList == Id::TYPE_NONE)
128 return -1;
130 const CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
131 if (playlist.size() <= 0)
132 return -1;
134 int song = m_iCurrentSong;
136 // party mode
137 if (g_partyModeManager.IsEnabled() && GetCurrentPlaylist() == Id::TYPE_MUSIC)
138 return song + offset;
140 // wrap around in the case of repeating
141 if (RepeatedOne(m_iCurrentPlayList))
142 return song;
144 song += offset;
145 if (song >= playlist.size() && Repeated(m_iCurrentPlayList))
146 song %= playlist.size();
148 return song;
151 int CPlayListPlayer::GetNextItemIdx()
153 if (m_iCurrentPlayList == Id::TYPE_NONE)
154 return -1;
155 CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
156 if (playlist.size() <= 0)
157 return -1;
158 int iSong = m_iCurrentSong;
160 // party mode
161 if (g_partyModeManager.IsEnabled() && GetCurrentPlaylist() == Id::TYPE_MUSIC)
162 return iSong + 1;
164 // if repeat one, keep playing the current song if its valid
165 if (RepeatedOne(m_iCurrentPlayList))
167 // otherwise immediately abort playback
168 if (m_iCurrentSong >= 0 && m_iCurrentSong < playlist.size() && playlist[m_iCurrentSong]->GetProperty("unplayable").asBoolean())
170 CLog::Log(LOGERROR, "Playlist Player: RepeatOne stuck on unplayable item: {}, path [{}]",
171 m_iCurrentSong, playlist[m_iCurrentSong]->GetPath());
172 CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, static_cast<int>(m_iCurrentPlayList),
173 m_iCurrentSong);
174 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
175 Reset();
176 m_iCurrentPlayList = Id::TYPE_NONE;
177 return -1;
179 return iSong;
182 // if we've gone beyond the playlist and repeat all is enabled,
183 // then we clear played status and wrap around
184 iSong++;
185 if (iSong >= playlist.size() && Repeated(m_iCurrentPlayList))
186 iSong = 0;
188 return iSong;
191 bool CPlayListPlayer::PlayNext(int offset, bool bAutoPlay)
193 int iSong = GetNextItemIdx(offset);
194 const CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
196 if ((iSong < 0) || (iSong >= playlist.size()) || (playlist.GetPlayable() <= 0))
198 if(!bAutoPlay)
199 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559), g_localizeStrings.Get(34201));
201 CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, static_cast<int>(m_iCurrentPlayList),
202 m_iCurrentSong);
203 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
204 Reset();
205 m_iCurrentPlayList = Id::TYPE_NONE;
206 return false;
209 const auto& components = CServiceBroker::GetAppComponents();
210 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
211 const std::string player = appPlayer->GetName();
213 return Play(iSong, player, false);
216 bool CPlayListPlayer::PlayPrevious()
218 if (m_iCurrentPlayList == Id::TYPE_NONE)
219 return false;
221 const CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
222 int iSong = m_iCurrentSong;
224 if (!RepeatedOne(m_iCurrentPlayList))
225 iSong--;
227 if (iSong < 0 && Repeated(m_iCurrentPlayList))
228 iSong = playlist.size() - 1;
230 if (iSong < 0 || playlist.size() <= 0)
232 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559), g_localizeStrings.Get(34202));
233 return false;
236 return Play(iSong, "", false, true);
239 bool CPlayListPlayer::IsSingleItemNonRepeatPlaylist() const
241 const CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
242 return (playlist.size() <= 1 && !RepeatedOne(m_iCurrentPlayList) && !Repeated(m_iCurrentPlayList));
245 bool CPlayListPlayer::Play()
247 if (m_iCurrentPlayList == Id::TYPE_NONE)
248 return false;
250 const CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
251 if (playlist.size() <= 0)
252 return false;
254 return Play(0, "");
257 bool CPlayListPlayer::PlayItemIdx(int itemIdx)
259 if (m_iCurrentPlayList == Id::TYPE_NONE)
260 return false;
262 CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
263 if (playlist.size() <= 0)
264 return Play();
266 for (int i = 0; i < playlist.size(); i++)
268 if (playlist[i]->HasMusicInfoTag() &&
269 playlist[i]->GetMusicInfoTag()->GetDatabaseId() == itemIdx)
270 return Play(i, "");
272 return Play();
275 bool CPlayListPlayer::Play(const CFileItemPtr& pItem,
276 const std::string& player,
277 bool forceSelection /* = false */)
279 Id playlistId;
280 bool isVideo{IsVideo(*pItem)};
281 bool isAudio{MUSIC::IsAudio(*pItem)};
283 if (isAudio && !isVideo)
284 playlistId = Id::TYPE_MUSIC;
285 else if (isVideo && !isAudio)
286 playlistId = Id::TYPE_VIDEO;
287 else if (pItem->HasProperty("playlist_type_hint"))
289 // There are two main cases that can fall here:
290 // - If an extension is set on both audio / video extension lists example .strm
291 // see GetFileExtensionProvider() -> GetVideoExtensions() / GetAudioExtensions()
292 // When you play the .strm containing single path, cause that
293 // IsVideo() and IsAudio() methods both return true
295 // - When you play a playlist (e.g. .m3u / .strm) containing multiple paths,
296 // and the path played is generic (e.g.without extension) and have no properties
297 // to detect the media type, IsVideo() / IsAudio() both return false
299 // for these cases the type is unknown so we rely on the hint
300 playlistId =
301 Id{pItem->GetProperty("playlist_type_hint").asInteger32(static_cast<int>(Id::TYPE_NONE))};
303 else
305 CLog::LogF(LOGWARNING, "ListItem type must be audio or video type. The type can be specified "
306 "by using ListItem::getVideoInfoTag or ListItem::getMusicInfoTag, in "
307 "the case of playlist entries by adding #KODIPROP mimetype value.");
308 return false;
311 ClearPlaylist(playlistId);
312 Reset();
313 SetCurrentPlaylist(playlistId);
314 Add(playlistId, pItem);
316 return Play(0, player, false, false, forceSelection);
319 bool CPlayListPlayer::Play(int iSong,
320 const std::string& player,
321 bool bAutoPlay /* = false */,
322 bool bPlayPrevious /* = false */,
323 bool forceSelection /* = false */)
325 if (m_iCurrentPlayList == Id::TYPE_NONE)
326 return false;
328 CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
329 if (playlist.size() <= 0)
330 return false;
331 if (iSong < 0)
332 iSong = 0;
333 if (iSong >= playlist.size())
334 iSong = playlist.size() - 1;
336 // check if the item itself is a playlist, and can be expanded
337 // only allow a few levels, this could end up in a loop
338 // if they refer to each other in a loop
339 for (int i=0; i<5; i++)
341 if(!playlist.Expand(iSong))
342 break;
345 m_iCurrentSong = iSong;
346 CFileItemPtr item = playlist[m_iCurrentSong];
347 if (IsVideoDb(*item) && !item->HasVideoInfoTag())
348 *(item->GetVideoInfoTag()) = XFILE::CVideoDatabaseFile::GetVideoTag(CURL(item->GetDynPath()));
350 playlist.SetPlayed(true);
352 m_bPlaybackStarted = false;
354 const auto playAttempt = std::chrono::steady_clock::now();
355 bool ret = g_application.PlayFile(*item, player, bAutoPlay, forceSelection);
356 if (!ret)
358 CLog::Log(LOGERROR, "Playlist Player: skipping unplayable item: {}, path [{}]", m_iCurrentSong,
359 CURL::GetRedacted(item->GetDynPath()));
360 playlist.SetUnPlayable(m_iCurrentSong);
362 // abort on 100 failed CONSECUTIVE songs
363 if (!m_iFailedSongs)
364 m_failedSongsStart = playAttempt;
365 m_iFailedSongs++;
366 const std::shared_ptr<CAdvancedSettings> advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
368 auto now = std::chrono::steady_clock::now();
369 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_failedSongsStart);
371 if ((m_iFailedSongs >= advancedSettings->m_playlistRetries &&
372 advancedSettings->m_playlistRetries >= 0) ||
373 ((duration.count() >=
374 static_cast<unsigned int>(advancedSettings->m_playlistTimeout) * 1000) &&
375 advancedSettings->m_playlistTimeout))
377 CLog::Log(LOGDEBUG,"Playlist Player: one or more items failed to play... aborting playback");
379 // open error dialog
380 HELPERS::ShowOKDialogText(CVariant{16026}, CVariant{16027});
382 CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, static_cast<int>(m_iCurrentPlayList),
383 m_iCurrentSong);
384 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
385 Reset();
386 GetPlaylist(m_iCurrentPlayList).Clear();
387 m_iCurrentPlayList = Id::TYPE_NONE;
388 m_iFailedSongs = 0;
389 m_failedSongsStart = std::chrono::steady_clock::now();
390 return false;
393 // how many playable items are in the playlist?
394 if (playlist.GetPlayable() > 0)
396 return bPlayPrevious ? PlayPrevious() : PlayNext();
398 // none? then abort playback
399 else
401 CLog::Log(LOGDEBUG,"Playlist Player: no more playable items... aborting playback");
402 CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, static_cast<int>(m_iCurrentPlayList),
403 m_iCurrentSong);
404 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
405 Reset();
406 m_iCurrentPlayList = Id::TYPE_NONE;
407 return false;
411 // reset the start offset of this item
412 if (item->GetStartOffset() == STARTOFFSET_RESUME)
413 item->SetStartOffset(0);
415 //! @todo - move the above failure logic and the below success logic
416 //! to callbacks instead so we don't rely on the return value
417 //! of PlayFile()
419 // consecutive error counter so reset if the current item is playing
420 m_iFailedSongs = 0;
421 m_failedSongsStart = std::chrono::steady_clock::now();
422 m_bPlayedFirstFile = true;
423 return true;
426 void CPlayListPlayer::SetCurrentItemIdx(int iSong)
428 if (iSong >= -1 && iSong < GetPlaylist(m_iCurrentPlayList).size())
429 m_iCurrentSong = iSong;
432 int CPlayListPlayer::GetCurrentItemIdx() const
434 return m_iCurrentSong;
437 Id CPlayListPlayer::GetCurrentPlaylist() const
439 return m_iCurrentPlayList;
442 void CPlayListPlayer::SetCurrentPlaylist(Id playlistId)
444 if (playlistId == m_iCurrentPlayList)
445 return;
447 // changing the current playlist while party mode is on
448 // disables party mode
449 if (g_partyModeManager.IsEnabled())
450 g_partyModeManager.Disable();
452 m_iCurrentPlayList = playlistId;
453 m_bPlayedFirstFile = false;
456 void CPlayListPlayer::ClearPlaylist(Id playlistId)
458 // clear our applications playlist file
459 g_application.m_strPlayListFile.clear();
461 CPlayList& playlist = GetPlaylist(playlistId);
462 playlist.Clear();
464 // its likely that the playlist changed
465 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
466 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
469 CPlayList& CPlayListPlayer::GetPlaylist(Id playlistId)
471 switch (playlistId)
473 case Id::TYPE_MUSIC:
474 return *m_PlaylistMusic;
475 break;
476 case Id::TYPE_VIDEO:
477 return *m_PlaylistVideo;
478 break;
479 default:
480 m_PlaylistEmpty->Clear();
481 return *m_PlaylistEmpty;
482 break;
486 const CPlayList& CPlayListPlayer::GetPlaylist(Id playlistId) const
488 switch (playlistId)
490 case Id::TYPE_MUSIC:
491 return *m_PlaylistMusic;
492 break;
493 case Id::TYPE_VIDEO:
494 return *m_PlaylistVideo;
495 break;
496 default:
497 // NOTE: This playlist may not be empty if the caller of the non-const version alters it!
498 return *m_PlaylistEmpty;
499 break;
503 int CPlayListPlayer::RemoveDVDItems()
505 int nRemovedM = m_PlaylistMusic->RemoveDVDItems();
506 int nRemovedV = m_PlaylistVideo->RemoveDVDItems();
508 return nRemovedM + nRemovedV;
511 void CPlayListPlayer::Reset()
513 m_iCurrentSong = -1;
514 m_bPlayedFirstFile = false;
515 m_bPlaybackStarted = false;
517 // its likely that the playlist changed
518 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
519 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
522 bool CPlayListPlayer::HasPlayedFirstFile() const
524 return m_bPlayedFirstFile;
527 bool CPlayListPlayer::Repeated(Id playlistId) const
529 const auto repStatePos = m_repeatState.find(playlistId);
530 if (repStatePos != m_repeatState.end())
531 return repStatePos->second == RepeatState::ALL;
532 return false;
535 bool CPlayListPlayer::RepeatedOne(Id playlistId) const
537 const auto repStatePos = m_repeatState.find(playlistId);
538 if (repStatePos != m_repeatState.end())
539 return (repStatePos->second == RepeatState::ONE);
540 return false;
543 void CPlayListPlayer::SetShuffle(Id playlistId, bool bYesNo, bool bNotify /* = false */)
545 if (playlistId != Id::TYPE_MUSIC && playlistId != Id::TYPE_VIDEO)
546 return;
548 // disable shuffle in party mode
549 if (g_partyModeManager.IsEnabled() && playlistId == Id::TYPE_MUSIC)
550 return;
552 // do we even need to do anything?
553 if (bYesNo != IsShuffled(playlistId))
555 // save the order value of the current song so we can use it find its new location later
556 int iOrder = -1;
557 CPlayList& playlist = GetPlaylist(playlistId);
558 if (m_iCurrentSong >= 0 && m_iCurrentSong < playlist.size())
559 iOrder = playlist[m_iCurrentSong]->m_iprogramCount;
561 // shuffle or unshuffle as necessary
562 if (bYesNo)
563 playlist.Shuffle();
564 else
565 playlist.UnShuffle();
567 if (bNotify)
569 std::string shuffleStr =
570 StringUtils::Format("{}: {}", g_localizeStrings.Get(191),
571 g_localizeStrings.Get(bYesNo ? 593 : 591)); // Shuffle: All/Off
572 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559), shuffleStr);
575 // find the previous order value and fix the current song marker
576 if (iOrder >= 0)
578 int iIndex = playlist.FindOrder(iOrder);
579 if (iIndex >= 0)
580 m_iCurrentSong = iIndex;
581 // if iIndex < 0, something unexpected happened
582 // so dont do anything
586 // its likely that the playlist changed
587 if (CServiceBroker::GetGUI() != nullptr)
589 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
590 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
593 AnnouncePropertyChanged(playlistId, "shuffled", IsShuffled(playlistId));
596 bool CPlayListPlayer::IsShuffled(Id playlistId) const
598 // even if shuffled, party mode says its not
599 if (g_partyModeManager.IsEnabled() && playlistId == Id::TYPE_MUSIC)
600 return false;
602 if (playlistId == Id::TYPE_MUSIC || playlistId == Id::TYPE_VIDEO)
603 return GetPlaylist(playlistId).IsShuffled();
605 return false;
608 void CPlayListPlayer::SetRepeat(Id playlistId, RepeatState state, bool bNotify /* = false */)
610 if (playlistId != Id::TYPE_MUSIC && playlistId != Id::TYPE_VIDEO)
611 return;
613 // disable repeat in party mode
614 if (g_partyModeManager.IsEnabled() && playlistId == Id::TYPE_MUSIC)
615 state = RepeatState::NONE;
617 // notify the user if there was a change in the repeat state
618 if (m_repeatState[playlistId] != state && bNotify)
620 int iLocalizedString;
621 if (state == RepeatState::NONE)
622 iLocalizedString = 595; // Repeat: Off
623 else if (state == RepeatState::ONE)
624 iLocalizedString = 596; // Repeat: One
625 else
626 iLocalizedString = 597; // Repeat: All
627 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559), g_localizeStrings.Get(iLocalizedString));
630 m_repeatState[playlistId] = state;
632 CVariant data;
633 switch (state)
635 case RepeatState::ONE:
636 data = "one";
637 break;
638 case RepeatState::ALL:
639 data = "all";
640 break;
641 default:
642 data = "off";
643 break;
646 // its likely that the playlist changed
647 if (CServiceBroker::GetGUI() != nullptr)
649 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
650 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
653 AnnouncePropertyChanged(playlistId, "repeat", data);
656 RepeatState CPlayListPlayer::GetRepeat(Id playlistId) const
658 const auto repStatePos = m_repeatState.find(playlistId);
659 if (repStatePos != m_repeatState.end())
660 return repStatePos->second;
661 return RepeatState::NONE;
664 void CPlayListPlayer::ReShuffle(Id playlistId, int iPosition)
666 // playlist has not played yet so shuffle the entire list
667 // (this only really works for new video playlists)
668 if (!GetPlaylist(playlistId).WasPlayed())
670 GetPlaylist(playlistId).Shuffle();
672 // we're trying to shuffle new items into the currently playing playlist
673 // so we shuffle starting at two positions below the current item
674 else if (playlistId == m_iCurrentPlayList)
676 const auto& components = CServiceBroker::GetAppComponents();
677 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
678 if ((appPlayer->IsPlayingAudio() && playlistId == Id::TYPE_MUSIC) ||
679 (appPlayer->IsPlayingVideo() && playlistId == Id::TYPE_VIDEO))
681 GetPlaylist(playlistId).Shuffle(m_iCurrentSong + 2);
684 // otherwise, shuffle from the passed position
685 // which is the position of the first new item added
686 else
688 GetPlaylist(playlistId).Shuffle(iPosition);
692 void CPlayListPlayer::Add(Id playlistId, const CPlayList& playlist)
694 if (playlistId != Id::TYPE_MUSIC && playlistId != Id::TYPE_VIDEO)
695 return;
696 CPlayList& list = GetPlaylist(playlistId);
697 int iSize = list.size();
698 list.Add(playlist);
699 if (list.IsShuffled())
700 ReShuffle(playlistId, iSize);
703 void CPlayListPlayer::Add(Id playlistId, const CFileItemPtr& pItem)
705 if (playlistId != Id::TYPE_MUSIC && playlistId != Id::TYPE_VIDEO)
706 return;
707 CPlayList& list = GetPlaylist(playlistId);
708 int iSize = list.size();
709 list.Add(pItem);
710 if (list.IsShuffled())
711 ReShuffle(playlistId, iSize);
713 // its likely that the playlist changed
714 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
715 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
718 void CPlayListPlayer::Add(Id playlistId, const CFileItemList& items)
720 if (playlistId != Id::TYPE_MUSIC && playlistId != Id::TYPE_VIDEO)
721 return;
722 CPlayList& list = GetPlaylist(playlistId);
723 int iSize = list.size();
724 list.Add(items);
725 if (list.IsShuffled())
726 ReShuffle(playlistId, iSize);
728 // its likely that the playlist changed
729 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
730 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
733 void CPlayListPlayer::Insert(Id playlistId, const CPlayList& playlist, int iIndex)
735 if (playlistId != Id::TYPE_MUSIC && playlistId != Id::TYPE_VIDEO)
736 return;
737 CPlayList& list = GetPlaylist(playlistId);
738 int iSize = list.size();
739 list.Insert(playlist, iIndex);
740 if (list.IsShuffled())
741 ReShuffle(playlistId, iSize);
742 else if (m_iCurrentPlayList == playlistId && m_iCurrentSong >= iIndex)
743 m_iCurrentSong++;
746 void CPlayListPlayer::Insert(Id playlistId, const CFileItemPtr& pItem, int iIndex)
748 if (playlistId != Id::TYPE_MUSIC && playlistId != Id::TYPE_VIDEO)
749 return;
750 CPlayList& list = GetPlaylist(playlistId);
751 int iSize = list.size();
752 list.Insert(pItem, iIndex);
753 if (list.IsShuffled())
754 ReShuffle(playlistId, iSize);
755 else if (m_iCurrentPlayList == playlistId && m_iCurrentSong >= iIndex)
756 m_iCurrentSong++;
759 void CPlayListPlayer::Insert(Id playlistId, const CFileItemList& items, int iIndex)
761 if (playlistId != Id::TYPE_MUSIC && playlistId != Id::TYPE_VIDEO)
762 return;
763 CPlayList& list = GetPlaylist(playlistId);
764 int iSize = list.size();
765 list.Insert(items, iIndex);
766 if (list.IsShuffled())
767 ReShuffle(playlistId, iSize);
768 else if (m_iCurrentPlayList == playlistId && m_iCurrentSong >= iIndex)
769 m_iCurrentSong++;
771 // its likely that the playlist changed
772 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
773 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
776 void CPlayListPlayer::Remove(Id playlistId, int iPosition)
778 if (playlistId != Id::TYPE_MUSIC && playlistId != Id::TYPE_VIDEO)
779 return;
780 CPlayList& list = GetPlaylist(playlistId);
781 list.Remove(iPosition);
782 if (m_iCurrentPlayList == playlistId && m_iCurrentSong >= iPosition)
783 m_iCurrentSong--;
785 // its likely that the playlist changed
786 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
787 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
790 void CPlayListPlayer::Clear()
792 if (m_PlaylistMusic)
793 m_PlaylistMusic->Clear();
794 if (m_PlaylistVideo)
795 m_PlaylistVideo->Clear();
796 if (m_PlaylistEmpty)
797 m_PlaylistEmpty->Clear();
800 void CPlayListPlayer::Swap(Id playlistId, int indexItem1, int indexItem2)
802 if (playlistId != Id::TYPE_MUSIC && playlistId != Id::TYPE_VIDEO)
803 return;
805 CPlayList& list = GetPlaylist(playlistId);
806 if (list.Swap(indexItem1, indexItem2) && playlistId == m_iCurrentPlayList)
808 if (m_iCurrentSong == indexItem1)
809 m_iCurrentSong = indexItem2;
810 else if (m_iCurrentSong == indexItem2)
811 m_iCurrentSong = indexItem1;
814 // its likely that the playlist changed
815 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
816 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
819 void CPlayListPlayer::AnnouncePropertyChanged(Id playlistId,
820 const std::string& strProperty,
821 const CVariant& value)
823 const auto& components = CServiceBroker::GetAppComponents();
824 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
826 if (strProperty.empty() || value.isNull() ||
827 (playlistId == Id::TYPE_VIDEO && !appPlayer->IsPlayingVideo()) ||
828 (playlistId == Id::TYPE_MUSIC && !appPlayer->IsPlayingAudio()))
829 return;
831 CVariant data;
832 data["player"]["playerid"] = static_cast<int>(playlistId);
833 data["property"][strProperty] = value;
834 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnPropertyChanged",
835 data);
838 int PLAYLIST::CPlayListPlayer::GetMessageMask()
840 return TMSG_MASK_PLAYLISTPLAYER;
843 void PLAYLIST::CPlayListPlayer::OnApplicationMessage(KODI::MESSAGING::ThreadMessage* pMsg)
845 auto& components = CServiceBroker::GetAppComponents();
846 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
848 auto wakeScreensaver = []() {
849 auto& components = CServiceBroker::GetAppComponents();
850 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
851 appPower->ResetScreenSaver();
852 appPower->WakeUpScreenSaverAndDPMS();
855 switch (pMsg->dwMessage)
857 case TMSG_PLAYLISTPLAYER_PLAY:
858 if (pMsg->param1 != -1)
859 Play(pMsg->param1, "");
860 else
861 Play();
862 break;
864 case TMSG_PLAYLISTPLAYER_PLAY_ITEM_ID:
865 if (pMsg->param1 != -1)
867 bool *result = (bool*)pMsg->lpVoid;
868 *result = PlayItemIdx(pMsg->param1);
870 else
871 Play();
872 break;
874 case TMSG_PLAYLISTPLAYER_NEXT:
875 PlayNext();
876 break;
878 case TMSG_PLAYLISTPLAYER_PREV:
879 PlayPrevious();
880 break;
882 case TMSG_PLAYLISTPLAYER_ADD:
883 if (pMsg->lpVoid)
885 CFileItemList *list = static_cast<CFileItemList*>(pMsg->lpVoid);
887 Add(Id{pMsg->param1}, (*list));
888 delete list;
890 break;
892 case TMSG_PLAYLISTPLAYER_INSERT:
893 if (pMsg->lpVoid)
895 CFileItemList *list = static_cast<CFileItemList*>(pMsg->lpVoid);
896 Insert(Id{pMsg->param1}, (*list), pMsg->param2);
897 delete list;
899 break;
901 case TMSG_PLAYLISTPLAYER_REMOVE:
902 if (pMsg->param1 != -1)
903 Remove(Id{pMsg->param1}, pMsg->param2);
904 break;
906 case TMSG_PLAYLISTPLAYER_CLEAR:
907 ClearPlaylist(Id{pMsg->param1});
908 break;
910 case TMSG_PLAYLISTPLAYER_SHUFFLE:
911 SetShuffle(Id{pMsg->param1}, pMsg->param2 > 0);
912 break;
914 case TMSG_PLAYLISTPLAYER_REPEAT:
915 SetRepeat(Id{pMsg->param1}, static_cast<RepeatState>(pMsg->param2));
916 break;
918 case TMSG_PLAYLISTPLAYER_GET_ITEMS:
919 if (pMsg->lpVoid)
921 PLAYLIST::CPlayList playlist = GetPlaylist(Id{pMsg->param1});
922 CFileItemList *list = static_cast<CFileItemList*>(pMsg->lpVoid);
924 for (int i = 0; i < playlist.size(); i++)
925 list->Add(std::make_shared<CFileItem>(*playlist[i]));
927 break;
929 case TMSG_PLAYLISTPLAYER_SWAP:
930 if (pMsg->lpVoid)
932 auto indexes = static_cast<std::vector<int>*>(pMsg->lpVoid);
933 if (indexes->size() == 2)
934 Swap(Id{pMsg->param1}, indexes->at(0), indexes->at(1));
935 delete indexes;
937 break;
939 case TMSG_MEDIA_PLAY:
941 wakeScreensaver();
943 // first check if we were called from the PlayFile() function
944 if (pMsg->lpVoid && pMsg->param2 == 0)
946 // Discard the current playlist, if TMSG_MEDIA_PLAY gets posted with just a single item.
947 // Otherwise items may fail to play, when started while a playlist is playing.
948 Reset();
950 CFileItem *item = static_cast<CFileItem*>(pMsg->lpVoid);
951 g_application.PlayFile(*item, "", pMsg->param1 != 0);
952 delete item;
953 return;
956 //g_application.StopPlaying();
957 // play file
958 if (pMsg->lpVoid)
960 CFileItemList *list = static_cast<CFileItemList*>(pMsg->lpVoid);
962 if (list->Size() > 0)
964 Id playlistId = Id::TYPE_MUSIC;
965 for (int i = 0; i < list->Size(); i++)
967 if (IsVideo(*list->Get(i)))
969 playlistId = Id::TYPE_VIDEO;
970 break;
974 ClearPlaylist(playlistId);
975 SetCurrentPlaylist(playlistId);
976 if (list->Size() == 1 && !(*list)[0]->IsPlayList())
978 CFileItemPtr item = (*list)[0];
979 // if the item is a plugin we need to resolve the URL to ensure the infotags are filled.
980 if (URIUtils::HasPluginPath(*item) &&
981 !XFILE::CPluginDirectory::GetResolvedPluginResult(*item))
983 return;
985 if (MUSIC::IsAudio(*item) || IsVideo(*item))
986 Play(item, pMsg->strParam);
987 else
988 g_application.PlayMedia(*item, pMsg->strParam, playlistId);
990 else
992 // Handle "shuffled" option if present
993 if (list->HasProperty("shuffled") && list->GetProperty("shuffled").isBoolean())
994 SetShuffle(playlistId, list->GetProperty("shuffled").asBoolean(), false);
995 // Handle "repeat" option if present
996 if (list->HasProperty("repeat") && list->GetProperty("repeat").isInteger())
997 SetRepeat(playlistId, static_cast<RepeatState>(list->GetProperty("repeat").asInteger()),
998 false);
1000 Add(playlistId, (*list));
1001 Play(pMsg->param1, pMsg->strParam);
1005 delete list;
1007 else if (Id{pMsg->param1} == Id::TYPE_MUSIC || Id{pMsg->param1} == Id::TYPE_VIDEO)
1009 if (GetCurrentPlaylist() != Id{pMsg->param1})
1010 SetCurrentPlaylist(Id{pMsg->param1});
1012 CServiceBroker::GetAppMessenger()->SendMsg(TMSG_PLAYLISTPLAYER_PLAY, pMsg->param2);
1015 break;
1017 case TMSG_MEDIA_RESTART:
1018 g_application.Restart(true);
1019 break;
1021 case TMSG_MEDIA_STOP:
1023 // restore to previous window if needed
1024 bool stopSlideshow = true;
1025 bool stopVideo = true;
1026 bool stopMusic = true;
1028 Id playlistId = Id{pMsg->param1};
1029 if (playlistId != Id::TYPE_NONE)
1031 stopSlideshow = (playlistId == Id::TYPE_PICTURE);
1032 stopVideo = (playlistId == Id::TYPE_VIDEO);
1033 stopMusic = (playlistId == Id::TYPE_MUSIC);
1036 if ((stopSlideshow && CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW) ||
1037 (stopVideo && CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO) ||
1038 (stopVideo && CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_GAME) ||
1039 (stopMusic && CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VISUALISATION))
1040 CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow();
1042 wakeScreensaver();
1044 // stop playing file
1045 if (appPlayer->IsPlaying())
1046 g_application.StopPlaying();
1048 break;
1050 case TMSG_MEDIA_PAUSE:
1051 if (appPlayer->HasPlayer())
1053 wakeScreensaver();
1054 appPlayer->Pause();
1056 break;
1058 case TMSG_MEDIA_UNPAUSE:
1059 if (appPlayer->IsPausedPlayback())
1061 wakeScreensaver();
1062 appPlayer->Pause();
1064 break;
1066 case TMSG_MEDIA_PAUSE_IF_PLAYING:
1067 if (appPlayer->IsPlaying() && !appPlayer->IsPaused())
1069 wakeScreensaver();
1070 appPlayer->Pause();
1072 break;
1074 case TMSG_MEDIA_SEEK_TIME:
1076 if (appPlayer->IsPlaying() || appPlayer->IsPaused())
1077 appPlayer->SeekTime(pMsg->param3);
1079 break;
1081 default:
1082 break;
1086 } // namespace KODI::PLAYLIST