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.
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"
37 using namespace ADDON
;
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()
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
))
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
);
113 client
= GetClient(clientId
);
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();
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;
148 CLog::LogF(LOGINFO
, "Recreating PVR client: addonId={}, instanceId={}, clientId={}",
149 addon
->ID(), instanceId
, clientId
);
150 clientsToReCreate
.emplace_back(clientId
, addon
->Name());
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
171 for (const auto& client
: clientsToCreate
)
173 progressHandler
->UpdateProgress(
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()),
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
)
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
,
232 CServiceBroker::GetJobManager()->Submit([this, addonId
, instanceId
] {
233 UpdateClients(addonId
, instanceId
);
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
);
256 const auto it
= m_clientMap
.find(clientId
);
257 if (it
!= m_clientMap
.end())
258 m_clientMap
.erase(it
);
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
))
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
);
290 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
292 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
294 std::shared_ptr
<CPVRClient
> CPVRClients::GetClient(int clientId
) const
296 if (clientId
== PVR_CLIENT_INVALID_UID
)
299 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
300 const auto it
= m_clientMap
.find(clientId
);
301 if (it
!= m_clientMap
.end())
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())
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
));
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
)};
383 fullName
= client
->GetFullClientName();
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
));
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
);
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
);
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());
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
);
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 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
574 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
576 std::vector
<SBackend
> CPVRClients::GetBackendProperties() const
578 std::vector
<SBackend
> backendProperties
;
581 __FUNCTION__
, [&backendProperties
](const std::shared_ptr
<const CPVRClient
>& client
) {
584 if (client
->GetDriveSpace(properties
.diskTotal
, properties
.diskUsed
) == PVR_ERROR_NO_ERROR
)
586 properties
.diskTotal
*= 1024;
587 properties
.diskUsed
*= 1024;
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
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
)
632 __FUNCTION__
, clients
,
633 [](const std::shared_ptr
<CPVRClient
>& client
) { return client
->UpdateTimerTypes(); },
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());
655 PVR_ERROR
CPVRClients::GetRecordings(const std::vector
<std::shared_ptr
<CPVRClient
>>& clients
,
656 CPVRRecordings
* recordings
,
658 std::vector
<int>& failedClients
) const
661 __FUNCTION__
, clients
,
662 [recordings
, deleted
](const std::shared_ptr
<const CPVRClient
>& client
) {
663 return client
->GetRecordings(recordings
, deleted
);
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
,
691 std::vector
<std::shared_ptr
<CPVRChannel
>>& channels
,
692 std::vector
<int>& failedClients
) const
695 __FUNCTION__
, clients
,
696 [bRadio
, &channels
](const std::shared_ptr
<const CPVRClient
>& client
) {
697 return client
->GetChannels(bRadio
, channels
);
702 PVR_ERROR
CPVRClients::GetProviders(const std::vector
<std::shared_ptr
<CPVRClient
>>& clients
,
703 CPVRProvidersContainer
* providers
,
704 std::vector
<int>& failedClients
) const
707 __FUNCTION__
, clients
,
708 [providers
](const std::shared_ptr
<const CPVRClient
>& client
) {
709 return client
->GetProviders(*providers
);
714 PVR_ERROR
CPVRClients::GetChannelGroups(const std::vector
<std::shared_ptr
<CPVRClient
>>& clients
,
715 CPVRChannelGroups
* groups
,
716 std::vector
<int>& failedClients
) const
719 __FUNCTION__
, clients
,
720 [groups
](const std::shared_ptr
<const CPVRClient
>& client
) {
721 return client
->GetChannelGroups(groups
);
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
733 __FUNCTION__
, clients
,
734 [group
, &groupMembers
](const std::shared_ptr
<const CPVRClient
>& client
) {
735 return client
->GetChannelGroupMembers(group
, groupMembers
);
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
)
857 EventLevel eLevel
= EventLevel::Error
;
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.
872 case PVR_CONNECTION_STATE_SERVER_MISMATCH
:
873 iMsg
= 35506; // Server does not respond properly
875 case PVR_CONNECTION_STATE_VERSION_MISMATCH
:
876 iMsg
= 35507; // Server version is not compatible
878 case PVR_CONNECTION_STATE_ACCESS_DENIED
:
879 iMsg
= 35508; // Access denied
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
)
888 case PVR_CONNECTION_STATE_DISCONNECTED
:
889 iMsg
= 36030; // Connection lost
891 case PVR_CONNECTION_STATE_CONNECTING
:
892 eLevel
= EventLevel::Information
;
893 iMsg
= 35509; // Connecting
897 CLog::LogF(LOGERROR
, "Unknown connection state");
901 // Use addon-supplied message, if present
903 if (!strMessage
.empty())
906 strMsg
= g_localizeStrings
.Get(iMsg
);
908 if (!strConnectionString
.empty())
909 strMsg
= StringUtils::Format("{} ({})", strMsg
, strConnectionString
);
912 CServiceBroker::GetJobManager()->AddJob(
913 new CPVREventLogJob(bNotify
, eLevel
, client
->GetFullClientName(), strMsg
, client
->Icon()),
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
,
929 CLog::Log(LOGERROR
, "{}: Not calling add-on '{}' for unexpected reason.", strFunctionName
,
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
);
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
);
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
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
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
);
1040 LogClientWarning(strFunctionName
, client
);