46c0602d67c7008dde5b8152b8bcd96f0baf69aa
[vuplus_xbmc] / xbmc / PartyModeManager.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 "PartyModeManager.h"
23 #include "PlayListPlayer.h"
24 #include "music/MusicDatabase.h"
25 #include "music/windows/GUIWindowMusicPlaylist.h"
26 #include "video/VideoDatabase.h"
27 #include "playlists/SmartPlayList.h"
28 #include "profiles/ProfilesManager.h"
29 #include "dialogs/GUIDialogProgress.h"
30 #include "GUIUserMessages.h"
31 #include "guilib/GUIWindowManager.h"
32 #include "dialogs/GUIDialogOK.h"
33 #include "playlists/PlayList.h"
34 #include "utils/TimeUtils.h"
35 #include "utils/log.h"
36 #include "Application.h"
37 #include "interfaces/AnnouncementManager.h"
38
39 using namespace std;
40 using namespace PLAYLIST;
41
42 #define QUEUE_DEPTH       10
43
44 CPartyModeManager::CPartyModeManager(void)
45 {
46   m_bIsVideo = false;
47   m_bEnabled = false;
48   m_strCurrentFilterMusic.Empty();
49   m_strCurrentFilterVideo.Empty();
50   ClearState();
51 }
52
53 CPartyModeManager::~CPartyModeManager(void)
54 {
55 }
56
57 bool CPartyModeManager::Enable(PartyModeContext context /*= PARTYMODECONTEXT_MUSIC*/, const CStdString& strXspPath /*= ""*/)
58 {
59   // Filter using our PartyMode xml file
60   CSmartPlaylist playlist;
61   CStdString partyModePath;
62   bool playlistLoaded;
63
64   m_bIsVideo = context == PARTYMODECONTEXT_VIDEO;
65   if (!strXspPath.IsEmpty()) //if a path to a smartplaylist is supplied use it
66     partyModePath = strXspPath;
67   else if (m_bIsVideo)
68     partyModePath = CProfilesManager::Get().GetUserDataItem("PartyMode-Video.xsp");
69   else
70     partyModePath = CProfilesManager::Get().GetUserDataItem("PartyMode.xsp");
71
72   playlistLoaded=playlist.Load(partyModePath);
73
74   if ( playlistLoaded )
75   {
76     m_type = playlist.GetType();
77     if (context == PARTYMODECONTEXT_UNKNOWN)
78     {
79       //get it from the xsp file
80       m_bIsVideo = (m_type.Equals("video") || m_type.Equals("musicvideos") || m_type.Equals("mixed"));
81     }
82
83     if (m_type.Equals("mixed"))
84       playlist.SetType("songs");
85
86     if (m_type.Equals("mixed"))
87       playlist.SetType("video");
88
89     playlist.SetType(m_type);
90   }
91   else
92   {
93     m_strCurrentFilterMusic.Empty();
94     m_strCurrentFilterVideo.Empty();
95     m_type = m_bIsVideo ? "musicvideos" : "songs";
96   }
97
98   CGUIDialogProgress* pDialog = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
99   int iHeading = (m_bIsVideo ? 20250 : 20121);
100   int iLine0 = (m_bIsVideo ? 20251 : 20123);
101   pDialog->SetHeading(iHeading);
102   pDialog->SetLine(0, iLine0);
103   pDialog->SetLine(1, "");
104   pDialog->SetLine(2, "");
105   pDialog->StartModal();
106
107   ClearState();
108   unsigned int time = XbmcThreads::SystemClockMillis();
109   vector< pair<int,int> > songIDs;
110   if (m_type.Equals("songs") || m_type.Equals("mixed"))
111   {
112     CMusicDatabase db;
113     if (db.Open())
114     {
115       set<CStdString> playlists;
116       if ( playlistLoaded )
117         m_strCurrentFilterMusic = playlist.GetWhereClause(db, playlists);
118
119       CLog::Log(LOGINFO, "PARTY MODE MANAGER: Registering filter:[%s]", m_strCurrentFilterMusic.c_str());
120       m_iMatchingSongs = (int)db.GetSongIDs(m_strCurrentFilterMusic, songIDs);
121       if (m_iMatchingSongs < 1 && m_type.Equals("songs"))
122       {
123         pDialog->Close();
124         db.Close();
125         OnError(16031, (CStdString)"Party mode found no matching songs. Aborting.");
126         return false;
127       }
128     }
129     else
130     {
131       pDialog->Close();
132       OnError(16033, (CStdString)"Party mode could not open database. Aborting.");
133       return false;
134     }
135     db.Close();
136   }
137
138   if (m_type.Equals("musicvideos") || m_type.Equals("mixed"))
139   {
140     vector< pair<int,int> > songIDs2;
141     CVideoDatabase db;
142     if (db.Open())
143     {
144       set<CStdString> playlists;
145       if ( playlistLoaded )
146         m_strCurrentFilterVideo = playlist.GetWhereClause(db, playlists);
147
148       CLog::Log(LOGINFO, "PARTY MODE MANAGER: Registering filter:[%s]", m_strCurrentFilterVideo.c_str());
149       m_iMatchingSongs += (int)db.GetMusicVideoIDs(m_strCurrentFilterVideo, songIDs2);
150       if (m_iMatchingSongs < 1)
151       {
152         pDialog->Close();
153         db.Close();
154         OnError(16031, (CStdString)"Party mode found no matching songs. Aborting.");
155         return false;
156       }
157     }
158     else
159     {
160       pDialog->Close();
161       OnError(16033, (CStdString)"Party mode could not open database. Aborting.");
162       return false;
163     }
164     db.Close();
165     songIDs.insert(songIDs.end(),songIDs2.begin(),songIDs2.end());
166   }
167
168   // calculate history size
169   if (m_iMatchingSongs < 50)
170     m_songsInHistory = 0;
171   else
172     m_songsInHistory = (int)(m_iMatchingSongs/2);
173   if (m_songsInHistory > 200)
174     m_songsInHistory = 200;
175
176   CLog::Log(LOGINFO,"PARTY MODE MANAGER: Matching songs = %i, History size = %i", m_iMatchingSongs, m_songsInHistory);
177   CLog::Log(LOGINFO,"PARTY MODE MANAGER: Party mode enabled!");
178
179   int iPlaylist = m_bIsVideo ? PLAYLIST_VIDEO : PLAYLIST_MUSIC;
180
181   g_playlistPlayer.ClearPlaylist(iPlaylist);
182   g_playlistPlayer.SetShuffle(iPlaylist, false);
183   g_playlistPlayer.SetRepeat(iPlaylist, PLAYLIST::REPEAT_NONE);
184
185   pDialog->SetLine(0, (m_bIsVideo ? 20252 : 20124));
186   pDialog->Progress();
187   // add initial songs
188   if (!AddInitialSongs(songIDs))
189   {
190     pDialog->Close();
191     return false;
192   }
193   CLog::Log(LOGDEBUG, "%s time for song fetch: %u",
194             __FUNCTION__, XbmcThreads::SystemClockMillis() - time);
195
196   // start playing
197   g_playlistPlayer.SetCurrentPlaylist(iPlaylist);
198   Play(0);
199
200   pDialog->Close();
201   // open now playing window
202   if (m_type.Equals("songs"))
203   {
204     if (g_windowManager.GetActiveWindow() != WINDOW_MUSIC_PLAYLIST)
205       g_windowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST);
206   }
207
208   // done
209   m_bEnabled = true;
210   Announce();
211   return true;
212 }
213
214 void CPartyModeManager::Disable()
215 {
216   if (!IsEnabled())
217     return;
218   m_bEnabled = false;
219   Announce();
220   CLog::Log(LOGINFO,"PARTY MODE MANAGER: Party mode disabled.");
221 }
222
223 void CPartyModeManager::OnSongChange(bool bUpdatePlayed /* = false */)
224 {
225   if (!IsEnabled())
226     return;
227   Process();
228   if (bUpdatePlayed)
229     m_iSongsPlayed++;
230 }
231
232 void CPartyModeManager::AddUserSongs(CPlayList& tempList, bool bPlay /* = false */)
233 {
234   if (!IsEnabled())
235     return;
236
237   // where do we add?
238   int iAddAt = -1;
239   if (m_iLastUserSong < 0 || bPlay)
240     iAddAt = 1; // under the currently playing song
241   else
242     iAddAt = m_iLastUserSong + 1; // under the last user added song
243
244   int iNewUserSongs = tempList.size();
245   CLog::Log(LOGINFO,"PARTY MODE MANAGER: Adding %i user selected songs at %i", iNewUserSongs, iAddAt);
246
247   int iPlaylist = PLAYLIST_MUSIC;
248   if (m_bIsVideo)
249     iPlaylist = PLAYLIST_VIDEO;
250   g_playlistPlayer.GetPlaylist(iPlaylist).Insert(tempList, iAddAt);
251
252   // update last user added song location
253   if (m_iLastUserSong < 0)
254     m_iLastUserSong = 0;
255   m_iLastUserSong += iNewUserSongs;
256
257   if (bPlay)
258     Play(1);
259 }
260
261 void CPartyModeManager::AddUserSongs(CFileItemList& tempList, bool bPlay /* = false */)
262 {
263   if (!IsEnabled())
264     return;
265
266   // where do we add?
267   int iAddAt = -1;
268   if (m_iLastUserSong < 0 || bPlay)
269     iAddAt = 1; // under the currently playing song
270   else
271     iAddAt = m_iLastUserSong + 1; // under the last user added song
272
273   int iNewUserSongs = tempList.Size();
274   CLog::Log(LOGINFO,"PARTY MODE MANAGER: Adding %i user selected songs at %i", iNewUserSongs, iAddAt);
275
276   int iPlaylist = PLAYLIST_MUSIC;
277   if (m_bIsVideo)
278     iPlaylist = PLAYLIST_VIDEO;
279
280   g_playlistPlayer.GetPlaylist(iPlaylist).Insert(tempList, iAddAt);
281
282   // update last user added song location
283   if (m_iLastUserSong < 0)
284     m_iLastUserSong = 0;
285   m_iLastUserSong += iNewUserSongs;
286
287   if (bPlay)
288     Play(1);
289 }
290
291 void CPartyModeManager::Process()
292 {
293   ReapSongs();
294   MovePlaying();
295   AddRandomSongs();
296   UpdateStats();
297   SendUpdateMessage();
298 }
299
300 bool CPartyModeManager::AddRandomSongs(int iSongs /* = 0 */)
301 {
302   int iPlaylist = PLAYLIST_MUSIC;
303   if (m_bIsVideo)
304     iPlaylist = PLAYLIST_VIDEO;
305
306   CPlayList& playlist = g_playlistPlayer.GetPlaylist(iPlaylist);
307   int iMissingSongs = QUEUE_DEPTH - playlist.size();
308   if (iSongs <= 0)
309     iSongs = iMissingSongs;
310   // distribute between types if mixed
311   int iSongsToAdd=iSongs;
312   int iVidsToAdd=iSongs;
313   if (m_type.Equals("mixed"))
314   {
315     if (iSongs == 1)
316     {
317       if (rand() % 10 < 7) // 70 % chance of grabbing a song
318         iVidsToAdd = 0;
319       else
320         iSongsToAdd = 0;
321     }
322     if (iSongs > 1) // grab 70 % songs, 30 % mvids
323     {
324       iSongsToAdd = (int).7f*iSongs;
325       iVidsToAdd = (int).3f*iSongs;
326       while (iSongsToAdd+iVidsToAdd < iSongs) // correct any rounding by adding songs
327         iSongsToAdd++;
328     }
329   }
330
331   // add songs to fill queue
332   if (m_type.Equals("songs") || m_type.Equals("mixed"))
333   {
334     CMusicDatabase database;
335     if (database.Open())
336     {
337       // Method:
338       // 1. Grab a random entry from the database using a where clause
339       // 2. Iterate on iSongs.
340
341       // Note: At present, this method is faster than the alternative, which is to grab
342       // all valid songids, then select a random number of them (as done in AddInitialSongs()).
343       // The reason for this is simply the number of songs we are requesting - we generally
344       // only want one here.  Any more than about 3 songs and it is more efficient
345       // to use the technique in AddInitialSongs.  As it's unlikely that we'll require
346       // more than 1 song at a time here, this method is faster.
347       bool error(false);
348       for (int i = 0; i < iSongsToAdd; i++)
349       {
350         pair<CStdString,CStdString> whereClause = GetWhereClauseWithHistory();
351         CFileItemPtr item(new CFileItem);
352         int songID;
353         if (database.GetRandomSong(item.get(), songID, whereClause.first))
354         { // success
355           Add(item);
356           AddToHistory(1,songID);
357         }
358         else
359         {
360           error = true;
361           break;
362         }
363       }
364
365       if (error)
366       {
367         database.Close();
368         OnError(16034, (CStdString)"Cannot get songs from database. Aborting.");
369         return false;
370       }
371     }
372     else
373     {
374       OnError(16033, (CStdString)"Party mode could not open database. Aborting.");
375       return false;
376     }
377     database.Close();
378   }
379   if (m_type.Equals("musicvideos") || m_type.Equals("mixed"))
380   {
381     CVideoDatabase database;
382     if (database.Open())
383     {
384       // Method:
385       // 1. Grab a random entry from the database using a where clause
386       // 2. Iterate on iSongs.
387
388       // Note: At present, this method is faster than the alternative, which is to grab
389       // all valid songids, then select a random number of them (as done in AddInitialSongs()).
390       // The reason for this is simply the number of songs we are requesting - we generally
391       // only want one here.  Any more than about 3 songs and it is more efficient
392       // to use the technique in AddInitialSongs.  As it's unlikely that we'll require
393       // more than 1 song at a time here, this method is faster.
394       bool error(false);
395       for (int i = 0; i < iVidsToAdd; i++)
396       {
397         pair<CStdString,CStdString> whereClause = GetWhereClauseWithHistory();
398         CFileItemPtr item(new CFileItem);
399         int songID;
400         if (database.GetRandomMusicVideo(item.get(), songID, whereClause.second))
401         { // success
402           Add(item);
403           AddToHistory(2,songID);
404         }
405         else
406         {
407           error = true;
408           break;
409         }
410       }
411
412       if (error)
413       {
414         database.Close();
415         OnError(16034, (CStdString)"Cannot get songs from database. Aborting.");
416         return false;
417       }
418     }
419     else
420     {
421       OnError(16033, (CStdString)"Party mode could not open database. Aborting.");
422       return false;
423     }
424     database.Close();
425   }
426   return true;
427 }
428
429 void CPartyModeManager::Add(CFileItemPtr &pItem)
430 {
431   int iPlaylist = m_bIsVideo ? PLAYLIST_VIDEO : PLAYLIST_MUSIC;
432   if (pItem->HasMusicInfoTag())
433   {
434     CMusicDatabase database;
435     database.Open();
436     database.SetPropertiesForFileItem(*pItem);
437   }
438
439   CPlayList& playlist = g_playlistPlayer.GetPlaylist(iPlaylist);
440   playlist.Add(pItem);
441   CLog::Log(LOGINFO,"PARTY MODE MANAGER: Adding randomly selected song at %i:[%s]", playlist.size() - 1, pItem->GetPath().c_str());
442   m_iMatchingSongsPicked++;
443 }
444
445 bool CPartyModeManager::ReapSongs()
446 {
447   int iPlaylist = m_bIsVideo ? PLAYLIST_VIDEO : PLAYLIST_MUSIC;
448
449   // reap any played songs
450   int iCurrentSong = g_playlistPlayer.GetCurrentSong();
451   int i=0;
452   while (i < g_playlistPlayer.GetPlaylist(iPlaylist).size())
453   {
454     if (i < iCurrentSong)
455     {
456       g_playlistPlayer.GetPlaylist(iPlaylist).Remove(i);
457       iCurrentSong--;
458       if (i <= m_iLastUserSong)
459         m_iLastUserSong--;
460     }
461     else
462       i++;
463   }
464
465   g_playlistPlayer.SetCurrentSong(iCurrentSong);
466   return true;
467 }
468
469 bool CPartyModeManager::MovePlaying()
470 {
471   // move current song to the top if its not there
472   int iCurrentSong = g_playlistPlayer.GetCurrentSong();
473   int iPlaylist = m_bIsVideo ? PLAYLIST_MUSIC : PLAYLIST_VIDEO;
474
475   if (iCurrentSong > 0)
476   {
477     CLog::Log(LOGINFO,"PARTY MODE MANAGER: Moving currently playing song from %i to 0", iCurrentSong);
478     CPlayList &playlist = g_playlistPlayer.GetPlaylist(iPlaylist);
479     CPlayList playlistTemp;
480     playlistTemp.Add(playlist[iCurrentSong]);
481     playlist.Remove(iCurrentSong);
482     for (int i=0; i<playlist.size(); i++)
483       playlistTemp.Add(playlist[i]);
484     playlist.Clear();
485     for (int i=0; i<playlistTemp.size(); i++)
486       playlist.Add(playlistTemp[i]);
487   }
488   g_playlistPlayer.SetCurrentSong(0);
489   return true;
490 }
491
492 void CPartyModeManager::SendUpdateMessage()
493 {
494   CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
495   g_windowManager.SendThreadMessage(msg);
496 }
497
498 void CPartyModeManager::Play(int iPos)
499 {
500   // move current song to the top if its not there
501   g_playlistPlayer.Play(iPos);
502   CLog::Log(LOGINFO,"PARTY MODE MANAGER: Playing song at %i", iPos);
503   Process();
504 }
505
506 void CPartyModeManager::OnError(int iError, const CStdString&  strLogMessage)
507 {
508   // open error dialog
509   CGUIDialogOK::ShowAndGetInput(257, 16030, iError, 0);
510   CLog::Log(LOGERROR, "PARTY MODE MANAGER: %s", strLogMessage.c_str());
511   m_bEnabled = false;
512   SendUpdateMessage();
513 }
514
515 int CPartyModeManager::GetSongsPlayed()
516 {
517   if (!IsEnabled())
518     return -1;
519   return m_iSongsPlayed;
520 }
521
522 int CPartyModeManager::GetMatchingSongs()
523 {
524   if (!IsEnabled())
525     return -1;
526   return m_iMatchingSongs;
527 }
528
529 int CPartyModeManager::GetMatchingSongsPicked()
530 {
531   if (!IsEnabled())
532     return -1;
533   return m_iMatchingSongsPicked;
534 }
535
536 int CPartyModeManager::GetMatchingSongsLeft()
537 {
538   if (!IsEnabled())
539     return -1;
540   return m_iMatchingSongsLeft;
541 }
542
543 int CPartyModeManager::GetRelaxedSongs()
544 {
545   if (!IsEnabled())
546     return -1;
547   return m_iRelaxedSongs;
548 }
549
550 int CPartyModeManager::GetRandomSongs()
551 {
552   if (!IsEnabled())
553     return -1;
554   return m_iRandomSongs;
555 }
556
557 PartyModeContext CPartyModeManager::GetType() const
558 {
559   if (!IsEnabled())
560     return PARTYMODECONTEXT_UNKNOWN;
561
562   if (m_bIsVideo)
563     return PARTYMODECONTEXT_VIDEO;
564
565   return PARTYMODECONTEXT_MUSIC;
566 }
567
568 void CPartyModeManager::ClearState()
569 {
570   m_iLastUserSong = -1;
571   m_iSongsPlayed = 0;
572   m_iMatchingSongs = 0;
573   m_iMatchingSongsPicked = 0;
574   m_iMatchingSongsLeft = 0;
575   m_iRelaxedSongs = 0;
576   m_iRandomSongs = 0;
577
578   m_songsInHistory = 0;
579   m_history.clear();
580 }
581
582 void CPartyModeManager::UpdateStats()
583 {
584   m_iMatchingSongsLeft = m_iMatchingSongs - m_iMatchingSongsPicked;
585   m_iRandomSongs = m_iMatchingSongsPicked;
586   m_iRelaxedSongs = 0;  // unsupported at this stage
587 }
588
589 bool CPartyModeManager::AddInitialSongs(vector<pair<int,int> > &songIDs)
590 {
591   int iPlaylist = m_bIsVideo ? PLAYLIST_VIDEO : PLAYLIST_MUSIC;
592
593   CPlayList& playlist = g_playlistPlayer.GetPlaylist(iPlaylist);
594   int iMissingSongs = QUEUE_DEPTH - playlist.size();
595   if (iMissingSongs > 0)
596   {
597     // generate iMissingSongs random ids from songIDs
598     if (iMissingSongs > (int)songIDs.size())
599       return false; // can't do it if we have less songs than we need
600
601     vector<pair<int,int> > chosenSongIDs;
602     GetRandomSelection(songIDs, iMissingSongs, chosenSongIDs);
603     CStdString sqlWhereMusic = "songview.idSong IN (";
604     CStdString sqlWhereVideo = "idMVideo IN (";
605
606     for (vector< pair<int,int> >::iterator it = chosenSongIDs.begin(); it != chosenSongIDs.end(); it++)
607     {
608       CStdString song;
609       song.Format("%i,", it->second);
610       if (it->first == 1)
611         sqlWhereMusic += song;
612       if (it->first == 2)
613         sqlWhereVideo += song;
614     }
615     // add songs to fill queue
616     CFileItemList items;
617
618     if (sqlWhereMusic.size() > 26)
619     {
620       sqlWhereMusic[sqlWhereMusic.size() - 1] = ')'; // replace the last comma with closing bracket
621       CMusicDatabase database;
622       database.Open();
623       database.GetSongsByWhere("musicdb://songs/", sqlWhereMusic, items);
624     }
625     if (sqlWhereVideo.size() > 19)
626     {
627       sqlWhereVideo[sqlWhereVideo.size() - 1] = ')'; // replace the last comma with closing bracket
628       CVideoDatabase database;
629       database.Open();
630       database.GetMusicVideosByWhere("videodb://musicvideos/titles/", sqlWhereVideo, items);
631     }
632
633     m_history = chosenSongIDs;
634     items.Randomize(); //randomizing the initial list or they will be in database order
635     for (int i = 0; i < items.Size(); i++)
636     {
637       CFileItemPtr item(items[i]);
638       Add(item);
639       // TODO: Allow "relaxed restrictions" later?
640     }
641   }
642   return true;
643 }
644
645 pair<CStdString,CStdString> CPartyModeManager::GetWhereClauseWithHistory() const
646 {
647   CStdString historyWhereMusic;
648   CStdString historyWhereVideo;
649   // now add this on to the normal where clause
650   if (m_history.size())
651   {
652     if (m_strCurrentFilterMusic.IsEmpty())
653       historyWhereMusic = "songview.idSong not in (";
654     else
655       historyWhereMusic = m_strCurrentFilterMusic + " and songview.idSong not in (";
656     if (m_strCurrentFilterVideo.IsEmpty())
657       historyWhereVideo = "idMVideo not in (";
658     else
659       historyWhereVideo = m_strCurrentFilterVideo + " and idMVideo not in (";
660
661     for (unsigned int i = 0; i < m_history.size(); i++)
662     {
663       CStdString number;
664       number.Format("%i,", m_history[i].second);
665       if (m_history[i].first == 1)
666         historyWhereMusic += number;
667       if (m_history[i].first == 2)
668         historyWhereVideo += number;
669     }
670     historyWhereMusic.TrimRight(",");
671     historyWhereMusic += ")";
672     historyWhereVideo.TrimRight(",");
673     historyWhereVideo += ")";
674   }
675   return make_pair(historyWhereMusic,historyWhereVideo);
676 }
677
678 void CPartyModeManager::AddToHistory(int type, int songID)
679 {
680   while (m_history.size() >= m_songsInHistory && m_songsInHistory)
681     m_history.erase(m_history.begin());
682   m_history.push_back(make_pair(type,songID));
683 }
684
685 void CPartyModeManager::GetRandomSelection(vector< pair<int,int> >& in, unsigned int number, vector< pair<int,int> >& out)
686 {
687   number = min(number, (unsigned int)in.size());
688   random_shuffle(in.begin(), in.end());
689   out.assign(in.begin(), in.begin() + number);
690 }
691
692 bool CPartyModeManager::IsEnabled(PartyModeContext context /* = PARTYMODECONTEXT_UNKNOWN */) const
693 {
694   if (!m_bEnabled) return false;
695   if (context == PARTYMODECONTEXT_VIDEO)
696     return m_bIsVideo;
697   if (context == PARTYMODECONTEXT_MUSIC)
698     return !m_bIsVideo;
699   return true; // unknown, but we're enabled
700 }
701
702 void CPartyModeManager::Announce()
703 {
704   if (g_application.m_pPlayer->IsPlaying())
705   {
706     CVariant data;
707     
708     data["player"]["playerid"] = g_playlistPlayer.GetCurrentPlaylist();
709     data["property"]["partymode"] = m_bEnabled;
710     ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Player, "xbmc", "OnPropertyChanged", data);
711   }
712 }