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