Merge pull request #26073 from sundermann/ffmpeg-new-codec-profiles
[xbmc.git] / xbmc / pvr / addons / PVRClients.cpp
blob32f290dcee7528ac05f6e84b098ffb65f542fb13
1 /*
2 * Copyright (C) 2012-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 "PVRClients.h"
11 #include "ServiceBroker.h"
12 #include "addons/AddonEvents.h"
13 #include "addons/AddonManager.h"
14 #include "addons/addoninfo/AddonInfo.h"
15 #include "addons/addoninfo/AddonType.h"
16 #include "guilib/LocalizeStrings.h"
17 #include "messaging/ApplicationMessenger.h"
18 #include "pvr/PVRConstants.h" // PVR_CLIENT_INVALID_UID
19 #include "pvr/PVREventLogJob.h"
20 #include "pvr/PVRManager.h"
21 #include "pvr/PVRPlaybackState.h"
22 #include "pvr/addons/PVRClient.h"
23 #include "pvr/addons/PVRClientUID.h"
24 #include "pvr/guilib/PVRGUIProgressHandler.h"
25 #include "utils/JobManager.h"
26 #include "utils/StringUtils.h"
27 #include "utils/log.h"
29 #include <algorithm>
30 #include <functional>
31 #include <memory>
32 #include <mutex>
33 #include <string>
34 #include <utility>
35 #include <vector>
37 using namespace ADDON;
38 using namespace PVR;
40 CPVRClients::CPVRClients()
42 CServiceBroker::GetAddonMgr().RegisterAddonMgrCallback(AddonType::PVRDLL, this);
43 CServiceBroker::GetAddonMgr().Events().Subscribe(this, &CPVRClients::OnAddonEvent);
46 CPVRClients::~CPVRClients()
48 CServiceBroker::GetAddonMgr().Events().Unsubscribe(this);
49 CServiceBroker::GetAddonMgr().UnregisterAddonMgrCallback(AddonType::PVRDLL);
51 for (const auto& client : m_clientMap)
53 client.second->Destroy();
57 void CPVRClients::Start()
59 UpdateClients();
62 void CPVRClients::Stop()
64 std::unique_lock<CCriticalSection> lock(m_critSection);
65 for (const auto& client : m_clientMap)
67 client.second->Stop();
71 void CPVRClients::Continue()
73 std::unique_lock<CCriticalSection> lock(m_critSection);
74 for (const auto& client : m_clientMap)
76 client.second->Continue();
80 void CPVRClients::UpdateClients(
81 const std::string& changedAddonId /* = "" */,
82 ADDON::AddonInstanceId changedInstanceId /* = ADDON::ADDON_SINGLETON_INSTANCE_ID */)
84 std::vector<std::pair<AddonInfoPtr, bool>> addonsWithStatus;
85 if (!GetAddonsWithStatus(changedAddonId, addonsWithStatus))
86 return;
88 std::vector<std::shared_ptr<CPVRClient>> clientsToCreate; // client
89 std::vector<std::pair<int, std::string>> clientsToReCreate; // client id, addon name
90 std::vector<int> clientsToDestroy; // client id
93 std::unique_lock<CCriticalSection> lock(m_critSection);
94 for (const auto& addonWithStatus : addonsWithStatus)
96 const AddonInfoPtr addon = addonWithStatus.first;
97 const std::vector<std::pair<ADDON::AddonInstanceId, bool>> instanceIdsWithStatus =
98 GetInstanceIdsWithStatus(addon, addonWithStatus.second);
100 for (const auto& instanceIdWithStatus : instanceIdsWithStatus)
102 const ADDON::AddonInstanceId instanceId = instanceIdWithStatus.first;
103 bool instanceEnabled = instanceIdWithStatus.second;
104 const CPVRClientUID clientUID(addon->ID(), instanceId);
105 const int clientId = clientUID.GetUID();
107 if (instanceEnabled && (!IsKnownClient(clientId) || !IsCreatedClient(clientId)))
109 std::shared_ptr<CPVRClient> client;
110 const bool isKnownClient = IsKnownClient(clientId);
111 if (isKnownClient)
113 client = GetClient(clientId);
115 else
117 client = std::make_shared<CPVRClient>(addon, instanceId, clientId);
120 // determine actual enabled state of instance
121 if (instanceId != ADDON_SINGLETON_INSTANCE_ID)
122 instanceEnabled = client->IsEnabled();
124 if (instanceEnabled)
126 CLog::LogF(LOGINFO, "Creating PVR client: addonId={}, instanceId={}, clientId={}",
127 addon->ID(), instanceId, clientId);
128 clientsToCreate.emplace_back(client);
130 else if (isKnownClient)
132 CLog::LogF(LOGINFO, "Destroying PVR client: addonId={}, instanceId={}, clientId={}",
133 addon->ID(), instanceId, clientId);
134 clientsToDestroy.emplace_back(clientId);
137 else if (IsCreatedClient(clientId))
139 // determine actual enabled state of instance
140 if (instanceEnabled && instanceId != ADDON_SINGLETON_INSTANCE_ID)
142 const std::shared_ptr<const CPVRClient> client = GetClient(clientId);
143 instanceEnabled = client ? client->IsEnabled() : false;
146 if (instanceEnabled)
148 CLog::LogF(LOGINFO, "Recreating PVR client: addonId={}, instanceId={}, clientId={}",
149 addon->ID(), instanceId, clientId);
150 clientsToReCreate.emplace_back(clientId, addon->Name());
152 else
154 CLog::LogF(LOGINFO, "Destroying PVR client: addonId={}, instanceId={}, clientId={}",
155 addon->ID(), instanceId, clientId);
156 clientsToDestroy.emplace_back(clientId);
163 if (!clientsToCreate.empty() || !clientsToReCreate.empty() || !clientsToDestroy.empty())
165 CServiceBroker::GetPVRManager().Stop();
167 auto progressHandler = std::make_unique<CPVRGUIProgressHandler>(
168 g_localizeStrings.Get(19239)); // Creating PVR clients
170 unsigned int i = 0;
171 for (const auto& client : clientsToCreate)
173 progressHandler->UpdateProgress(
174 client->Name(), i++,
175 static_cast<unsigned int>(clientsToCreate.size() + clientsToReCreate.size()));
177 const ADDON_STATUS status = client->Create();
179 if (status != ADDON_STATUS_OK)
181 if (status == ADDON_STATUS_PERMANENT_FAILURE)
183 CServiceBroker::GetAddonMgr().DisableAddon(client->ID(),
184 AddonDisabledReason::PERMANENT_FAILURE);
185 CServiceBroker::GetJobManager()->AddJob(
186 new CPVREventLogJob(true, EventLevel::Error, client->Name(),
187 g_localizeStrings.Get(24070), client->Icon()),
188 nullptr);
193 for (const auto& clientInfo : clientsToReCreate)
195 progressHandler->UpdateProgress(
196 clientInfo.second, i++,
197 static_cast<unsigned int>(clientsToCreate.size() + clientsToReCreate.size()));
199 // stop and recreate client
200 StopClient(clientInfo.first, true /* restart */);
203 progressHandler.reset();
205 for (const auto& client : clientsToDestroy)
207 // destroy client
208 StopClient(client, false /* no restart */);
211 if (!clientsToCreate.empty())
213 // update created clients map
214 std::unique_lock<CCriticalSection> lock(m_critSection);
215 for (const auto& client : clientsToCreate)
217 if (m_clientMap.find(client->GetID()) == m_clientMap.end())
219 m_clientMap.insert({client->GetID(), client});
224 CServiceBroker::GetPVRManager().Start();
228 bool CPVRClients::RequestRestart(const std::string& addonId,
229 ADDON::AddonInstanceId instanceId,
230 bool bDataChanged)
232 CServiceBroker::GetJobManager()->Submit([this, addonId, instanceId] {
233 UpdateClients(addonId, instanceId);
234 return true;
236 return true;
239 bool CPVRClients::StopClient(int clientId, bool restart)
241 // stop playback if needed
242 if (CServiceBroker::GetPVRManager().PlaybackState()->IsPlaying())
243 CServiceBroker::GetAppMessenger()->SendMsg(TMSG_MEDIA_STOP);
245 std::unique_lock<CCriticalSection> lock(m_critSection);
247 const std::shared_ptr<CPVRClient> client = GetClient(clientId);
248 if (client)
250 if (restart)
252 client->ReCreate();
254 else
256 const auto it = m_clientMap.find(clientId);
257 if (it != m_clientMap.end())
258 m_clientMap.erase(it);
260 client->Destroy();
262 return true;
265 return false;
268 void CPVRClients::OnAddonEvent(const AddonEvent& event)
270 if (typeid(event) == typeid(AddonEvents::Enabled) || // also called on install,
271 typeid(event) == typeid(AddonEvents::Disabled) || // not called on uninstall
272 typeid(event) == typeid(AddonEvents::UnInstalled) ||
273 typeid(event) == typeid(AddonEvents::ReInstalled) ||
274 typeid(event) == typeid(AddonEvents::InstanceAdded) ||
275 typeid(event) == typeid(AddonEvents::InstanceRemoved))
277 // update addons
278 const std::string addonId = event.addonId;
279 const ADDON::AddonInstanceId instanceId = event.instanceId;
280 if (CServiceBroker::GetAddonMgr().HasType(addonId, AddonType::PVRDLL))
282 CServiceBroker::GetJobManager()->Submit([this, addonId, instanceId] {
283 UpdateClients(addonId, instanceId);
284 return true;
290 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
291 // client access
292 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
294 std::shared_ptr<CPVRClient> CPVRClients::GetClient(int clientId) const
296 if (clientId == PVR_CLIENT_INVALID_UID)
297 return {};
299 std::unique_lock<CCriticalSection> lock(m_critSection);
300 const auto it = m_clientMap.find(clientId);
301 if (it != m_clientMap.end())
302 return it->second;
304 return {};
307 int CPVRClients::CreatedClientAmount() const
309 std::unique_lock<CCriticalSection> lock(m_critSection);
310 return static_cast<int>(std::count_if(m_clientMap.cbegin(), m_clientMap.cend(),
311 [](const auto& client)
312 { return client.second->ReadyToUse(); }));
315 bool CPVRClients::HasCreatedClients() const
317 std::unique_lock<CCriticalSection> lock(m_critSection);
318 return std::any_of(m_clientMap.cbegin(), m_clientMap.cend(),
319 [](const auto& client) { return client.second->ReadyToUse(); });
322 bool CPVRClients::IsKnownClient(int clientId) const
324 std::unique_lock<CCriticalSection> lock(m_critSection);
326 // valid client IDs start at 1
327 const auto it = m_clientMap.find(clientId);
328 return (it != m_clientMap.end() && (*it).second->GetID() > 0);
331 bool CPVRClients::IsCreatedClient(int iClientId) const
333 return GetCreatedClient(iClientId) != nullptr;
336 std::shared_ptr<CPVRClient> CPVRClients::GetCreatedClient(int clientId) const
338 std::shared_ptr<CPVRClient> client = GetClient(clientId);
339 if (client && client->ReadyToUse())
340 return client;
342 return {};
345 CPVRClientMap CPVRClients::GetCreatedClients() const
347 CPVRClientMap clients;
349 std::unique_lock<CCriticalSection> lock(m_critSection);
350 for (const auto& client : m_clientMap)
352 if (client.second->ReadyToUse())
354 clients.insert(std::make_pair(client.second->GetID(), client.second));
358 return clients;
361 std::vector<CVariant> CPVRClients::GetClientProviderInfos() const
363 std::vector<AddonInfoPtr> addonInfos;
364 // Get enabled and disabled PVR client addon infos
365 CServiceBroker::GetAddonMgr().GetAddonInfos(addonInfos, false, AddonType::PVRDLL);
367 std::unique_lock<CCriticalSection> lock(m_critSection);
369 std::vector<CVariant> clientProviderInfos;
370 for (const auto& addonInfo : addonInfos)
372 std::vector<ADDON::AddonInstanceId> instanceIds = addonInfo->GetKnownInstanceIds();
373 for (const auto& instanceId : instanceIds)
375 CVariant clientProviderInfo(CVariant::VariantTypeObject);
376 const int clientId{CPVRClientUID(addonInfo->ID(), instanceId).GetUID()};
377 clientProviderInfo["clientid"] = clientId;
378 clientProviderInfo["addonid"] = addonInfo->ID();
379 clientProviderInfo["instanceid"] = instanceId;
380 std::string fullName;
381 const std::shared_ptr<const CPVRClient> client{GetClient(clientId)};
382 if (client)
383 fullName = client->GetFullClientName();
384 else
385 fullName = addonInfo->Name();
386 clientProviderInfo["fullname"] = fullName;
387 clientProviderInfo["enabled"] =
388 !CServiceBroker::GetAddonMgr().IsAddonDisabled(addonInfo->ID());
389 clientProviderInfo["name"] = addonInfo->Name();
390 clientProviderInfo["icon"] = addonInfo->Icon();
391 auto& artMap = addonInfo->Art();
392 auto thumbEntry = artMap.find("thumb");
393 if (thumbEntry != artMap.end())
394 clientProviderInfo["thumb"] = thumbEntry->second;
396 clientProviderInfos.emplace_back(clientProviderInfo);
400 return clientProviderInfos;
403 int CPVRClients::GetFirstCreatedClientID() const
405 std::unique_lock<CCriticalSection> lock(m_critSection);
406 const auto it = std::find_if(m_clientMap.cbegin(), m_clientMap.cend(),
407 [](const auto& client) { return client.second->ReadyToUse(); });
408 return it != m_clientMap.cend() ? (*it).second->GetID() : PVR_CLIENT_INVALID_UID;
411 PVR_ERROR CPVRClients::GetCallableClients(CPVRClientMap& clientsReady,
412 std::vector<int>& clientsNotReady) const
414 clientsNotReady.clear();
416 std::vector<AddonInfoPtr> addons;
417 CServiceBroker::GetAddonMgr().GetAddonInfos(addons, true, AddonType::PVRDLL);
419 for (const auto& addon : addons)
421 std::vector<ADDON::AddonInstanceId> instanceIds = addon->GetKnownInstanceIds();
422 for (const auto& instanceId : instanceIds)
424 const int clientId = CPVRClientUID(addon->ID(), instanceId).GetUID();
425 const std::shared_ptr<CPVRClient> client = GetClient(clientId);
427 if (client && client->ReadyToUse() && !client->IgnoreClient())
429 clientsReady.insert(std::make_pair(clientId, client));
431 else
433 clientsNotReady.emplace_back(clientId);
438 return clientsNotReady.empty() ? PVR_ERROR_NO_ERROR : PVR_ERROR_SERVER_ERROR;
441 int CPVRClients::EnabledClientAmount() const
443 CPVRClientMap clientMap;
445 std::unique_lock<CCriticalSection> lock(m_critSection);
446 clientMap = m_clientMap;
449 ADDON::CAddonMgr& addonMgr = CServiceBroker::GetAddonMgr();
450 return static_cast<int>(std::count_if(
451 clientMap.cbegin(), clientMap.cend(),
452 [&addonMgr](const auto& client) { return !addonMgr.IsAddonDisabled(client.second->ID()); }));
455 bool CPVRClients::IsEnabledClient(int clientId) const
457 const std::shared_ptr<const CPVRClient> client = GetClient(clientId);
458 return client && !CServiceBroker::GetAddonMgr().IsAddonDisabled(client->ID());
461 std::vector<CVariant> CPVRClients::GetEnabledClientInfos() const
463 std::vector<CVariant> clientInfos;
465 CPVRClientMap clientMap;
467 std::unique_lock<CCriticalSection> lock(m_critSection);
468 clientMap = m_clientMap;
471 for (const auto& client : clientMap)
473 const auto& addonInfo =
474 CServiceBroker::GetAddonMgr().GetAddonInfo(client.second->ID(), AddonType::PVRDLL);
476 if (addonInfo)
478 // This will be the same variant structure used in the json api
479 CVariant clientInfo(CVariant::VariantTypeObject);
480 clientInfo["clientid"] = client.first;
481 clientInfo["addonid"] = client.second->ID();
482 clientInfo["instanceid"] = client.second->InstanceId();
483 clientInfo["label"] = addonInfo->Name(); // Note that this is called label instead of name
485 const auto& capabilities = client.second->GetClientCapabilities();
486 clientInfo["supportstv"] = capabilities.SupportsTV();
487 clientInfo["supportsradio"] = capabilities.SupportsRadio();
488 clientInfo["supportsepg"] = capabilities.SupportsEPG();
489 clientInfo["supportsrecordings"] = capabilities.SupportsRecordings();
490 clientInfo["supportstimers"] = capabilities.SupportsTimers();
491 clientInfo["supportschannelgroups"] = capabilities.SupportsChannelGroups();
492 clientInfo["supportschannelscan"] = capabilities.SupportsChannelScan();
493 clientInfo["supportchannelproviders"] = capabilities.SupportsProviders();
495 clientInfos.push_back(clientInfo);
499 return clientInfos;
502 bool CPVRClients::HasIgnoredClients() const
504 std::unique_lock<CCriticalSection> lock(m_critSection);
505 return std::any_of(m_clientMap.cbegin(), m_clientMap.cend(),
506 [](const auto& client) { return client.second->IgnoreClient(); });
509 std::vector<ADDON::AddonInstanceId> CPVRClients::GetKnownInstanceIds(
510 const std::string& addonID) const
512 std::vector<ADDON::AddonInstanceId> instanceIds;
514 std::unique_lock<CCriticalSection> lock(m_critSection);
515 for (const auto& entry : m_clientMap)
517 if (entry.second->ID() == addonID)
518 instanceIds.emplace_back(entry.second->InstanceId());
521 return instanceIds;
524 bool CPVRClients::GetAddonsWithStatus(
525 const std::string& changedAddonId,
526 std::vector<std::pair<AddonInfoPtr, bool>>& addonsWithStatus) const
528 std::vector<AddonInfoPtr> addons;
529 CServiceBroker::GetAddonMgr().GetAddonInfos(addons, false, AddonType::PVRDLL);
531 if (addons.empty())
532 return false;
534 bool foundChangedAddon = changedAddonId.empty();
535 for (const auto& addon : addons)
537 bool enabled = !CServiceBroker::GetAddonMgr().IsAddonDisabled(addon->ID());
538 addonsWithStatus.emplace_back(addon, enabled);
540 if (!foundChangedAddon && addon->ID() == changedAddonId)
541 foundChangedAddon = true;
544 return foundChangedAddon;
547 std::vector<std::pair<ADDON::AddonInstanceId, bool>> CPVRClients::GetInstanceIdsWithStatus(
548 const AddonInfoPtr& addon, bool addonIsEnabled) const
550 std::vector<std::pair<ADDON::AddonInstanceId, bool>> instanceIdsWithStatus;
552 std::vector<ADDON::AddonInstanceId> instanceIds = addon->GetKnownInstanceIds();
553 std::transform(instanceIds.cbegin(), instanceIds.cend(),
554 std::back_inserter(instanceIdsWithStatus), [addonIsEnabled](const auto& id) {
555 return std::pair<ADDON::AddonInstanceId, bool>(id, addonIsEnabled);
558 // find removed instances
559 const std::vector<ADDON::AddonInstanceId> knownInstanceIds = GetKnownInstanceIds(addon->ID());
560 for (const auto& knownInstanceId : knownInstanceIds)
562 if (std::find(instanceIds.begin(), instanceIds.end(), knownInstanceId) == instanceIds.end())
564 // instance was removed
565 instanceIdsWithStatus.emplace_back(knownInstanceId, false);
569 return instanceIdsWithStatus;
572 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
573 // client API calls
574 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
576 std::vector<SBackend> CPVRClients::GetBackendProperties() const
578 std::vector<SBackend> backendProperties;
580 ForCreatedClients(
581 __FUNCTION__, [&backendProperties](const std::shared_ptr<const CPVRClient>& client) {
582 SBackend properties;
584 if (client->GetDriveSpace(properties.diskTotal, properties.diskUsed) == PVR_ERROR_NO_ERROR)
586 properties.diskTotal *= 1024;
587 properties.diskUsed *= 1024;
590 int iAmount = 0;
591 if (client->GetProvidersAmount(iAmount) == PVR_ERROR_NO_ERROR)
592 properties.numProviders = iAmount;
593 if (client->GetChannelGroupsAmount(iAmount) == PVR_ERROR_NO_ERROR)
594 properties.numChannelGroups = iAmount;
595 if (client->GetChannelsAmount(iAmount) == PVR_ERROR_NO_ERROR)
596 properties.numChannels = iAmount;
597 if (client->GetTimersAmount(iAmount) == PVR_ERROR_NO_ERROR)
598 properties.numTimers = iAmount;
599 if (client->GetRecordingsAmount(false, iAmount) == PVR_ERROR_NO_ERROR)
600 properties.numRecordings = iAmount;
601 if (client->GetRecordingsAmount(true, iAmount) == PVR_ERROR_NO_ERROR)
602 properties.numDeletedRecordings = iAmount;
603 properties.clientname = client->GetClientName();
604 properties.instancename = client->GetInstanceName();
605 properties.name = client->GetBackendName();
606 properties.version = client->GetBackendVersion();
607 properties.host = client->GetConnectionString();
609 backendProperties.emplace_back(properties);
610 return PVR_ERROR_NO_ERROR;
613 return backendProperties;
616 bool CPVRClients::GetTimers(const std::vector<std::shared_ptr<CPVRClient>>& clients,
617 CPVRTimersContainer* timers,
618 std::vector<int>& failedClients) const
620 return ForClients(
621 __FUNCTION__, clients,
622 [timers](const std::shared_ptr<const CPVRClient>& client) {
623 return client->GetTimers(timers);
625 failedClients) == PVR_ERROR_NO_ERROR;
628 PVR_ERROR CPVRClients::UpdateTimerTypes(const std::vector<std::shared_ptr<CPVRClient>>& clients,
629 std::vector<int>& failedClients)
631 return ForClients(
632 __FUNCTION__, clients,
633 [](const std::shared_ptr<CPVRClient>& client) { return client->UpdateTimerTypes(); },
634 failedClients);
637 const std::vector<std::shared_ptr<CPVRTimerType>> CPVRClients::GetTimerTypes() const
639 std::vector<std::shared_ptr<CPVRTimerType>> types;
641 std::unique_lock<CCriticalSection> lock(m_critSection);
642 for (const auto& entry : m_clientMap)
644 const auto& client = entry.second;
645 if (client->ReadyToUse() && !client->IgnoreClient())
647 const auto& clientTypes = client->GetTimerTypes();
648 types.insert(types.end(), clientTypes.begin(), clientTypes.end());
652 return types;
655 PVR_ERROR CPVRClients::GetRecordings(const std::vector<std::shared_ptr<CPVRClient>>& clients,
656 CPVRRecordings* recordings,
657 bool deleted,
658 std::vector<int>& failedClients) const
660 return ForClients(
661 __FUNCTION__, clients,
662 [recordings, deleted](const std::shared_ptr<const CPVRClient>& client) {
663 return client->GetRecordings(recordings, deleted);
665 failedClients);
668 PVR_ERROR CPVRClients::DeleteAllRecordingsFromTrash()
670 return ForCreatedClients(__FUNCTION__, [](const std::shared_ptr<CPVRClient>& client) {
671 return client->DeleteAllRecordingsFromTrash();
675 PVR_ERROR CPVRClients::SetEPGMaxPastDays(int iPastDays)
677 return ForCreatedClients(__FUNCTION__, [iPastDays](const std::shared_ptr<CPVRClient>& client) {
678 return client->SetEPGMaxPastDays(iPastDays);
682 PVR_ERROR CPVRClients::SetEPGMaxFutureDays(int iFutureDays)
684 return ForCreatedClients(__FUNCTION__, [iFutureDays](const std::shared_ptr<CPVRClient>& client) {
685 return client->SetEPGMaxFutureDays(iFutureDays);
689 PVR_ERROR CPVRClients::GetChannels(const std::vector<std::shared_ptr<CPVRClient>>& clients,
690 bool bRadio,
691 std::vector<std::shared_ptr<CPVRChannel>>& channels,
692 std::vector<int>& failedClients) const
694 return ForClients(
695 __FUNCTION__, clients,
696 [bRadio, &channels](const std::shared_ptr<const CPVRClient>& client) {
697 return client->GetChannels(bRadio, channels);
699 failedClients);
702 PVR_ERROR CPVRClients::GetProviders(const std::vector<std::shared_ptr<CPVRClient>>& clients,
703 CPVRProvidersContainer* providers,
704 std::vector<int>& failedClients) const
706 return ForClients(
707 __FUNCTION__, clients,
708 [providers](const std::shared_ptr<const CPVRClient>& client) {
709 return client->GetProviders(*providers);
711 failedClients);
714 PVR_ERROR CPVRClients::GetChannelGroups(const std::vector<std::shared_ptr<CPVRClient>>& clients,
715 CPVRChannelGroups* groups,
716 std::vector<int>& failedClients) const
718 return ForClients(
719 __FUNCTION__, clients,
720 [groups](const std::shared_ptr<const CPVRClient>& client) {
721 return client->GetChannelGroups(groups);
723 failedClients);
726 PVR_ERROR CPVRClients::GetChannelGroupMembers(
727 const std::vector<std::shared_ptr<CPVRClient>>& clients,
728 CPVRChannelGroup* group,
729 std::vector<std::shared_ptr<CPVRChannelGroupMember>>& groupMembers,
730 std::vector<int>& failedClients) const
732 return ForClients(
733 __FUNCTION__, clients,
734 [group, &groupMembers](const std::shared_ptr<const CPVRClient>& client) {
735 return client->GetChannelGroupMembers(group, groupMembers);
737 failedClients);
740 std::vector<std::shared_ptr<CPVRClient>> CPVRClients::GetClientsSupportingChannelScan() const
742 std::vector<std::shared_ptr<CPVRClient>> possibleScanClients;
744 std::unique_lock<CCriticalSection> lock(m_critSection);
745 for (const auto& entry : m_clientMap)
747 const auto& client = entry.second;
748 if (client->ReadyToUse() && !client->IgnoreClient() &&
749 client->GetClientCapabilities().SupportsChannelScan())
750 possibleScanClients.emplace_back(client);
753 return possibleScanClients;
756 std::vector<std::shared_ptr<CPVRClient>> CPVRClients::GetClientsSupportingChannelSettings(bool bRadio) const
758 std::vector<std::shared_ptr<CPVRClient>> possibleSettingsClients;
760 std::unique_lock<CCriticalSection> lock(m_critSection);
761 for (const auto& entry : m_clientMap)
763 const auto& client = entry.second;
764 if (client->ReadyToUse() && !client->IgnoreClient())
766 const CPVRClientCapabilities& caps = client->GetClientCapabilities();
767 if (caps.SupportsChannelSettings() &&
768 ((bRadio && caps.SupportsRadio()) || (!bRadio && caps.SupportsTV())))
769 possibleSettingsClients.emplace_back(client);
773 return possibleSettingsClients;
776 bool CPVRClients::AnyClientSupportingRecordingsSize() const
778 std::unique_lock<CCriticalSection> lock(m_critSection);
779 return std::any_of(m_clientMap.cbegin(), m_clientMap.cend(), [](const auto& entry) {
780 const auto& client = entry.second;
781 return client->ReadyToUse() && !client->IgnoreClient() &&
782 client->GetClientCapabilities().SupportsRecordingsSize();
786 bool CPVRClients::AnyClientSupportingEPG() const
788 std::unique_lock<CCriticalSection> lock(m_critSection);
789 return std::any_of(m_clientMap.cbegin(), m_clientMap.cend(), [](const auto& entry) {
790 const auto& client = entry.second;
791 return client->ReadyToUse() && !client->IgnoreClient() &&
792 client->GetClientCapabilities().SupportsEPG();
796 bool CPVRClients::AnyClientSupportingRecordings() const
798 std::unique_lock<CCriticalSection> lock(m_critSection);
799 return std::any_of(m_clientMap.cbegin(), m_clientMap.cend(), [](const auto& entry) {
800 const auto& client = entry.second;
801 return client->ReadyToUse() && !client->IgnoreClient() &&
802 client->GetClientCapabilities().SupportsRecordings();
806 bool CPVRClients::AnyClientSupportingRecordingsDelete() const
808 std::unique_lock<CCriticalSection> lock(m_critSection);
809 return std::any_of(m_clientMap.cbegin(), m_clientMap.cend(), [](const auto& entry) {
810 const auto& client = entry.second;
811 return client->ReadyToUse() && !client->IgnoreClient() &&
812 client->GetClientCapabilities().SupportsRecordingsDelete();
816 void CPVRClients::OnSystemSleep()
818 ForCreatedClients(__FUNCTION__, [](const std::shared_ptr<CPVRClient>& client) {
819 client->OnSystemSleep();
820 return PVR_ERROR_NO_ERROR;
824 void CPVRClients::OnSystemWake()
826 ForCreatedClients(__FUNCTION__, [](const std::shared_ptr<CPVRClient>& client) {
827 client->OnSystemWake();
828 return PVR_ERROR_NO_ERROR;
832 void CPVRClients::OnPowerSavingActivated()
834 ForCreatedClients(__FUNCTION__, [](const std::shared_ptr<CPVRClient>& client) {
835 client->OnPowerSavingActivated();
836 return PVR_ERROR_NO_ERROR;
840 void CPVRClients::OnPowerSavingDeactivated()
842 ForCreatedClients(__FUNCTION__, [](const std::shared_ptr<CPVRClient>& client) {
843 client->OnPowerSavingDeactivated();
844 return PVR_ERROR_NO_ERROR;
848 void CPVRClients::ConnectionStateChange(CPVRClient* client,
849 const std::string& strConnectionString,
850 PVR_CONNECTION_STATE newState,
851 const std::string& strMessage)
853 if (!client)
854 return;
856 int iMsg = -1;
857 EventLevel eLevel = EventLevel::Error;
858 bool bNotify = true;
860 switch (newState)
862 case PVR_CONNECTION_STATE_SERVER_UNREACHABLE:
863 iMsg = 35505; // Server is unreachable
864 if (client->GetPreviousConnectionState() == PVR_CONNECTION_STATE_UNKNOWN ||
865 client->GetPreviousConnectionState() == PVR_CONNECTION_STATE_CONNECTING)
867 // Make our users happy. There were so many complaints about this notification because their TV backend
868 // was not up quick enough after Kodi start. So, ignore the very first 'server not reachable' notification.
869 bNotify = false;
871 break;
872 case PVR_CONNECTION_STATE_SERVER_MISMATCH:
873 iMsg = 35506; // Server does not respond properly
874 break;
875 case PVR_CONNECTION_STATE_VERSION_MISMATCH:
876 iMsg = 35507; // Server version is not compatible
877 break;
878 case PVR_CONNECTION_STATE_ACCESS_DENIED:
879 iMsg = 35508; // Access denied
880 break;
881 case PVR_CONNECTION_STATE_CONNECTED:
882 eLevel = EventLevel::Basic;
883 iMsg = 36034; // Connection established
884 if (client->GetPreviousConnectionState() == PVR_CONNECTION_STATE_UNKNOWN ||
885 client->GetPreviousConnectionState() == PVR_CONNECTION_STATE_CONNECTING)
886 bNotify = false;
887 break;
888 case PVR_CONNECTION_STATE_DISCONNECTED:
889 iMsg = 36030; // Connection lost
890 break;
891 case PVR_CONNECTION_STATE_CONNECTING:
892 eLevel = EventLevel::Information;
893 iMsg = 35509; // Connecting
894 bNotify = false;
895 break;
896 default:
897 CLog::LogF(LOGERROR, "Unknown connection state");
898 return;
901 // Use addon-supplied message, if present
902 std::string strMsg;
903 if (!strMessage.empty())
904 strMsg = strMessage;
905 else
906 strMsg = g_localizeStrings.Get(iMsg);
908 if (!strConnectionString.empty())
909 strMsg = StringUtils::Format("{} ({})", strMsg, strConnectionString);
911 // Notify user.
912 CServiceBroker::GetJobManager()->AddJob(
913 new CPVREventLogJob(bNotify, eLevel, client->GetFullClientName(), strMsg, client->Icon()),
914 nullptr);
917 namespace
920 void LogClientWarning(const char* strFunctionName, const std::shared_ptr<const CPVRClient>& client)
922 if (client->IgnoreClient())
923 CLog::Log(LOGWARNING, "{}: Not calling add-on '{}'. Add-on not (yet) connected.",
924 strFunctionName, client->ID());
925 else if (!client->ReadyToUse())
926 CLog::Log(LOGWARNING, "{}: Not calling add-on '{}'. Add-on not ready to use.", strFunctionName,
927 client->ID());
928 else
929 CLog::Log(LOGERROR, "{}: Not calling add-on '{}' for unexpected reason.", strFunctionName,
930 client->ID());
933 } // unnamed namespace
935 PVR_ERROR CPVRClients::ForCreatedClients(const char* strFunctionName,
936 const PVRClientFunction& function) const
938 std::vector<int> failedClients;
939 return ForCreatedClients(strFunctionName, function, failedClients);
942 PVR_ERROR CPVRClients::ForCreatedClients(const char* strFunctionName,
943 const PVRClientFunction& function,
944 std::vector<int>& failedClients) const
946 PVR_ERROR lastError = PVR_ERROR_NO_ERROR;
948 CPVRClientMap clients;
949 GetCallableClients(clients, failedClients);
951 if (!failedClients.empty())
953 std::shared_ptr<CPVRClient> client;
954 for (int id : failedClients)
956 client = GetClient(id);
957 if (client)
958 LogClientWarning(strFunctionName, client);
962 for (const auto& clientEntry : clients)
964 // CLog::LogFC(LOGDEBUG, LOGPVR, "Calling add-on function '{}' on client {}.", strFunctionName,
965 // clientEntry.second->GetID());
967 PVR_ERROR currentError = function(clientEntry.second);
969 // CLog::LogFC(LOGDEBUG, LOGPVR, "Called add-on function '{}' on client {}. return={}",
970 // strFunctionName, clientEntry.second->GetID(), currentError);
972 if (currentError != PVR_ERROR_NO_ERROR && currentError != PVR_ERROR_NOT_IMPLEMENTED)
974 lastError = currentError;
975 failedClients.emplace_back(clientEntry.first);
977 CLog::LogFC(LOGDEBUG, LOGPVR,
978 "Added client {} to failed clients list after call to "
979 "function '{}‘ returned error {}.",
980 clientEntry.second->GetID(), strFunctionName, currentError);
983 return lastError;
986 PVR_ERROR CPVRClients::ForClients(const char* strFunctionName,
987 const std::vector<std::shared_ptr<CPVRClient>>& clients,
988 const PVRClientFunction& function,
989 std::vector<int>& failedClients) const
991 if (clients.empty())
992 return ForCreatedClients(strFunctionName, function, failedClients);
994 PVR_ERROR lastError = PVR_ERROR_NO_ERROR;
996 failedClients.clear();
999 std::unique_lock<CCriticalSection> lock(m_critSection);
1000 for (const auto& entry : m_clientMap)
1002 if (entry.second->ReadyToUse() && !entry.second->IgnoreClient() &&
1003 std::any_of(clients.cbegin(), clients.cend(),
1004 [&entry](const auto& client) { return client->GetID() == entry.first; }))
1006 // Allow ready to use clients that shall be called
1007 continue;
1010 failedClients.emplace_back(entry.first);
1014 for (const auto& client : clients)
1016 if (std::none_of(failedClients.cbegin(), failedClients.cend(),
1017 [&client](int failedClientId) { return failedClientId == client->GetID(); }))
1019 // CLog::LogFC(LOGDEBUG, LOGPVR, "Calling add-on function '{}' on client {}.", strFunctionName,
1020 // client->GetID());
1022 PVR_ERROR currentError = function(client);
1024 // CLog::LogFC(LOGDEBUG, LOGPVR, "Called add-on function '{}' on client {}. return={}",
1025 // strFunctionName, client->GetID(), currentError);
1027 if (currentError != PVR_ERROR_NO_ERROR && currentError != PVR_ERROR_NOT_IMPLEMENTED)
1029 lastError = currentError;
1030 failedClients.emplace_back(client->GetID());
1032 CLog::LogFC(LOGDEBUG, LOGPVR,
1033 "Added client {} to failed clients list after call to "
1034 "function '{}‘ returned error {}.",
1035 client->GetID(), strFunctionName, currentError);
1038 else
1040 LogClientWarning(strFunctionName, client);
1043 return lastError;