/* * Copyright (C) 2012-2018 Team Kodi * This file is part of Kodi - https://kodi.tv * * SPDX-License-Identifier: GPL-2.0-or-later * See LICENSES/README.md for more information. */ #include "PVRProviders.h" #include "ServiceBroker.h" #include "pvr/PVRCachedImages.h" #include "pvr/PVRDatabase.h" #include "pvr/PVRManager.h" #include "pvr/addons/PVRClient.h" #include "pvr/addons/PVRClients.h" #include "pvr/providers/PVRProvider.h" #include "settings/Settings.h" #include "utils/log.h" #include #include #include #include #include #include using namespace PVR; bool CPVRProvidersContainer::UpdateFromClient(const std::shared_ptr& provider) { std::unique_lock lock(m_critSection); const std::shared_ptr providerToUpdate = GetByClient(provider->GetClientId(), provider->GetUniqueId()); if (providerToUpdate) { return providerToUpdate->UpdateEntry(provider, ProviderUpdateMode::BY_CLIENT); } else { provider->SetDatabaseId(++m_iLastId); InsertEntry(provider, ProviderUpdateMode::BY_CLIENT); } return true; } std::shared_ptr CPVRProvidersContainer::GetByClient(int iClientId, int iUniqueId) const { std::unique_lock lock(m_critSection); const auto it = std::find_if( m_providers.cbegin(), m_providers.cend(), [iClientId, iUniqueId](const auto& provider) { return provider->GetClientId() == iClientId && provider->GetUniqueId() == iUniqueId; }); return it != m_providers.cend() ? (*it) : std::shared_ptr(); } void CPVRProvidersContainer::InsertEntry(const std::shared_ptr& newProvider, ProviderUpdateMode updateMode) { bool found = false; for (auto& provider : m_providers) { if (provider->GetClientId() == newProvider->GetClientId() && provider->GetUniqueId() == newProvider->GetUniqueId()) { found = true; provider->UpdateEntry(newProvider, updateMode); } } if (!found) { m_providers.emplace_back(newProvider); } } std::vector> CPVRProvidersContainer::GetProvidersList() const { std::unique_lock lock(m_critSection); return m_providers; } std::size_t CPVRProvidersContainer::GetNumProviders() const { std::unique_lock lock(m_critSection); return m_providers.size(); } bool CPVRProviders::Update(const std::vector>& clients) { return LoadFromDatabase(clients) && UpdateFromClients(clients); } void CPVRProviders::Unload() { // remove all tags std::unique_lock lock(m_critSection); m_providers.clear(); } bool CPVRProviders::LoadFromDatabase(const std::vector>& clients) { const std::shared_ptr database = CServiceBroker::GetPVRManager().GetTVDatabase(); if (database) { m_iLastId = database->GetMaxProviderId(); CPVRProviders providers; database->Get(providers, clients); for (auto& provider : providers.GetProvidersList()) CheckAndAddEntry(provider, ProviderUpdateMode::BY_DATABASE); } return true; } bool CPVRProviders::UpdateFromClients(const std::vector>& clients) { { std::unique_lock lock(m_critSection); if (m_bIsUpdating) return false; m_bIsUpdating = true; } // Default client providers are the add-ons themselves, we retrieve both enabled // and disabled add-ons as we don't want them removed from the DB CPVRProviders newAddonProviderList; std::vector disabledClients; std::vector clientProviderInfos = CServiceBroker::GetPVRManager().Clients()->GetClientProviderInfos(); CLog::LogFC(LOGDEBUG, LOGPVR, "Adding default providers, found {} PVR add-ons", clientProviderInfos.size()); for (const auto& clientInfo : clientProviderInfos) { auto addonProvider = std::make_shared( clientInfo["clientid"].asInteger32(), clientInfo["name"].asString(), clientInfo["icon"].asString(), clientInfo["thumb"].asString()); newAddonProviderList.CheckAndAddEntry(addonProvider, ProviderUpdateMode::BY_CLIENT); if (!clientInfo["enabled"].asBoolean()) disabledClients.emplace_back(clientInfo["clientid"].asInteger32()); } UpdateDefaultEntries(newAddonProviderList); // Client providers are retrieved from the clients CLog::LogFC(LOGDEBUG, LOGPVR, "Updating providers"); CPVRProvidersContainer newProviderList; std::vector failedClients; CServiceBroker::GetPVRManager().Clients()->GetProviders(clients, &newProviderList, failedClients); return UpdateClientEntries(newProviderList, failedClients, disabledClients); } bool CPVRProviders::UpdateDefaultEntries(const CPVRProvidersContainer& newProviders) { bool bChanged = false; std::unique_lock lock(m_critSection); // go through the provider list and check for updated or new providers const auto newProviderList = newProviders.GetProvidersList(); bChanged = std::accumulate( newProviderList.cbegin(), newProviderList.cend(), false, [this](bool changed, const auto& newProvider) { return (CheckAndPersistEntry(newProvider, ProviderUpdateMode::BY_CLIENT) != nullptr) ? true : changed; }); // check for deleted providers for (std::vector>::iterator it = m_providers.begin(); it != m_providers.end();) { const std::shared_ptr provider = *it; if (!newProviders.GetByClient(provider->GetClientId(), provider->GetUniqueId())) { // provider was not found bool bIgnoreProvider = false; // ignore add-on any providers that are no PVR Client addon providers if (!provider->IsClientProvider()) bIgnoreProvider = true; if (bIgnoreProvider) { ++it; continue; } CLog::LogFC(LOGDEBUG, LOGPVR, "Deleted provider {} on client {}", provider->GetUniqueId(), provider->GetClientId()); (*it)->DeleteFromDatabase(); it = m_providers.erase(it); bChanged |= true; } else { ++it; } } return bChanged; } bool CPVRProviders::UpdateClientEntries(const CPVRProvidersContainer& newProviders, const std::vector& failedClients, const std::vector& disabledClients) { std::unique_lock lock(m_critSection); // go through the provider list and check for updated or new providers for (const auto& newProvider : newProviders.GetProvidersList()) { CheckAndPersistEntry(newProvider, ProviderUpdateMode::BY_CLIENT); } // check for deleted providers for (auto it = m_providers.begin(); it != m_providers.end();) { const std::shared_ptr provider = *it; if (!newProviders.GetByClient(provider->GetClientId(), provider->GetUniqueId())) { const bool bIgnoreProvider = (provider->IsClientProvider() || // ignore add-on providers as they are a special case std::any_of(failedClients.cbegin(), failedClients.cend(), [&provider](const auto& failedClient) { return failedClient == provider->GetClientId(); }) || std::any_of(disabledClients.cbegin(), disabledClients.cend(), [&provider](const auto& disabledClient) { return disabledClient == provider->GetClientId(); })); if (bIgnoreProvider) { ++it; continue; } CLog::LogFC(LOGDEBUG, LOGPVR, "Deleted provider {} on client {}", provider->GetUniqueId(), provider->GetClientId()); (*it)->DeleteFromDatabase(); it = m_providers.erase(it); } else { ++it; } } m_bIsUpdating = false; return true; } std::shared_ptr CPVRProviders::CheckAndAddEntry( const std::shared_ptr& newProvider, ProviderUpdateMode updateMode) { bool bChanged = false; std::unique_lock lock(m_critSection); std::shared_ptr provider = GetByClient(newProvider->GetClientId(), newProvider->GetUniqueId()); if (provider) { bChanged = provider->UpdateEntry(newProvider, updateMode); } else { // We don't set an id as this came from the DB so it has one already InsertEntry(newProvider, updateMode); if (newProvider->GetDatabaseId() > m_iLastId) m_iLastId = newProvider->GetDatabaseId(); provider = newProvider; bChanged = true; } if (bChanged) return provider; return {}; } std::shared_ptr CPVRProviders::CheckAndPersistEntry( const std::shared_ptr& newProvider, ProviderUpdateMode updateMode) { bool bChanged = false; std::unique_lock lock(m_critSection); std::shared_ptr provider = GetByClient(newProvider->GetClientId(), newProvider->GetUniqueId()); if (provider) { bChanged = provider->UpdateEntry(newProvider, updateMode); if (bChanged) provider->Persist(true); CLog::LogFC(LOGDEBUG, LOGPVR, "Updated provider {} on client {}", newProvider->GetUniqueId(), newProvider->GetClientId()); } else { newProvider->SetDatabaseId(++m_iLastId); InsertEntry(newProvider, updateMode); newProvider->Persist(); CLog::LogFC(LOGDEBUG, LOGPVR, "Added provider {} on client {}", newProvider->GetUniqueId(), newProvider->GetClientId()); provider = newProvider; bChanged = true; } if (bChanged) return provider; return {}; } bool CPVRProviders::PersistUserChanges(const std::vector>& providers) { for (const auto& provider : providers) { provider->Persist(true); CLog::LogFC(LOGDEBUG, LOGPVR, "Updated provider {} on client {}", provider->GetUniqueId(), provider->GetClientId()); } return true; } std::shared_ptr CPVRProviders::GetById(int iProviderId) const { std::unique_lock lock(m_critSection); const auto it = std::find_if(m_providers.cbegin(), m_providers.cend(), [iProviderId](const auto& provider) { return provider->GetDatabaseId() == iProviderId; }); return it != m_providers.cend() ? (*it) : std::shared_ptr(); } void CPVRProviders::RemoveEntry(const std::shared_ptr& provider) { std::unique_lock lock(m_critSection); m_providers.erase( std::remove_if(m_providers.begin(), m_providers.end(), [&provider](const std::shared_ptr& providerToRemove) { return provider->GetClientId() == providerToRemove->GetClientId() && provider->GetUniqueId() == providerToRemove->GetUniqueId(); }), m_providers.end()); } int CPVRProviders::CleanupCachedImages() { std::vector urlsToCheck; { std::unique_lock lock(m_critSection); for (const auto& provider : m_providers) { urlsToCheck.emplace_back(provider->GetClientIconPath()); urlsToCheck.emplace_back(provider->GetClientThumbPath()); } } static const std::vector urlPatterns = {{CPVRProvider::IMAGE_OWNER_PATTERN, ""}}; return CPVRCachedImages::Cleanup(urlPatterns, urlsToCheck); }