1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "PlayerCoreFactory.h"
10 
11 #include "FileItem.h"
12 #include "PlayerCoreConfig.h"
13 #include "PlayerSelectionRule.h"
14 #include "URL.h"
15 #include "cores/IPlayerCallback.h"
16 #include "cores/paplayer/PAPlayer.h"
17 #include "dialogs/GUIDialogContextMenu.h"
18 #include "guilib/LocalizeStrings.h"
19 #include "profiles/ProfileManager.h"
20 #include "settings/AdvancedSettings.h"
21 #include "settings/Settings.h"
22 #include "settings/SettingsComponent.h"
23 #include "settings/lib/SettingsManager.h"
24 #include "threads/SingleLock.h"
25 #include "utils/StringUtils.h"
26 #include "utils/XMLUtils.h"
27 
28 #include <sstream>
29 
30 #define PLAYERCOREFACTORY_XML "playercorefactory.xml"
31 
CPlayerCoreFactory(const CProfileManager & profileManager)32 CPlayerCoreFactory::CPlayerCoreFactory(const CProfileManager &profileManager) :
33   m_profileManager(profileManager)
34 {
35   m_settings = CServiceBroker::GetSettingsComponent()->GetSettings();
36 
37   if (m_settings->IsLoaded())
38     OnSettingsLoaded();
39 
40   m_settings->GetSettingsManager()->RegisterSettingsHandler(this);
41 }
42 
~CPlayerCoreFactory()43 CPlayerCoreFactory::~CPlayerCoreFactory()
44 {
45   m_settings->GetSettingsManager()->UnregisterSettingsHandler(this);
46 
47   for(std::vector<CPlayerCoreConfig *>::iterator it = m_vecPlayerConfigs.begin(); it != m_vecPlayerConfigs.end(); ++it)
48     delete *it;
49   for(std::vector<CPlayerSelectionRule *>::iterator it = m_vecCoreSelectionRules.begin(); it != m_vecCoreSelectionRules.end(); ++it)
50     delete *it;
51 }
52 
OnSettingsLoaded()53 void CPlayerCoreFactory::OnSettingsLoaded()
54 {
55   LoadConfiguration("special://xbmc/system/" PLAYERCOREFACTORY_XML, true);
56   LoadConfiguration(m_profileManager.GetUserDataItem(PLAYERCOREFACTORY_XML), false);
57 }
58 
CreatePlayer(const std::string & nameId,IPlayerCallback & callback) const59 IPlayer* CPlayerCoreFactory::CreatePlayer(const std::string& nameId, IPlayerCallback& callback) const
60 {
61   CSingleLock lock(m_section);
62   size_t idx = GetPlayerIndex(nameId);
63 
64   if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size())
65     return nullptr;
66 
67   return m_vecPlayerConfigs[idx]->CreatePlayer(callback);
68 }
69 
GetPlayers(std::vector<std::string> & players) const70 void CPlayerCoreFactory::GetPlayers(std::vector<std::string>&players) const
71 {
72   CSingleLock lock(m_section);
73   players.clear();
74   for (auto conf: m_vecPlayerConfigs)
75   {
76     if (conf->m_bPlaysAudio || conf->m_bPlaysVideo)
77       players.push_back(conf->m_name);
78   }
79 }
80 
GetPlayers(std::vector<std::string> & players,const bool audio,const bool video) const81 void CPlayerCoreFactory::GetPlayers(std::vector<std::string>&players, const bool audio, const bool video) const
82 {
83   CSingleLock lock(m_section);
84   CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: for video=%d, audio=%d", video, audio);
85 
86   for (auto conf: m_vecPlayerConfigs)
87   {
88     if (audio == conf->m_bPlaysAudio && video == conf->m_bPlaysVideo)
89     {
90       if (std::find(players.begin(), players.end(), conf->m_name) != players.end())
91         continue;
92 
93       CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding player: %s", conf->m_name.c_str());
94       players.push_back(conf->m_name);
95     }
96   }
97 }
98 
GetPlayers(const CFileItem & item,std::vector<std::string> & players) const99 void CPlayerCoreFactory::GetPlayers(const CFileItem& item, std::vector<std::string>&players) const
100 {
101   CURL url(item.GetDynPath());
102 
103   CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers(%s)", CURL::GetRedacted(item.GetDynPath()).c_str());
104 
105   std::vector<std::string>validPlayers;
106   GetPlayers(validPlayers);
107 
108   // Process rules
109   for (auto rule: m_vecCoreSelectionRules)
110     rule->GetPlayers(item, validPlayers, players);
111 
112   CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: matched {0} rules with players", players.size());
113 
114   // Process defaults
115 
116   // Set video default player. Check whether it's video first (overrule audio and
117   // game check). Also push these players in case it is NOT audio or game either.
118   if (item.IsVideo() || (!item.IsAudio() && !item.IsGame()))
119   {
120     int idx = GetPlayerIndex("videodefaultplayer");
121     if (idx > -1)
122     {
123       std::string eVideoDefault = GetPlayerName(idx);
124       CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding videodefaultplayer (%s)", eVideoDefault.c_str());
125       players.push_back(eVideoDefault);
126     }
127     GetPlayers(players, false, true);  // Video-only players
128     GetPlayers(players, true, true);   // Audio & video players
129   }
130 
131   // Set audio default player
132   // Pushback all audio players in case we don't know the type
133   if (item.IsAudio())
134   {
135     int idx = GetPlayerIndex("audiodefaultplayer");
136     if (idx > -1)
137     {
138       std::string eAudioDefault = GetPlayerName(idx);
139       CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding audiodefaultplayer (%s)", eAudioDefault.c_str());
140         players.push_back(eAudioDefault);
141     }
142     GetPlayers(players, true, false); // Audio-only players
143     GetPlayers(players, true, true);  // Audio & video players
144   }
145 
146   if (item.IsGame())
147   {
148     CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: adding retroplayer");
149     players.emplace_back("RetroPlayer");
150   }
151 
152   CLog::Log(LOGDEBUG, "CPlayerCoreFactory::GetPlayers: added {0} players", players.size());
153 }
154 
GetPlayerIndex(const std::string & strCoreName) const155 int CPlayerCoreFactory::GetPlayerIndex(const std::string& strCoreName) const
156 {
157   CSingleLock lock(m_section);
158   if (!strCoreName.empty())
159   {
160     // Dereference "*default*player" aliases
161     std::string strRealCoreName;
162     if (StringUtils::EqualsNoCase(strCoreName, "audiodefaultplayer"))
163       strRealCoreName = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_audioDefaultPlayer;
164     else if (StringUtils::EqualsNoCase(strCoreName, "videodefaultplayer"))
165       strRealCoreName = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoDefaultPlayer;
166     else
167       strRealCoreName = strCoreName;
168 
169     for(size_t i = 0; i < m_vecPlayerConfigs.size(); i++)
170     {
171       if (StringUtils::EqualsNoCase(m_vecPlayerConfigs[i]->GetName(), strRealCoreName))
172         return i;
173     }
174     CLog::Log(LOGWARNING, "CPlayerCoreFactory::GetPlayer(%s): no such player: %s", strCoreName.c_str(), strRealCoreName.c_str());
175   }
176   return -1;
177 }
178 
GetPlayerName(size_t idx) const179 std::string CPlayerCoreFactory::GetPlayerName(size_t idx) const
180 {
181   CSingleLock lock(m_section);
182   if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size())
183     return "";
184 
185   return m_vecPlayerConfigs[idx]->m_name;
186 }
187 
GetPlayers(std::vector<std::string> & players,std::string & type) const188 void CPlayerCoreFactory::GetPlayers(std::vector<std::string>&players, std::string &type) const
189 {
190   CSingleLock lock(m_section);
191   for (auto config: m_vecPlayerConfigs)
192   {
193     if (config->m_type != type)
194       continue;
195     players.push_back(config->m_name);
196   }
197 }
198 
GetRemotePlayers(std::vector<std::string> & players) const199 void CPlayerCoreFactory::GetRemotePlayers(std::vector<std::string>&players) const
200 {
201   CSingleLock lock(m_section);
202   for (auto config: m_vecPlayerConfigs)
203   {
204     if (config->m_type != "remote")
205       continue;
206     players.push_back(config->m_name);
207   }
208 }
209 
GetPlayerType(const std::string & player) const210 std::string CPlayerCoreFactory::GetPlayerType(const std::string& player) const
211 {
212   CSingleLock lock(m_section);
213   size_t idx = GetPlayerIndex(player);
214 
215   if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size())
216     return "";
217 
218   return m_vecPlayerConfigs[idx]->m_type;
219 }
220 
PlaysAudio(const std::string & player) const221 bool CPlayerCoreFactory::PlaysAudio(const std::string& player) const
222 {
223   CSingleLock lock(m_section);
224   size_t idx = GetPlayerIndex(player);
225 
226   if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size())
227     return false;
228 
229   return m_vecPlayerConfigs[idx]->m_bPlaysAudio;
230 }
231 
PlaysVideo(const std::string & player) const232 bool CPlayerCoreFactory::PlaysVideo(const std::string& player) const
233 {
234   CSingleLock lock(m_section);
235   size_t idx = GetPlayerIndex(player);
236 
237   if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size())
238     return false;
239 
240   return m_vecPlayerConfigs[idx]->m_bPlaysVideo;
241 }
242 
GetDefaultPlayer(const CFileItem & item) const243 std::string CPlayerCoreFactory::GetDefaultPlayer(const CFileItem& item) const
244 {
245   std::vector<std::string>players;
246   GetPlayers(item, players);
247 
248   //If we have any players return the first one
249   if (!players.empty())
250     return players.at(0);
251 
252   return "";
253 }
254 
SelectPlayerDialog(const std::vector<std::string> & players,float posX,float posY) const255 std::string CPlayerCoreFactory::SelectPlayerDialog(const std::vector<std::string>&players, float posX, float posY) const
256 {
257   CContextButtons choices;
258   if (players.size())
259   {
260     //Add default player
261     std::string strCaption = players[0];
262     strCaption += " (";
263     strCaption += g_localizeStrings.Get(13278);
264     strCaption += ")";
265     choices.Add(0, strCaption);
266 
267     //Add all other players
268     for (unsigned int i = 1; i < players.size(); i++)
269       choices.Add(i, players[i]);
270 
271     int choice = CGUIDialogContextMenu::ShowAndGetChoice(choices);
272     if (choice >= 0)
273       return players[choice];
274   }
275   return "";
276 }
277 
SelectPlayerDialog(float posX,float posY) const278 std::string CPlayerCoreFactory::SelectPlayerDialog(float posX, float posY) const
279 {
280   std::vector<std::string>players;
281   GetPlayers(players);
282   return SelectPlayerDialog(players, posX, posY);
283 }
284 
LoadConfiguration(const std::string & file,bool clear)285 bool CPlayerCoreFactory::LoadConfiguration(const std::string &file, bool clear)
286 {
287   CSingleLock lock(m_section);
288 
289   CLog::Log(LOGINFO, "Loading player core factory settings from %s.", file.c_str());
290   if (!XFILE::CFile::Exists(file))
291   { // tell the user it doesn't exist
292     CLog::Log(LOGINFO, "%s does not exist. Skipping.", file.c_str());
293     return false;
294   }
295 
296   CXBMCTinyXML playerCoreFactoryXML;
297   if (!playerCoreFactoryXML.LoadFile(file))
298   {
299     CLog::Log(LOGERROR, "Error loading %s, Line %d (%s)", file.c_str(), playerCoreFactoryXML.ErrorRow(), playerCoreFactoryXML.ErrorDesc());
300     return false;
301   }
302 
303   TiXmlElement *pConfig = playerCoreFactoryXML.RootElement();
304   if (pConfig == NULL)
305   {
306     CLog::Log(LOGERROR, "Error loading %s, Bad structure", file.c_str());
307     return false;
308   }
309 
310   if (clear)
311   {
312     for (auto config: m_vecPlayerConfigs)
313       delete config;
314     m_vecPlayerConfigs.clear();
315 
316     for (auto rule: m_vecCoreSelectionRules)
317       delete rule;
318     m_vecCoreSelectionRules.clear();
319 
320     // Builtin players
321     CPlayerCoreConfig* VideoPlayer = new CPlayerCoreConfig("VideoPlayer", "video", nullptr);
322     VideoPlayer->m_bPlaysAudio = true;
323     VideoPlayer->m_bPlaysVideo = true;
324     m_vecPlayerConfigs.push_back(VideoPlayer);
325 
326     CPlayerCoreConfig* paplayer = new CPlayerCoreConfig("PAPlayer", "music", nullptr);
327     paplayer->m_bPlaysAudio = true;
328     m_vecPlayerConfigs.push_back(paplayer);
329 
330     CPlayerCoreConfig* retroPlayer = new CPlayerCoreConfig("RetroPlayer", "game", nullptr);
331     m_vecPlayerConfigs.push_back(retroPlayer);
332   }
333 
334   if (!pConfig || StringUtils::CompareNoCase(pConfig->Value(), "playercorefactory") != 0)
335   {
336     CLog::Log(LOGERROR, "Error loading configuration, no <playercorefactory> node");
337     return false;
338   }
339 
340   TiXmlElement *pPlayers = pConfig->FirstChildElement("players");
341   if (pPlayers)
342   {
343     TiXmlElement* pPlayer = pPlayers->FirstChildElement("player");
344     while (pPlayer)
345     {
346       std::string name = XMLUtils::GetAttribute(pPlayer, "name");
347       std::string type = XMLUtils::GetAttribute(pPlayer, "type");
348       if (type.empty()) type = name;
349       StringUtils::ToLower(type);
350 
351       std::string internaltype;
352       if (type == "videoplayer")
353         internaltype = "video";
354       else if (type == "paplayer")
355         internaltype = "music";
356       else if (type == "externalplayer")
357         internaltype = "external";
358 
359       int count = 0;
360       std::string playername = name;
361       while (GetPlayerIndex(playername) >= 0)
362       {
363         count++;
364         std::stringstream itoa;
365         itoa << count;
366         playername = name + itoa.str();
367       }
368 
369       if (!internaltype.empty())
370       {
371         m_vecPlayerConfigs.push_back(new CPlayerCoreConfig(playername, internaltype, pPlayer));
372       }
373 
374       pPlayer = pPlayer->NextSiblingElement("player");
375     }
376   }
377 
378   TiXmlElement *pRule = pConfig->FirstChildElement("rules");
379   while (pRule)
380   {
381     const char* szAction = pRule->Attribute("action");
382     if (szAction)
383     {
384       if (StringUtils::CompareNoCase(szAction, "append") == 0)
385       {
386         m_vecCoreSelectionRules.push_back(new CPlayerSelectionRule(pRule));
387       }
388       else if (StringUtils::CompareNoCase(szAction, "prepend") == 0)
389       {
390         m_vecCoreSelectionRules.insert(m_vecCoreSelectionRules.begin(), 1, new CPlayerSelectionRule(pRule));
391       }
392       else
393       {
394         m_vecCoreSelectionRules.clear();
395         m_vecCoreSelectionRules.push_back(new CPlayerSelectionRule(pRule));
396       }
397     }
398     else
399     {
400       m_vecCoreSelectionRules.push_back(new CPlayerSelectionRule(pRule));
401     }
402 
403     pRule = pRule->NextSiblingElement("rules");
404   }
405 
406   // succeeded - tell the user it worked
407   CLog::Log(LOGINFO, "Loaded playercorefactory configuration");
408 
409   return true;
410 }
411 
OnPlayerDiscovered(const std::string & id,const std::string & name)412 void CPlayerCoreFactory::OnPlayerDiscovered(const std::string& id, const std::string& name)
413 {
414   CSingleLock lock(m_section);
415   std::vector<CPlayerCoreConfig *>::iterator it;
416   for (it = m_vecPlayerConfigs.begin(); it != m_vecPlayerConfigs.end(); ++it)
417   {
418     if ((*it)->GetId() == id)
419     {
420       (*it)->m_name  = name;
421       (*it)->m_type = "remote";
422       return;
423     }
424   }
425 
426   int count = 0;
427   std::string playername = name;
428   while (GetPlayerIndex(playername) >= 0)
429   {
430     count++;
431     std::stringstream itoa;
432     itoa << count;
433     playername = name + itoa.str();
434   }
435 
436   CPlayerCoreConfig* player = new CPlayerCoreConfig(playername, "remote", nullptr, id);
437   player->m_bPlaysAudio = true;
438   player->m_bPlaysVideo = true;
439   m_vecPlayerConfigs.push_back(player);
440 }
441 
OnPlayerRemoved(const std::string & id)442 void CPlayerCoreFactory::OnPlayerRemoved(const std::string& id)
443 {
444   CSingleLock lock(m_section);
445   std::vector<CPlayerCoreConfig *>::iterator it;
446   for(it = m_vecPlayerConfigs.begin(); it != m_vecPlayerConfigs.end(); ++it)
447   {
448     if ((*it)->GetId() == id)
449       (*it)->m_type = "";
450   }
451 }
452