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.
9 #include "PlayListPlayer.h"
12 #include "GUIUserMessages.h"
13 #include "PartyModeManager.h"
14 #include "ServiceBroker.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
;
50 m_bPlayedFirstFile
= false;
51 m_bPlaybackStarted
= false;
53 m_failedSongsStart
= std::chrono::steady_clock::now();
56 CPlayListPlayer::~CPlayListPlayer(void)
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())
71 else if (action
.GetID() == ACTION_NEXT_ITEM
&& !IsSingleItemNonRepeatPlaylist())
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());
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
);
103 m_iCurrentPlayList
= TYPE_NONE
;
108 case GUI_MSG_PLAYBACK_STARTED
:
110 m_bPlaybackStarted
= true;
118 int CPlayListPlayer::GetNextSong(int offset
) const
120 if (m_iCurrentPlayList
== TYPE_NONE
)
123 const CPlayList
& playlist
= GetPlaylist(m_iCurrentPlayList
);
124 if (playlist
.size() <= 0)
127 int song
= m_iCurrentSong
;
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
))
138 if (song
>= playlist
.size() && Repeated(m_iCurrentPlayList
))
139 song
%= playlist
.size();
144 int CPlayListPlayer::GetNextSong()
146 if (m_iCurrentPlayList
== TYPE_NONE
)
148 CPlayList
& playlist
= GetPlaylist(m_iCurrentPlayList
);
149 if (playlist
.size() <= 0)
151 int iSong
= m_iCurrentSong
;
154 if (g_partyModeManager
.IsEnabled() && GetCurrentPlaylist() == TYPE_MUSIC
)
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
);
168 m_iCurrentPlayList
= TYPE_NONE
;
174 // if we've gone beyond the playlist and repeat all is enabled,
175 // then we clear played status and wrap around
177 if (iSong
>= playlist
.size() && Repeated(m_iCurrentPlayList
))
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))
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
);
196 m_iCurrentPlayList
= TYPE_NONE
;
200 return Play(iSong
, "", false);
203 bool CPlayListPlayer::PlayPrevious()
205 if (m_iCurrentPlayList
== TYPE_NONE
)
208 const CPlayList
& playlist
= GetPlaylist(m_iCurrentPlayList
);
209 int iSong
= m_iCurrentSong
;
211 if (!RepeatedOne(m_iCurrentPlayList
))
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));
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
)
237 const CPlayList
& playlist
= GetPlaylist(m_iCurrentPlayList
);
238 if (playlist
.size() <= 0)
244 bool CPlayListPlayer::PlaySongId(int songId
)
246 if (m_iCurrentPlayList
== TYPE_NONE
)
249 CPlayList
& playlist
= GetPlaylist(m_iCurrentPlayList
);
250 if (playlist
.size() <= 0)
253 for (int i
= 0; i
< playlist
.size(); i
++)
255 if (playlist
[i
]->HasMusicInfoTag() && playlist
[i
]->GetMusicInfoTag()->GetDatabaseId() == songId
)
261 bool CPlayListPlayer::Play(const CFileItemPtr
& pItem
, const std::string
& player
)
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
);
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.");
294 ClearPlaylist(playlistId
);
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
)
310 CPlayList
& playlist
= GetPlaylist(m_iCurrentPlayList
);
311 if (playlist
.size() <= 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
))
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
);
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
346 m_failedSongsStart
= playAttempt
;
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");
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
);
367 GetPlaylist(m_iCurrentPlayList
).Clear();
368 m_iCurrentPlayList
= TYPE_NONE
;
370 m_failedSongsStart
= std::chrono::steady_clock::now();
374 // how many playable items are in the playlist?
375 if (playlist
.GetPlayable() > 0)
377 return bPlayPrevious
? PlayPrevious() : PlayNext();
379 // none? then abort playback
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
);
386 m_iCurrentPlayList
= TYPE_NONE
;
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
399 // consecutive error counter so reset if the current item is playing
401 m_failedSongsStart
= std::chrono::steady_clock::now();
402 m_bPlayedFirstFile
= 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
)
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
);
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
)
454 return *m_PlaylistMusic
;
457 return *m_PlaylistVideo
;
460 m_PlaylistEmpty
->Clear();
461 return *m_PlaylistEmpty
;
466 const CPlayList
& CPlayListPlayer::GetPlaylist(Id playlistId
) const
471 return *m_PlaylistMusic
;
474 return *m_PlaylistVideo
;
477 // NOTE: This playlist may not be empty if the caller of the non-const version alters it!
478 return *m_PlaylistEmpty
;
483 int CPlayListPlayer::RemoveDVDItems()
485 int nRemovedM
= m_PlaylistMusic
->RemoveDVDItems();
486 int nRemovedV
= m_PlaylistVideo
->RemoveDVDItems();
488 return nRemovedM
+ nRemovedV
;
491 void CPlayListPlayer::Reset()
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
;
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
);
523 void CPlayListPlayer::SetShuffle(Id playlistId
, bool bYesNo
, bool bNotify
/* = false */)
525 if (playlistId
!= TYPE_MUSIC
&& playlistId
!= TYPE_VIDEO
)
528 // disable shuffle in party mode
529 if (g_partyModeManager
.IsEnabled() && playlistId
== TYPE_MUSIC
)
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
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
545 playlist
.UnShuffle();
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
558 int iIndex
= playlist
.FindOrder(iOrder
);
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
)
582 if (playlistId
== TYPE_MUSIC
|| playlistId
== TYPE_VIDEO
)
583 return GetPlaylist(playlistId
).IsShuffled();
588 void CPlayListPlayer::SetRepeat(Id playlistId
, RepeatState state
, bool bNotify
/* = false */)
590 if (playlistId
!= TYPE_MUSIC
&& playlistId
!= TYPE_VIDEO
)
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
606 iLocalizedString
= 597; // Repeat: All
607 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info
, g_localizeStrings
.Get(559), g_localizeStrings
.Get(iLocalizedString
));
610 m_repeatState
[playlistId
] = state
;
615 case RepeatState::ONE
:
618 case RepeatState::ALL
:
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
668 GetPlaylist(playlistId
).Shuffle(iPosition
);
672 void CPlayListPlayer::Add(Id playlistId
, const CPlayList
& playlist
)
674 if (playlistId
!= TYPE_MUSIC
&& playlistId
!= TYPE_VIDEO
)
676 CPlayList
& list
= GetPlaylist(playlistId
);
677 int iSize
= list
.size();
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
)
687 CPlayList
& list
= GetPlaylist(playlistId
);
688 int iSize
= list
.size();
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
)
698 CPlayList
& list
= GetPlaylist(playlistId
);
699 int iSize
= list
.size();
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
)
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
)
722 void CPlayListPlayer::Insert(Id playlistId
, const CFileItemPtr
& pItem
, int iIndex
)
724 if (playlistId
!= TYPE_MUSIC
&& playlistId
!= TYPE_VIDEO
)
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
)
735 void CPlayListPlayer::Insert(Id playlistId
, const CFileItemList
& items
, int iIndex
)
737 if (playlistId
!= TYPE_MUSIC
&& playlistId
!= TYPE_VIDEO
)
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
)
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
)
756 CPlayList
& list
= GetPlaylist(playlistId
);
757 list
.Remove(iPosition
);
758 if (m_iCurrentPlayList
== playlistId
&& m_iCurrentSong
>= iPosition
)
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()
769 m_PlaylistMusic
->Clear();
771 m_PlaylistVideo
->Clear();
773 m_PlaylistEmpty
->Clear();
776 void CPlayListPlayer::Swap(Id playlistId
, int indexItem1
, int indexItem2
)
778 if (playlistId
!= TYPE_MUSIC
&& playlistId
!= TYPE_VIDEO
)
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()))
808 data
["player"]["playerid"] = playlistId
;
809 data
["property"][strProperty
] = value
;
810 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player
, "OnPropertyChanged",
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
, "");
840 case TMSG_PLAYLISTPLAYER_PLAY_SONG_ID
:
841 if (pMsg
->param1
!= -1)
843 bool *result
= (bool*)pMsg
->lpVoid
;
844 *result
= PlaySongId(pMsg
->param1
);
850 case TMSG_PLAYLISTPLAYER_NEXT
:
854 case TMSG_PLAYLISTPLAYER_PREV
:
858 case TMSG_PLAYLISTPLAYER_ADD
:
861 CFileItemList
*list
= static_cast<CFileItemList
*>(pMsg
->lpVoid
);
863 Add(pMsg
->param1
, (*list
));
868 case TMSG_PLAYLISTPLAYER_INSERT
:
871 CFileItemList
*list
= static_cast<CFileItemList
*>(pMsg
->lpVoid
);
872 Insert(pMsg
->param1
, (*list
), pMsg
->param2
);
877 case TMSG_PLAYLISTPLAYER_REMOVE
:
878 if (pMsg
->param1
!= -1)
879 Remove(pMsg
->param1
, pMsg
->param2
);
882 case TMSG_PLAYLISTPLAYER_CLEAR
:
883 ClearPlaylist(pMsg
->param1
);
886 case TMSG_PLAYLISTPLAYER_SHUFFLE
:
887 SetShuffle(pMsg
->param1
, pMsg
->param2
> 0);
890 case TMSG_PLAYLISTPLAYER_REPEAT
:
891 SetRepeat(pMsg
->param1
, static_cast<RepeatState
>(pMsg
->param2
));
894 case TMSG_PLAYLISTPLAYER_GET_ITEMS
:
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
]));
905 case TMSG_PLAYLISTPLAYER_SWAP
:
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));
915 case TMSG_MEDIA_PLAY
:
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.
926 CFileItem
*item
= static_cast<CFileItem
*>(pMsg
->lpVoid
);
927 g_application
.PlayFile(*item
, "", pMsg
->param1
!= 0);
932 //g_application.StopPlaying();
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
;
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
))
961 if (item
->IsAudio() || item
->IsVideo())
962 Play(item
, pMsg
->strParam
);
964 g_application
.PlayMedia(*item
, pMsg
->strParam
, playlistId
);
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()),
976 Add(playlistId
, (*list
));
977 Play(pMsg
->param1
, pMsg
->strParam
);
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
);
993 case TMSG_MEDIA_RESTART
:
994 g_application
.Restart(true);
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();
1020 // stop playing file
1021 if (appPlayer
->IsPlaying())
1022 g_application
.StopPlaying();
1026 case TMSG_MEDIA_PAUSE
:
1027 if (appPlayer
->HasPlayer())
1034 case TMSG_MEDIA_UNPAUSE
:
1035 if (appPlayer
->IsPausedPlayback())
1042 case TMSG_MEDIA_PAUSE_IF_PLAYING
:
1043 if (appPlayer
->IsPlaying() && !appPlayer
->IsPaused())
1050 case TMSG_MEDIA_SEEK_TIME
:
1052 if (appPlayer
->IsPlaying() || appPlayer
->IsPaused())
1053 appPlayer
->SeekTime(pMsg
->param3
);