Merge pull request #2770 from t4-ravenbird/woa-mac-discovery
[vuplus_xbmc] / xbmc / PlayListPlayer.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "threads/SystemClock.h"
22 #include "PlayListPlayer.h"
23 #include "playlists/PlayListFactory.h"
24 #include "Application.h"
25 #include "PartyModeManager.h"
26 #include "settings/AdvancedSettings.h"
27 #include "GUIUserMessages.h"
28 #include "guilib/GUIWindowManager.h"
29 #include "dialogs/GUIDialogOK.h"
30 #include "playlists/PlayList.h"
31 #include "utils/log.h"
32 #include "utils/TimeUtils.h"
33 #include "music/tags/MusicInfoTag.h"
34 #include "dialogs/GUIDialogKaiToast.h"
35 #include "guilib/LocalizeStrings.h"
36 #include "interfaces/AnnouncementManager.h"
37 #include "guilib/Key.h"
38
39 using namespace PLAYLIST;
40
41 CPlayListPlayer::CPlayListPlayer(void)
42 {
43   m_PlaylistMusic = new CPlayList(PLAYLIST_MUSIC);
44   m_PlaylistVideo = new CPlayList(PLAYLIST_VIDEO);
45   m_PlaylistEmpty = new CPlayList;
46   m_iCurrentSong = -1;
47   m_bPlayedFirstFile = false;
48   m_bPlaybackStarted = false;
49   m_iCurrentPlayList = PLAYLIST_NONE;
50   for (int i = 0; i < 2; i++)
51     m_repeatState[i] = REPEAT_NONE;
52   m_iFailedSongs = 0;
53   m_failedSongsStart = 0;
54 }
55
56 CPlayListPlayer::~CPlayListPlayer(void)
57 {
58   Clear();
59   delete m_PlaylistMusic;
60   delete m_PlaylistVideo;
61   delete m_PlaylistEmpty;
62 }
63
64 bool CPlayListPlayer::OnAction(const CAction &action)
65 {
66   if (action.GetID() == ACTION_PREV_ITEM && !IsSingleItemNonRepeatPlaylist())
67   {
68     PlayPrevious();
69     return true;
70   }
71   else if (action.GetID() == ACTION_NEXT_ITEM && !IsSingleItemNonRepeatPlaylist())
72   {
73     PlayNext();
74     return true;
75   }
76   else
77     return false;
78 }
79
80 bool CPlayListPlayer::OnMessage(CGUIMessage &message)
81 {
82   switch (message.GetMessage())
83   {
84   case GUI_MSG_NOTIFY_ALL:
85     if (message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem())
86     {
87       // update the items in our playlist(s) if necessary
88       for (int i = PLAYLIST_MUSIC; i <= PLAYLIST_VIDEO; i++)
89       {
90         CPlayList &playlist = GetPlaylist(i);
91         CFileItemPtr item = boost::static_pointer_cast<CFileItem>(message.GetItem());
92         playlist.UpdateItem(item.get());
93       }
94     }
95     break;
96   case GUI_MSG_PLAYBACK_STOPPED:
97     {
98       if (m_iCurrentPlayList != PLAYLIST_NONE && m_bPlaybackStarted)
99       {
100         CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, m_iCurrentPlayList, m_iCurrentSong);
101         g_windowManager.SendThreadMessage(msg);
102         Reset();
103         m_iCurrentPlayList = PLAYLIST_NONE;
104         return true;
105       }
106     }
107     break;
108   }
109
110   return false;
111 }
112
113 int CPlayListPlayer::GetNextSong(int offset) const
114 {
115   if (m_iCurrentPlayList == PLAYLIST_NONE)
116     return -1;
117
118   const CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
119   if (playlist.size() <= 0)
120     return -1;
121
122   int song = m_iCurrentSong;
123
124   // party mode
125   if (g_partyModeManager.IsEnabled() && GetCurrentPlaylist() == PLAYLIST_MUSIC)
126     return song + offset;
127
128   // wrap around in the case of repeating
129   if (RepeatedOne(m_iCurrentPlayList))
130     return song;
131
132   song += offset;
133   if (song >= playlist.size() && Repeated(m_iCurrentPlayList))
134     song %= playlist.size();
135
136   return song;
137 }
138
139 int CPlayListPlayer::GetNextSong()
140 {
141   if (m_iCurrentPlayList == PLAYLIST_NONE)
142     return -1;
143   CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
144   if (playlist.size() <= 0)
145     return -1;
146   int iSong = m_iCurrentSong;
147
148   // party mode
149   if (g_partyModeManager.IsEnabled() && GetCurrentPlaylist() == PLAYLIST_MUSIC)
150     return iSong + 1;
151
152   // if repeat one, keep playing the current song if its valid
153   if (RepeatedOne(m_iCurrentPlayList))
154   {
155     // otherwise immediately abort playback
156     if (m_iCurrentSong >= 0 && m_iCurrentSong < playlist.size() && playlist[m_iCurrentSong]->GetProperty("unplayable").asBoolean())
157     {
158       CLog::Log(LOGERROR,"Playlist Player: RepeatOne stuck on unplayable item: %i, path [%s]", m_iCurrentSong, playlist[m_iCurrentSong]->GetPath().c_str());
159       CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, m_iCurrentPlayList, m_iCurrentSong);
160       g_windowManager.SendThreadMessage(msg);
161       Reset();
162       m_iCurrentPlayList = PLAYLIST_NONE;
163       return -1;
164     }
165     return iSong;
166   }
167
168   // if we've gone beyond the playlist and repeat all is enabled,
169   // then we clear played status and wrap around
170   iSong++;
171   if (iSong >= playlist.size() && Repeated(m_iCurrentPlayList))
172     iSong = 0;
173
174   return iSong;
175 }
176
177 bool CPlayListPlayer::PlayNext(int offset, bool bAutoPlay)
178 {
179   int iSong = GetNextSong(offset);
180   CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
181
182   if ((iSong < 0) || (iSong >= playlist.size()) || (playlist.GetPlayable() <= 0))
183   {
184     if(!bAutoPlay)
185       CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559), g_localizeStrings.Get(34201));
186
187     CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, m_iCurrentPlayList, m_iCurrentSong);
188     g_windowManager.SendThreadMessage(msg);
189     Reset();
190     m_iCurrentPlayList = PLAYLIST_NONE;
191     return false;
192   }
193
194   return Play(iSong, false);
195 }
196
197 bool CPlayListPlayer::PlayPrevious()
198 {
199   if (m_iCurrentPlayList == PLAYLIST_NONE)
200     return false;
201
202   CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
203   int iSong = m_iCurrentSong;
204
205   if (!RepeatedOne(m_iCurrentPlayList))
206     iSong--;
207
208   if (iSong < 0 && Repeated(m_iCurrentPlayList))
209     iSong = playlist.size() - 1;
210
211   if (iSong < 0 || playlist.size() <= 0)
212   {
213     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559), g_localizeStrings.Get(34202));
214     return false;
215   }
216
217   return Play(iSong, false, true);
218 }
219
220 bool CPlayListPlayer::IsSingleItemNonRepeatPlaylist() const
221 {
222   const CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
223   return (playlist.size() <= 1 && !RepeatedOne(m_iCurrentPlayList) && !Repeated(m_iCurrentPlayList));
224 }
225
226 bool CPlayListPlayer::Play()
227 {
228   if (m_iCurrentPlayList == PLAYLIST_NONE)
229     return false;
230
231   CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
232   if (playlist.size() <= 0) 
233     return false;
234
235   return Play(0);
236 }
237
238 bool CPlayListPlayer::PlaySongId(int songId)
239 {
240   if (m_iCurrentPlayList == PLAYLIST_NONE)
241     return false;
242
243   CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
244   if (playlist.size() <= 0) 
245     return Play();
246
247   for (int i = 0; i < playlist.size(); i++)
248   {
249     if (playlist[i]->HasMusicInfoTag() && playlist[i]->GetMusicInfoTag()->GetDatabaseId() == songId)
250       return Play(i);
251   }
252   return Play();
253 }
254
255 bool CPlayListPlayer::Play(int iSong, bool bAutoPlay /* = false */, bool bPlayPrevious /* = false */)
256 {
257   if (m_iCurrentPlayList == PLAYLIST_NONE)
258     return false;
259
260   CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
261   if (playlist.size() <= 0) 
262     return false;
263   if (iSong < 0) 
264     iSong = 0;
265   if (iSong >= playlist.size()) 
266     iSong = playlist.size() - 1;
267
268   // check if the item itself is a playlist, and can be expanded
269   // only allow a few levels, this could end up in a loop
270   // if they refer to each other in a loop
271   for(int i=0;i<5;i++)
272   {
273      if(!playlist.Expand(iSong))
274         break;
275   }
276
277   m_iCurrentSong = iSong;
278   CFileItemPtr item = playlist[m_iCurrentSong];
279   playlist.SetPlayed(true);
280
281   m_bPlaybackStarted = false;
282
283   unsigned int playAttempt = XbmcThreads::SystemClockMillis();
284   PlayBackRet ret = g_application.PlayFile(*item, bAutoPlay);
285   if (ret == PLAYBACK_CANCELED)
286     return false;
287   if (ret == PLAYBACK_FAIL)
288   {
289     CLog::Log(LOGERROR,"Playlist Player: skipping unplayable item: %i, path [%s]", m_iCurrentSong, item->GetPath().c_str());
290     playlist.SetUnPlayable(m_iCurrentSong);
291
292     // abort on 100 failed CONSECTUTIVE songs
293     if (!m_iFailedSongs)
294       m_failedSongsStart = playAttempt;
295     m_iFailedSongs++;
296     if ((m_iFailedSongs >= g_advancedSettings.m_playlistRetries && g_advancedSettings.m_playlistRetries >= 0)
297         || ((XbmcThreads::SystemClockMillis() - m_failedSongsStart  >= (unsigned int)g_advancedSettings.m_playlistTimeout * 1000) && g_advancedSettings.m_playlistTimeout))
298     {
299       CLog::Log(LOGDEBUG,"Playlist Player: one or more items failed to play... aborting playback");
300
301       // open error dialog
302       CGUIDialogOK::ShowAndGetInput(16026, 16027, 16029, 0);
303
304       CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, m_iCurrentPlayList, m_iCurrentSong);
305       g_windowManager.SendThreadMessage(msg);
306       Reset();
307       GetPlaylist(m_iCurrentPlayList).Clear();
308       m_iCurrentPlayList = PLAYLIST_NONE;
309       m_iFailedSongs = 0;
310       m_failedSongsStart = 0;
311       return false;
312     }
313
314     // how many playable items are in the playlist?
315     if (playlist.GetPlayable() > 0)
316     {
317       return bPlayPrevious ? PlayPrevious() : PlayNext();
318     }
319     // none? then abort playback
320     else
321     {
322       CLog::Log(LOGDEBUG,"Playlist Player: no more playable items... aborting playback");
323       CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_STOPPED, 0, 0, m_iCurrentPlayList, m_iCurrentSong);
324       g_windowManager.SendThreadMessage(msg);
325       Reset();
326       m_iCurrentPlayList = PLAYLIST_NONE;
327       return false;
328     }
329   }
330
331   // reset the start offset of this item
332   if (item->m_lStartOffset == STARTOFFSET_RESUME)
333     item->m_lStartOffset = 0;
334
335   // TODO - move the above failure logic and the below success logic
336   //        to callbacks instead so we don't rely on the return value
337   //        of PlayFile()
338
339   // consecutive error counter so reset if the current item is playing
340   m_iFailedSongs = 0;
341   m_failedSongsStart = 0;
342   m_bPlaybackStarted = true;
343   m_bPlayedFirstFile = true;
344   return true;
345 }
346
347 void CPlayListPlayer::SetCurrentSong(int iSong)
348 {
349   if (iSong >= -1 && iSong < GetPlaylist(m_iCurrentPlayList).size())
350     m_iCurrentSong = iSong;
351 }
352
353 int CPlayListPlayer::GetCurrentSong() const
354 {
355   return m_iCurrentSong;
356 }
357
358 int CPlayListPlayer::GetCurrentPlaylist() const
359 {
360   return m_iCurrentPlayList;
361 }
362
363 void CPlayListPlayer::SetCurrentPlaylist(int iPlaylist)
364 {
365   if (iPlaylist == m_iCurrentPlayList)
366     return;
367
368   // changing the current playlist while party mode is on
369   // disables party mode
370   if (g_partyModeManager.IsEnabled())
371     g_partyModeManager.Disable();
372
373   m_iCurrentPlayList = iPlaylist;
374   m_bPlayedFirstFile = false;
375 }
376
377 void CPlayListPlayer::ClearPlaylist(int iPlaylist)
378 {
379   // clear our applications playlist file
380   g_application.m_strPlayListFile.Empty();
381
382   CPlayList& playlist = GetPlaylist(iPlaylist);
383   playlist.Clear();
384
385   // its likely that the playlist changed
386   CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
387   g_windowManager.SendMessage(msg);
388 }
389
390 CPlayList& CPlayListPlayer::GetPlaylist(int iPlaylist)
391 {
392   switch ( iPlaylist )
393   {
394   case PLAYLIST_MUSIC:
395     return *m_PlaylistMusic;
396     break;
397   case PLAYLIST_VIDEO:
398     return *m_PlaylistVideo;
399     break;
400   default:
401     m_PlaylistEmpty->Clear();
402     return *m_PlaylistEmpty;
403     break;
404   }
405 }
406
407 const CPlayList& CPlayListPlayer::GetPlaylist(int iPlaylist) const
408 {
409   switch ( iPlaylist )
410   {
411   case PLAYLIST_MUSIC:
412     return *m_PlaylistMusic;
413     break;
414   case PLAYLIST_VIDEO:
415     return *m_PlaylistVideo;
416     break;
417   default:
418     // NOTE: This playlist may not be empty if the caller of the non-const version alters it!
419     return *m_PlaylistEmpty;
420     break;
421   }
422 }
423
424 int CPlayListPlayer::RemoveDVDItems()
425 {
426   int nRemovedM = m_PlaylistMusic->RemoveDVDItems();
427   int nRemovedV = m_PlaylistVideo->RemoveDVDItems();
428
429   return nRemovedM + nRemovedV;
430 }
431
432 void CPlayListPlayer::Reset()
433 {
434   m_iCurrentSong = -1;
435   m_bPlayedFirstFile = false;
436   m_bPlaybackStarted = false;
437
438   // its likely that the playlist changed
439   CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
440   g_windowManager.SendMessage(msg);
441 }
442
443 bool CPlayListPlayer::HasPlayedFirstFile() const
444 {
445   return m_bPlayedFirstFile;
446 }
447
448 bool CPlayListPlayer::Repeated(int iPlaylist) const
449 {
450   if (iPlaylist >= PLAYLIST_MUSIC && iPlaylist <= PLAYLIST_VIDEO)
451     return (m_repeatState[iPlaylist] == REPEAT_ALL);
452   return false;
453 }
454
455 bool CPlayListPlayer::RepeatedOne(int iPlaylist) const
456 {
457   if (iPlaylist == PLAYLIST_MUSIC || iPlaylist == PLAYLIST_VIDEO)
458     return (m_repeatState[iPlaylist] == REPEAT_ONE);
459   return false;
460 }
461
462 void CPlayListPlayer::SetShuffle(int iPlaylist, bool bYesNo, bool bNotify /* = false */)
463 {
464   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
465     return;
466
467   // disable shuffle in party mode
468   if (g_partyModeManager.IsEnabled() && iPlaylist == PLAYLIST_MUSIC)
469     return;
470
471   // do we even need to do anything?
472   if (bYesNo != IsShuffled(iPlaylist))
473   {
474     // save the order value of the current song so we can use it find its new location later
475     int iOrder = -1;
476     CPlayList &playlist = GetPlaylist(iPlaylist);
477     if (m_iCurrentSong >= 0 && m_iCurrentSong < playlist.size())
478       iOrder = playlist[m_iCurrentSong]->m_iprogramCount;
479
480     // shuffle or unshuffle as necessary
481     if (bYesNo)
482       playlist.Shuffle();
483     else
484       playlist.UnShuffle();
485
486     if (bNotify)
487     {
488       CStdString shuffleStr;
489       shuffleStr.Format("%s: %s", g_localizeStrings.Get(191), g_localizeStrings.Get(bYesNo ? 593 : 591)); // Shuffle: All/Off
490       CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559),  shuffleStr);
491     }
492
493     // find the previous order value and fix the current song marker
494     if (iOrder >= 0)
495     {
496       int iIndex = playlist.FindOrder(iOrder);
497       if (iIndex >= 0)
498         m_iCurrentSong = iIndex;
499       // if iIndex < 0, something unexpected happened
500       // so dont do anything
501     }
502   }
503   
504   AnnouncePropertyChanged(iPlaylist, "shuffled", IsShuffled(iPlaylist));
505 }
506
507 bool CPlayListPlayer::IsShuffled(int iPlaylist) const
508 {
509   // even if shuffled, party mode says its not
510   if (g_partyModeManager.IsEnabled() && iPlaylist == PLAYLIST_MUSIC)
511     return false;
512
513   if (iPlaylist == PLAYLIST_MUSIC || iPlaylist == PLAYLIST_VIDEO)
514     return GetPlaylist(iPlaylist).IsShuffled();
515
516   return false;
517 }
518
519 void CPlayListPlayer::SetRepeat(int iPlaylist, REPEAT_STATE state, bool bNotify /* = false */)
520 {
521   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
522     return;
523
524   // disable repeat in party mode
525   if (g_partyModeManager.IsEnabled() && iPlaylist == PLAYLIST_MUSIC)
526     state = REPEAT_NONE;
527
528   // notify the user if there was a change in the repeat state
529   if (m_repeatState[iPlaylist] != state && bNotify)
530   {
531     int iLocalizedString;
532     if (state == REPEAT_NONE)
533       iLocalizedString = 595; // Repeat: Off
534     else if (state == REPEAT_ONE)
535       iLocalizedString = 596; // Repeat: One
536     else
537       iLocalizedString = 597; // Repeat: All
538     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(559), g_localizeStrings.Get(iLocalizedString));
539   }
540
541   m_repeatState[iPlaylist] = state;
542
543   CVariant data;
544   switch (state)
545   {
546   case REPEAT_ONE:
547     data = "one";
548     break;
549   case REPEAT_ALL:
550     data = "all";
551     break;
552   default:
553     data = "off";
554     break;
555   }
556   AnnouncePropertyChanged(iPlaylist, "repeat", data);
557 }
558
559 REPEAT_STATE CPlayListPlayer::GetRepeat(int iPlaylist) const
560 {
561   if (iPlaylist == PLAYLIST_MUSIC || iPlaylist == PLAYLIST_VIDEO)
562     return m_repeatState[iPlaylist];
563   return REPEAT_NONE;
564 }
565
566 void CPlayListPlayer::ReShuffle(int iPlaylist, int iPosition)
567 {
568   // playlist has not played yet so shuffle the entire list
569   // (this only really works for new video playlists)
570   if (!GetPlaylist(iPlaylist).WasPlayed())
571   {
572     GetPlaylist(iPlaylist).Shuffle();
573   }
574   // we're trying to shuffle new items into the curently playing playlist
575   // so we shuffle starting at two positions below the current item
576   else if (iPlaylist == m_iCurrentPlayList)
577   {
578     if (
579       (g_application.m_pPlayer->IsPlayingAudio() && iPlaylist == PLAYLIST_MUSIC) ||
580       (g_application.m_pPlayer->IsPlayingVideo() && iPlaylist == PLAYLIST_VIDEO)
581       )
582     {
583       g_playlistPlayer.GetPlaylist(iPlaylist).Shuffle(m_iCurrentSong + 2);
584     }
585   }
586   // otherwise, shuffle from the passed position
587   // which is the position of the first new item added
588   else
589   {
590     g_playlistPlayer.GetPlaylist(iPlaylist).Shuffle(iPosition);
591   }
592 }
593
594 void CPlayListPlayer::Add(int iPlaylist, CPlayList& playlist)
595 {
596   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
597     return;
598   CPlayList& list = GetPlaylist(iPlaylist);
599   int iSize = list.size();
600   list.Add(playlist);
601   if (list.IsShuffled())
602     ReShuffle(iPlaylist, iSize);
603 }
604
605 void CPlayListPlayer::Add(int iPlaylist, const CFileItemPtr &pItem)
606 {
607   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
608     return;
609   CPlayList& list = GetPlaylist(iPlaylist);
610   int iSize = list.size();
611   list.Add(pItem);
612   if (list.IsShuffled())
613     ReShuffle(iPlaylist, iSize);
614 }
615
616 void CPlayListPlayer::Add(int iPlaylist, CFileItemList& items)
617 {
618   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
619     return;
620   CPlayList& list = GetPlaylist(iPlaylist);
621   int iSize = list.size();
622   list.Add(items);
623   if (list.IsShuffled())
624     ReShuffle(iPlaylist, iSize);
625 }
626
627 void CPlayListPlayer::Insert(int iPlaylist, CPlayList& playlist, int iIndex)
628 {
629   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
630     return;
631   CPlayList& list = GetPlaylist(iPlaylist);
632   int iSize = list.size();
633   list.Insert(playlist, iIndex);
634   if (list.IsShuffled())
635     ReShuffle(iPlaylist, iSize);
636   else if (m_iCurrentPlayList == iPlaylist && m_iCurrentSong >= iIndex)
637     m_iCurrentSong++;
638 }
639
640 void CPlayListPlayer::Insert(int iPlaylist, const CFileItemPtr &pItem, int iIndex)
641 {
642   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
643     return;
644   CPlayList& list = GetPlaylist(iPlaylist);
645   int iSize = list.size();
646   list.Insert(pItem, iIndex);
647   if (list.IsShuffled())
648     ReShuffle(iPlaylist, iSize);
649   else if (m_iCurrentPlayList == iPlaylist && m_iCurrentSong >= iIndex)
650     m_iCurrentSong++;
651 }
652
653 void CPlayListPlayer::Insert(int iPlaylist, CFileItemList& items, int iIndex)
654 {
655   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
656     return;
657   CPlayList& list = GetPlaylist(iPlaylist);
658   int iSize = list.size();
659   list.Insert(items, iIndex);
660   if (list.IsShuffled())
661     ReShuffle(iPlaylist, iSize);
662   else if (m_iCurrentPlayList == iPlaylist && m_iCurrentSong >= iIndex)
663     m_iCurrentSong++;
664 }
665
666 void CPlayListPlayer::Remove(int iPlaylist, int iPosition)
667 {
668   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
669     return;
670   CPlayList& list = GetPlaylist(iPlaylist);
671   list.Remove(iPosition);
672   if (m_iCurrentPlayList == iPlaylist && m_iCurrentSong >= iPosition)
673     m_iCurrentSong--;
674
675   // its likely that the playlist changed
676   CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
677   g_windowManager.SendMessage(msg);
678 }
679
680 void CPlayListPlayer::Clear()
681 {
682   if (m_PlaylistMusic)
683     m_PlaylistMusic->Clear();
684   if (m_PlaylistVideo)
685     m_PlaylistVideo->Clear();
686   if (m_PlaylistEmpty)
687     m_PlaylistEmpty->Clear();
688 }
689
690 void CPlayListPlayer::Swap(int iPlaylist, int indexItem1, int indexItem2)
691 {
692   if (iPlaylist != PLAYLIST_MUSIC && iPlaylist != PLAYLIST_VIDEO)
693     return;
694
695   CPlayList& list = GetPlaylist(iPlaylist);
696   if (list.Swap(indexItem1, indexItem2) && iPlaylist == m_iCurrentPlayList)
697   {
698     if (m_iCurrentSong == indexItem1)
699       m_iCurrentSong = indexItem2;
700     else if (m_iCurrentSong == indexItem2)
701       m_iCurrentSong = indexItem1;
702   }
703
704   // its likely that the playlist changed
705   CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
706   g_windowManager.SendMessage(msg);
707 }
708
709 void CPlayListPlayer::AnnouncePropertyChanged(int iPlaylist, const std::string &strProperty, const CVariant &value)
710 {
711   if (strProperty.empty() || value.isNull() ||
712      (iPlaylist == PLAYLIST_VIDEO && !g_application.m_pPlayer->IsPlayingVideo()) ||
713      (iPlaylist == PLAYLIST_MUSIC && !g_application.m_pPlayer->IsPlayingAudio()))
714     return;
715
716   CVariant data;
717   data["player"]["playerid"] = iPlaylist;
718   data["property"][strProperty] = value;
719   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Player, "xbmc", "OnPropertyChanged", data);
720 }