Merge pull request #26264 from the-black-eagle/mka_end_durations
[xbmc.git] / xbmc / addons / Addon.cpp
blob898e5fb6f1ae77a85c0dc24f2731c3b3fd88b6eb
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 "Addon.h"
11 #include "ServiceBroker.h"
12 #include "addons/AddonManager.h"
13 #include "addons/RepositoryUpdater.h"
14 #include "addons/addoninfo/AddonInfo.h"
15 #include "addons/addoninfo/AddonType.h"
16 #include "addons/settings/AddonSettings.h"
17 #include "filesystem/Directory.h"
18 #include "filesystem/File.h"
19 #include "settings/Settings.h"
20 #include "settings/lib/Setting.h"
21 #include "utils/StringUtils.h"
22 #include "utils/URIUtils.h"
23 #include "utils/XMLUtils.h"
24 #include "utils/log.h"
26 #include <algorithm>
27 #include <ostream>
28 #include <string.h>
29 #include <utility>
30 #include <vector>
32 #ifdef HAS_PYTHON
33 #include "interfaces/python/XBPython.h"
34 #endif
36 using XFILE::CDirectory;
37 using XFILE::CFile;
39 namespace ADDON
42 CAddon::CAddon(const AddonInfoPtr& addonInfo, AddonType addonType)
43 : m_addonInfo(addonInfo),
44 m_type(addonType == AddonType::UNKNOWN ? addonInfo->MainType() : addonType)
48 AddonType CAddon::MainType() const
50 return m_addonInfo->MainType();
53 bool CAddon::HasType(AddonType type) const
55 return m_addonInfo->HasType(type);
58 bool CAddon::HasMainType(AddonType type) const
60 return m_addonInfo->HasType(type, true);
63 const CAddonType* CAddon::Type(AddonType type) const
65 return m_addonInfo->Type(type);
68 std::string CAddon::ID() const
70 return m_addonInfo->ID();
73 std::string CAddon::Name() const
75 return m_addonInfo->Name();
78 bool CAddon::IsBinary() const
80 return m_addonInfo->IsBinary();
83 CAddonVersion CAddon::Version() const
85 return m_addonInfo->Version();
88 CAddonVersion CAddon::MinVersion() const
90 return m_addonInfo->MinVersion();
93 std::string CAddon::Summary() const
95 return m_addonInfo->Summary();
98 std::string CAddon::Description() const
100 return m_addonInfo->Description();
103 std::string CAddon::Path() const
105 return m_addonInfo->Path();
108 std::string CAddon::Profile() const
110 return m_addonInfo->ProfilePath();
113 std::string CAddon::Author() const
115 return m_addonInfo->Author();
118 std::string CAddon::ChangeLog() const
120 return m_addonInfo->ChangeLog();
123 std::string CAddon::Icon() const
125 return m_addonInfo->Icon();
128 ArtMap CAddon::Art() const
130 return m_addonInfo->Art();
133 std::vector<std::string> CAddon::Screenshots() const
135 return m_addonInfo->Screenshots();
138 std::string CAddon::Disclaimer() const
140 return m_addonInfo->Disclaimer();
143 AddonLifecycleState CAddon::LifecycleState() const
145 return m_addonInfo->LifecycleState();
148 std::string CAddon::LifecycleStateDescription() const
150 return m_addonInfo->LifecycleStateDescription();
153 CDateTime CAddon::InstallDate() const
155 return m_addonInfo->InstallDate();
158 CDateTime CAddon::LastUpdated() const
160 return m_addonInfo->LastUpdated();
163 CDateTime CAddon::LastUsed() const
165 return m_addonInfo->LastUsed();
168 std::string CAddon::Origin() const
170 return m_addonInfo->Origin();
173 std::string CAddon::OriginName() const
175 return m_addonInfo->OriginName();
178 uint64_t CAddon::PackageSize() const
180 return m_addonInfo->PackageSize();
183 const InfoMap& CAddon::ExtraInfo() const
185 return m_addonInfo->ExtraInfo();
188 const std::vector<DependencyInfo>& CAddon::GetDependencies() const
190 return m_addonInfo->GetDependencies();
193 std::string CAddon::FanArt() const
195 auto it = m_addonInfo->Art().find("fanart");
196 return it != m_addonInfo->Art().end() ? it->second : "";
199 bool CAddon::MeetsVersion(const CAddonVersion& versionMin, const CAddonVersion& version) const
201 return m_addonInfo->MeetsVersion(versionMin, version);
205 * Settings Handling
208 std::vector<AddonInstanceId> CAddon::GetKnownInstanceIds() const
210 return m_addonInfo->GetKnownInstanceIds();
213 bool CAddon::SupportsMultipleInstances() const
215 return m_addonInfo->SupportsMultipleInstances();
218 AddonInstanceSupport CAddon::InstanceUseType() const
220 return m_addonInfo->InstanceUseType();
223 bool CAddon::SupportsInstanceSettings() const
225 return m_addonInfo->SupportsInstanceSettings();
228 bool CAddon::DeleteInstanceSettings(AddonInstanceId instance)
230 if (instance == ADDON_SETTINGS_ID)
231 return false;
233 const auto itr = m_settings.find(instance);
234 if (itr == m_settings.end())
235 return false;
237 if (CFile::Exists(itr->second.m_userSettingsPath))
238 CFile::Delete(itr->second.m_userSettingsPath);
240 ResetSettings(instance);
242 return true;
245 bool CAddon::CanHaveAddonOrInstanceSettings()
247 return HasSettings(ADDON_SETTINGS_ID) || SupportsInstanceSettings();
250 bool CAddon::HasSettings(AddonInstanceId id /* = ADDON_SETTINGS_ID */)
252 return LoadSettings(false, true, id) && m_settings[id].m_addonSettings->HasSettings();
255 bool CAddon::SettingsInitialized(AddonInstanceId id /* = ADDON_SETTINGS_ID */) const
257 const auto addonSettings = FindInstanceSettings(id);
258 return addonSettings && addonSettings->IsInitialized();
261 bool CAddon::SettingsLoaded(AddonInstanceId id /* = ADDON_SETTINGS_ID */) const
263 const auto addonSettings = FindInstanceSettings(id);
264 return addonSettings && addonSettings->IsLoaded();
267 bool CAddon::LoadSettings(bool bForce,
268 bool loadUserSettings,
269 AddonInstanceId id /* = ADDON_SETTINGS_ID */)
271 if (SettingsInitialized(id) && !bForce)
272 return true;
274 const auto itr = m_settings.find(id);
275 if (itr != m_settings.end())
277 if (itr->second.m_loadSettingsFailed)
278 return false;
280 else
282 InitSettings(id);
285 // assume loading settings fails
286 m_settings[id].m_loadSettingsFailed = true;
288 // reset the settings if we are forced to
289 if (SettingsInitialized(id) && bForce)
290 GetSettings(id)->Uninitialize();
292 // load the settings definition XML file
293 const auto addonSettingsDefinitionFile = m_settings[id].m_addonSettingsPath;
294 CXBMCTinyXML addonSettingsDefinitionDoc;
295 if (!addonSettingsDefinitionDoc.LoadFile(addonSettingsDefinitionFile))
297 if (CFile::Exists(addonSettingsDefinitionFile))
299 CLog::Log(LOGERROR, "CAddon[{}]: unable to load: {}, Line {}\n{}", ID(),
300 addonSettingsDefinitionFile, addonSettingsDefinitionDoc.ErrorRow(),
301 addonSettingsDefinitionDoc.ErrorDesc());
304 return false;
307 // initialize the settings definition
308 if (!GetSettings(id)->Initialize(addonSettingsDefinitionDoc))
310 CLog::Log(LOGERROR, "CAddon[{}]: failed to initialize addon settings", ID());
311 return false;
314 // loading settings didn't fail
315 m_settings[id].m_loadSettingsFailed = false;
317 // load user settings / values
318 if (loadUserSettings)
319 LoadUserSettings(id);
321 return true;
324 bool CAddon::HasUserSettings(AddonInstanceId id /* = ADDON_SETTINGS_ID */)
326 if (!LoadSettings(false, true, id))
327 return false;
329 return SettingsLoaded(id) && m_settings[id].m_hasUserSettings;
332 bool CAddon::ReloadSettings(AddonInstanceId id /* = ADDON_SETTINGS_ID */)
334 return LoadSettings(true, true, id);
337 void CAddon::ResetSettings(AddonInstanceId id /* = ADDON_SETTINGS_ID */)
339 m_settings.erase(id);
342 bool CAddon::LoadUserSettings(AddonInstanceId id /* = ADDON_SETTINGS_ID */)
344 if (!SettingsInitialized(id) && !InitSettings(id))
345 return false;
347 CSettingsData& data = m_settings[id];
349 data.m_hasUserSettings = false;
351 // there are no user settings
352 if (!CFile::Exists(data.m_userSettingsPath))
354 // mark the settings as loaded
355 GetSettings(id)->SetLoaded();
356 return true;
359 CXBMCTinyXML doc;
360 if (!doc.LoadFile(data.m_userSettingsPath))
362 CLog::Log(LOGERROR, "CAddon[{}]: failed to load addon settings from {}", ID(),
363 data.m_userSettingsPath);
364 return false;
367 return SettingsFromXML(doc, false, id);
370 bool CAddon::HasSettingsToSave(AddonInstanceId id /* = ADDON_SETTINGS_ID */) const
372 return SettingsLoaded(id);
375 bool CAddon::SaveSettings(AddonInstanceId id /* = ADDON_SETTINGS_ID */)
377 if (!HasSettingsToSave(id))
378 return false; // no settings to save
380 bool success{true};
381 CSettingsData& data = m_settings[id];
383 // break down the path into directories
384 const std::string strAddon = URIUtils::GetDirectory(data.m_userSettingsPath);
385 const std::string strRoot = URIUtils::GetDirectory(strAddon);
387 // create the individual folders
388 if (!CDirectory::Exists(strRoot))
389 success = CDirectory::Create(strRoot);
390 if (!CDirectory::Exists(strAddon))
391 success = CDirectory::Create(strAddon);
393 // create the XML file
394 CXBMCTinyXML doc;
395 if (SettingsToXML(doc, id))
396 success = doc.SaveFile(data.m_userSettingsPath);
398 data.m_hasUserSettings = true;
400 //push the settings changes to the running addon instance
401 CServiceBroker::GetAddonMgr().ReloadSettings(ID(), id);
402 #ifdef HAS_PYTHON
403 CServiceBroker::GetXBPython().OnSettingsChanged(ID());
404 #endif
405 return success;
408 std::string CAddon::GetSetting(const std::string& key, AddonInstanceId id)
410 if (key.empty() || !LoadSettings(false, true, id))
411 return ""; // no settings available
413 auto setting = m_settings[id].m_addonSettings->GetSetting(key);
414 if (setting != nullptr)
415 return setting->ToString();
417 return "";
420 template<class TSetting>
421 bool GetSettingValue(CAddon& addon,
422 AddonInstanceId instanceId,
423 const std::string& key,
424 typename TSetting::Value& value)
426 if (key.empty() || !addon.HasSettings(instanceId))
427 return false;
429 auto setting = addon.GetSettings(instanceId)->GetSetting(key);
430 if (setting == nullptr || setting->GetType() != TSetting::Type())
431 return false;
433 value = std::static_pointer_cast<TSetting>(setting)->GetValue();
434 return true;
437 bool CAddon::GetSettingBool(const std::string& key,
438 bool& value,
439 AddonInstanceId id /* = ADDON_SETTINGS_ID */)
441 return GetSettingValue<CSettingBool>(*this, id, key, value);
444 bool CAddon::GetSettingInt(const std::string& key,
445 int& value,
446 AddonInstanceId id /* = ADDON_SETTINGS_ID */)
448 return GetSettingValue<CSettingInt>(*this, id, key, value);
451 bool CAddon::GetSettingNumber(const std::string& key,
452 double& value,
453 AddonInstanceId id /* = ADDON_SETTINGS_ID */)
455 return GetSettingValue<CSettingNumber>(*this, id, key, value);
458 bool CAddon::GetSettingString(const std::string& key,
459 std::string& value,
460 AddonInstanceId id /* = ADDON_SETTINGS_ID */)
462 return GetSettingValue<CSettingString>(*this, id, key, value);
465 void CAddon::UpdateSetting(const std::string& key,
466 const std::string& value,
467 AddonInstanceId id /* = ADDON_SETTINGS_ID */)
469 if (key.empty() || !LoadSettings(false, true, id))
470 return;
472 // try to get the setting
473 auto setting = m_settings[id].m_addonSettings->GetSetting(key);
475 // if the setting doesn't exist, try to add it
476 if (setting == nullptr)
478 setting = m_settings[id].m_addonSettings->AddSetting(key, value);
479 if (setting == nullptr)
481 CLog::Log(LOGERROR, "CAddon[{}]: failed to add undefined setting \"{}\"", ID(), key);
482 return;
486 setting->FromString(value);
489 template<class TSetting>
490 bool UpdateSettingValue(CAddon& addon,
491 AddonInstanceId instanceId,
492 const std::string& key,
493 typename TSetting::Value value)
495 if (key.empty() || !addon.HasSettings(instanceId))
496 return false;
498 // try to get the setting
499 auto setting = addon.GetSettings(instanceId)->GetSetting(key);
501 // if the setting doesn't exist, try to add it
502 if (setting == nullptr)
504 setting = addon.GetSettings(instanceId)->AddSetting(key, value);
505 if (setting == nullptr)
507 CLog::Log(LOGERROR, "CAddon[{}]: failed to add undefined setting \"{}\"", addon.ID(), key);
508 return false;
512 if (setting->GetType() != TSetting::Type())
513 return false;
515 return std::static_pointer_cast<TSetting>(setting)->SetValue(value);
518 bool CAddon::UpdateSettingBool(const std::string& key,
519 bool value,
520 AddonInstanceId id /* = ADDON_SETTINGS_ID */)
522 return UpdateSettingValue<CSettingBool>(*this, id, key, value);
525 bool CAddon::UpdateSettingInt(const std::string& key,
526 int value,
527 AddonInstanceId id /* = ADDON_SETTINGS_ID */)
529 return UpdateSettingValue<CSettingInt>(*this, id, key, value);
532 bool CAddon::UpdateSettingNumber(const std::string& key,
533 double value,
534 AddonInstanceId id /* = ADDON_SETTINGS_ID */)
536 return UpdateSettingValue<CSettingNumber>(*this, id, key, value);
539 bool CAddon::UpdateSettingString(const std::string& key,
540 const std::string& value,
541 AddonInstanceId id /* = ADDON_SETTINGS_ID */)
543 return UpdateSettingValue<CSettingString>(*this, id, key, value);
546 bool CAddon::SettingsFromXML(const CXBMCTinyXML& doc,
547 bool loadDefaults,
548 AddonInstanceId id /* = ADDON_SETTINGS_ID */)
550 if (doc.RootElement() == nullptr)
551 return false;
553 // if the settings haven't been initialized yet, try it from the given XML
554 if (!SettingsInitialized(id))
556 if (!GetSettings(id)->Initialize(doc))
558 CLog::Log(LOGERROR, "CAddon[{}]: failed to initialize addon settings", ID());
559 return false;
563 // reset all setting values to their default value
564 if (loadDefaults)
565 GetSettings(id)->SetDefaults();
567 // try to load the setting's values from the given XML
568 if (!GetSettings(id)->Load(doc))
570 CLog::Log(LOGERROR, "CAddon[{}]: failed to load user settings", ID());
571 return false;
574 m_settings[id].m_hasUserSettings = true;
576 return true;
579 bool CAddon::SettingsToXML(CXBMCTinyXML& doc, AddonInstanceId id /* = ADDON_SETTINGS_ID */) const
581 if (!SettingsInitialized(id))
582 return false;
584 if (!m_settings[id].m_addonSettings->Save(doc))
586 CLog::Log(LOGERROR, "CAddon[{}]: failed to save addon settings", ID());
587 return false;
590 return true;
593 bool CAddon::InitSettings(AddonInstanceId id)
595 // initialize addon settings if necessary
596 if (!FindInstanceSettings(id))
598 CSettingsData data;
600 data.m_addonSettings =
601 std::make_shared<CAddonSettings>(enable_shared_from_this::shared_from_this(), id);
602 if (id == ADDON_SETTINGS_ID)
604 data.m_addonSettingsPath =
605 URIUtils::AddFileToFolder(m_addonInfo->Path(), "resources", "settings.xml");
606 data.m_userSettingsPath = URIUtils::AddFileToFolder(Profile(), "settings.xml");
608 else
610 data.m_addonSettingsPath =
611 URIUtils::AddFileToFolder(m_addonInfo->Path(), "resources", "instance-settings.xml");
612 data.m_userSettingsPath =
613 URIUtils::AddFileToFolder(Profile(), StringUtils::Format("instance-settings-{}.xml", id));
616 m_settings[id] = std::move(data);
617 return true;
620 return false;
623 std::shared_ptr<CAddonSettings> CAddon::FindInstanceSettings(AddonInstanceId id) const
625 const auto itr = m_settings.find(id);
626 if (itr == m_settings.end())
627 return nullptr;
629 return itr->second.m_addonSettings;
632 std::shared_ptr<CAddonSettings> CAddon::GetSettings(AddonInstanceId id /* = ADDON_SETTINGS_ID */)
634 if (InitSettings(id))
635 LoadSettings(false, true, id);
637 return m_settings[id].m_addonSettings;
640 std::string CAddon::LibPath() const
642 // Get library related to given type on construction
643 std::string libName = m_addonInfo->Type(m_type)->LibName();
644 if (libName.empty())
646 // If not present fallback to master library
647 libName = m_addonInfo->LibName();
648 if (libName.empty())
649 return "";
651 return URIUtils::AddFileToFolder(m_addonInfo->Path(), libName);
654 CAddonVersion CAddon::GetDependencyVersion(const std::string& dependencyID) const
656 return m_addonInfo->DependencyVersion(dependencyID);
659 void OnPreInstall(const AddonPtr& addon)
661 //Fallback to the pre-install callback in the addon.
662 //! @bug If primary extension point have changed we're calling the wrong method.
663 addon->OnPreInstall();
666 void OnPostInstall(const AddonPtr& addon, bool update, bool modal)
668 addon->OnPostInstall(update, modal);
671 void OnPreUnInstall(const AddonPtr& addon)
673 addon->OnPreUnInstall();
676 void OnPostUnInstall(const AddonPtr& addon)
678 addon->OnPostUnInstall();
681 } // namespace ADDON