[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / PartyModeManager.cpp
blob50de8a3286dc157470900ace477fa6ab366760c4
1 /*
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.
7 */
9 #include "PartyModeManager.h"
11 #include "FileItem.h"
12 #include "FileItemList.h"
13 #include "GUIUserMessages.h"
14 #include "PlayListPlayer.h"
15 #include "ServiceBroker.h"
16 #include "application/ApplicationComponents.h"
17 #include "application/ApplicationPlayer.h"
18 #include "dialogs/GUIDialogProgress.h"
19 #include "guilib/GUIComponent.h"
20 #include "guilib/GUIWindowManager.h"
21 #include "interfaces/AnnouncementManager.h"
22 #include "messaging/helpers/DialogOKHelper.h"
23 #include "music/MusicDatabase.h"
24 #include "music/tags/MusicInfoTag.h"
25 #include "playlists/PlayList.h"
26 #include "playlists/SmartPlayList.h"
27 #include "profiles/ProfileManager.h"
28 #include "settings/SettingsComponent.h"
29 #include "utils/Random.h"
30 #include "utils/StringUtils.h"
31 #include "utils/Variant.h"
32 #include "utils/log.h"
33 #include "video/VideoDatabase.h"
34 #include "video/VideoInfoTag.h"
36 #include <algorithm>
38 using namespace KODI;
39 using namespace KODI::MESSAGING;
41 #define QUEUE_DEPTH 10
43 CPartyModeManager::CPartyModeManager(void)
45 m_bIsVideo = false;
46 m_bEnabled = false;
47 ClearState();
50 bool CPartyModeManager::Enable(PartyModeContext context /*= PARTYMODECONTEXT_MUSIC*/, const std::string& strXspPath /*= ""*/)
52 // Filter using our PartyMode xml file
53 PLAYLIST::CSmartPlaylist playlist;
54 std::string partyModePath;
55 bool playlistLoaded;
57 m_bIsVideo = context == PARTYMODECONTEXT_VIDEO;
59 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
61 if (!strXspPath.empty()) //if a path to a smartplaylist is supplied use it
62 partyModePath = strXspPath;
63 else if (m_bIsVideo)
64 partyModePath = profileManager->GetUserDataItem("PartyMode-Video.xsp");
65 else
66 partyModePath = profileManager->GetUserDataItem("PartyMode.xsp");
68 playlistLoaded=playlist.Load(partyModePath);
70 if (playlistLoaded)
72 m_type = playlist.GetType();
73 if (context == PARTYMODECONTEXT_UNKNOWN)
75 //get it from the xsp file
76 m_bIsVideo = (StringUtils::EqualsNoCase(m_type, "video") ||
77 StringUtils::EqualsNoCase(m_type, "musicvideos") ||
78 StringUtils::EqualsNoCase(m_type, "mixed"));
81 else if (m_bIsVideo)
82 m_type = "musicvideos";
83 else
84 m_type = "songs";
86 CGUIDialogProgress* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
87 int iHeading = (m_bIsVideo ? 20250 : 20121);
88 int iLine0 = (m_bIsVideo ? 20251 : 20123);
89 pDialog->SetHeading(CVariant{iHeading});
90 pDialog->SetLine(0, CVariant{iLine0});
91 pDialog->SetLine(1, CVariant{""});
92 pDialog->SetLine(2, CVariant{""});
93 pDialog->Open();
95 ClearState();
96 std::string strCurrentFilterMusic;
97 std::string strCurrentFilterVideo;
98 unsigned int songcount = 0;
99 unsigned int videocount = 0;
100 auto start = std::chrono::steady_clock::now();
102 if (StringUtils::EqualsNoCase(m_type, "songs") ||
103 StringUtils::EqualsNoCase(m_type, "mixed"))
105 CMusicDatabase db;
106 if (db.Open())
108 std::set<std::string> playlists;
109 if (playlistLoaded)
111 playlist.SetType("songs");
112 strCurrentFilterMusic = playlist.GetWhereClause(db, playlists);
115 CLog::Log(LOGINFO, "PARTY MODE MANAGER: Registering filter:[{}]", strCurrentFilterMusic);
116 songcount = db.GetRandomSongIDs(CDatabase::Filter(strCurrentFilterMusic), m_songIDCache);
117 m_iMatchingSongs = static_cast<int>(songcount);
118 if (m_iMatchingSongs < 1 && StringUtils::EqualsNoCase(m_type, "songs"))
120 pDialog->Close();
121 db.Close();
122 OnError(16031, "Party mode found no matching songs. Aborting.");
123 return false;
126 else
128 pDialog->Close();
129 OnError(16033, "Party mode could not open database. Aborting.");
130 return false;
132 db.Close();
135 if (StringUtils::EqualsNoCase(m_type, "musicvideos") ||
136 StringUtils::EqualsNoCase(m_type, "mixed"))
138 std::vector< std::pair<int,int> > songIDs2;
139 CVideoDatabase db;
140 if (db.Open())
142 std::set<std::string> playlists;
143 if (playlistLoaded)
145 playlist.SetType("musicvideos");
146 strCurrentFilterVideo = playlist.GetWhereClause(db, playlists);
149 CLog::Log(LOGINFO, "PARTY MODE MANAGER: Registering filter:[{}]", strCurrentFilterVideo);
150 videocount = db.GetRandomMusicVideoIDs(strCurrentFilterVideo, songIDs2);
151 m_iMatchingSongs += static_cast<int>(videocount);
152 if (m_iMatchingSongs < 1)
154 pDialog->Close();
155 db.Close();
156 OnError(16031, "Party mode found no matching songs. Aborting.");
157 return false;
160 else
162 pDialog->Close();
163 OnError(16033, "Party mode could not open database. Aborting.");
164 return false;
166 db.Close();
167 m_songIDCache.insert(m_songIDCache.end(), songIDs2.begin(), songIDs2.end());
170 // Songs and music videos are random from query, but need mixing together when have both
171 if (songcount > 0 && videocount > 0 )
172 KODI::UTILS::RandomShuffle(m_songIDCache.begin(), m_songIDCache.end());
174 CLog::Log(LOGINFO,"PARTY MODE MANAGER: Matching songs = {0}", m_iMatchingSongs);
175 CLog::Log(LOGINFO,"PARTY MODE MANAGER: Party mode enabled!");
177 PLAYLIST::Id playlistId = GetPlaylistId();
179 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(playlistId);
180 CServiceBroker::GetPlaylistPlayer().SetShuffle(playlistId, false);
181 CServiceBroker::GetPlaylistPlayer().SetRepeat(playlistId, PLAYLIST::RepeatState::NONE);
183 pDialog->SetLine(0, CVariant{m_bIsVideo ? 20252 : 20124});
184 pDialog->Progress();
185 // add initial songs
186 if (!AddRandomSongs())
188 pDialog->Close();
189 return false;
192 auto end = std::chrono::steady_clock::now();
193 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
194 CLog::Log(LOGDEBUG, "{} time for song fetch: {} ms", __FUNCTION__, duration.count());
196 // start playing
197 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(playlistId);
198 Play(0);
200 pDialog->Close();
201 // open now playing window
202 if (StringUtils::EqualsNoCase(m_type, "songs"))
204 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() != WINDOW_MUSIC_PLAYLIST)
205 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_MUSIC_PLAYLIST);
208 // done
209 m_bEnabled = true;
210 Announce();
211 return true;
214 void CPartyModeManager::Disable()
216 if (!IsEnabled())
217 return;
218 m_bEnabled = false;
219 Announce();
220 CLog::Log(LOGINFO,"PARTY MODE MANAGER: Party mode disabled.");
223 void CPartyModeManager::OnSongChange(bool bUpdatePlayed /* = false */)
225 if (!IsEnabled())
226 return;
227 Process();
228 if (bUpdatePlayed)
229 m_iSongsPlayed++;
232 void CPartyModeManager::AddUserSongs(PLAYLIST::CPlayList& tempList, bool bPlay /* = false */)
234 if (!IsEnabled())
235 return;
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
244 int iNewUserSongs = tempList.size();
245 CLog::Log(LOGINFO, "PARTY MODE MANAGER: Adding {} user selected songs at {}", iNewUserSongs,
246 iAddAt);
248 CServiceBroker::GetPlaylistPlayer().GetPlaylist(GetPlaylistId()).Insert(tempList, iAddAt);
250 // update last user added song location
251 if (m_iLastUserSong < 0)
252 m_iLastUserSong = 0;
253 m_iLastUserSong += iNewUserSongs;
255 if (bPlay)
256 Play(1);
259 void CPartyModeManager::AddUserSongs(CFileItemList& tempList, bool bPlay /* = false */)
261 if (!IsEnabled())
262 return;
264 // where do we add?
265 int iAddAt = -1;
266 if (m_iLastUserSong < 0 || bPlay)
267 iAddAt = 1; // under the currently playing song
268 else
269 iAddAt = m_iLastUserSong + 1; // under the last user added song
271 int iNewUserSongs = tempList.Size();
272 CLog::Log(LOGINFO, "PARTY MODE MANAGER: Adding {} user selected songs at {}", iNewUserSongs,
273 iAddAt);
275 CServiceBroker::GetPlaylistPlayer().GetPlaylist(GetPlaylistId()).Insert(tempList, iAddAt);
277 // update last user added song location
278 if (m_iLastUserSong < 0)
279 m_iLastUserSong = 0;
280 m_iLastUserSong += iNewUserSongs;
282 if (bPlay)
283 Play(1);
286 void CPartyModeManager::Process()
288 ReapSongs();
289 MovePlaying();
290 AddRandomSongs();
291 UpdateStats();
292 SendUpdateMessage();
295 bool CPartyModeManager::AddRandomSongs()
297 // All songs have been picked, no more to add
298 if (static_cast<int>(m_songIDCache.size()) == m_iMatchingSongsPicked)
299 return false;
301 PLAYLIST::CPlayList& playlist = CServiceBroker::GetPlaylistPlayer().GetPlaylist(GetPlaylistId());
302 int iMissingSongs = QUEUE_DEPTH - playlist.size();
304 if (iMissingSongs > 0)
306 // Limit songs fetched to remainder of songID cache
307 iMissingSongs = std::min(iMissingSongs, static_cast<int>(m_songIDCache.size()) - m_iMatchingSongsPicked);
309 // Pick iMissingSongs from remaining songID cache
310 std::string sqlWhereMusic = "songview.idSong IN (";
311 std::string sqlWhereVideo = "idMVideo IN (";
313 bool bSongs = false;
314 bool bMusicVideos = false;
315 for (int i = m_iMatchingSongsPicked; i < m_iMatchingSongsPicked + iMissingSongs; i++)
317 std::string song = StringUtils::Format("{},", m_songIDCache[i].second);
318 if (m_songIDCache[i].first == 1)
320 sqlWhereMusic += song;
321 bSongs = true;
323 else if (m_songIDCache[i].first == 2)
325 sqlWhereVideo += song;
326 bMusicVideos = true;
329 CFileItemList items;
331 if (bSongs)
333 sqlWhereMusic.back() = ')'; // replace the last comma with closing bracket
334 // Apply random sort (and limit) at db query for efficiency
335 SortDescription SortDescription;
336 SortDescription.sortBy = SortByRandom;
337 SortDescription.limitEnd = QUEUE_DEPTH;
338 CMusicDatabase database;
339 if (database.Open())
341 database.GetSongsFullByWhere("musicdb://songs/", CDatabase::Filter(sqlWhereMusic),
342 items, SortDescription, true);
344 // Get artist and album properties for songs
345 for (auto& item : items)
346 database.SetPropertiesForFileItem(*item);
347 database.Close();
349 else
351 OnError(16033, "Party mode could not open database. Aborting.");
352 return false;
355 if (bMusicVideos)
357 sqlWhereVideo.back() = ')'; // replace the last comma with closing bracket
358 CVideoDatabase database;
359 if (database.Open())
361 database.GetMusicVideosByWhere("videodb://musicvideos/titles/",
362 CDatabase::Filter(sqlWhereVideo), items);
363 database.Close();
365 else
367 OnError(16033, "Party mode could not open database. Aborting.");
368 return false;
372 // Randomize if the list has music videos or they will be in db order
373 // Songs only are already random.
374 if (bMusicVideos)
375 items.Randomize();
376 for (const auto& item : items)
378 // Update songID cache with order items in playlist
379 if (item->HasMusicInfoTag())
381 m_songIDCache[m_iMatchingSongsPicked].first = 1;
382 m_songIDCache[m_iMatchingSongsPicked].second = item->GetMusicInfoTag()->GetDatabaseId();
384 else if (item->HasVideoInfoTag())
386 m_songIDCache[m_iMatchingSongsPicked].first = 2;
387 m_songIDCache[m_iMatchingSongsPicked].second = item->GetVideoInfoTag()->m_iDbId;
389 CFileItemPtr pItem(item);
390 Add(pItem); // inc m_iMatchingSongsPicked
393 return true;
396 void CPartyModeManager::Add(CFileItemPtr &pItem)
398 PLAYLIST::CPlayList& playlist = CServiceBroker::GetPlaylistPlayer().GetPlaylist(GetPlaylistId());
399 playlist.Add(pItem);
400 CLog::Log(LOGINFO, "PARTY MODE MANAGER: Adding randomly selected song at {}:[{}]",
401 playlist.size() - 1, pItem->GetPath());
402 m_iMatchingSongsPicked++;
405 bool CPartyModeManager::ReapSongs()
407 const PLAYLIST::Id playlistId = GetPlaylistId();
409 // reap any played songs
410 int iCurrentSong = CServiceBroker::GetPlaylistPlayer().GetCurrentItemIdx();
411 int i=0;
412 while (i < CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId).size())
414 if (i < iCurrentSong)
416 CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId).Remove(i);
417 iCurrentSong--;
418 if (i <= m_iLastUserSong)
419 m_iLastUserSong--;
421 else
422 i++;
425 CServiceBroker::GetPlaylistPlayer().SetCurrentItemIdx(iCurrentSong);
426 return true;
429 bool CPartyModeManager::MovePlaying()
431 // move current song to the top if its not there
432 int iCurrentSong = CServiceBroker::GetPlaylistPlayer().GetCurrentItemIdx();
434 if (iCurrentSong > 0)
436 CLog::Log(LOGINFO, "PARTY MODE MANAGER: Moving currently playing song from {} to 0",
437 iCurrentSong);
438 PLAYLIST::CPlayList& playlist =
439 CServiceBroker::GetPlaylistPlayer().GetPlaylist(GetPlaylistId());
440 PLAYLIST::CPlayList playlistTemp;
441 playlistTemp.Add(playlist[iCurrentSong]);
442 playlist.Remove(iCurrentSong);
443 for (int i=0; i<playlist.size(); i++)
444 playlistTemp.Add(playlist[i]);
445 playlist.Clear();
446 for (int i=0; i<playlistTemp.size(); i++)
447 playlist.Add(playlistTemp[i]);
449 CServiceBroker::GetPlaylistPlayer().SetCurrentItemIdx(0);
450 return true;
453 void CPartyModeManager::SendUpdateMessage()
455 CGUIMessage msg(GUI_MSG_PLAYLIST_CHANGED, 0, 0);
456 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
459 void CPartyModeManager::Play(int iPos)
461 // Move current song to the top if its not there. Playlist filled up below by
462 // OnSongChange call from application GUI_MSG_PLAYBACK_STARTED processing
463 CServiceBroker::GetPlaylistPlayer().Play(iPos, "");
464 CLog::Log(LOGINFO, "PARTY MODE MANAGER: Playing song at {}", iPos);
467 void CPartyModeManager::OnError(int iError, const std::string& strLogMessage)
469 // open error dialog
470 HELPERS::ShowOKDialogLines(CVariant{257}, CVariant{16030}, CVariant{iError}, CVariant{0});
471 CLog::Log(LOGERROR, "PARTY MODE MANAGER: {}", strLogMessage);
472 m_bEnabled = false;
473 SendUpdateMessage();
476 int CPartyModeManager::GetSongsPlayed()
478 if (!IsEnabled())
479 return -1;
480 return m_iSongsPlayed;
483 int CPartyModeManager::GetMatchingSongs()
485 if (!IsEnabled())
486 return -1;
487 return m_iMatchingSongs;
490 int CPartyModeManager::GetMatchingSongsPicked()
492 if (!IsEnabled())
493 return -1;
494 return m_iMatchingSongsPicked;
497 int CPartyModeManager::GetMatchingSongsLeft()
499 if (!IsEnabled())
500 return -1;
501 return m_iMatchingSongsLeft;
504 int CPartyModeManager::GetRelaxedSongs()
506 if (!IsEnabled())
507 return -1;
508 return m_iRelaxedSongs;
511 int CPartyModeManager::GetRandomSongs()
513 if (!IsEnabled())
514 return -1;
515 return m_iRandomSongs;
518 PartyModeContext CPartyModeManager::GetType() const
520 if (!IsEnabled())
521 return PARTYMODECONTEXT_UNKNOWN;
523 if (m_bIsVideo)
524 return PARTYMODECONTEXT_VIDEO;
526 return PARTYMODECONTEXT_MUSIC;
529 void CPartyModeManager::ClearState()
531 m_iLastUserSong = -1;
532 m_iSongsPlayed = 0;
533 m_iMatchingSongs = 0;
534 m_iMatchingSongsPicked = 0;
535 m_iMatchingSongsLeft = 0;
536 m_iRelaxedSongs = 0;
537 m_iRandomSongs = 0;
539 m_songIDCache.clear();
542 void CPartyModeManager::UpdateStats()
544 m_iMatchingSongsLeft = m_iMatchingSongs - m_iMatchingSongsPicked;
545 m_iRandomSongs = m_iMatchingSongsPicked;
546 m_iRelaxedSongs = 0; // unsupported at this stage
549 bool CPartyModeManager::IsEnabled(PartyModeContext context /* = PARTYMODECONTEXT_UNKNOWN */) const
551 if (!m_bEnabled) return false;
552 if (context == PARTYMODECONTEXT_VIDEO)
553 return m_bIsVideo;
554 if (context == PARTYMODECONTEXT_MUSIC)
555 return !m_bIsVideo;
556 return true; // unknown, but we're enabled
559 void CPartyModeManager::Announce()
561 const auto& components = CServiceBroker::GetAppComponents();
562 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
563 if (appPlayer->IsPlaying())
565 CVariant data;
567 data["player"]["playerid"] =
568 static_cast<int>(CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist());
569 data["property"]["partymode"] = m_bEnabled;
570 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnPropertyChanged",
571 data);
575 PLAYLIST::Id CPartyModeManager::GetPlaylistId() const
577 return m_bIsVideo ? PLAYLIST::Id::TYPE_VIDEO : PLAYLIST::Id::TYPE_MUSIC;