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.
13 #include "ServiceBroker.h"
14 #include "addons/AddonManager.h"
15 #include "addons/IAddon.h"
16 #include "addons/addoninfo/AddonType.h"
17 #include "filesystem/Directory.h"
18 #include "guilib/LocalizeStrings.h"
19 #include "utils/StringUtils.h"
20 #include "utils/URIUtils.h"
24 #include <string_view>
31 std::string_view name
;
32 std::string_view old_name
;
35 AddonInstanceSupport instance_support
;
36 std::string_view icon
;
40 static constexpr const std::array
<TypeMapping
, 40> types
=
42 {"unknown", "", AddonType::UNKNOWN
, 0, AddonInstanceSupport::SUPPORT_NONE
, "" },
43 {"xbmc.metadata.scraper.albums", "", AddonType::SCRAPER_ALBUMS
, 24016, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonAlbumInfo.png" },
44 {"xbmc.metadata.scraper.artists", "", AddonType::SCRAPER_ARTISTS
, 24017, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonArtistInfo.png" },
45 {"xbmc.metadata.scraper.movies", "", AddonType::SCRAPER_MOVIES
, 24007, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonMovieInfo.png" },
46 {"xbmc.metadata.scraper.musicvideos", "", AddonType::SCRAPER_MUSICVIDEOS
, 24015, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonMusicVideoInfo.png" },
47 {"xbmc.metadata.scraper.tvshows", "", AddonType::SCRAPER_TVSHOWS
, 24014, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonTvInfo.png" },
48 {"xbmc.metadata.scraper.library", "", AddonType::SCRAPER_LIBRARY
, 24083, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonInfoLibrary.png" },
49 {"xbmc.ui.screensaver", "", AddonType::SCREENSAVER
, 24008, AddonInstanceSupport::SUPPORT_OPTIONAL
, "DefaultAddonScreensaver.png" },
50 {"xbmc.player.musicviz", "", AddonType::VISUALIZATION
, 24010, AddonInstanceSupport::SUPPORT_OPTIONAL
, "DefaultAddonVisualization.png" },
51 {"xbmc.python.pluginsource", "", AddonType::PLUGIN
, 24005, AddonInstanceSupport::SUPPORT_NONE
, "" },
52 {"xbmc.python.script", "", AddonType::SCRIPT
, 24009, AddonInstanceSupport::SUPPORT_NONE
, "" },
53 {"xbmc.python.weather", "", AddonType::SCRIPT_WEATHER
, 24027, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonWeather.png" },
54 {"xbmc.python.lyrics", "", AddonType::SCRIPT_LYRICS
, 24013, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonLyrics.png" },
55 {"xbmc.python.library", "", AddonType::SCRIPT_LIBRARY
, 24081, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonHelper.png" },
56 {"xbmc.python.module", "", AddonType::SCRIPT_MODULE
, 24082, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonLibrary.png" },
57 {"xbmc.subtitle.module", "", AddonType::SUBTITLE_MODULE
, 24012, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonSubtitles.png" },
58 {"kodi.context.item", "", AddonType::CONTEXTMENU_ITEM
, 24025, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonContextItem.png" },
59 {"kodi.game.controller", "", AddonType::GAME_CONTROLLER
, 35050, AddonInstanceSupport::SUPPORT_OPTIONAL
, "DefaultAddonGame.png" },
60 {"xbmc.gui.skin", "", AddonType::SKIN
, 166, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonSkin.png" },
61 {"xbmc.webinterface", "", AddonType::WEB_INTERFACE
, 199, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonWebSkin.png" },
62 {"xbmc.addon.repository", "", AddonType::REPOSITORY
, 24011, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonRepository.png" },
63 {"kodi.pvrclient", "xbmc.pvrclient", AddonType::PVRDLL
, 24019, AddonInstanceSupport::SUPPORT_SETTINGS
, "DefaultAddonPVRClient.png" },
64 {"kodi.gameclient", "", AddonType::GAMEDLL
, 35049, AddonInstanceSupport::SUPPORT_OPTIONAL
, "DefaultAddonGame.png" },
65 {"kodi.peripheral", "", AddonType::PERIPHERALDLL
, 35010, AddonInstanceSupport::SUPPORT_MANDATORY
, "DefaultAddonPeripheral.png" },
66 {"xbmc.addon.video", "", AddonType::VIDEO
, 1037, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonVideo.png" },
67 {"xbmc.addon.audio", "", AddonType::AUDIO
, 1038, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonMusic.png" },
68 {"xbmc.addon.image", "", AddonType::IMAGE
, 1039, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonPicture.png" },
69 {"xbmc.addon.executable", "", AddonType::EXECUTABLE
, 1043, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonProgram.png" },
70 {"kodi.addon.game", "", AddonType::GAME
, 35049, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonGame.png" },
71 {"kodi.audioencoder", "", AddonType::AUDIOENCODER
, 200, AddonInstanceSupport::SUPPORT_MANDATORY
, "DefaultAddonAudioEncoder.png" },
72 {"kodi.audiodecoder", "", AddonType::AUDIODECODER
, 201, AddonInstanceSupport::SUPPORT_MANDATORY
, "DefaultAddonAudioDecoder.png" },
73 {"xbmc.service", "", AddonType::SERVICE
, 24018, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonService.png" },
74 {"kodi.resource.images", "", AddonType::RESOURCE_IMAGES
, 24035, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonImages.png" },
75 {"kodi.resource.language", "", AddonType::RESOURCE_LANGUAGE
, 24026, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonLanguage.png" },
76 {"kodi.resource.uisounds", "", AddonType::RESOURCE_UISOUNDS
, 24006, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonUISounds.png" },
77 {"kodi.resource.games", "", AddonType::RESOURCE_GAMES
, 35209, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonGame.png" },
78 {"kodi.resource.font", "", AddonType::RESOURCE_FONT
, 13303, AddonInstanceSupport::SUPPORT_NONE
, "DefaultAddonFont.png" },
79 {"kodi.inputstream", "", AddonType::INPUTSTREAM
, 24048, AddonInstanceSupport::SUPPORT_MANDATORY
, "DefaultAddonInputstream.png" },
80 {"kodi.vfs", "", AddonType::VFS
, 39013, AddonInstanceSupport::SUPPORT_MANDATORY
, "DefaultAddonVfs.png" },
81 {"kodi.imagedecoder", "", AddonType::IMAGEDECODER
, 39015, AddonInstanceSupport::SUPPORT_MANDATORY
, "DefaultAddonImageDecoder.png" },
85 const std::string
& CAddonInfo::OriginName() const
89 ADDON::AddonPtr origin
;
90 if (CServiceBroker::GetAddonMgr().GetAddon(m_origin
, origin
, ADDON::OnlyEnabled::CHOICE_NO
))
91 m_originName
= std::make_unique
<std::string
>(origin
->Name());
93 m_originName
= std::make_unique
<std::string
>(); // remember we tried to fetch the name
99 * static public helper functions
103 std::string
CAddonInfo::TranslateType(AddonType type
, bool pretty
/*= false*/)
105 for (const TypeMapping
& map
: types
)
107 if (type
== map
.type
)
109 if (pretty
&& map
.pretty
)
110 return g_localizeStrings
.Get(map
.pretty
);
112 return std::string(map
.name
.data(), map
.name
.size());
118 AddonType
CAddonInfo::TranslateType(const std::string
& string
)
120 for (const TypeMapping
& map
: types
)
122 if (string
== map
.name
|| (!map
.old_name
.empty() && string
== map
.old_name
))
126 return AddonType::UNKNOWN
;
129 std::string
CAddonInfo::TranslateIconType(AddonType type
)
131 for (const TypeMapping
& map
: types
)
133 if (type
== map
.type
)
134 return std::string(map
.icon
.data(), map
.icon
.size());
139 AddonType
CAddonInfo::TranslateSubContent(const std::string
& content
)
141 if (content
== "audio")
142 return AddonType::AUDIO
;
143 else if (content
== "image")
144 return AddonType::IMAGE
;
145 else if (content
== "executable")
146 return AddonType::EXECUTABLE
;
147 else if (content
== "video")
148 return AddonType::VIDEO
;
149 else if (content
== "game")
150 return AddonType::GAME
;
152 return AddonType::UNKNOWN
;
155 AddonInstanceSupport
CAddonInfo::InstanceSupportType(AddonType type
)
157 const auto it
= std::find_if(types
.begin(), types
.end(),
158 [type
](const TypeMapping
& entry
) { return entry
.type
== type
; });
159 if (it
!= types
.end())
160 return it
->instance_support
;
162 return AddonInstanceSupport::SUPPORT_NONE
;
165 CAddonInfo::CAddonInfo(std::string id
, AddonType type
) : m_id(std::move(id
)), m_mainType(type
)
170 const CAddonType
* CAddonInfo::Type(AddonType type
) const
172 static CAddonType dummy
;
174 if (!m_types
.empty())
176 if (type
== AddonType::UNKNOWN
)
179 for (auto& addonType
: m_types
)
181 if (addonType
.Type() == type
)
189 bool CAddonInfo::HasType(AddonType type
, bool mainOnly
/*= false*/) const
191 return (m_mainType
== type
||
192 ProvidesSubContent(type
, mainOnly
? m_mainType
: AddonType::UNKNOWN
));
195 bool CAddonInfo::ProvidesSubContent(AddonType content
, AddonType mainType
) const
197 if (content
== AddonType::UNKNOWN
)
200 for (const auto& addonType
: m_types
)
202 if ((mainType
== AddonType::UNKNOWN
|| addonType
.Type() == mainType
) &&
203 addonType
.ProvidesSubContent(content
))
210 bool CAddonInfo::ProvidesSeveralSubContents() const
213 for (const auto& addonType
: m_types
)
214 contents
+= addonType
.ProvidedSubContents();
215 return contents
> 0 ? true : false;
218 bool CAddonInfo::MeetsVersion(const CAddonVersion
& versionMin
, const CAddonVersion
& version
) const
220 return !(versionMin
> m_version
|| version
< m_minversion
);
223 const CAddonVersion
& CAddonInfo::DependencyMinVersion(const std::string
& dependencyID
) const
225 auto it
= std::find_if(m_dependencies
.begin(), m_dependencies
.end(),
226 [&](const DependencyInfo
& other
) { return other
.id
== dependencyID
; });
228 if (it
!= m_dependencies
.end())
229 return it
->versionMin
;
231 static CAddonVersion emptyVersion
;
235 const CAddonVersion
& CAddonInfo::DependencyVersion(const std::string
& dependencyID
) const
237 auto it
= std::find_if(m_dependencies
.begin(), m_dependencies
.end(), [&](const DependencyInfo
& other
) { return other
.id
== dependencyID
; });
239 if (it
!= m_dependencies
.end())
242 static CAddonVersion emptyVersion
;
246 const std::string
& CAddonInfo::GetTranslatedText(const std::unordered_map
<std::string
, std::string
>& locales
) const
248 if (locales
.size() == 1)
249 return locales
.begin()->second
;
250 else if (locales
.empty())
251 return StringUtils::Empty
;
253 // find the language from the list that matches the current locale best
254 std::string matchingLanguage
= g_langInfo
.GetLocale().FindBestMatch(locales
);
255 if (matchingLanguage
.empty())
256 matchingLanguage
= KODI_ADDON_DEFAULT_LANGUAGE_CODE
;
258 auto const& translatedValue
= locales
.find(matchingLanguage
);
259 if (translatedValue
!= locales
.end())
260 return translatedValue
->second
;
261 return StringUtils::Empty
;
264 bool CAddonInfo::SupportsMultipleInstances() const
266 switch (m_addonInstanceSupportType
)
268 case AddonInstanceSupport::SUPPORT_MANDATORY
:
269 case AddonInstanceSupport::SUPPORT_OPTIONAL
:
271 case AddonInstanceSupport::SUPPORT_SETTINGS
:
272 return m_supportsInstanceSettings
;
273 case AddonInstanceSupport::SUPPORT_NONE
:
279 std::vector
<AddonInstanceId
> CAddonInfo::GetKnownInstanceIds() const
281 static const std::vector
<AddonInstanceId
> singletonInstance
= {ADDON_SINGLETON_INSTANCE_ID
};
283 if (!m_supportsInstanceSettings
)
284 return singletonInstance
;
286 const std::string searchPath
= StringUtils::Format("special://profile/addon_data/{}/", m_id
);
288 XFILE::CDirectory::GetDirectory(searchPath
, items
, ".xml", XFILE::DIR_FLAG_NO_FILE_DIRS
);
290 std::vector
<AddonInstanceId
> ret
;
292 for (const auto& item
: items
)
294 const std::string startName
= "instance-settings-";
295 std::string filename
= URIUtils::GetFileName(item
->GetPath());
296 if (StringUtils::StartsWithNoCase(URIUtils::GetFileName(item
->GetPath()), startName
))
298 URIUtils::RemoveExtension(filename
);
299 const std::string uid
= filename
.substr(startName
.length());
300 if (!uid
.empty() && StringUtils::IsInteger(uid
))
301 ret
.emplace_back(std::atoi(uid
.c_str()));
305 // If no instances are used, create first as default.
307 ret
.emplace_back(ADDON_FIRST_INSTANCE_ID
);
312 } /* namespace ADDON */