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