[PVR][Estuary] Timer settings dialog: Show client name in timer type selection dialog...
[xbmc.git] / xbmc / filesystem / AddonsDirectory.cpp
blob1439ae054b48ed8895ee9f8d312db4496802d8d9
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 "AddonsDirectory.h"
11 #include "FileItem.h"
12 #include "ServiceBroker.h"
13 #include "URL.h"
14 #include "addons/AddonDatabase.h"
15 #include "addons/AddonInstaller.h"
16 #include "addons/AddonManager.h"
17 #include "addons/AddonRepos.h"
18 #include "addons/AddonSystemSettings.h"
19 #include "addons/PluginSource.h"
20 #include "addons/RepositoryUpdater.h"
21 #include "addons/addoninfo/AddonInfo.h"
22 #include "addons/addoninfo/AddonType.h"
23 #include "games/GameUtils.h"
24 #include "games/addons/GameClient.h"
25 #include "guilib/LocalizeStrings.h"
26 #include "guilib/TextureManager.h"
27 #include "interfaces/generic/ScriptInvocationManager.h"
28 #include "messaging/helpers/DialogOKHelper.h"
29 #include "settings/Settings.h"
30 #include "settings/SettingsComponent.h"
31 #include "utils/StringUtils.h"
32 #include "utils/URIUtils.h"
34 #include <algorithm>
35 #include <array>
36 #include <functional>
37 #include <set>
39 using namespace KODI;
40 using namespace ADDON;
41 using namespace KODI::MESSAGING;
43 namespace XFILE
46 CAddonsDirectory::CAddonsDirectory(void) = default;
48 CAddonsDirectory::~CAddonsDirectory(void) = default;
50 const auto CATEGORY_INFO_PROVIDERS = "category.infoproviders";
51 const auto CATEGORY_LOOK_AND_FEEL = "category.lookandfeel";
52 const auto CATEGORY_GAME_ADDONS = "category.gameaddons";
53 const auto CATEGORY_EMULATORS = "category.emulators";
54 const auto CATEGORY_STANDALONE_GAMES = "category.standalonegames";
55 const auto CATEGORY_GAME_PROVIDERS = "category.gameproviders";
56 const auto CATEGORY_GAME_RESOURCES = "category.gameresources";
57 const auto CATEGORY_GAME_SUPPORT_ADDONS = "category.gamesupport";
59 const std::set<AddonType> infoProviderTypes = {
60 AddonType::SCRAPER_ALBUMS, AddonType::SCRAPER_ARTISTS, AddonType::SCRAPER_MOVIES,
61 AddonType::SCRAPER_MUSICVIDEOS, AddonType::SCRAPER_TVSHOWS,
64 const std::set<AddonType> lookAndFeelTypes = {
65 AddonType::SKIN,
66 AddonType::SCREENSAVER,
67 AddonType::RESOURCE_IMAGES,
68 AddonType::RESOURCE_LANGUAGE,
69 AddonType::RESOURCE_UISOUNDS,
70 AddonType::RESOURCE_FONT,
71 AddonType::VISUALIZATION,
74 const std::set<AddonType> gameTypes = {
75 AddonType::GAME_CONTROLLER,
76 AddonType::GAMEDLL,
77 AddonType::GAME,
78 AddonType::RESOURCE_GAMES,
81 static bool IsInfoProviderType(AddonType type)
83 return infoProviderTypes.find(type) != infoProviderTypes.end();
86 static bool IsInfoProviderTypeAddon(const AddonPtr& addon)
88 return IsInfoProviderType(addon->Type());
91 static bool IsLookAndFeelType(AddonType type)
93 return lookAndFeelTypes.find(type) != lookAndFeelTypes.end();
96 static bool IsLookAndFeelTypeAddon(const AddonPtr& addon)
98 return IsLookAndFeelType(addon->Type());
101 static bool IsGameType(AddonType type)
103 return gameTypes.find(type) != gameTypes.end();
106 static bool IsStandaloneGame(const AddonPtr& addon)
108 return GAME::CGameUtils::IsStandaloneGame(addon);
111 static bool IsEmulator(const AddonPtr& addon)
113 return addon->Type() == AddonType::GAMEDLL &&
114 std::static_pointer_cast<GAME::CGameClient>(addon)->SupportsPath();
117 static bool IsGameProvider(const AddonPtr& addon)
119 return addon->Type() == AddonType::PLUGIN && addon->HasType(AddonType::GAME);
122 static bool IsGameResource(const AddonPtr& addon)
124 return addon->Type() == AddonType::RESOURCE_GAMES;
127 static bool IsGameSupportAddon(const AddonPtr& addon)
129 return addon->Type() == AddonType::GAMEDLL &&
130 !std::static_pointer_cast<GAME::CGameClient>(addon)->SupportsPath() &&
131 !std::static_pointer_cast<GAME::CGameClient>(addon)->SupportsStandalone();
134 static bool IsGameAddon(const AddonPtr& addon)
136 return IsGameType(addon->Type()) ||
137 IsStandaloneGame(addon) ||
138 IsGameProvider(addon) ||
139 IsGameResource(addon) ||
140 IsGameSupportAddon(addon);
143 static bool IsUserInstalled(const AddonPtr& addon)
145 return !CAddonType::IsDependencyType(addon->MainType());
148 // Creates categories from addon types, if we have any addons with that type.
149 static void GenerateTypeListing(const CURL& path,
150 const std::set<AddonType>& types,
151 const VECADDONS& addons,
152 CFileItemList& items)
154 for (const auto& type : types)
156 for (const auto& addon : addons)
158 if (addon->HasType(type))
160 CFileItemPtr item(new CFileItem(CAddonInfo::TranslateType(type, true)));
161 CURL itemPath = path;
162 itemPath.SetFileName(CAddonInfo::TranslateType(type, false));
163 item->SetPath(itemPath.Get());
164 item->m_bIsFolder = true;
165 std::string thumb = CAddonInfo::TranslateIconType(type);
166 if (!thumb.empty() && CServiceBroker::GetGUI()->GetTextureManager().HasTexture(thumb))
167 item->SetArt("thumb", thumb);
168 items.Add(item);
169 break;
175 // Creates categories for game add-ons, if we have any game add-ons
176 static void GenerateGameListing(const CURL& path, const VECADDONS& addons, CFileItemList& items)
178 // Game controllers
179 for (const auto& addon : addons)
181 if (addon->Type() == AddonType::GAME_CONTROLLER)
183 CFileItemPtr item(new CFileItem(CAddonInfo::TranslateType(AddonType::GAME_CONTROLLER, true)));
184 CURL itemPath = path;
185 itemPath.SetFileName(CAddonInfo::TranslateType(AddonType::GAME_CONTROLLER, false));
186 item->SetPath(itemPath.Get());
187 item->m_bIsFolder = true;
188 std::string thumb = CAddonInfo::TranslateIconType(AddonType::GAME_CONTROLLER);
189 if (!thumb.empty() && CServiceBroker::GetGUI()->GetTextureManager().HasTexture(thumb))
190 item->SetArt("thumb", thumb);
191 items.Add(item);
192 break;
195 // Emulators
196 for (const auto& addon : addons)
198 if (IsEmulator(addon))
200 CFileItemPtr item(new CFileItem(g_localizeStrings.Get(35207))); // Emulators
201 CURL itemPath = path;
202 itemPath.SetFileName(CATEGORY_EMULATORS);
203 item->SetPath(itemPath.Get());
204 item->m_bIsFolder = true;
205 std::string thumb = CAddonInfo::TranslateIconType(AddonType::GAMEDLL);
206 if (!thumb.empty() && CServiceBroker::GetGUI()->GetTextureManager().HasTexture(thumb))
207 item->SetArt("thumb", thumb);
208 items.Add(item);
209 break;
212 // Standalone games
213 for (const auto& addon : addons)
215 if (IsStandaloneGame(addon))
217 CFileItemPtr item(new CFileItem(g_localizeStrings.Get(35208))); // Standalone games
218 CURL itemPath = path;
219 itemPath.SetFileName(CATEGORY_STANDALONE_GAMES);
220 item->SetPath(itemPath.Get());
221 item->m_bIsFolder = true;
222 std::string thumb = CAddonInfo::TranslateIconType(AddonType::GAMEDLL);
223 if (!thumb.empty() && CServiceBroker::GetGUI()->GetTextureManager().HasTexture(thumb))
224 item->SetArt("thumb", thumb);
225 items.Add(item);
226 break;
229 // Game providers
230 for (const auto& addon : addons)
232 if (IsGameProvider(addon))
234 CFileItemPtr item(new CFileItem(g_localizeStrings.Get(35220))); // Game providers
235 CURL itemPath = path;
236 itemPath.SetFileName(CATEGORY_GAME_PROVIDERS);
237 item->SetPath(itemPath.Get());
238 item->m_bIsFolder = true;
239 std::string thumb = CAddonInfo::TranslateIconType(AddonType::GAMEDLL);
240 if (!thumb.empty() && CServiceBroker::GetGUI()->GetTextureManager().HasTexture(thumb))
241 item->SetArt("thumb", thumb);
242 items.Add(item);
243 break;
246 // Game resources
247 for (const auto& addon : addons)
249 if (IsGameResource(addon))
251 CFileItemPtr item(new CFileItem(g_localizeStrings.Get(35209))); // Game resources
252 CURL itemPath = path;
253 itemPath.SetFileName(CATEGORY_GAME_RESOURCES);
254 item->SetPath(itemPath.Get());
255 item->m_bIsFolder = true;
256 std::string thumb = CAddonInfo::TranslateIconType(AddonType::GAMEDLL);
257 if (!thumb.empty() && CServiceBroker::GetGUI()->GetTextureManager().HasTexture(thumb))
258 item->SetArt("thumb", thumb);
259 items.Add(item);
260 break;
263 // Game support add-ons
264 for (const auto& addon : addons)
266 if (IsGameSupportAddon(addon))
268 CFileItemPtr item(new CFileItem(g_localizeStrings.Get(35216))); // Support add-ons
269 CURL itemPath = path;
270 itemPath.SetFileName(CATEGORY_GAME_SUPPORT_ADDONS);
271 item->SetPath(itemPath.Get());
272 item->m_bIsFolder = true;
273 std::string thumb = CAddonInfo::TranslateIconType(AddonType::GAMEDLL);
274 if (!thumb.empty() && CServiceBroker::GetGUI()->GetTextureManager().HasTexture(thumb))
275 item->SetArt("thumb", thumb);
276 items.Add(item);
277 break;
282 //Creates the top-level category list
283 static void GenerateMainCategoryListing(const CURL& path, const VECADDONS& addons,
284 CFileItemList& items)
286 if (std::any_of(addons.begin(), addons.end(), IsInfoProviderTypeAddon))
288 CFileItemPtr item(new CFileItem(g_localizeStrings.Get(24993)));
289 item->SetPath(URIUtils::AddFileToFolder(path.Get(), CATEGORY_INFO_PROVIDERS));
290 item->m_bIsFolder = true;
291 const std::string thumb = "DefaultAddonInfoProvider.png";
292 if (CServiceBroker::GetGUI()->GetTextureManager().HasTexture(thumb))
293 item->SetArt("thumb", thumb);
294 items.Add(item);
296 if (std::any_of(addons.begin(), addons.end(), IsLookAndFeelTypeAddon))
298 CFileItemPtr item(new CFileItem(g_localizeStrings.Get(24997)));
299 item->SetPath(URIUtils::AddFileToFolder(path.Get(), CATEGORY_LOOK_AND_FEEL));
300 item->m_bIsFolder = true;
301 const std::string thumb = "DefaultAddonLookAndFeel.png";
302 if (CServiceBroker::GetGUI()->GetTextureManager().HasTexture(thumb))
303 item->SetArt("thumb", thumb);
304 items.Add(item);
306 if (std::any_of(addons.begin(), addons.end(), IsGameAddon))
308 CFileItemPtr item(new CFileItem(CAddonInfo::TranslateType(AddonType::GAME, true)));
309 item->SetPath(URIUtils::AddFileToFolder(path.Get(), CATEGORY_GAME_ADDONS));
310 item->m_bIsFolder = true;
311 const std::string thumb = CAddonInfo::TranslateIconType(AddonType::GAME);
312 if (CServiceBroker::GetGUI()->GetTextureManager().HasTexture(thumb))
313 item->SetArt("thumb", thumb);
314 items.Add(item);
317 std::set<AddonType> uncategorized;
318 for (unsigned int i = static_cast<unsigned int>(AddonType::UNKNOWN) + 1;
319 i < static_cast<unsigned int>(AddonType::MAX_TYPES) - 1; ++i)
321 const AddonType type = static_cast<AddonType>(i);
323 * Check and prevent insert for this cases:
324 * - By a provider, look and feel, dependency and game becomes given to
325 * subdirectory to control the types
326 * - By ADDON_SCRIPT and ADDON_PLUGIN, them contains one of the possible
327 * subtypes (audio, video, app or/and game) and not needed to show
328 * together in a Script or Plugin list
330 if (!IsInfoProviderType(type) && !IsLookAndFeelType(type) &&
331 !CAddonType::IsDependencyType(type) && !IsGameType(type) && type != AddonType::SCRIPT &&
332 type != AddonType::PLUGIN)
333 uncategorized.insert(type);
335 GenerateTypeListing(path, uncategorized, addons, items);
338 //Creates sub-categories or addon list for a category
339 static void GenerateCategoryListing(const CURL& path, VECADDONS& addons,
340 CFileItemList& items)
342 const std::string& category = path.GetFileName();
343 if (category == CATEGORY_INFO_PROVIDERS)
345 items.SetProperty("addoncategory", g_localizeStrings.Get(24993));
346 items.SetLabel(g_localizeStrings.Get(24993));
347 GenerateTypeListing(path, infoProviderTypes, addons, items);
349 else if (category == CATEGORY_LOOK_AND_FEEL)
351 items.SetProperty("addoncategory", g_localizeStrings.Get(24997));
352 items.SetLabel(g_localizeStrings.Get(24997));
353 GenerateTypeListing(path, lookAndFeelTypes, addons, items);
355 else if (category == CATEGORY_GAME_ADDONS)
357 items.SetProperty("addoncategory", CAddonInfo::TranslateType(AddonType::GAME, true));
358 items.SetLabel(CAddonInfo::TranslateType(AddonType::GAME, true));
359 GenerateGameListing(path, addons, items);
361 else if (category == CATEGORY_EMULATORS)
363 items.SetProperty("addoncategory", g_localizeStrings.Get(35207)); // Emulators
364 addons.erase(std::remove_if(addons.begin(), addons.end(),
365 [](const AddonPtr& addon){ return !IsEmulator(addon); }), addons.end());
366 CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(35207)); // Emulators
368 else if (category == CATEGORY_STANDALONE_GAMES)
370 items.SetProperty("addoncategory", g_localizeStrings.Get(35208)); // Standalone games
371 addons.erase(std::remove_if(addons.begin(), addons.end(),
372 [](const AddonPtr& addon){ return !IsStandaloneGame(addon); }), addons.end());
373 CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(35208)); // Standalone games
375 else if (category == CATEGORY_GAME_PROVIDERS)
377 items.SetProperty("addoncategory", g_localizeStrings.Get(35220)); // Game providers
378 addons.erase(std::remove_if(addons.begin(), addons.end(),
379 [](const AddonPtr& addon){ return !IsGameProvider(addon); }), addons.end());
380 CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(35220)); // Game providers
382 else if (category == CATEGORY_GAME_RESOURCES)
384 items.SetProperty("addoncategory", g_localizeStrings.Get(35209)); // Game resources
385 addons.erase(std::remove_if(addons.begin(), addons.end(),
386 [](const AddonPtr& addon){ return !IsGameResource(addon); }), addons.end());
387 CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(35209)); // Game resources
389 else if (category == CATEGORY_GAME_SUPPORT_ADDONS)
391 items.SetProperty("addoncategory", g_localizeStrings.Get(35216)); // Support add-ons
392 addons.erase(std::remove_if(addons.begin(), addons.end(),
393 [](const AddonPtr& addon) { return !IsGameSupportAddon(addon); }), addons.end());
394 CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(35216)); // Support add-ons
396 else
397 { // fallback to addon type
398 AddonType type = CAddonInfo::TranslateType(category);
399 items.SetProperty("addoncategory", CAddonInfo::TranslateType(type, true));
400 addons.erase(std::remove_if(addons.begin(), addons.end(),
401 [type](const AddonPtr& addon) { return !addon->HasType(type); }),
402 addons.end());
403 CAddonsDirectory::GenerateAddonListing(path, addons, items, CAddonInfo::TranslateType(type, true));
407 bool CAddonsDirectory::GetSearchResults(const CURL& path, CFileItemList &items)
409 std::string search(path.GetFileName());
410 if (search.empty() && !GetKeyboardInput(16017, search))
411 return false;
413 CAddonDatabase database;
414 database.Open();
416 VECADDONS addons;
417 database.Search(search, addons);
418 CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(283));
419 CURL searchPath(path);
420 searchPath.SetFileName(search);
421 items.SetPath(searchPath.Get());
422 return true;
425 static void UserInstalledAddons(const CURL& path, CFileItemList &items)
427 items.ClearItems();
428 items.SetLabel(g_localizeStrings.Get(24998));
430 VECADDONS addons;
431 CServiceBroker::GetAddonMgr().GetInstalledAddons(addons);
432 addons.erase(std::remove_if(addons.begin(), addons.end(),
433 [](const AddonPtr& addon) { return !IsUserInstalled(addon); }), addons.end());
435 if (addons.empty())
436 return;
438 const std::string& category = path.GetFileName();
439 if (category.empty())
441 GenerateMainCategoryListing(path, addons, items);
443 //"All" node
444 CFileItemPtr item(new CFileItem());
445 item->m_bIsFolder = true;
446 CURL itemPath = path;
447 itemPath.SetFileName("all");
448 item->SetPath(itemPath.Get());
449 item->SetLabel(g_localizeStrings.Get(593));
450 item->SetSpecialSort(SortSpecialOnTop);
451 items.Add(item);
453 else if (category == "all")
454 CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(24998));
455 else
456 GenerateCategoryListing(path, addons, items);
459 static void DependencyAddons(const CURL& path, CFileItemList &items)
461 VECADDONS all;
462 CServiceBroker::GetAddonMgr().GetInstalledAddons(all);
464 VECADDONS deps;
465 std::copy_if(all.begin(), all.end(), std::back_inserter(deps),
466 [&](const AddonPtr& _){ return !IsUserInstalled(_); });
468 CAddonsDirectory::GenerateAddonListing(path, deps, items, g_localizeStrings.Get(24996));
470 //Set orphaned status
471 std::set<std::string> orphaned;
472 for (const auto& addon : deps)
474 if (CServiceBroker::GetAddonMgr().IsOrphaned(addon, all))
475 orphaned.insert(addon->ID());
478 for (int i = 0; i < items.Size(); ++i)
480 if (orphaned.find(items[i]->GetProperty("Addon.ID").asString()) != orphaned.end())
482 items[i]->SetProperty("Addon.Status", g_localizeStrings.Get(24995));
483 items[i]->SetProperty("Addon.Orphaned", true);
488 static void OutdatedAddons(const CURL& path, CFileItemList &items)
490 VECADDONS addons = CServiceBroker::GetAddonMgr().GetAvailableUpdates();
491 CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(24043));
493 if (!items.IsEmpty())
495 if (CAddonSystemSettings::GetInstance().GetAddonAutoUpdateMode() == AUTO_UPDATES_ON)
497 const CFileItemPtr itemUpdateAllowed(
498 std::make_shared<CFileItem>("addons://update_allowed/", false));
499 itemUpdateAllowed->SetLabel(g_localizeStrings.Get(24137));
500 itemUpdateAllowed->SetSpecialSort(SortSpecialOnTop);
501 items.Add(itemUpdateAllowed);
504 const CFileItemPtr itemUpdateAll(std::make_shared<CFileItem>("addons://update_all/", false));
505 itemUpdateAll->SetLabel(g_localizeStrings.Get(24122));
506 itemUpdateAll->SetSpecialSort(SortSpecialOnTop);
507 items.Add(itemUpdateAll);
511 static void RunningAddons(const CURL& path, CFileItemList &items)
513 VECADDONS addons;
514 CServiceBroker::GetAddonMgr().GetAddons(addons, AddonType::SERVICE);
516 addons.erase(std::remove_if(addons.begin(), addons.end(),
517 [](const AddonPtr& addon){ return !CScriptInvocationManager::GetInstance().IsRunning(addon->LibPath()); }), addons.end());
518 CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(24994));
521 static bool Browse(const CURL& path, CFileItemList &items)
523 const std::string& repoId = path.GetHostName();
525 VECADDONS addons;
526 items.SetPath(path.Get());
527 if (repoId == "all")
529 CAddonRepos addonRepos;
530 if (!addonRepos.IsValid())
531 return false;
533 // get all latest addon versions by repo
534 addonRepos.GetLatestAddonVersionsFromAllRepos(addons);
536 items.SetProperty("reponame", g_localizeStrings.Get(24087));
537 items.SetLabel(g_localizeStrings.Get(24087));
539 else
541 AddonPtr repoAddon;
542 if (!CServiceBroker::GetAddonMgr().GetAddon(repoId, repoAddon, AddonType::REPOSITORY,
543 OnlyEnabled::CHOICE_YES))
545 return false;
548 CAddonRepos addonRepos(repoAddon);
549 if (!addonRepos.IsValid())
550 return false;
552 // get all addons from the single repository
553 addonRepos.GetLatestAddonVersions(addons);
555 items.SetProperty("reponame", repoAddon->Name());
556 items.SetLabel(repoAddon->Name());
559 const std::string& category = path.GetFileName();
560 if (category.empty())
561 GenerateMainCategoryListing(path, addons, items);
562 else
563 GenerateCategoryListing(path, addons, items);
564 return true;
567 static bool GetRecentlyUpdatedAddons(VECADDONS& addons)
569 if (!CServiceBroker::GetAddonMgr().GetInstalledAddons(addons))
570 return false;
572 auto limit = CDateTime::GetCurrentDateTime() - CDateTimeSpan(14, 0, 0, 0);
573 auto isOld = [limit](const AddonPtr& addon){ return addon->LastUpdated() < limit; };
574 addons.erase(std::remove_if(addons.begin(), addons.end(), isOld), addons.end());
575 return true;
578 static bool HasRecentlyUpdatedAddons()
580 VECADDONS addons;
581 return GetRecentlyUpdatedAddons(addons) && !addons.empty();
584 static bool Repos(const CURL& path, CFileItemList &items)
586 items.SetLabel(g_localizeStrings.Get(24033));
588 VECADDONS addons;
589 CServiceBroker::GetAddonMgr().GetAddons(addons, AddonType::REPOSITORY);
590 if (addons.empty())
591 return true;
592 else if (addons.size() == 1)
593 return Browse(CURL("addons://" + addons[0]->ID()), items);
594 CFileItemPtr item(new CFileItem("addons://all/", true));
595 item->SetLabel(g_localizeStrings.Get(24087));
596 item->SetSpecialSort(SortSpecialOnTop);
597 items.Add(item);
598 for (const auto& repo : addons)
600 CFileItemPtr item = CAddonsDirectory::FileItemFromAddon(repo, "addons://" + repo->ID(), true);
601 items.Add(item);
603 items.SetContent("addons");
604 return true;
607 static void RootDirectory(CFileItemList& items)
609 items.SetLabel(g_localizeStrings.Get(10040));
611 CFileItemPtr item(new CFileItem("addons://user/", true));
612 item->SetLabel(g_localizeStrings.Get(24998));
613 item->SetArt("icon", "DefaultAddonsInstalled.png");
614 items.Add(item);
616 if (CServiceBroker::GetAddonMgr().HasAvailableUpdates())
618 CFileItemPtr item(new CFileItem("addons://outdated/", true));
619 item->SetLabel(g_localizeStrings.Get(24043));
620 item->SetArt("icon", "DefaultAddonsUpdates.png");
621 items.Add(item);
623 if (CAddonInstaller::GetInstance().IsDownloading())
625 CFileItemPtr item(new CFileItem("addons://downloading/", true));
626 item->SetLabel(g_localizeStrings.Get(24067));
627 item->SetArt("icon", "DefaultNetwork.png");
628 items.Add(item);
630 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_ADDONS_AUTOUPDATES) == ADDON::AUTO_UPDATES_ON
631 && HasRecentlyUpdatedAddons())
633 CFileItemPtr item(new CFileItem("addons://recently_updated/", true));
634 item->SetLabel(g_localizeStrings.Get(24004));
635 item->SetArt("icon", "DefaultAddonsRecentlyUpdated.png");
636 items.Add(item);
638 if (CServiceBroker::GetAddonMgr().HasAddons(AddonType::REPOSITORY))
640 CFileItemPtr item(new CFileItem("addons://repos/", true));
641 item->SetLabel(g_localizeStrings.Get(24033));
642 item->SetArt("icon", "DefaultAddonsRepo.png");
643 items.Add(item);
646 CFileItemPtr item(new CFileItem("addons://install/", false));
647 item->SetLabel(g_localizeStrings.Get(24041));
648 item->SetArt("icon", "DefaultAddonsZip.png");
649 items.Add(item);
652 CFileItemPtr item(new CFileItem("addons://search/", true));
653 item->SetLabel(g_localizeStrings.Get(137));
654 item->SetArt("icon", "DefaultAddonsSearch.png");
655 items.Add(item);
659 bool CAddonsDirectory::GetDirectory(const CURL& url, CFileItemList &items)
661 std::string tmp(url.Get());
662 URIUtils::RemoveSlashAtEnd(tmp);
663 CURL path(tmp);
664 const std::string& endpoint = path.GetHostName();
665 items.ClearItems();
666 items.ClearProperties();
667 items.SetCacheToDisc(CFileItemList::CACHE_NEVER);
668 items.SetPath(path.Get());
670 if (endpoint.empty())
672 RootDirectory(items);
673 return true;
675 else if (endpoint == "user")
677 UserInstalledAddons(path, items);
678 return true;
680 else if (endpoint == "dependencies")
682 DependencyAddons(path, items);
683 return true;
685 // PVR hardcodes this view so keep for compatibility
686 else if (endpoint == "disabled")
688 VECADDONS addons;
689 AddonType type;
691 if (path.GetFileName() == "kodi.pvrclient")
692 type = AddonType::PVRDLL;
693 else if (path.GetFileName() == "kodi.vfs")
694 type = AddonType::VFS;
695 else
696 type = AddonType::UNKNOWN;
698 if (type != AddonType::UNKNOWN &&
699 CServiceBroker::GetAddonMgr().GetInstalledAddons(addons, type))
701 CAddonsDirectory::GenerateAddonListing(path, addons, items, CAddonInfo::TranslateType(type, true));
702 return true;
704 return false;
706 else if (endpoint == "outdated")
708 OutdatedAddons(path, items);
709 return true;
711 else if (endpoint == "running")
713 RunningAddons(path, items);
714 return true;
716 else if (endpoint == "repos")
718 return Repos(path, items);
720 else if (endpoint == "sources")
722 return GetScriptsAndPlugins(path.GetFileName(), items);
724 else if (endpoint == "search")
726 return GetSearchResults(path, items);
728 else if (endpoint == "recently_updated")
730 VECADDONS addons;
731 if (!GetRecentlyUpdatedAddons(addons))
732 return false;
734 CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(24004));
735 return true;
737 else if (endpoint == "downloading")
739 VECADDONS addons;
740 CAddonInstaller::GetInstance().GetInstallList(addons);
741 CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(24067));
742 return true;
744 else if (endpoint == "more")
746 const std::string& type = path.GetFileName();
747 if (type == "video" || type == "audio" || type == "image" || type == "executable")
748 return Browse(CURL("addons://all/xbmc.addon." + type), items);
749 else if (type == "game")
750 return Browse(CURL("addons://all/category.gameaddons"), items);
751 return false;
753 else
755 return Browse(path, items);
759 bool CAddonsDirectory::IsRepoDirectory(const CURL& url)
761 if (url.GetHostName().empty() || !url.IsProtocol("addons"))
762 return false;
764 AddonPtr tmp;
765 return url.GetHostName() == "repos" || url.GetHostName() == "all" ||
766 url.GetHostName() == "search" ||
767 CServiceBroker::GetAddonMgr().GetAddon(url.GetHostName(), tmp, AddonType::REPOSITORY,
768 OnlyEnabled::CHOICE_YES);
771 void CAddonsDirectory::GenerateAddonListing(const CURL& path,
772 const VECADDONS& addons,
773 CFileItemList& items,
774 const std::string& label)
776 std::map<std::string, AddonWithUpdate> addonsWithUpdate =
777 CServiceBroker::GetAddonMgr().GetAddonsWithAvailableUpdate();
779 items.ClearItems();
780 items.SetContent("addons");
781 items.SetLabel(label);
782 for (const auto& addon : addons)
784 CURL itemPath = path;
785 itemPath.SetFileName(addon->ID());
786 CFileItemPtr pItem = FileItemFromAddon(addon, itemPath.Get(), false);
788 bool installed = CServiceBroker::GetAddonMgr().IsAddonInstalled(addon->ID(), addon->Origin(),
789 addon->Version());
790 bool disabled = CServiceBroker::GetAddonMgr().IsAddonDisabled(addon->ID());
792 std::function<bool(bool)> CheckOutdatedOrUpdate = [&](bool checkOutdated) -> bool {
793 auto mapEntry = addonsWithUpdate.find(addon->ID());
794 if (mapEntry != addonsWithUpdate.end())
796 const std::shared_ptr<IAddon>& checkedObject =
797 checkOutdated ? mapEntry->second.m_installed : mapEntry->second.m_update;
799 return (checkedObject->Origin() == addon->Origin() &&
800 checkedObject->Version() == addon->Version());
802 return false;
805 bool isUpdate = CheckOutdatedOrUpdate(false); // check if it's an available update
806 bool hasUpdate = CheckOutdatedOrUpdate(true); // check if it's an outdated addon
808 std::string validUpdateVersion;
809 std::string validUpdateOrigin;
810 if (hasUpdate)
812 auto mapEntry = addonsWithUpdate.find(addon->ID());
813 validUpdateVersion = mapEntry->second.m_update->Version().asString();
814 validUpdateOrigin = mapEntry->second.m_update->Origin();
817 bool fromOfficialRepo = CAddonRepos::IsFromOfficialRepo(addon, CheckAddonPath::CHOICE_NO);
819 pItem->SetProperty("Addon.IsInstalled", installed);
820 pItem->SetProperty("Addon.IsEnabled", installed && !disabled);
821 pItem->SetProperty("Addon.HasUpdate", hasUpdate);
822 pItem->SetProperty("Addon.IsUpdate", isUpdate);
823 pItem->SetProperty("Addon.ValidUpdateVersion", validUpdateVersion);
824 pItem->SetProperty("Addon.ValidUpdateOrigin", validUpdateOrigin);
825 pItem->SetProperty("Addon.IsFromOfficialRepo", fromOfficialRepo);
826 pItem->SetProperty("Addon.IsBinary", addon->IsBinary());
828 if (installed)
829 pItem->SetProperty("Addon.Status", g_localizeStrings.Get(305));
830 if (disabled)
831 pItem->SetProperty("Addon.Status", g_localizeStrings.Get(24023));
832 if (hasUpdate)
833 pItem->SetProperty("Addon.Status", g_localizeStrings.Get(24068));
834 else if (addon->LifecycleState() == AddonLifecycleState::BROKEN)
835 pItem->SetProperty("Addon.Status", g_localizeStrings.Get(24098));
836 else if (addon->LifecycleState() == AddonLifecycleState::DEPRECATED)
837 pItem->SetProperty("Addon.Status", g_localizeStrings.Get(24170));
839 items.Add(pItem);
843 CFileItemPtr CAddonsDirectory::FileItemFromAddon(const AddonPtr &addon,
844 const std::string& path, bool folder)
846 if (!addon)
847 return CFileItemPtr();
849 CFileItemPtr item(new CFileItem(addon));
850 item->m_bIsFolder = folder;
851 item->SetPath(path);
853 std::string strLabel(addon->Name());
854 if (CURL(path).GetHostName() == "search")
855 strLabel = StringUtils::Format("{} - {}", CAddonInfo::TranslateType(addon->Type(), true),
856 addon->Name());
857 item->SetLabel(strLabel);
858 item->SetArt(addon->Art());
859 item->SetArt("thumb", addon->Icon());
860 item->SetArt("icon", "DefaultAddon.png");
862 //! @todo fix hacks that depends on these
863 item->SetProperty("Addon.ID", addon->ID());
864 item->SetProperty("Addon.Name", addon->Name());
865 item->SetCanQueue(false);
866 const auto it = addon->ExtraInfo().find("language");
867 if (it != addon->ExtraInfo().end())
868 item->SetProperty("Addon.Language", it->second);
870 return item;
873 bool CAddonsDirectory::GetScriptsAndPlugins(const std::string &content, VECADDONS &addons)
875 CPluginSource::Content type = CPluginSource::Translate(content);
876 if (type == CPluginSource::UNKNOWN)
877 return false;
879 VECADDONS tempAddons;
880 CServiceBroker::GetAddonMgr().GetAddons(tempAddons, AddonType::PLUGIN);
881 for (unsigned i=0; i<tempAddons.size(); i++)
883 const auto plugin = std::dynamic_pointer_cast<CPluginSource>(tempAddons[i]);
884 if (plugin && plugin->Provides(type))
885 addons.push_back(tempAddons[i]);
887 tempAddons.clear();
888 CServiceBroker::GetAddonMgr().GetAddons(tempAddons, AddonType::SCRIPT);
889 for (unsigned i=0; i<tempAddons.size(); i++)
891 const auto plugin = std::dynamic_pointer_cast<CPluginSource>(tempAddons[i]);
892 if (plugin && plugin->Provides(type))
893 addons.push_back(tempAddons[i]);
895 tempAddons.clear();
897 if (type == CPluginSource::GAME)
899 CServiceBroker::GetAddonMgr().GetAddons(tempAddons, AddonType::GAMEDLL);
900 for (auto& addon : tempAddons)
902 if (IsStandaloneGame(addon))
903 addons.push_back(addon);
907 return true;
910 bool CAddonsDirectory::GetScriptsAndPlugins(const std::string &content, CFileItemList &items)
912 VECADDONS addons;
913 if (!GetScriptsAndPlugins(content, addons))
914 return false;
916 for (AddonPtr& addon : addons)
918 const bool bIsFolder = (addon->Type() == AddonType::PLUGIN);
920 std::string path;
921 if (addon->HasType(AddonType::PLUGIN))
923 path = "plugin://" + addon->ID();
924 const auto plugin = std::dynamic_pointer_cast<CPluginSource>(addon);
925 if (plugin && plugin->ProvidesSeveral())
927 CURL url(path);
928 std::string opt = StringUtils::Format("?content_type={}", content);
929 url.SetOptions(opt);
930 path = url.Get();
933 else if (addon->HasType(AddonType::SCRIPT))
935 path = "script://" + addon->ID();
937 else if (addon->HasType(AddonType::GAMEDLL))
939 // Kodi fails to launch games with empty path from home screen
940 path = "game://" + addon->ID();
943 items.Add(FileItemFromAddon(addon, path, bIsFolder));
946 items.SetContent("addons");
947 items.SetLabel(g_localizeStrings.Get(24001)); // Add-ons
949 return true;