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.
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"
33 #include "interfaces/python/XBPython.h"
36 using XFILE::CDirectory
;
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
);
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
)
233 const auto itr
= m_settings
.find(instance
);
234 if (itr
== m_settings
.end())
237 if (CFile::Exists(itr
->second
.m_userSettingsPath
))
238 CFile::Delete(itr
->second
.m_userSettingsPath
);
240 ResetSettings(instance
);
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
)
274 const auto itr
= m_settings
.find(id
);
275 if (itr
!= m_settings
.end())
277 if (itr
->second
.m_loadSettingsFailed
)
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());
307 // initialize the settings definition
308 if (!GetSettings(id
)->Initialize(addonSettingsDefinitionDoc
))
310 CLog::Log(LOGERROR
, "CAddon[{}]: failed to initialize addon settings", ID());
314 // loading settings didn't fail
315 m_settings
[id
].m_loadSettingsFailed
= false;
317 // load user settings / values
318 if (loadUserSettings
)
319 LoadUserSettings(id
);
324 bool CAddon::HasUserSettings(AddonInstanceId id
/* = ADDON_SETTINGS_ID */)
326 if (!LoadSettings(false, true, id
))
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
))
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();
360 if (!doc
.LoadFile(data
.m_userSettingsPath
))
362 CLog::Log(LOGERROR
, "CAddon[{}]: failed to load addon settings from {}", ID(),
363 data
.m_userSettingsPath
);
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
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
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
);
403 CServiceBroker::GetXBPython().OnSettingsChanged(ID());
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();
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
))
429 auto setting
= addon
.GetSettings(instanceId
)->GetSetting(key
);
430 if (setting
== nullptr || setting
->GetType() != TSetting::Type())
433 value
= std::static_pointer_cast
<TSetting
>(setting
)->GetValue();
437 bool CAddon::GetSettingBool(const std::string
& key
,
439 AddonInstanceId id
/* = ADDON_SETTINGS_ID */)
441 return GetSettingValue
<CSettingBool
>(*this, id
, key
, value
);
444 bool CAddon::GetSettingInt(const std::string
& key
,
446 AddonInstanceId id
/* = ADDON_SETTINGS_ID */)
448 return GetSettingValue
<CSettingInt
>(*this, id
, key
, value
);
451 bool CAddon::GetSettingNumber(const std::string
& key
,
453 AddonInstanceId id
/* = ADDON_SETTINGS_ID */)
455 return GetSettingValue
<CSettingNumber
>(*this, id
, key
, value
);
458 bool CAddon::GetSettingString(const std::string
& key
,
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
))
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
);
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
))
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
);
512 if (setting
->GetType() != TSetting::Type())
515 return std::static_pointer_cast
<TSetting
>(setting
)->SetValue(value
);
518 bool CAddon::UpdateSettingBool(const std::string
& key
,
520 AddonInstanceId id
/* = ADDON_SETTINGS_ID */)
522 return UpdateSettingValue
<CSettingBool
>(*this, id
, key
, value
);
525 bool CAddon::UpdateSettingInt(const std::string
& key
,
527 AddonInstanceId id
/* = ADDON_SETTINGS_ID */)
529 return UpdateSettingValue
<CSettingInt
>(*this, id
, key
, value
);
532 bool CAddon::UpdateSettingNumber(const std::string
& key
,
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
,
548 AddonInstanceId id
/* = ADDON_SETTINGS_ID */)
550 if (doc
.RootElement() == nullptr)
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());
563 // reset all setting values to their default value
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());
574 m_settings
[id
].m_hasUserSettings
= true;
579 bool CAddon::SettingsToXML(CXBMCTinyXML
& doc
, AddonInstanceId id
/* = ADDON_SETTINGS_ID */) const
581 if (!SettingsInitialized(id
))
584 if (!m_settings
[id
].m_addonSettings
->Save(doc
))
586 CLog::Log(LOGERROR
, "CAddon[{}]: failed to save addon settings", ID());
593 bool CAddon::InitSettings(AddonInstanceId id
)
595 // initialize addon settings if necessary
596 if (!FindInstanceSettings(id
))
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");
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
);
623 std::shared_ptr
<CAddonSettings
> CAddon::FindInstanceSettings(AddonInstanceId id
) const
625 const auto itr
= m_settings
.find(id
);
626 if (itr
== m_settings
.end())
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();
646 // If not present fallback to master library
647 libName
= m_addonInfo
->LibName();
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();