aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/resource.language.en_gb/resources/strings.po14
-rw-r--r--cmake/treedata/common/pvr.txt1
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/PVR.h104
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/pvr/Channels.h19
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/pvr/General.h14
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/pvr/Providers.h195
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr.h11
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/CMakeLists.txt1
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_channels.h1
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_defines.h2
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_general.h1
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_providers.h97
-rw-r--r--xbmc/addons/kodi-dev-kit/include/kodi/versions.h1
-rw-r--r--xbmc/guilib/guiinfo/GUIInfoLabels.h4
-rw-r--r--xbmc/pvr/PVRDatabase.cpp161
-rw-r--r--xbmc/pvr/PVRDatabase.h37
-rw-r--r--xbmc/pvr/PVRManager.cpp23
-rw-r--r--xbmc/pvr/PVRManager.h13
-rw-r--r--xbmc/pvr/addons/PVRClient.cpp56
-rw-r--r--xbmc/pvr/addons/PVRClient.h41
-rw-r--r--xbmc/pvr/addons/PVRClients.cpp44
-rw-r--r--xbmc/pvr/addons/PVRClients.h17
-rw-r--r--xbmc/pvr/channels/PVRChannel.cpp37
-rw-r--r--xbmc/pvr/channels/PVRChannel.h37
-rw-r--r--xbmc/pvr/guilib/PVRGUIActions.cpp2
-rw-r--r--xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp29
-rw-r--r--xbmc/pvr/guilib/guiinfo/PVRGUIInfo.h4
-rw-r--r--xbmc/pvr/providers/CMakeLists.txt7
-rw-r--r--xbmc/pvr/providers/PVRProvider.cpp368
-rw-r--r--xbmc/pvr/providers/PVRProvider.h232
-rw-r--r--xbmc/pvr/providers/PVRProviders.cpp384
-rw-r--r--xbmc/pvr/providers/PVRProviders.h122
-rw-r--r--xbmc/windows/GUIWindowSystemInfo.cpp2
33 files changed, 2064 insertions, 17 deletions
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po
index 4cedc1c974..ac920be2f1 100644
--- a/addons/resource.language.en_gb/resources/strings.po
+++ b/addons/resource.language.en_gb/resources/strings.po
@@ -9460,7 +9460,11 @@ msgctxt "#19041"
msgid "Refresh channel logos"
msgstr ""
-#empty string with id 19042
+#. label for PVR backend number of channel groups in system information's PVR section
+#: xbmc/windows/GUIWindowSystemInfo.cpp
+msgctxt "#19042"
+msgid "Channel Groups"
+msgstr ""
#. pvr settings "recording" category label
#: system/settings/settings.xml
@@ -11226,7 +11230,13 @@ msgctxt "#19333"
msgid "Switched to channel for auto-closed PVR reminder for channel '{0:s}' at '{1:s}'"
msgstr ""
-#empty strings from id 19334 to 19498
+#. label for PVR backend number of channel providers in system information's PVR section
+#: xbmc/windows/GUIWindowSystemInfo.cpp
+msgctxt "#19334"
+msgid "Providers"
+msgstr ""
+
+#empty strings from id 19335 to 19498
#. label for epg genre value
#: xbmc/pvr/epg/Epg.cpp
diff --git a/cmake/treedata/common/pvr.txt b/cmake/treedata/common/pvr.txt
index 74e766f2d0..b656415291 100644
--- a/cmake/treedata/common/pvr.txt
+++ b/cmake/treedata/common/pvr.txt
@@ -6,6 +6,7 @@ xbmc/pvr/epg pvr/epg
xbmc/pvr/filesystem pvr/filesystem
xbmc/pvr/guilib pvr/guilib
xbmc/pvr/guilib/guiinfo pvr/guilib/guiinfo
+xbmc/pvr/providers pvr/providers
xbmc/pvr/recordings pvr/recordings
xbmc/pvr/settings pvr/settings
xbmc/pvr/timers pvr/timers
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/PVR.h b/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/PVR.h
index 61e294fcd5..4d36bfe813 100644
--- a/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/PVR.h
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/PVR.h
@@ -15,6 +15,7 @@
#include "pvr/EPG.h"
#include "pvr/General.h"
#include "pvr/MenuHook.h"
+#include "pvr/Providers.h"
#include "pvr/Recordings.h"
#include "pvr/Stream.h"
#include "pvr/Timers.h"
@@ -278,6 +279,8 @@ namespace addon
/// PVR_ERROR GetBackendName(std::string& name) override;
/// PVR_ERROR GetBackendVersion(std::string& version) override;
///
+/// PVR_ERROR GetProvidersAmount(int& amount) override;
+/// PVR_ERROR GetProviders(std::vector<kodi::addon::PVRProvider>& providers) override;
/// PVR_ERROR GetChannelsAmount(int& amount) override;
/// PVR_ERROR GetChannels(bool radio, std::vector<kodi::addon::PVRChannel>& channels) override;
/// PVR_ERROR GetChannelStreamProperties(const kodi::addon::PVRChannel& channel,
@@ -315,6 +318,18 @@ namespace addon
/// return PVR_ERROR_NO_ERROR;
/// }
///
+/// PVR_ERROR CMyInstance::GetProvidersAmount(int& amount)
+/// {
+/// amount = m_myProviders.size();
+/// return PVR_ERROR_NO_ERROR;
+/// }
+///
+/// PVR_ERROR CMyPVRClient::GetProviders(std::vector<kodi::addon::PVRProvider>& providers)
+/// {
+/// providers = m_myProviders;
+/// return PVR_ERROR_NO_ERROR;
+/// }
+///
/// PVR_ERROR CMyInstance::GetChannelsAmount(int& amount)
/// {
/// amount = m_myChannels.size();
@@ -827,6 +842,78 @@ public:
///@{
//============================================================================
+ /// @brief The total amount of providers on the backend
+ ///
+ /// @param[out] amount The total amount of providers on the backend
+ /// @return @ref PVR_ERROR_NO_ERROR if the amount has been fetched successfully.
+ ///
+ /// @remarks Optional, and only used if @ref PVRCapabilities::SetSupportsProviders
+ /// "supportsProviders" is set to true.
+ ///
+ virtual PVR_ERROR GetProvidersAmount(int& amount) { return PVR_ERROR_NOT_IMPLEMENTED; }
+ //----------------------------------------------------------------------------
+
+ //============================================================================
+ /// @brief Request the list of all providers from the backend.
+ ///
+ /// @param[out] results The channels defined with @ref
+ /// cpp_kodi_addon_pvr_Defs_PVRProviders
+ /// and available at the addon, then transferred with
+ /// @ref cpp_kodi_addon_pvr_Defs_PVRProvidersResultSet.
+ /// @return @ref PVR_ERROR_NO_ERROR if the list has been fetched successfully.
+ ///
+ /// @remarks Optional, and only used if @ref PVRCapabilities::SetSupportsProviders
+ /// "supportsProviders" is set to true.
+ ///
+ /// --------------------------------------------------------------------------
+ ///
+ /// @copydetails cpp_kodi_addon_pvr_Defs_PVRProviders_Help
+ ///
+ ///
+ /// --------------------------------------------------------------------------
+ ///
+ ///
+ ///
+ ///---------------------------------------------------------------------------
+ ///
+ /// **Example:**
+ /// ~~~~~~~~~~~~~{.cpp}
+ /// ...
+ /// PVR_ERROR CMyPVRInstance::GetProviders(kodi::addon::PVRProvidersResultSet& results)
+ /// {
+ /// // Minimal demo example, in reality bigger and loop to transfer all
+ /// kodi::addon::PVRProvider provider;
+ /// provider.SetUniqueId(123);
+ /// provider.SetProviderName("My provider name");
+ /// provider.SetProviderType(PVR_PROVIDER_TYPE_SATELLITE);
+ /// ...
+ ///
+ /// // Give it now to Kodi
+ /// results.Add(provider);
+ /// return PVR_ERROR_NO_ERROR;
+ /// }
+ /// ...
+ /// ~~~~~~~~~~~~~
+ ///
+ virtual PVR_ERROR GetProviders(kodi::addon::PVRProvidersResultSet& results)
+ {
+ return PVR_ERROR_NOT_IMPLEMENTED;
+ }
+ //----------------------------------------------------------------------------
+
+ //============================================================================
+ /// @brief **Callback to Kodi Function**\n
+ /// Request Kodi to update it's list of providers.
+ ///
+ /// @remarks Only called from addon itself.
+ ///
+ inline void TriggerProvidersUpdate()
+ {
+ m_instanceData->toKodi->TriggerProvidersUpdate(m_instanceData->toKodi->kodiInstance);
+ }
+ //----------------------------------------------------------------------------
+
+ //============================================================================
/// @brief The total amount of channels on the backend
///
/// @param[out] amount The total amount of channels on the backend
@@ -2676,6 +2763,9 @@ private:
m_instanceData->toAddon->GetSignalStatus = ADDON_GetSignalStatus;
m_instanceData->toAddon->GetDescrambleInfo = ADDON_GetDescrambleInfo;
//--==----==----==----==----==----==----==----==----==----==----==----==----==
+ m_instanceData->toAddon->GetProvidersAmount = ADDON_GetProvidersAmount;
+ m_instanceData->toAddon->GetProviders = ADDON_GetProviders;
+ //--==----==----==----==----==----==----==----==----==----==----==----==----==
m_instanceData->toAddon->GetChannelGroupsAmount = ADDON_GetChannelGroupsAmount;
m_instanceData->toAddon->GetChannelGroups = ADDON_GetChannelGroups;
m_instanceData->toAddon->GetChannelGroupMembers = ADDON_GetChannelGroupMembers;
@@ -2886,6 +2976,20 @@ private:
//--==----==----==----==----==----==----==----==----==----==----==----==----==
+ inline static PVR_ERROR ADDON_GetProvidersAmount(const AddonInstance_PVR* instance, int* amount)
+ {
+ return static_cast<CInstancePVRClient*>(instance->toAddon->addonInstance)
+ ->GetProvidersAmount(*amount);
+ }
+
+ inline static PVR_ERROR ADDON_GetProviders(const AddonInstance_PVR* instance, ADDON_HANDLE handle)
+ {
+ PVRProvidersResultSet result(instance, handle);
+ return static_cast<CInstancePVRClient*>(instance->toAddon->addonInstance)->GetProviders(result);
+ }
+
+ //--==----==----==----==----==----==----==----==----==----==----==----==----==
+
inline static PVR_ERROR ADDON_GetChannelGroupsAmount(const AddonInstance_PVR* instance,
int* amount)
{
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/pvr/Channels.h b/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/pvr/Channels.h
index 9c2f5d26e9..466c23874d 100644
--- a/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/pvr/Channels.h
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/pvr/Channels.h
@@ -41,7 +41,11 @@ class PVRChannel : public CStructHdl<PVRChannel, PVR_CHANNEL>
public:
/*! \cond PRIVATE */
- PVRChannel() { memset(m_cStructure, 0, sizeof(PVR_CHANNEL)); }
+ PVRChannel()
+ {
+ memset(m_cStructure, 0, sizeof(PVR_CHANNEL));
+ m_cStructure->iClientProviderUid = PVR_PROVIDER_INVALID_UID;
+ }
PVRChannel(const PVRChannel& channel) : CStructHdl(channel) {}
/*! \endcond */
@@ -62,6 +66,7 @@ public:
/// | **Is hidden** | `bool` | @ref PVRChannel::SetIsHidden "SetIsHidden" | @ref PVRChannel::GetIsHidden "GetIsHidden" | *optional*
/// | **Has archive** | `bool` | @ref PVRChannel::SetHasArchive "SetHasArchive" | @ref PVRChannel::GetHasArchive "GetHasArchive" | *optional*
/// | **Order** | `int` | @ref PVRChannel::SetOrder "SetOrder" | @ref PVRChannel::GetOrder "GetOrder" | *optional*
+ /// | **Client provider unique identifier** | `int` | @ref PVRChannel::SetClientProviderUid "SetClientProviderUid" | @ref PVRTimer::GetClientProviderUid "GetClientProviderUid" | *optional*
///
/// @addtogroup cpp_kodi_addon_pvr_Defs_Channel_PVRChannel
@@ -173,6 +178,18 @@ public:
bool GetOrder() const { return m_cStructure->iOrder; }
///@}
+ /// @brief **optional**\n
+ /// Unique identifier of the provider this channel belongs to.
+ ///
+ /// @ref PVR_PROVIDER_INVALID_UID denotes that provider uid is not available.
+ void SetClientProviderUid(int iClientProviderUid)
+ {
+ m_cStructure->iClientProviderUid = iClientProviderUid;
+ }
+
+ /// @brief To get with @ref SetClientProviderUid changed values
+ int GetClientProviderUid() const { return m_cStructure->iClientProviderUid; }
+
private:
PVRChannel(const PVR_CHANNEL* channel) : CStructHdl(channel) {}
PVRChannel(PVR_CHANNEL* channel) : CStructHdl(channel) {}
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/pvr/General.h b/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/pvr/General.h
index c7977c2786..6f479d8e35 100644
--- a/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/pvr/General.h
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/pvr/General.h
@@ -135,6 +135,7 @@ public:
/// | **Supports recordings** | `boolean` | @ref PVRCapabilities::SetSupportsRecordings "SetSupportsRecordings" | @ref PVRCapabilities::GetSupportsRecordings "GetSupportsRecordings"
/// | **Supports recordings undelete** | `boolean` | @ref PVRCapabilities::SetSupportsRecordingsUndelete "SetSupportsRecordingsUndelete" | @ref PVRCapabilities::GetSupportsRecordingsUndelete "SetSupportsRecordingsUndelete"
/// | **Supports timers** | `boolean` | @ref PVRCapabilities::SetSupportsTimers "SetSupportsTimers" | @ref PVRCapabilities::GetSupportsTimers "GetSupportsTimers"
+ /// | **Supports providers** | `boolean` | @ref PVRCapabilities::SetSupportsProviders "SetSupportsProviders" | @ref PVRCapabilities::GetSupportsProviders "GetSupportsProviders"
/// | **Supports channel groups** | `boolean` | @ref PVRCapabilities::SetSupportsChannelGroups "SetSupportsChannelGroups" | @ref PVRCapabilities::GetSupportsChannelGroups "GetSupportsChannelGroups"
/// | **Supports channel scan** | `boolean` | @ref PVRCapabilities::SetSupportsChannelScan "SetSupportsChannelScan" | @ref PVRCapabilities::GetSupportsChannelScan "GetSupportsChannelScan"
/// | **Supports channel settings** | `boolean` | @ref PVRCapabilities::SetSupportsChannelSettings "SetSupportsChannelSettings" | @ref PVRCapabilities::GetSupportsChannelSettings "GetSupportsChannelSettings"
@@ -208,6 +209,19 @@ public:
/// @brief To get with @ref SetSupportsTimers changed values.
bool GetSupportsTimers() const { return m_capabilities->bSupportsTimers; }
+ /// @brief Set **true** if this add-on supports providers.
+ ///
+ /// It uses the following functions:
+ /// - @ref kodi::addon::CInstancePVRClient::GetProvidersAmount()
+ /// - @ref kodi::addon::CInstancePVRClient::GetProviders()
+ void SetSupportsProviders(bool supportsProviders)
+ {
+ m_capabilities->bSupportsProviders = supportsProviders;
+ }
+
+ /// @brief To get with @ref SetSupportsProviders changed values.
+ bool GetSupportsProviders() const { return m_capabilities->bSupportsProviders; }
+
/// @brief Set **true** if this add-on supports channel groups.
///
/// It use the following functions:
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/pvr/Providers.h b/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/pvr/Providers.h
new file mode 100644
index 0000000000..ddbb1887cc
--- /dev/null
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/pvr/Providers.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2005-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.
+ */
+
+#pragma once
+
+#include "../../AddonBase.h"
+#include "../../c-api/addon-instance/pvr.h"
+#include "../../tools/StringUtils.h"
+
+//¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
+// "C++" Definitions group 2 - PVR provider
+#ifdef __cplusplus
+
+namespace kodi
+{
+namespace addon
+{
+
+//==============================================================================
+/// @defgroup cpp_kodi_addon_pvr_Defs_PVRProvider class PVRProvider
+/// @ingroup cpp_kodi_addon_pvr_Defs_Provider
+/// @brief **Provider data structure**\n
+/// Representation of a provider.
+///
+/// This is used to store all the necessary provider data and can
+/// either provide the necessary data from / to Kodi for the associated
+/// functions or can also be used in the addon to store its data.
+///
+/// ----------------------------------------------------------------------------
+///
+/// @copydetails cpp_kodi_addon_pvr_Defs_PVRProvider_Help
+///
+///@{
+class PVRProvider : public CStructHdl<PVRProvider, PVR_PROVIDER>
+{
+ friend class CInstancePVRClient;
+
+public:
+ /*! \cond PRIVATE */
+ PVRProvider() { memset(m_cStructure, 0, sizeof(PVR_PROVIDER)); }
+ PVRProvider(const PVRProvider& provider) : CStructHdl(provider) {}
+ /*! \endcond */
+
+ /// @defgroup cpp_kodi_addon_pvr_Defs_PVRProvider_Help Value Help
+ /// @ingroup cpp_kodi_addon_pvr_Defs_PVRProvider
+ ///
+ /// <b>The following table contains values that can be set with @ref cpp_kodi_addon_pvr_Defs_PVRProvider :</b>
+ /// | Name | Type | Set call | Get call | Usage
+ /// |------|------|----------|----------|-----------
+ /// | **Unique id** | `unsigned int` | @ref PVRProvider::SetUniqueId "SetUniqueId" | @ref PVRProvider::GetUniqueId "GetUniqueId" | *required to set*
+ /// | **Provider name** | `std::string` | @ref PVRProvider::SetName "SetName" | @ref PVRProvider::GetName "GetName" | *required to set*
+ /// | **Provider type** | @ref PVR_PROVIDER_TYPE | @ref PVRProvider::SetType "SetType" | @ref PVRProvider::GetType "GetType" | *optional*
+ /// | **Icon path** | `std::string` | @ref PVRProvider::SetIconPath "SetIconPath" | @ref PVRProvider::GetIconPath "GetIconPath" | *optional*
+ /// | **Countries** | `std::vecotr<std::string>` | @ref PVRProvider::SetCountries "SetCountries" | @ref PVRProvider::GetCountries "GetCountries" | *optional*
+ /// | **Langauges** | `std::vecotr<std::string>` | @ref PVRProvider::SetLangauges "SetLangauges" | @ref PVRProvider::GetLangauges "GetLangauges" | *optional*
+ ///
+
+ /// @addtogroup cpp_kodi_addon_pvr_Defs_PVRProvider
+ ///@{
+
+ /// @brief **required**\n
+ /// Unique identifier for this provider.
+ void SetUniqueId(unsigned int uniqueId) { m_cStructure->iUniqueId = uniqueId; }
+
+ /// @brief To get with @ref SetUniqueId changed values.
+ unsigned int GetUniqueId() const { return m_cStructure->iUniqueId; }
+
+ /// @brief **required**\n
+ /// Name given to this provider.
+ void SetName(const std::string& name)
+ {
+ strncpy(m_cStructure->strName, name.c_str(), sizeof(m_cStructure->strName) - 1);
+ }
+
+ /// @brief To get with @ref SetName changed values.
+ std::string GetName() const { return m_cStructure->strName; }
+
+ /// @brief **optional**\n
+ /// Provider type.
+ ///
+ /// Set to @ref PVR_PVR_PROVIDER_TYPE_UNKNOWN if the type cannot be
+ /// determined.
+ ///
+ /// --------------------------------------------------------------------------
+ ///
+ /// Example:
+ /// ~~~~~~~~~~~~~{.cpp}
+ /// kodi::addon::PVRProvider tag;
+ /// tag.SetType(PVR_PROVIDER_TYPE_SATELLITE);
+ /// ~~~~~~~~~~~~~
+ ///
+ void SetType(PVR_PROVIDER_TYPE type) { m_cStructure->type = type; }
+
+ /// @brief To get with @ref SetType changed values
+ PVR_PROVIDER_TYPE GetType() const { return m_cStructure->type; }
+
+ /// @brief **optional**\n
+ /// Path to the provider icon (if present).
+ void SetIconPath(const std::string& iconPath)
+ {
+ strncpy(m_cStructure->strIconPath, iconPath.c_str(), sizeof(m_cStructure->strIconPath) - 1);
+ }
+
+ /// @brief To get with @ref SetIconPath changed values.
+ std::string GetIconPath() const { return m_cStructure->strIconPath; }
+ ///@}
+
+ /// @brief **optional**\n
+ /// The country codes for the provider.
+ ///
+ /// @note ISO 3166 country codes required (e.g 'GB,IE,CA').
+ void SetCountries(const std::vector<std::string>& countries)
+ {
+ const std::string str = tools::StringUtils::Join(countries, PROVIDER_STRING_TOKEN_SEPARATOR);
+ strncpy(m_cStructure->strCountries, str.c_str(), sizeof(m_cStructure->strCountries) - 1);
+ }
+
+ /// @brief To get with @ref SetCountries changed values.
+ std::vector<std::string> GetCountries() const
+ {
+ return tools::StringUtils::Split(m_cStructure->strCountries, PROVIDER_STRING_TOKEN_SEPARATOR);
+ }
+ ///@}
+
+ /// @brief **optional**\n
+ /// The language codes for the provider.
+ ///
+ /// @note RFC 5646 standard codes required (e.g.: 'en_GB,fr_CA').
+ void SetLanguages(const std::vector<std::string>& languages)
+ {
+ const std::string str = tools::StringUtils::Join(languages, PROVIDER_STRING_TOKEN_SEPARATOR);
+ strncpy(m_cStructure->strLanguages, str.c_str(), sizeof(m_cStructure->strLanguages) - 1);
+ }
+
+ /// @brief To get with @ref SetLanguages changed values.
+ std::vector<std::string> GetLanguages() const
+ {
+ return tools::StringUtils::Split(m_cStructure->strLanguages, PROVIDER_STRING_TOKEN_SEPARATOR);
+ }
+ ///@}
+
+private:
+ PVRProvider(const PVR_PROVIDER* provider) : CStructHdl(provider) {}
+ PVRProvider(PVR_PROVIDER* provider) : CStructHdl(provider) {}
+};
+///@}
+//------------------------------------------------------------------------------
+
+//==============================================================================
+/// @defgroup cpp_kodi_addon_pvr_Defs_PVRProvidersResultSet class PVRProvidersResultSet
+/// @ingroup cpp_kodi_addon_pvr_Defs_PVRProvider
+/// @brief **PVR add-on provider transfer class**\n
+/// To transfer the content of @ref kodi::addon::CInstancePVRClient::GetProviders().
+///
+///@{
+class PVRProvidersResultSet
+{
+public:
+ /*! \cond PRIVATE */
+ PVRProvidersResultSet() = delete;
+ PVRProvidersResultSet(const AddonInstance_PVR* instance, ADDON_HANDLE handle)
+ : m_instance(instance), m_handle(handle)
+ {
+ }
+ /*! \endcond */
+
+ /// @addtogroup cpp_kodi_addon_pvr_Defs_PVRProvidersResultSet
+ ///@{
+
+ /// @brief To add and give content from addon to Kodi on related call.
+ ///
+ /// @param[in] provider The to transferred data.
+ void Add(const kodi::addon::PVRProvider& provider)
+ {
+ m_instance->toKodi->TransferProviderEntry(m_instance->toKodi->kodiInstance, m_handle, provider);
+ }
+
+ ///@}
+
+private:
+ const AddonInstance_PVR* m_instance = nullptr;
+ const ADDON_HANDLE m_handle;
+};
+///@}
+//------------------------------------------------------------------------------
+
+} /* namespace addon */
+} /* namespace kodi */
+
+#endif /* __cplusplus */
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr.h b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr.h
index cc24e5db50..48eb9922cf 100644
--- a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr.h
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr.h
@@ -17,6 +17,7 @@
#include "pvr/pvr_epg.h"
#include "pvr/pvr_general.h"
#include "pvr/pvr_menu_hook.h"
+#include "pvr/pvr_providers.h"
#include "pvr/pvr_recordings.h"
#include "pvr/pvr_stream.h"
#include "pvr/pvr_timers.h"
@@ -97,6 +98,9 @@ extern "C"
void (*TransferChannelEntry)(void* kodiInstance,
const ADDON_HANDLE handle,
const struct PVR_CHANNEL* chan);
+ void (*TransferProviderEntry)(void* kodiInstance,
+ const ADDON_HANDLE handle,
+ const struct PVR_PROVIDER* chanProvider);
void (*TransferChannelGroup)(void* kodiInstance,
const ADDON_HANDLE handle,
const struct PVR_CHANNEL_GROUP* group);
@@ -116,6 +120,7 @@ extern "C"
//--==----==----==----==----==----==----==----==----==----==----==----==----==
// Kodi inform interface functions
void (*TriggerChannelUpdate)(void* kodiInstance);
+ void (*TriggerProvidersUpdate)(void* kodiInstance);
void (*TriggerChannelGroupsUpdate)(void* kodiInstance);
void (*TriggerEpgUpdate)(void* kodiInstance, unsigned int iChannelUid);
void (*TriggerRecordingUpdate)(void* kodiInstance);
@@ -169,6 +174,12 @@ extern "C"
struct PVR_DESCRAMBLE_INFO*);
//--==----==----==----==----==----==----==----==----==----==----==----==----==
+ // Provider interface functions
+
+ enum PVR_ERROR(__cdecl* GetProvidersAmount)(const struct AddonInstance_PVR*, int*);
+ enum PVR_ERROR(__cdecl* GetProviders)(const struct AddonInstance_PVR*, ADDON_HANDLE);
+
+ //--==----==----==----==----==----==----==----==----==----==----==----==----==
// Channel group interface functions
enum PVR_ERROR(__cdecl* GetChannelGroupsAmount)(const struct AddonInstance_PVR*, int*);
enum PVR_ERROR(__cdecl* GetChannelGroups)(const struct AddonInstance_PVR*, ADDON_HANDLE, bool);
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/CMakeLists.txt b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/CMakeLists.txt
index 0e37ea41c1..e29b87d66f 100644
--- a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/CMakeLists.txt
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/CMakeLists.txt
@@ -1,4 +1,5 @@
set(HEADERS pvr_channel_groups.h
+ pvr_providers.h
pvr_channels.h
pvr_defines.h
pvr_edl.h
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_channels.h b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_channels.h
index a19fba1acb..134384f833 100644
--- a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_channels.h
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_channels.h
@@ -50,6 +50,7 @@ extern "C"
bool bIsHidden;
bool bHasArchive;
int iOrder;
+ int iClientProviderUid;
} PVR_CHANNEL;
/*!
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_defines.h b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_defines.h
index a7651215d1..e88e69a319 100644
--- a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_defines.h
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_defines.h
@@ -37,6 +37,8 @@ extern "C"
#define PVR_ADDON_ATTRIBUTE_VALUES_ARRAY_SIZE 512
#define PVR_ADDON_DESCRAMBLE_INFO_STRING_LENGTH 64
#define PVR_ADDON_DATE_STRING_LENGTH 32
+ #define PVR_ADDON_COUNTRIES_STRING_LENGTH 128
+ #define PVR_ADDON_LANGUAGES_STRING_LENGTH 128
///@}
/*!
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_general.h b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_general.h
index b4470bb16b..f191867a53 100644
--- a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_general.h
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_general.h
@@ -276,6 +276,7 @@ extern "C"
bool bSupportsDescrambleInfo;
bool bSupportsAsyncEPGTransfer;
bool bSupportsRecordingSize;
+ bool bSupportsProviders;
unsigned int iRecordingsLifetimesSize;
struct PVR_ATTRIBUTE_INT_VALUE recordingsLifetimeValues[PVR_ADDON_ATTRIBUTE_VALUES_ARRAY_SIZE];
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_providers.h b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_providers.h
new file mode 100644
index 0000000000..b1744aae63
--- /dev/null
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_providers.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2005-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.
+ */
+
+#ifndef C_API_ADDONINSTANCE_PVR_PROVIDERS_H
+#define C_API_ADDONINSTANCE_PVR_PROVIDERS_H
+
+#include "pvr_defines.h"
+
+#include <stdbool.h>
+
+//¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
+// "C" Definitions group 2 - PVR providers
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+ //============================================================================
+ /// @ingroup cpp_kodi_addon_pvr_Defs_Provider
+ /// @brief Denotes that no provider uid is available.
+ ///
+ /// Special @ref kodi::addon::PVRChannel::SetClientProviderUid()
+ #define PVR_PROVIDER_INVALID_UID -1
+ //----------------------------------------------------------------------------
+
+ //============================================================================
+ /// @ingroup cpp_kodi_addon_pvr_Defs_Provider
+ /// @brief Separator to use in strings containing different tokens, for example
+ /// country and language.
+ ///
+ #define PROVIDER_STRING_TOKEN_SEPARATOR ","
+
+ //============================================================================
+ /// @defgroup cpp_kodi_addon_pvr_Defs_Channel_PVR_PROVIDER_TYPE enum PVR_PROVIDER_TYPE
+ /// @ingroup cpp_kodi_addon_pvr_Defs_Channel
+ /// @brief **PVR provider types**\n
+ /// Used on @ref kodi::addon::PVRProvider:SetProviderType() value to set related
+ /// type.
+ ///
+ ///@{
+ typedef enum PVR_PROVIDER_TYPE
+ {
+ /// @brief __0__ : Unknown type.
+ PVR_PROVIDER_TYPE_UNKNOWN = 0,
+
+ /// @brief __1__ : IPTV provider.
+ PVR_PROVIDER_TYPE_ADDON = 1,
+
+ /// @brief __2__ : Satellite provider.
+ PVR_PROVIDER_TYPE_SATELLITE = 2,
+
+ /// @brief __3__ : Cable provider.
+ PVR_PROVIDER_TYPE_CABLE = 3,
+
+ /// @brief __4__ : Aerial provider.
+ PVR_PROVIDER_TYPE_AERIAL = 4,
+
+ /// @brief __5__ : IPTV provider.
+ PVR_PROVIDER_TYPE_IPTV = 5,
+
+ /// @brief __6__ : Other type of provider.
+ PVR_PROVIDER_TYPE_OTHER = 6,
+ } PVR_PROVIDER_TYPE;
+ ///@}
+ //----------------------------------------------------------------------------
+
+ /*!
+ * @brief "C" PVR add-on provider.
+ *
+ * Structure used to interface in "C" between Kodi and Addon.
+ *
+ * See @ref kodi::addon::PVRProvider for description of values.
+ */
+ typedef struct PVR_PROVIDER
+ {
+ unsigned int iUniqueId;
+ char strName[PVR_ADDON_NAME_STRING_LENGTH];
+ enum PVR_PROVIDER_TYPE type;
+ char strIconPath[PVR_ADDON_URL_STRING_LENGTH];
+ //! @brief ISO 3166 country codes, separated by PROVIDER_STRING_TOKEN_SEPARATOR
+ /// (e.g 'GB,IE,FR,CA'), an empty string means this value is undefined
+ char strCountries[PVR_ADDON_COUNTRIES_STRING_LENGTH];
+ //! @brief RFC 5646 language codes, separated by PROVIDER_STRING_TOKEN_SEPARATOR
+ /// (e.g. 'en_GB,fr_CA'), an empty string means this value is undefined
+ char strLanguages[PVR_ADDON_LANGUAGES_STRING_LENGTH];
+ } PVR_PROVIDER;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* !C_API_ADDONINSTANCE_PVR_PROVIDERS_H */
diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/versions.h b/xbmc/addons/kodi-dev-kit/include/kodi/versions.h
index e3431eb6e4..0ff5245819 100644
--- a/xbmc/addons/kodi-dev-kit/include/kodi/versions.h
+++ b/xbmc/addons/kodi-dev-kit/include/kodi/versions.h
@@ -133,6 +133,7 @@
#define ADDON_INSTANCE_VERSION_PVR_MIN "7.1.0"
#define ADDON_INSTANCE_VERSION_PVR_XML_ID "kodi.binary.instance.pvr"
#define ADDON_INSTANCE_VERSION_PVR_DEPENDS "c-api/addon-instance/pvr.h" \
+ "c-api/addon-instance/pvr/pvr_providers.h" \
"c-api/addon-instance/pvr/pvr_channel_groups.h" \
"c-api/addon-instance/pvr/pvr_channels.h" \
"c-api/addon-instance/pvr/pvr_defines.h" \
diff --git a/xbmc/guilib/guiinfo/GUIInfoLabels.h b/xbmc/guilib/guiinfo/GUIInfoLabels.h
index ceb7800825..641eca8d7d 100644
--- a/xbmc/guilib/guiinfo/GUIInfoLabels.h
+++ b/xbmc/guilib/guiinfo/GUIInfoLabels.h
@@ -646,7 +646,9 @@
#define PVR_TIMESHIFT_PROGRESS_END_TIME (PVR_STRINGS_START + 71)
#define PVR_EPG_EVENT_ICON (PVR_STRINGS_START + 72)
#define PVR_TIMESHIFT_SEEKBAR (PVR_STRINGS_START + 73)
-#define PVR_STRINGS_END PVR_TIMESHIFT_SEEKBAR
+#define PVR_BACKEND_PROVIDERS (PVR_STRINGS_START + 74)
+#define PVR_BACKEND_CHANNEL_GROUPS (PVR_STRINGS_START + 75)
+#define PVR_STRINGS_END PVR_BACKEND_CHANNEL_GROUPS
#define RDS_DATA_START 1400
#define RDS_HAS_RDS (RDS_DATA_START)
diff --git a/xbmc/pvr/PVRDatabase.cpp b/xbmc/pvr/PVRDatabase.cpp
index a9030a74bf..4994d3bbfb 100644
--- a/xbmc/pvr/PVRDatabase.cpp
+++ b/xbmc/pvr/PVRDatabase.cpp
@@ -14,6 +14,8 @@
#include "pvr/channels/PVRChannel.h"
#include "pvr/channels/PVRChannelGroupMember.h"
#include "pvr/channels/PVRChannelGroups.h"
+#include "pvr/providers/PVRProvider.h"
+#include "pvr/providers/PVRProviders.h"
#include "pvr/timers/PVRTimerInfoTag.h"
#include "pvr/timers/PVRTimerType.h"
#include "settings/AdvancedSettings.h"
@@ -76,7 +78,19 @@ namespace
"iLastOpened bigint unsigned"
")";
-// clang-format on
+ static const std::string sqlCreateProvidersTable =
+ "CREATE TABLE providers ("
+ "idProvider integer primary key, "
+ "iUniqueId integer, "
+ "iClientId integer, "
+ "sName varchar(64), "
+ "iType integer, "
+ "sIconPath varchar(255), "
+ "sCountries varchar(64), "
+ "sLanguages varchar(64) "
+ ")";
+
+ // clang-format on
} // unnamed namespace
bool CPVRDatabase::Open()
@@ -125,7 +139,8 @@ void CPVRDatabase::CreateTables()
"iLastWatched integer, "
"iClientId integer, " //! @todo use mapping table
"idEpg integer, "
- "bHasArchive bool"
+ "bHasArchive bool, "
+ "iClientProviderUid integer "
")"
);
@@ -155,6 +170,9 @@ void CPVRDatabase::CreateTables()
CLog::LogFC(LOGDEBUG, LOGPVR, "Creating table 'timers'");
m_pDS->exec(sqlCreateTimersTable);
+
+ CLog::LogFC(LOGDEBUG, LOGPVR, "Creating table 'providers'");
+ m_pDS->exec(sqlCreateProvidersTable);
}
void CPVRDatabase::CreateAnalytics()
@@ -252,6 +270,14 @@ void CPVRDatabase::UpdateTables(int iVersion)
m_pDS->exec("DROP TABLE channelgroups_old");
}
+
+ if (iVersion < 39)
+ {
+ m_pDS->exec(sqlCreateProvidersTable);
+ m_pDS->exec("CREATE UNIQUE INDEX idx_iUniqueId_iClientId on providers(iUniqueId, iClientId);");
+ m_pDS->exec("ALTER TABLE channels ADD iClientProviderUid integer");
+ m_pDS->exec("UPDATE channels SET iClientProviderUid = -1");
+ }
}
/********** Client methods **********/
@@ -312,6 +338,116 @@ int CPVRDatabase::GetPriority(const CPVRClient& client)
return atoi(strValue.c_str());
}
+/********** Channel provider methods **********/
+
+bool CPVRDatabase::DeleteProviders()
+{
+ CLog::LogFC(LOGDEBUG, LOGPVR, "Deleting all providers from the database");
+
+ CSingleLock lock(m_critSection);
+ return DeleteValues("providers");
+}
+
+bool CPVRDatabase::Persist(CPVRProvider& provider, bool updateRecord /* = false */)
+{
+ bool bReturn = false;
+ if (provider.GetName().empty())
+ {
+ CLog::LogF(LOGERROR, "Empty provider name");
+ return bReturn;
+ }
+
+ std::string strQuery;
+
+ CSingleLock lock(m_critSection);
+ {
+ /* insert a new entry when this is a new group, or replace the existing one otherwise */
+ if (!updateRecord)
+ strQuery =
+ PrepareSQL("INSERT INTO providers (idProvider, iUniqueId, iClientId, sName, "
+ "iType, sIconPath, sCountries, sLanguages) "
+ "VALUES (%i, %i, %i, '%s', %i, '%s', '%s', '%s');",
+ provider.GetDatabaseId(), provider.GetUniqueId(), provider.GetClientId(),
+ provider.GetName().c_str(), static_cast<int>(provider.GetType()),
+ provider.GetIconPath().c_str(), provider.GetCountriesDBString().c_str(),
+ provider.GetLanguagesDBString().c_str());
+ else
+ strQuery =
+ PrepareSQL("REPLACE INTO providers (idProvider, iUniqueId, iClientId, sName, "
+ "iType, sIconPath, sCountries, sLanguages) "
+ "VALUES (%i, %i, %i, '%s', %i, '%s', '%s', '%s');",
+ provider.GetDatabaseId(), provider.GetUniqueId(), provider.GetClientId(),
+ provider.GetName().c_str(), static_cast<int>(provider.GetType()),
+ provider.GetIconPath().c_str(), provider.GetCountriesDBString().c_str(),
+ provider.GetLanguagesDBString().c_str());
+
+ bReturn = ExecuteQuery(strQuery);
+
+ /* set the provider id if it was <= 0 */
+ if (bReturn && provider.GetDatabaseId() <= 0)
+ {
+ provider.SetDatabaseId(static_cast<int>(m_pDS->lastinsertid()));
+ }
+ }
+
+ return bReturn;
+}
+
+bool CPVRDatabase::Delete(const CPVRProvider& provider)
+{
+ CLog::LogFC(LOGDEBUG, LOGPVR, "Deleting provider '{}' from the database",
+ provider.GetName());
+
+ CSingleLock lock(m_critSection);
+
+ Filter filter;
+ filter.AppendWhere(PrepareSQL("idProvider = '%i'", provider.GetDatabaseId()));
+
+ return DeleteValues("providers", filter);
+}
+
+bool CPVRDatabase::Get(CPVRProviders& results)
+{
+ bool bReturn = false;
+ CSingleLock lock(m_critSection);
+
+ const std::string strQuery = PrepareSQL("SELECT * from providers");
+ if (ResultQuery(strQuery))
+ {
+ try
+ {
+ while (!m_pDS->eof())
+ {
+ std::shared_ptr<CPVRProvider> provider = std::make_shared<CPVRProvider>(
+ m_pDS->fv("iUniqueId").get_asInt(), m_pDS->fv("iClientId").get_asInt());
+
+ provider->SetDatabaseId(m_pDS->fv("idProvider").get_asInt());
+ provider->SetName(m_pDS->fv("sName").get_asString());
+ provider->SetType(
+ static_cast<PVR_PROVIDER_TYPE>(m_pDS->fv("iType").get_asInt()));
+ provider->SetIconPath(m_pDS->fv("sIconPath").get_asString());
+ provider->SetCountriesFromDBString(m_pDS->fv("sCountries").get_asString());
+ provider->SetLanguagesFromDBString(m_pDS->fv("sLanguages").get_asString());
+
+ results.CheckAndAddEntry(provider, ProviderUpdateMode::BY_DATABASE);
+
+ CLog::LogFC(LOGDEBUG, LOGPVR, "Channel Provider '{}' loaded from PVR database",
+ provider->GetName());
+ m_pDS->next();
+ }
+
+ m_pDS->close();
+ bReturn = true;
+ }
+ catch (...)
+ {
+ CLog::LogF(LOGERROR, "Couldn't load providers from PVR database");
+ }
+ }
+
+ return bReturn;
+}
+
/********** Channel methods **********/
int CPVRDatabase::Get(bool bRadio,
@@ -343,6 +479,8 @@ int CPVRDatabase::Get(bool bRadio,
channel->m_iClientId = m_pDS->fv("iClientId").get_asInt();
channel->m_iEpgId = m_pDS->fv("idEpg").get_asInt();
channel->m_bHasArchive = m_pDS->fv("bHasArchive").get_asBool();
+ channel->m_iClientProviderUid = m_pDS->fv("iClientProviderUid").get_asInt();
+
channel->UpdateEncryptionName();
results.insert({channel->StorageId(), channel});
@@ -733,14 +871,14 @@ bool CPVRDatabase::Persist(CPVRChannel& channel, bool bCommit)
"INSERT INTO channels ("
"iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, bIsUserSetName, bIsLocked, "
"sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, "
- "idEpg, bHasArchive) "
- "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, %i)",
+ "idEpg, bHasArchive, iClientProviderUid) "
+ "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i)",
channel.UniqueID(), (channel.IsRadio() ? 1 : 0), (channel.IsHidden() ? 1 : 0),
(channel.IsUserSetIcon() ? 1 : 0), (channel.IsUserSetName() ? 1 : 0),
- (channel.IsLocked() ? 1 : 0), channel.ClientIconPath().c_str(),
- channel.ChannelName().c_str(), 0, (channel.EPGEnabled() ? 1 : 0),
- channel.EPGScraper().c_str(), static_cast<unsigned int>(channel.LastWatched()),
- channel.ClientID(), channel.EpgID(), channel.HasArchive());
+ (channel.IsLocked() ? 1 : 0), channel.IconPath().c_str(), channel.ChannelName().c_str(), 0,
+ (channel.EPGEnabled() ? 1 : 0), channel.EPGScraper().c_str(),
+ static_cast<unsigned int>(channel.LastWatched()), channel.ClientID(), channel.EpgID(),
+ channel.HasArchive(), channel.ClientProviderUid());
}
else
{
@@ -749,14 +887,15 @@ bool CPVRDatabase::Persist(CPVRChannel& channel, bool bCommit)
"REPLACE INTO channels ("
"iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, bIsUserSetName, bIsLocked, "
"sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, "
- "idChannel, idEpg, bHasArchive) "
- "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %s, %i, %i)",
+ "idChannel, idEpg, bHasArchive, iClientProviderUid) "
+ "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %s, %i, %i, %i)",
channel.UniqueID(), (channel.IsRadio() ? 1 : 0), (channel.IsHidden() ? 1 : 0),
(channel.IsUserSetIcon() ? 1 : 0), (channel.IsUserSetName() ? 1 : 0),
(channel.IsLocked() ? 1 : 0), channel.ClientIconPath().c_str(),
channel.ChannelName().c_str(), 0, (channel.EPGEnabled() ? 1 : 0),
channel.EPGScraper().c_str(), static_cast<unsigned int>(channel.LastWatched()),
- channel.ClientID(), strValue.c_str(), channel.EpgID(), channel.HasArchive());
+ channel.ClientID(), strValue.c_str(), channel.EpgID(), channel.HasArchive(),
+ channel.ClientProviderUid());
}
if (QueueInsertQuery(strQuery))
diff --git a/xbmc/pvr/PVRDatabase.h b/xbmc/pvr/PVRDatabase.h
index 4488231170..853de802be 100644
--- a/xbmc/pvr/PVRDatabase.h
+++ b/xbmc/pvr/PVRDatabase.h
@@ -20,6 +20,8 @@ namespace PVR
class CPVRChannelGroup;
class CPVRChannelGroupMember;
class CPVRChannelGroups;
+ class CPVRProvider;
+ class CPVRProviders;
class CPVRClient;
class CPVRTimerInfoTag;
class CPVRTimers;
@@ -62,7 +64,7 @@ namespace PVR
* @brief Get the minimal database version that is required to operate correctly.
* @return The minimal database version.
*/
- int GetSchemaVersion() const override { return 38; }
+ int GetSchemaVersion() const override { return 39; }
/*!
* @brief Get the default sqlite database filename.
@@ -146,6 +148,39 @@ namespace PVR
//@}
+ /*! @name Channel provider methods */
+ //@{
+
+ /*!
+ * @brief Remove all providers from the database.
+ * @return True if all providers were removed, false otherwise.
+ */
+ bool DeleteProviders();
+
+ /*!
+ * @brief Add or update a provider entry in the database
+ * @param provider The provider to persist.
+ * @param updateRecord True if record to be updated, false for insert
+ * @return True when persisted, false otherwise.
+ */
+ bool Persist(CPVRProvider& provider, bool updateRecord = false);
+
+ /*!
+ * @brief Remove a provider entry from the database
+ * @param provider The provider to remove.
+ * @return True if the provider was removed, false otherwise.
+ */
+ bool Delete(const CPVRProvider& provider);
+
+ /*!
+ * @brief Get the list of providers from the database
+ * @param results The providers to store the results in.
+ * @return The amount of providers that were added.
+ */
+ bool Get(CPVRProviders& results);
+
+ //@}
+
/*! @name Channel group methods */
//@{
diff --git a/xbmc/pvr/PVRManager.cpp b/xbmc/pvr/PVRManager.cpp
index 0ff6828044..2d7ed5f0a1 100644
--- a/xbmc/pvr/PVRManager.cpp
+++ b/xbmc/pvr/PVRManager.cpp
@@ -26,6 +26,8 @@
#include "pvr/guilib/PVRGUIChannelIconUpdater.h"
#include "pvr/guilib/PVRGUIProgressHandler.h"
#include "pvr/guilib/guiinfo/PVRGUIInfo.h"
+#include "pvr/providers/PVRProvider.h"
+#include "pvr/providers/PVRProviders.h"
#include "pvr/recordings/PVRRecording.h"
#include "pvr/recordings/PVRRecordings.h"
#include "pvr/timers/PVRTimerInfoTag.h"
@@ -188,6 +190,7 @@ void CPVRManagerJobQueue::ExecutePendingJobs()
CPVRManager::CPVRManager() :
CThread("PVRManager"),
+ m_providers(new CPVRProviders),
m_channelGroups(new CPVRChannelGroupsContainer),
m_recordings(new CPVRRecordings),
m_timers(new CPVRTimers),
@@ -245,6 +248,12 @@ std::shared_ptr<CPVRDatabase> CPVRManager::GetTVDatabase() const
return m_database;
}
+std::shared_ptr<CPVRProviders> CPVRManager::Providers() const
+{
+ CSingleLock lock(m_critSection);
+ return m_providers;
+}
+
std::shared_ptr<CPVRChannelGroupsContainer> CPVRManager::ChannelGroups() const
{
CSingleLock lock(m_critSection);
@@ -333,6 +342,7 @@ void CPVRManager::Clear()
m_guiInfo.reset();
m_timers.reset();
m_recordings.reset();
+ m_providers.reset();
m_channelGroups.reset();
m_parentalTimer.reset();
m_database.reset();
@@ -346,6 +356,7 @@ void CPVRManager::ResetProperties()
Clear();
m_database.reset(new CPVRDatabase);
+ m_providers.reset(new CPVRProviders);
m_channelGroups.reset(new CPVRChannelGroupsContainer);
m_recordings.reset(new CPVRRecordings);
m_timers.reset(new CPVRTimers);
@@ -643,6 +654,7 @@ void CPVRManager::OnWake()
/* trigger PVR data updates */
TriggerChannelGroupsUpdate();
+ TriggerProvidersUpdate();
TriggerChannelsUpdate();
TriggerRecordingsUpdate();
TriggerEpgsCreate();
@@ -664,6 +676,9 @@ bool CPVRManager::LoadComponents(CPVRGUIProgressHandler* progressHandler)
if (progressHandler)
progressHandler->UpdateProgress(g_localizeStrings.Get(19236), 0); // Loading channels from clients
+ if (!m_providers->Load() || !IsInitialising())
+ return false;
+
if (!m_channelGroups->Load() || !IsInitialising())
return false;
@@ -696,6 +711,7 @@ void CPVRManager::UnloadComponents()
m_recordings->Unload();
m_timers->Unload();
m_channelGroups->Unload();
+ m_providers->Unload();
m_epgContainer.Unload();
}
@@ -826,6 +842,13 @@ void CPVRManager::TriggerChannelsUpdate()
});
}
+void CPVRManager::TriggerProvidersUpdate()
+{
+ m_pendingUpdates->Append("pvr-update-channel-providers", [this]() {
+ return Providers()->Update();
+ });
+}
+
void CPVRManager::TriggerChannelGroupsUpdate()
{
m_pendingUpdates->Append("pvr-update-channelgroups", [this]() {
diff --git a/xbmc/pvr/PVRManager.h b/xbmc/pvr/PVRManager.h
index 94749e5ae0..322e8dd1f6 100644
--- a/xbmc/pvr/PVRManager.h
+++ b/xbmc/pvr/PVRManager.h
@@ -30,6 +30,7 @@ namespace PVR
class CPVRChannel;
class CPVRChannelGroup;
class CPVRChannelGroupsContainer;
+ class CPVRProviders;
class CPVRClient;
class CPVRClients;
class CPVRDatabase;
@@ -104,6 +105,12 @@ namespace PVR
const CVariant& data) override;
/*!
+ * @brief Get the providers container.
+ * @return The providers container.
+ */
+ std::shared_ptr<CPVRProviders> Providers() const;
+
+ /*!
* @brief Get the channel groups container.
* @return The groups container.
*/
@@ -285,6 +292,11 @@ namespace PVR
void TriggerChannelsUpdate();
/*!
+ * @brief Let the background thread update the provider list.
+ */
+ void TriggerProvidersUpdate();
+
+ /*!
* @brief Let the background thread update the channel groups list.
*/
void TriggerChannelGroupsUpdate();
@@ -419,6 +431,7 @@ namespace PVR
/** @name containers */
//@{
+ std::shared_ptr<CPVRProviders> m_providers; /*!< pointer to the providers container */
std::shared_ptr<CPVRChannelGroupsContainer> m_channelGroups; /*!< pointer to the channel groups container */
std::shared_ptr<CPVRRecordings> m_recordings; /*!< pointer to the recordings container */
std::shared_ptr<CPVRTimers> m_timers; /*!< pointer to the timers container */
diff --git a/xbmc/pvr/addons/PVRClient.cpp b/xbmc/pvr/addons/PVRClient.cpp
index 3665fc2d6b..4d815aaa6d 100644
--- a/xbmc/pvr/addons/PVRClient.cpp
+++ b/xbmc/pvr/addons/PVRClient.cpp
@@ -30,6 +30,8 @@
#include "pvr/epg/Epg.h"
#include "pvr/epg/EpgContainer.h"
#include "pvr/epg/EpgInfoTag.h"
+#include "pvr/providers/PVRProvider.h"
+#include "pvr/providers/PVRProviders.h"
#include "pvr/recordings/PVRRecording.h"
#include "pvr/recordings/PVRRecordings.h"
#include "pvr/timers/PVRTimerInfoTag.h"
@@ -132,11 +134,13 @@ void CPVRClient::ResetProperties(int iClientId /* = PVR_INVALID_CLIENT_ID */)
m_struct.toKodi->kodiInstance = this;
m_struct.toKodi->TransferEpgEntry = cb_transfer_epg_entry;
m_struct.toKodi->TransferChannelEntry = cb_transfer_channel_entry;
+ m_struct.toKodi->TransferProviderEntry = cb_transfer_provider_entry;
m_struct.toKodi->TransferTimerEntry = cb_transfer_timer_entry;
m_struct.toKodi->TransferRecordingEntry = cb_transfer_recording_entry;
m_struct.toKodi->AddMenuHook = cb_add_menu_hook;
m_struct.toKodi->RecordingNotification = cb_recording_notification;
m_struct.toKodi->TriggerChannelUpdate = cb_trigger_channel_update;
+ m_struct.toKodi->TriggerProvidersUpdate = cb_trigger_provider_update;
m_struct.toKodi->TriggerChannelGroupsUpdate = cb_trigger_channel_groups_update;
m_struct.toKodi->TriggerTimerUpdate = cb_trigger_timer_update;
m_struct.toKodi->TriggerRecordingUpdate = cb_trigger_recording_update;
@@ -401,6 +405,7 @@ void CPVRClient::WriteClientChannelInfo(const std::shared_ptr<CPVRChannel>& xbmc
addonChannel.bIsHidden = xbmcChannel->IsHidden();
strncpy(addonChannel.strMimeType, xbmcChannel->MimeType().c_str(),
sizeof(addonChannel.strMimeType) - 1);
+ addonChannel.iClientProviderUid = xbmcChannel->ClientProviderUid();
}
bool CPVRClient::GetAddonProperties()
@@ -914,6 +919,27 @@ PVR_ERROR CPVRClient::GetChannelGroupMembers(
m_clientCapabilities.SupportsChannelGroups());
}
+PVR_ERROR CPVRClient::GetProvidersAmount(int& iProviders)
+{
+ iProviders = -1;
+ return DoAddonCall(__func__, [&iProviders](const AddonInstance* addon) {
+ return addon->toAddon->GetProvidersAmount(addon, &iProviders);
+ });
+}
+
+PVR_ERROR CPVRClient::GetProviders(CPVRProvidersContainer& providers)
+{
+ return DoAddonCall(
+ __func__,
+ [this, &providers](const AddonInstance* addon) {
+ ADDON_HANDLE_STRUCT handle = {};
+ handle.callerAddress = this;
+ handle.dataAddress = &providers;
+ return addon->toAddon->GetProviders(addon, &handle);
+ },
+ m_clientCapabilities.SupportsProviders());
+}
+
PVR_ERROR CPVRClient::GetChannelsAmount(int& iChannels)
{
iChannels = -1;
@@ -1767,6 +1793,30 @@ void CPVRClient::cb_transfer_epg_entry(void* kodiInstance,
});
}
+void CPVRClient::cb_transfer_provider_entry(void* kodiInstance,
+ const ADDON_HANDLE handle,
+ const PVR_PROVIDER* provider)
+{
+ if (!handle)
+ {
+ CLog::LogF(LOGERROR, "Invalid handler data");
+ return;
+ }
+
+ CPVRClient* client = static_cast<CPVRClient*>(kodiInstance);
+ CPVRProvidersContainer* kodiProviders = static_cast<CPVRProvidersContainer*>(handle->dataAddress);
+ if (!provider || !client || !kodiProviders)
+ {
+ CLog::LogF(LOGERROR, "Invalid handler data");
+ return;
+ }
+
+ /* transfer this entry to the internal channels group */
+ std::shared_ptr<CPVRProvider> transferProvider(
+ std::make_shared<CPVRProvider>(*provider, client->GetID()));
+ kodiProviders->UpdateFromClient(transferProvider);
+}
+
void CPVRClient::cb_transfer_channel_entry(void* kodiInstance,
const ADDON_HANDLE handle,
const PVR_CHANNEL* channel)
@@ -1879,6 +1929,12 @@ void CPVRClient::cb_trigger_channel_update(void* kodiInstance)
});
}
+void CPVRClient::cb_trigger_provider_update(void* kodiInstance)
+{
+ /* update the providers table in the next iteration of the pvrmanager's main loop */
+ CServiceBroker::GetPVRManager().TriggerProvidersUpdate();
+}
+
void CPVRClient::cb_trigger_timer_update(void* kodiInstance)
{
HandleAddonCallback(__func__, kodiInstance, [&](CPVRClient* client) {
diff --git a/xbmc/pvr/addons/PVRClient.h b/xbmc/pvr/addons/PVRClient.h
index f7ccab5cf6..257ac3458a 100644
--- a/xbmc/pvr/addons/PVRClient.h
+++ b/xbmc/pvr/addons/PVRClient.h
@@ -26,6 +26,8 @@ namespace PVR
class CPVRChannel;
class CPVRChannelGroup;
class CPVRChannelGroups;
+class CPVRProvider;
+class CPVRProvidersContainer;
class CPVRClientMenuHook;
class CPVRClientMenuHooks;
class CPVREpg;
@@ -71,6 +73,15 @@ public:
bool SupportsRadio() const { return m_addonCapabilities && m_addonCapabilities->bSupportsRadio; }
/*!
+ * @brief Check whether this add-on supports providers.
+ * @return True if supported, false otherwise.
+ */
+ bool SupportsProviders() const
+ {
+ return m_addonCapabilities && m_addonCapabilities->bSupportsProviders;
+ }
+
+ /*!
* @brief Check whether this add-on supports channel groups.
* @return True if supported, false otherwise.
*/
@@ -557,6 +568,20 @@ public:
*/
PVR_ERROR GetChannels(bool bRadio, std::vector<std::shared_ptr<CPVRChannel>>& channels);
+ /*!
+ * @brief Get the total amount of providers from the backend.
+ * @param iChannels The total amount of channels on the server or -1 on error.
+ * @return PVR_ERROR_NO_ERROR on success, respective error code otherwise.
+ */
+ PVR_ERROR GetProvidersAmount(int& iProviders);
+
+ /*!
+ * @brief Request the list of all providers from the backend.
+ * @param providers The providers list to add the providers to.
+ * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
+ */
+ PVR_ERROR GetProviders(CPVRProvidersContainer& providers);
+
//@}
/** @name PVR recording methods */
//@{
@@ -1139,6 +1164,16 @@ private:
const PVR_CHANNEL* entry);
/*!
+ * @brief Transfer a provider entry from the add-on to Kodi
+ * @param kodiInstance Pointer to Kodi's CPVRClient class
+ * @param handle The handle parameter that Kodi used when requesting the channel list
+ * @param entry The entry to transfer to Kodi
+ */
+ static void cb_transfer_provider_entry(void* kodiInstance,
+ const ADDON_HANDLE handle,
+ const PVR_PROVIDER* entry);
+
+ /*!
* @brief Transfer a timer entry from the add-on to Kodi
* @param kodiInstance Pointer to Kodi's CPVRClient class
* @param handle The handle parameter that Kodi used when requesting the timers list
@@ -1184,6 +1219,12 @@ private:
static void cb_trigger_channel_update(void* kodiInstance);
/*!
+ * @brief Request Kodi to update it's list of providers
+ * @param kodiInstance Pointer to Kodi's CPVRClient class
+ */
+ static void cb_trigger_provider_update(void* kodiInstance);
+
+ /*!
* @brief Request Kodi to update it's list of timers
* @param kodiInstance Pointer to Kodi's CPVRClient class
*/
diff --git a/xbmc/pvr/addons/PVRClients.cpp b/xbmc/pvr/addons/PVRClients.cpp
index edac2ec27f..557f65c674 100644
--- a/xbmc/pvr/addons/PVRClients.cpp
+++ b/xbmc/pvr/addons/PVRClients.cpp
@@ -360,6 +360,37 @@ int CPVRClients::GetCreatedClients(CPVRClientMap& clients) const
return iReturn;
}
+std::vector<CVariant> CPVRClients::GetClientProviderInfos() const
+{
+ std::vector<AddonInfoPtr> addonInfos;
+ // Get enabled and disabled PVR client addon infos
+ CServiceBroker::GetAddonMgr().GetAddonInfos(addonInfos, false, ADDON_PVRDLL);
+
+ CSingleLock lock(m_critSection);
+
+ std::vector<CVariant> clientProviderInfos;
+ for (const auto& addonInfo : addonInfos)
+ {
+ CVariant clientProviderInfo(CVariant::VariantTypeObject);
+ if (IsKnownClient(addonInfo->ID()))
+ clientProviderInfo["clientid"] = GetClientId(addonInfo->ID());
+ else
+ clientProviderInfo["clientid"] = ClientIdFromAddonId(addonInfo->ID());
+ clientProviderInfo["addonid"] = addonInfo->ID();
+ clientProviderInfo["enabled"] = !CServiceBroker::GetAddonMgr().IsAddonDisabled(addonInfo->ID());
+ clientProviderInfo["name"] = addonInfo->Name();
+ clientProviderInfo["icon"] = addonInfo->Icon();
+ auto& artMap = addonInfo->Art();
+ auto thumbEntry = artMap.find("thumb");
+ if (thumbEntry != artMap.end())
+ clientProviderInfo["thumb"] = thumbEntry->second;
+
+ clientProviderInfos.emplace_back(clientProviderInfo);
+ }
+
+ return clientProviderInfos;
+}
+
PVR_ERROR CPVRClients::GetCreatedClients(CPVRClientMap& clientsReady, std::vector<int>& clientsNotReady) const
{
clientsNotReady.clear();
@@ -447,6 +478,7 @@ std::vector<CVariant> CPVRClients::GetEnabledClientInfos() const
clientInfo["supportstimers"] = capabilities.SupportsTimers();
clientInfo["supportschannelgroups"] = capabilities.SupportsChannelGroups();
clientInfo["supportschannelscan"] = capabilities.SupportsChannelScan();
+ clientInfo["supportchannelproviders"] = capabilities.SupportsProviders();
clientInfos.push_back(clientInfo);
}
@@ -473,6 +505,10 @@ std::vector<SBackend> CPVRClients::GetBackendProperties() const
}
int iAmount = 0;
+ if (client->GetProvidersAmount(iAmount) == PVR_ERROR_NO_ERROR)
+ properties.numProviders = iAmount;
+ if (client->GetChannelGroupsAmount(iAmount) == PVR_ERROR_NO_ERROR)
+ properties.numChannelGroups = iAmount;
if (client->GetChannelsAmount(iAmount) == PVR_ERROR_NO_ERROR)
properties.numChannels = iAmount;
if (client->GetTimersAmount(iAmount) == PVR_ERROR_NO_ERROR)
@@ -555,6 +591,14 @@ PVR_ERROR CPVRClients::GetChannels(bool bRadio,
failedClients);
}
+PVR_ERROR CPVRClients::GetProviders(CPVRProvidersContainer* providers,
+ std::vector<int>& failedClients)
+{
+ return ForCreatedClients(__FUNCTION__, [providers](const std::shared_ptr<CPVRClient>& client) {
+ return client->GetProviders(*providers);
+ }, failedClients);
+}
+
PVR_ERROR CPVRClients::GetChannelGroups(CPVRChannelGroups* groups, std::vector<int>& failedClients)
{
return ForCreatedClients(__FUNCTION__, [groups](const std::shared_ptr<CPVRClient>& client) {
diff --git a/xbmc/pvr/addons/PVRClients.h b/xbmc/pvr/addons/PVRClients.h
index e15c992d3d..255c61d786 100644
--- a/xbmc/pvr/addons/PVRClients.h
+++ b/xbmc/pvr/addons/PVRClients.h
@@ -28,6 +28,7 @@ namespace PVR
class CPVRChannelGroupInternal;
class CPVRChannelGroup;
class CPVRChannelGroups;
+ class CPVRProvidersContainer;
class CPVRClient;
class CPVREpg;
class CPVRRecordings;
@@ -47,6 +48,8 @@ namespace PVR
int numTimers = 0;
int numRecordings = 0;
int numDeletedRecordings = 0;
+ int numProviders = 0;
+ int numChannelGroups = 0;
int numChannels = 0;
uint64_t diskUsed = 0;
uint64_t diskTotal = 0;
@@ -136,6 +139,12 @@ namespace PVR
bool GetCreatedClient(int iClientId, std::shared_ptr<CPVRClient>& addon) const;
/*!
+ * @brief Get info required for providers. Include both enabled and disabled PVR add-ons
+ * @return A list containing the information required to create client providers.
+ */
+ std::vector<CVariant> GetClientProviderInfos() const;
+
+ /*!
* @brief Get all created clients.
* @param clients All created clients will be added to this map.
* @return The amount of clients added to the map.
@@ -265,6 +274,14 @@ namespace PVR
std::vector<int>& failedClients);
/*!
+ * @brief Get all providers from backends.
+ * @param group The container to store the providers in.
+ * @param failedClients in case of errors will contain the ids of the clients for which the providers could not be obtained.
+ * @return PVR_ERROR_NO_ERROR if the providers were fetched successfully, last error otherwise.
+ */
+ PVR_ERROR GetProviders(CPVRProvidersContainer* providers, std::vector<int>& failedClients);
+
+ /*!
* @brief Get all channel groups from backends.
* @param groups Store the channel groups in this container.
* @param failedClients in case of errors will contain the ids of the clients for which the channel groups could not be obtained.
diff --git a/xbmc/pvr/channels/PVRChannel.cpp b/xbmc/pvr/channels/PVRChannel.cpp
index 7f1bda19de..658060cfc5 100644
--- a/xbmc/pvr/channels/PVRChannel.cpp
+++ b/xbmc/pvr/channels/PVRChannel.cpp
@@ -18,6 +18,7 @@
#include "pvr/epg/EpgChannelData.h"
#include "pvr/epg/EpgContainer.h"
#include "pvr/epg/EpgInfoTag.h"
+#include "pvr/providers/PVRProviders.h"
#include "threads/SingleLock.h"
#include "utils/StringUtils.h"
#include "utils/Variant.h"
@@ -69,7 +70,8 @@ CPVRChannel::CPVRChannel(const PVR_CHANNEL& channel, unsigned int iClientId)
m_clientChannelNumber(channel.iChannelNumber, channel.iSubChannelNumber),
m_strClientChannelName(channel.strChannelName),
m_strMimeType(channel.strMimeType),
- m_iClientEncryptionSystem(channel.iEncryptionSystem)
+ m_iClientEncryptionSystem(channel.iEncryptionSystem),
+ m_iClientProviderUid(channel.iClientProviderUid)
{
if (m_strChannelName.empty())
m_strChannelName = StringUtils::Format("{} {}", g_localizeStrings.Get(19029), m_iUniqueId);
@@ -191,6 +193,7 @@ bool CPVRChannel::UpdateFromClient(const std::shared_ptr<CPVRChannel>& channel)
SetClientID(channel->ClientID());
SetArchive(channel->HasArchive());
+ SetClientProviderUid(channel->ClientProviderUid());
m_clientChannelNumber = channel->m_clientChannelNumber;
m_strMimeType = channel->MimeType();
@@ -515,6 +518,20 @@ void CPVRChannel::UpdateEncryptionName()
m_strClientEncryptionName = GetEncryptionName(m_iClientEncryptionSystem);
}
+bool CPVRChannel::SetClientProviderUid(int iClientProviderUid)
+{
+ CSingleLock lock(m_critSection);
+
+ if (m_iClientProviderUid != iClientProviderUid)
+ {
+ m_iClientProviderUid = iClientProviderUid;
+ m_bChanged = true;
+ return true;
+ }
+
+ return false;
+}
+
/********** EPG methods **********/
std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVRChannel::GetEpgTags() const
@@ -797,3 +814,21 @@ bool CPVRChannel::CanRecord() const
const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
return client && client->GetClientCapabilities().SupportsRecordings();
}
+
+std::shared_ptr<CPVRProvider> CPVRChannel::GetDefaultProvider() const
+{
+ return CServiceBroker::GetPVRManager().Providers()->GetByClient(m_iClientId,
+ PVR_PROVIDER_INVALID_UID);
+}
+
+bool CPVRChannel::HasClientProvider() const
+{
+ CSingleLock lock(m_critSection);
+ return m_iClientProviderUid != PVR_PROVIDER_INVALID_UID;
+}
+
+std::shared_ptr<CPVRProvider> CPVRChannel::GetProvider() const
+{
+ return CServiceBroker::GetPVRManager().Providers()->GetByClient(m_iClientId,
+ m_iClientProviderUid);
+}
diff --git a/xbmc/pvr/channels/PVRChannel.h b/xbmc/pvr/channels/PVRChannel.h
index 1395c23644..cbad9f2704 100644
--- a/xbmc/pvr/channels/PVRChannel.h
+++ b/xbmc/pvr/channels/PVRChannel.h
@@ -9,6 +9,7 @@
#pragma once
#include "addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_channels.h"
+#include "addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_providers.h"
#include "pvr/PVRCachedImage.h"
#include "pvr/channels/PVRChannelNumber.h"
#include "threads/CriticalSection.h"
@@ -26,6 +27,7 @@ namespace PVR
{
enum class PVREvent;
+ class CPVRProvider;
class CPVREpg;
class CPVREpgInfoTag;
class CPVRRadioRDSInfoTag;
@@ -425,6 +427,12 @@ namespace PVR
int ClientOrder() const { return m_iClientOrder; }
/*!
+ * @brief Get the client provider Uid for this channel
+ * @return m_iClientProviderUid The provider Uid for this channel
+ */
+ int ClientProviderUid() const { return m_iClientProviderUid; }
+
+ /*!
* @brief CEventStream callback for PVR events.
* @param event The event.
*/
@@ -440,6 +448,27 @@ namespace PVR
*/
void Unlock() { m_critSection.unlock(); }
+ /*!
+ * @brief Get the default provider of this channel. The default
+ * provider represents the PVR add-on itself.
+ * @return The default provider of this channel
+ */
+ std::shared_ptr<CPVRProvider> GetDefaultProvider() const;
+
+ /*!
+ * @brief Whether or not this channel has a provider set by the client.
+ * @return True if a provider was set by the client, false otherwise.
+ */
+ bool HasClientProvider() const;
+
+ /*!
+ * @brief Get the provider of this channel. This may be the default provider or a
+ * custom provider set by the client. If @ref "HasClientProvider()" returns true
+ * the provider will be custom from the client, otherwise the default provider.
+ * @return The provider of this channel
+ */
+ std::shared_ptr<CPVRProvider> GetProvider() const;
+
//@}
private:
CPVRChannel() = delete;
@@ -456,6 +485,13 @@ namespace PVR
*/
void ResetEPG();
+ /*!
+ * @brief Set the client provider Uid for this channel
+ * @param iClientProviderUid The provider Uid for this channel
+ * @return True if the something changed, false otherwise.
+ */
+ bool SetClientProviderUid(int iClientProviderUid);
+
/*! @name XBMC related channel data
*/
//@{
@@ -494,6 +530,7 @@ namespace PVR
int m_iClientEncryptionSystem = -1; /*!< the encryption system used by this channel. 0 for FreeToAir, -1 for unknown */
std::string m_strClientEncryptionName; /*!< the name of the encryption system used by this channel */
int m_iClientOrder = 0; /*!< the order from this channels group member */
+ int m_iClientProviderUid = PVR_PROVIDER_INVALID_UID; /*!< the unique id for this provider from the client */
//@}
mutable CCriticalSection m_critSection;
diff --git a/xbmc/pvr/guilib/PVRGUIActions.cpp b/xbmc/pvr/guilib/PVRGUIActions.cpp
index af9839062d..59ed6f0113 100644
--- a/xbmc/pvr/guilib/PVRGUIActions.cpp
+++ b/xbmc/pvr/guilib/PVRGUIActions.cpp
@@ -1952,6 +1952,8 @@ namespace PVR
pDlgProgress->SetPercentage(iProgressStepPercentage * ++iProgressStepsDone);
pDlgProgress->Progress();
+ /* delete all providers */
+ pvrDatabase->DeleteProviders();
// delete all channels (including data only available locally, like user set icons)
pvrDatabase->DeleteChannels();
}
diff --git a/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp b/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp
index 772bca009e..c8990b2548 100644
--- a/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp
+++ b/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp
@@ -72,6 +72,8 @@ void CPVRGUIInfo::ResetProperties()
m_strBackendTimers .clear();
m_strBackendRecordings .clear();
m_strBackendDeletedRecordings .clear();
+ m_strBackendProviders.clear();
+ m_strBackendChannelGroups.clear();
m_strBackendChannels .clear();
m_iBackendDiskTotal = 0;
m_iBackendDiskUsed = 0;
@@ -903,6 +905,12 @@ bool CPVRGUIInfo::GetPVRLabel(const CFileItem* item, const CGUIInfo& info, std::
case PVR_BACKEND_DISKSPACE:
CharInfoBackendDiskspace(strValue);
return true;
+ case PVR_BACKEND_PROVIDERS:
+ CharInfoBackendProviders(strValue);
+ return true;
+ case PVR_BACKEND_CHANNEL_GROUPS:
+ CharInfoBackendChannelGroups(strValue);
+ return true;
case PVR_BACKEND_CHANNELS:
CharInfoBackendChannels(strValue);
return true;
@@ -1665,6 +1673,18 @@ void CPVRGUIInfo::CharInfoBackendDiskspace(std::string& strValue) const
strValue = g_localizeStrings.Get(13205);
}
+void CPVRGUIInfo::CharInfoBackendProviders(std::string& strValue) const
+{
+ m_updateBackendCacheRequested = true;
+ strValue = m_strBackendProviders;
+}
+
+void CPVRGUIInfo::CharInfoBackendChannelGroups(std::string& strValue) const
+{
+ m_updateBackendCacheRequested = true;
+ strValue = m_strBackendChannelGroups;
+}
+
void CPVRGUIInfo::CharInfoBackendChannels(std::string& strValue) const
{
m_updateBackendCacheRequested = true;
@@ -1764,6 +1784,8 @@ void CPVRGUIInfo::UpdateBackendCache()
m_strBackendName = g_localizeStrings.Get(13205);
m_strBackendVersion = g_localizeStrings.Get(13205);
m_strBackendHost = g_localizeStrings.Get(13205);
+ m_strBackendProviders = g_localizeStrings.Get(13205);
+ m_strBackendChannelGroups = g_localizeStrings.Get(13205);
m_strBackendChannels = g_localizeStrings.Get(13205);
m_strBackendTimers = g_localizeStrings.Get(13205);
m_strBackendRecordings = g_localizeStrings.Get(13205);
@@ -1780,6 +1802,13 @@ void CPVRGUIInfo::UpdateBackendCache()
m_strBackendVersion = backend.version;
m_strBackendHost = backend.host;
+ // We always display one extra as the add-on itself counts as a provider
+ if (backend.numProviders >= 0)
+ m_strBackendProviders = StringUtils::Format("%i", backend.numProviders + 1);
+
+ if (backend.numChannelGroups >= 0)
+ m_strBackendChannelGroups = StringUtils::Format("%i", backend.numChannelGroups);
+
if (backend.numChannels >= 0)
m_strBackendChannels = std::to_string(backend.numChannels);
diff --git a/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.h b/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.h
index 4adff0c3cb..2b67b39842 100644
--- a/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.h
+++ b/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.h
@@ -105,6 +105,8 @@ namespace PVR
void CharInfoBackendVersion(std::string& strValue) const;
void CharInfoBackendHost(std::string& strValue) const;
void CharInfoBackendDiskspace(std::string& strValue) const;
+ void CharInfoBackendProviders(std::string& strValue) const;
+ void CharInfoBackendChannelGroups(std::string& strValue) const;
void CharInfoBackendChannels(std::string& strValue) const;
void CharInfoBackendTimers(std::string& strValue) const;
void CharInfoBackendRecordings(std::string& strValue) const;
@@ -133,6 +135,8 @@ namespace PVR
std::string m_strBackendTimers;
std::string m_strBackendRecordings;
std::string m_strBackendDeletedRecordings;
+ std::string m_strBackendProviders;
+ std::string m_strBackendChannelGroups;
std::string m_strBackendChannels;
long long m_iBackendDiskTotal;
long long m_iBackendDiskUsed;
diff --git a/xbmc/pvr/providers/CMakeLists.txt b/xbmc/pvr/providers/CMakeLists.txt
new file mode 100644
index 0000000000..f01a35a9e3
--- /dev/null
+++ b/xbmc/pvr/providers/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(SOURCES PVRProvider.cpp
+ PVRProviders.cpp)
+
+set(HEADERS PVRProvider.h
+ PVRProviders.h)
+
+core_add_library(pvr_providers)
diff --git a/xbmc/pvr/providers/PVRProvider.cpp b/xbmc/pvr/providers/PVRProvider.cpp
new file mode 100644
index 0000000000..6db9f778e0
--- /dev/null
+++ b/xbmc/pvr/providers/PVRProvider.cpp
@@ -0,0 +1,368 @@
+/*
+ * 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 "PVRProvider.h"
+
+#include "ServiceBroker.h"
+#include "guilib/LocalizeStrings.h"
+#include "pvr/PVRDatabase.h"
+#include "pvr/PVRManager.h"
+#include "pvr/addons/PVRClient.h"
+#include "pvr/addons/PVRClients.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+
+#include <memory>
+#include <string>
+
+using namespace PVR;
+
+CPVRProvider::CPVRProvider(int iUniqueId, int iClientId)
+ : m_iUniqueId(iUniqueId),
+ m_iClientId(iClientId)
+{
+}
+
+CPVRProvider::CPVRProvider(const PVR_PROVIDER& provider, int iClientId)
+ : m_iUniqueId(provider.iUniqueId),
+ m_iClientId(iClientId),
+ m_strName(provider.strName),
+ m_type(provider.type),
+ m_strIconPath(provider.strIconPath),
+ m_strCountries(provider.strCountries),
+ m_strLanguages(provider.strLanguages)
+{
+}
+
+CPVRProvider::CPVRProvider(int iClientId,
+ const std::string& addonProviderName,
+ const std::string& addonIconPath,
+ const std::string& addonThumbPath)
+ : m_iClientId(iClientId),
+ m_strName(addonProviderName),
+ m_type(PVR_PROVIDER_TYPE_ADDON),
+ m_strIconPath(addonIconPath),
+ m_bIsClientProvider(true),
+ m_strThumbPath(addonThumbPath)
+{
+}
+
+bool CPVRProvider::operator==(const CPVRProvider& right) const
+{
+ return (m_iUniqueId == right.m_iUniqueId && m_iClientId == right.m_iClientId);
+}
+
+bool CPVRProvider::operator!=(const CPVRProvider& right) const
+{
+ return !(*this == right);
+}
+
+void CPVRProvider::Serialize(CVariant& value) const
+{
+ value["providerid"] = m_iDatabaseId;
+ value["clientid"] = m_iClientId;
+ value["providername"] = m_strName;
+ switch (m_type)
+ {
+ case PVR_PROVIDER_TYPE_ADDON:
+ value["providertype"] = "addon";
+ break;
+ case PVR_PROVIDER_TYPE_SATELLITE:
+ value["providertype"] = "satellite";
+ break;
+ case PVR_PROVIDER_TYPE_CABLE:
+ value["providertype"] = "cable";
+ break;
+ case PVR_PROVIDER_TYPE_AERIAL:
+ value["providertype"] = "aerial";
+ break;
+ case PVR_PROVIDER_TYPE_IPTV:
+ value["providertype"] = "iptv";
+ break;
+ case PVR_PROVIDER_TYPE_OTHER:
+ value["providertype"] = "other";
+ break;
+ default:
+ value["state"] = "unknown";
+ break;
+ }
+ value["iconpath"] = m_strIconPath;
+ value["countries"] = m_strCountries;
+ value["languages"] = m_strLanguages;
+}
+
+int CPVRProvider::GetDatabaseId() const
+{
+ CSingleLock lock(m_critSection);
+ return m_iDatabaseId;
+}
+
+bool CPVRProvider::SetDatabaseId(int iDatabaseId)
+{
+ CSingleLock lock(m_critSection);
+
+ if (m_iDatabaseId != iDatabaseId)
+ {
+ m_iDatabaseId = iDatabaseId;
+ return true;
+ }
+
+ return false;
+}
+
+int CPVRProvider::GetUniqueId() const
+{
+ CSingleLock lock(m_critSection);
+ return m_iUniqueId;
+}
+
+int CPVRProvider::GetClientId() const
+{
+ CSingleLock lock(m_critSection);
+ return m_iClientId;
+}
+
+std::string CPVRProvider::GetName() const
+{
+ CSingleLock lock(m_critSection);
+ return m_strName;
+}
+
+bool CPVRProvider::SetName(const std::string& strName)
+{
+ CSingleLock lock(m_critSection);
+ if (m_strName != strName)
+ {
+ m_strName = strName;
+ return true;
+ }
+
+ return false;
+}
+
+PVR_PROVIDER_TYPE CPVRProvider::GetType() const
+{
+ CSingleLock lock(m_critSection);
+ return m_type;
+}
+
+bool CPVRProvider::SetType(PVR_PROVIDER_TYPE type)
+{
+ CSingleLock lock(m_critSection);
+ if (m_type != type)
+ {
+ m_type = type;
+ return true;
+ }
+
+ return false;
+}
+
+std::string CPVRProvider::GetIconPath() const
+{
+ CSingleLock lock(m_critSection);
+ return m_strIconPath;
+}
+
+bool CPVRProvider::SetIconPath(const std::string& strIconPath)
+{
+ CSingleLock lock(m_critSection);
+ if (m_strIconPath != strIconPath)
+ {
+ m_strIconPath = strIconPath;
+ return true;
+ }
+
+ return false;
+}
+
+namespace
+{
+
+const std::vector<std::string> Tokenize(const std::string& str)
+{
+ return StringUtils::Split(str.c_str(), PROVIDER_STRING_TOKEN_SEPARATOR);
+}
+
+const std::string DeTokenize(const std::vector<std::string>& tokens)
+{
+ return StringUtils::Join(tokens, PROVIDER_STRING_TOKEN_SEPARATOR);
+}
+
+} // unnamed namespace
+
+std::vector<std::string> CPVRProvider::GetCountries() const
+{
+ CSingleLock lock(m_critSection);
+
+ return Tokenize(m_strCountries);
+}
+
+bool CPVRProvider::SetCountries(const std::vector<std::string>& countries)
+{
+ CSingleLock lock(m_critSection);
+ const std::string strCountries = DeTokenize(countries);
+ if (m_strCountries != strCountries)
+ {
+ m_strCountries = strCountries;
+ return true;
+ }
+
+ return false;
+}
+
+std::string CPVRProvider::GetCountriesDBString() const
+{
+ CSingleLock lock(m_critSection);
+ return m_strCountries;
+}
+
+bool CPVRProvider::SetCountriesFromDBString(const std::string& strCountries)
+{
+ CSingleLock lock(m_critSection);
+ if (m_strCountries != strCountries)
+ {
+ m_strCountries = strCountries;
+ return true;
+ }
+
+ return false;
+}
+
+std::vector<std::string> CPVRProvider::GetLanguages() const
+{
+ CSingleLock lock(m_critSection);
+ return Tokenize(m_strLanguages);
+}
+
+bool CPVRProvider::SetLanguages(const std::vector<std::string>& languages)
+{
+ CSingleLock lock(m_critSection);
+ const std::string strLanguages = DeTokenize(languages);
+ if (m_strLanguages != strLanguages)
+ {
+ m_strLanguages = strLanguages;
+ return true;
+ }
+
+ return false;
+}
+
+std::string CPVRProvider::GetLanguagesDBString() const
+{
+ CSingleLock lock(m_critSection);
+ return m_strLanguages;
+}
+
+bool CPVRProvider::SetLanguagesFromDBString(const std::string& strLanguages)
+{
+ CSingleLock lock(m_critSection);
+ if (m_strLanguages != strLanguages)
+ {
+ m_strLanguages = strLanguages;
+ return true;
+ }
+
+ return false;
+}
+
+bool CPVRProvider::Persist(bool updateRecord /* = false */)
+{
+ const std::shared_ptr<CPVRDatabase> database = CServiceBroker::GetPVRManager().GetTVDatabase();
+ if (database)
+ {
+ CSingleLock lock(m_critSection);
+ return database->Persist(*this, updateRecord);
+ }
+
+ return false;
+}
+
+bool CPVRProvider::DeleteFromDatabase()
+{
+ const std::shared_ptr<CPVRDatabase> database = CServiceBroker::GetPVRManager().GetTVDatabase();
+ if (database)
+ {
+ CSingleLock lock(m_critSection);
+ return database->Delete(*this);
+ }
+
+ return false;
+}
+
+bool CPVRProvider::UpdateEntry(const std::shared_ptr<CPVRProvider>& fromProvider,
+ ProviderUpdateMode updateMode)
+{
+ bool bChanged = false;
+ CSingleLock lock(m_critSection);
+
+ if (updateMode == ProviderUpdateMode::BY_DATABASE)
+ {
+ m_iDatabaseId = fromProvider->m_iDatabaseId;
+
+ m_strName = fromProvider->m_strName;
+ m_type = fromProvider->m_type;
+ m_strIconPath = fromProvider->m_strIconPath;
+
+ if (fromProvider->m_bIsClientProvider)
+ {
+ m_strThumbPath = fromProvider->m_strThumbPath;
+ m_bIsClientProvider = fromProvider->m_bIsClientProvider;
+ }
+
+ m_strCountries = fromProvider->m_strCountries;
+ m_strLanguages = fromProvider->m_strLanguages;
+ }
+ else if (updateMode == ProviderUpdateMode::BY_CLIENT)
+ {
+ if (m_strName != fromProvider->m_strName)
+ {
+ m_strName = fromProvider->m_strName;
+ bChanged = true;
+ }
+
+ if (m_type != fromProvider->m_type)
+ {
+ m_type = fromProvider->m_type;
+ bChanged = true;
+ }
+
+ if (m_strIconPath != fromProvider->m_strIconPath)
+ {
+ m_strIconPath = fromProvider->m_strIconPath;
+ bChanged = true;
+ }
+
+ if (fromProvider->m_bIsClientProvider)
+ {
+ m_strThumbPath = fromProvider->m_strThumbPath;
+ m_bIsClientProvider = fromProvider->m_bIsClientProvider;
+ }
+
+ if (m_strCountries != fromProvider->m_strCountries)
+ {
+ m_strCountries = fromProvider->m_strCountries;
+ bChanged = true;
+ }
+
+ if (m_strLanguages != fromProvider->m_strLanguages)
+ {
+ m_strLanguages = fromProvider->m_strLanguages;
+ bChanged = true;
+ }
+ }
+
+ return bChanged;
+}
+
+bool CPVRProvider::HasThumbPath() const
+{
+ CSingleLock lock(m_critSection);
+ return (m_type == PVR_PROVIDER_TYPE_ADDON && !m_strThumbPath.empty());
+}
diff --git a/xbmc/pvr/providers/PVRProvider.h b/xbmc/pvr/providers/PVRProvider.h
new file mode 100644
index 0000000000..431fc2f555
--- /dev/null
+++ b/xbmc/pvr/providers/PVRProvider.h
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_providers.h"
+#include "threads/CriticalSection.h"
+#include "utils/ISerializable.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace PVR
+{
+
+enum class ProviderUpdateMode
+{
+ BY_CLIENT,
+ BY_DATABASE
+};
+
+static constexpr int PVR_PROVIDER_ADDON_UID = -1;
+static constexpr int PVR_PROVIDER_INVALID_DB_ID = -1;
+
+class CPVRProvider final : public ISerializable
+{
+public:
+ CPVRProvider(int iUniqueId, int iClientId);
+ CPVRProvider(const PVR_PROVIDER& provider, int iClientId);
+ CPVRProvider(int iClientId,
+ const std::string& addonProviderName,
+ const std::string& addonIconPath,
+ const std::string& addonThumbPath);
+
+ bool operator==(const CPVRProvider& right) const;
+ bool operator!=(const CPVRProvider& right) const;
+
+ void Serialize(CVariant& value) const override;
+
+ /*!
+ * @brief The database id of this provider
+ *
+ * A unique identifier for this provider.
+ * It can be used to find the same provider on this clients backend
+ *
+ * @return The database id of this provider
+ */
+ int GetDatabaseId() const;
+
+ /*!
+ * @brief Set the database id of this provider
+ * @param iDatabaseId The new ID.
+ * @return True if the something changed, false otherwise.
+ */
+ bool SetDatabaseId(int iDatabaseId);
+
+ /*!
+ * @brief A unique identifier for this provider.
+ *
+ * A unique identifier for this provider.
+ * It can be used to find the same provider on this clients backend
+ *
+ * @return The Unique ID.
+ */
+ int GetUniqueId() const;
+
+ /*!
+ * @return The identifier of the client that supplies this provider.
+ */
+ int GetClientId() const;
+
+ /*!
+ * @return The name of the provider. Can be user provided or the backend name
+ */
+ std::string GetName() const;
+
+ /*!
+ * @brief Set the name of the provider.
+ * @param name The new name of the provider.
+ * @return True if the something changed, false otherwise.
+ */
+ bool SetName(const std::string& iName);
+
+ /*!
+ * @brief Checks whether this provider has a known type
+ * @return True if this provider has a type other than unknown, false otherwise
+ */
+ bool HasType() const { return m_type != PVR_PROVIDER_TYPE_UNKNOWN; }
+
+ /*!
+ * @brief Gets the type of this provider.
+ * @return the type of this provider.
+ */
+ PVR_PROVIDER_TYPE GetType() const;
+
+ /*!
+ * @brief Sets the type of this provider.
+ * @param type the new provider type.
+ * @return True if the something changed, false otherwise.
+ */
+ bool SetType(PVR_PROVIDER_TYPE type);
+
+ /*!
+ * @brief Get the path for this provider's icon
+ * @return iconpath for this provider's icon
+ */
+ std::string GetIconPath() const;
+
+ /*!
+ * @brief Set the path for this icon
+ * @param strIconPath The new path of the icon.
+ * @return true if the icon path was updated successfully
+ */
+ bool SetIconPath(const std::string& strIconPath);
+
+ /*!
+ * @brief Get this provider's country codes (ISO 3166).
+ * @return This provider's country codes.
+ */
+ std::vector<std::string> GetCountries() const;
+
+ /*!
+ * @brief Set the country codes for this provider
+ * @param countries The new ISO 3166 country codes for this provider.
+ * @return true if the country codes were updated successfully
+ */
+ bool SetCountries(const std::vector<std::string>& countries);
+
+ /*!
+ * @brief Get this provider's country codes (ISO 3166) as a string.
+ * @return This provider's country codes.
+ */
+ std::string GetCountriesDBString() const;
+
+ /*!
+ * @brief Set the country codes for this provider from a string
+ * @param strCountries The new ISO 3166 country codes for this provider.
+ * @return true if the country codes were updated successfully
+ */
+ bool SetCountriesFromDBString(const std::string& strCountries);
+
+ /*!
+ * @brief Get this provider's language codes (RFC 5646).
+ * @return This provider's language codes
+ */
+ std::vector<std::string> GetLanguages() const;
+
+ /*!
+ * @brief Set the language codes for this provider
+ * @param languages The new RFC 5646 language codes for this provider.
+ * @return true if the language codes were updated successfully
+ */
+ bool SetLanguages(const std::vector<std::string>& languages);
+
+ /*!
+ * @brief Get this provider's language codes (RFC 5646) as a string.
+ * @return This provider's language codes.
+ */
+ std::string GetLanguagesDBString() const;
+
+ /*!
+ * @brief Set the language codes for this provider from a string
+ * @param strLanguages The new RFC 5646 language codes for this provider.
+ * @return true if the language codes were updated successfully
+ */
+ bool SetLanguagesFromDBString(const std::string& strLanguages);
+
+ /*!
+ * @brief Get if this provider has a thumb image path.
+ * @return True if this add-on provider has a thumb image path, false otherwise.
+ */
+ bool HasThumbPath() const;
+
+ /*!
+ * @brief Get this provider's thumb image path. Note only PVR add-on providers will set this value.
+ * @return This add-on provider's thumb image path.
+ */
+ std::string GetThumbPath() const { return m_strThumbPath; }
+
+ /*!
+ * @brief Whether a provider is a default provider of a PVR Client add-on or not
+ * @return True if this provider is of a PVR Client add-on, false otherwise.
+ */
+ bool IsClientProvider() const { return m_bIsClientProvider; }
+
+ /*!
+ * @brief updates this provider from the provided entry
+ * @param fromProvider A provider containing the data that shall be merged into this provider's data.
+ * @param updateMode update as User, Client or DB
+ * @return true if the provider was updated successfully
+ */
+ bool UpdateEntry(const std::shared_ptr<CPVRProvider>& fromProvider,
+ ProviderUpdateMode updateMode);
+
+ /*!
+ * @brief Persist this provider in the local database.
+ * @param updateRecord True if an existing record should be updated, false for an insert
+ * @return True on success, false otherwise.
+ */
+ bool Persist(bool updateRecord = false);
+
+ /*!
+ * @brief Delete this provider from the local database.
+ * @return True on success, false otherwise.
+ */
+ bool DeleteFromDatabase();
+
+private:
+ CPVRProvider(const CPVRProvider& provider) = delete;
+ CPVRProvider& operator=(const CPVRProvider& orig) = delete;
+
+ int m_iDatabaseId = PVR_PROVIDER_INVALID_DB_ID; /*!< the identifier given to this provider by the TV database */
+
+ int m_iUniqueId = PVR_PROVIDER_ADDON_UID; /*!< @brief unique ID of the provider on the backend */
+ int m_iClientId; /*!< @brief ID of the backend */
+ std::string m_strName; /*!< @brief name of this provider */
+ PVR_PROVIDER_TYPE m_type = PVR_PROVIDER_TYPE_UNKNOWN; /*!< @brief service type for this provider */
+ std::string m_strIconPath; /*!< @brief the path to the icon for this provider */
+ std::string m_strCountries; /*!< @brief the country codes for this provider (empty if undefined) */
+ std::string m_strLanguages; /*!< @brief the language codes for this provider (empty if undefined) */
+ bool m_bIsClientProvider = false; /*!< the provider is a default provider of a PVR Client add-on */
+ std::string m_strThumbPath; /*!< a thumb image path for providers that are PVR add-ons */
+
+ mutable CCriticalSection m_critSection;
+};
+} // namespace PVR
diff --git a/xbmc/pvr/providers/PVRProviders.cpp b/xbmc/pvr/providers/PVRProviders.cpp
new file mode 100644
index 0000000000..2cfe30674a
--- /dev/null
+++ b/xbmc/pvr/providers/PVRProviders.cpp
@@ -0,0 +1,384 @@
+/*
+ * 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/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 "threads/SingleLock.h"
+#include "utils/log.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+using namespace PVR;
+
+bool CPVRProvidersContainer::UpdateFromClient(const std::shared_ptr<CPVRProvider>& provider)
+{
+ CSingleLock lock(m_critSection);
+ const std::shared_ptr<CPVRProvider> 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<CPVRProvider> CPVRProvidersContainer::GetByClient(int iClientId,
+ int iUniqueId) const
+{
+ CSingleLock lock(m_critSection);
+ for (const auto& provider : m_providers)
+ {
+ if (provider->GetClientId() == iClientId && provider->GetUniqueId() == iUniqueId)
+ {
+ return provider;
+ }
+ }
+
+ return {};
+}
+
+void CPVRProvidersContainer::InsertEntry(const std::shared_ptr<CPVRProvider>& 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<std::shared_ptr<CPVRProvider>> CPVRProvidersContainer::GetProvidersList() const
+{
+ CSingleLock lock(m_critSection);
+ return m_providers;
+}
+
+
+bool CPVRProviders::Load()
+{
+ // unload previous providers
+ Unload();
+
+ // load providers from database
+ bool bReturn = LoadFromDatabase();
+
+ // update from clients
+ Update();
+
+ return bReturn;
+}
+
+void CPVRProviders::Unload()
+{
+ // remove all tags
+ CSingleLock lock(m_critSection);
+ m_providers.clear();
+}
+
+bool CPVRProviders::LoadFromDatabase()
+{
+ const std::shared_ptr<CPVRDatabase> database = CServiceBroker::GetPVRManager().GetTVDatabase();
+ if (database)
+ {
+ bool bChanged = false;
+
+ CPVRProviders providers;
+ database->Get(providers);
+
+ for (auto& provider : providers.GetProvidersList())
+ {
+ bChanged |= (CheckAndAddEntry(provider, ProviderUpdateMode::BY_DATABASE) != nullptr);
+ }
+ }
+ return true;
+}
+
+bool CPVRProviders::Update()
+{
+ {
+ CSingleLock lock(m_critSection);
+ if (m_bIsUpdating)
+ return false;
+ m_bIsUpdating = true;
+ }
+
+ // Default client providers are the add-ons themselves, we retrieve both enabled
+ // and disbled add-ons as we don't want them removed from the DB
+ CPVRProviders newAddonProviderList;
+ std::vector<int> disabledClients;
+ std::vector<CVariant> 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<CPVRProvider>(
+ clientInfo["clientid"].asInteger(), clientInfo["name"].asString(),
+ clientInfo["icon"].asString(), clientInfo["thumb"].asString());
+
+ newAddonProviderList.CheckAndAddEntry(addonProvider, ProviderUpdateMode::BY_CLIENT);
+
+ if (!clientInfo["enabled"].asBoolean())
+ disabledClients.emplace_back(clientInfo["clientid"].asInteger());
+ }
+ UpdateDefaultEntries(newAddonProviderList);
+
+ // Client providers are retrieved from the clients
+ CLog::LogFC(LOGDEBUG, LOGPVR, "Updating providers");
+ CPVRProvidersContainer newProviderList;
+ std::vector<int> failedClients;
+ CServiceBroker::GetPVRManager().Clients()->GetProviders(&newProviderList, failedClients);
+ return UpdateClientEntries(newProviderList, failedClients, disabledClients);
+}
+
+bool CPVRProviders::UpdateDefaultEntries(const CPVRProvidersContainer& newProviders)
+{
+ bool bChanged = false;
+
+ CSingleLock lock(m_critSection);
+
+ // go through the provider list and check for updated or new providers
+ for (const auto& newProvider : newProviders.GetProvidersList())
+ {
+ bChanged |= (CheckAndPersistEntry(newProvider, ProviderUpdateMode::BY_CLIENT) != nullptr);
+ }
+
+ // check for deleted providers
+ for (std::vector<std::shared_ptr<CPVRProvider>>::iterator it = m_providers.begin();
+ it != m_providers.end();)
+ {
+ const std::shared_ptr<CPVRProvider> 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<int>& failedClients,
+ const std::vector<int>& disabledClients)
+{
+ bool bChanged = false;
+
+ CSingleLock lock(m_critSection);
+
+ // go through the provider list and check for updated or new providers
+ for (const auto& newProvider : newProviders.GetProvidersList())
+ {
+ bChanged |= (CheckAndPersistEntry(newProvider, ProviderUpdateMode::BY_CLIENT) != nullptr);
+ }
+
+ // check for deleted providers
+ for (auto it = m_providers.begin(); it != m_providers.end();)
+ {
+ const std::shared_ptr<CPVRProvider> provider = *it;
+ if (!newProviders.GetByClient(provider->GetClientId(), provider->GetUniqueId()))
+ {
+ // provider was not found
+ bool bIgnoreProvider = false;
+ for (const auto& failedClient : failedClients)
+ {
+ if (failedClient == provider->GetClientId())
+ {
+ bIgnoreProvider = true;
+ break;
+ }
+ }
+
+ for (const auto& disabledClient : disabledClients)
+ {
+ if (disabledClient == provider->GetClientId())
+ {
+ bIgnoreProvider = true;
+ break;
+ }
+ }
+
+ // ignore add-on providers as they are a special case
+ 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;
+ }
+ }
+
+ m_bIsUpdating = false;
+
+ return bChanged;
+}
+
+std::shared_ptr<CPVRProvider> CPVRProviders::CheckAndAddEntry(
+ const std::shared_ptr<CPVRProvider>& newProvider, ProviderUpdateMode updateMode)
+{
+ bool bChanged = false;
+
+ CSingleLock lock(m_critSection);
+ std::shared_ptr<CPVRProvider> 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<CPVRProvider> CPVRProviders::CheckAndPersistEntry(
+ const std::shared_ptr<CPVRProvider>& newProvider, ProviderUpdateMode updateMode)
+{
+ bool bChanged = false;
+
+ CSingleLock lock(m_critSection);
+ std::shared_ptr<CPVRProvider> 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<std::shared_ptr<CPVRProvider>>& providers)
+{
+ for (auto provider : providers)
+ {
+ provider->Persist(true);
+
+ CLog::LogFC(LOGDEBUG, LOGPVR, "Updated provider {} on client {}", provider->GetUniqueId(),
+ provider->GetClientId());
+ }
+
+ return true;
+}
+
+std::shared_ptr<CPVRProvider> CPVRProviders::GetById(unsigned int iProviderId) const
+{
+ CSingleLock lock(m_critSection);
+ for (const auto& provider : m_providers)
+ {
+ if (provider->GetDatabaseId() == iProviderId)
+ return provider;
+ }
+
+ return {};
+}
+
+void CPVRProviders::RemoveEntry(const std::shared_ptr<CPVRProvider>& provider)
+{
+ CSingleLock lock(m_critSection);
+
+ std::remove_if(m_providers.begin(), m_providers.end(),
+ [&provider](const std::shared_ptr<CPVRProvider>& providerToRemove) {
+ return provider->GetClientId() == providerToRemove->GetClientId() &&
+ provider->GetUniqueId() == providerToRemove->GetUniqueId();
+ });
+}
diff --git a/xbmc/pvr/providers/PVRProviders.h b/xbmc/pvr/providers/PVRProviders.h
new file mode 100644
index 0000000000..03db930504
--- /dev/null
+++ b/xbmc/pvr/providers/PVRProviders.h
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "threads/CriticalSection.h"
+
+#include <memory>
+#include <vector>
+
+namespace PVR
+{
+class CPVRProvider;
+
+enum class ProviderUpdateMode;
+
+class CPVRProvidersContainer
+{
+public:
+ /*!
+ * @brief Add a provider to this container or update the provider if already present in this container.
+ * @param The provider
+ * @return True, if the update was successful. False, otherwise.
+ */
+ bool UpdateFromClient(const std::shared_ptr<CPVRProvider>& provider);
+
+ /*!
+ * @brief Get the provider denoted by given client id and unique client provider id.
+ * @param iClientId The client id.
+ * @param iUniqueId The client provider id.
+ * @return the provider if found, null otherwise.
+ */
+ std::shared_ptr<CPVRProvider> GetByClient(int iClientId, int iUniqueId) const;
+
+ /*!
+ * Get all providers in this container
+ * @return The list of all providers
+ */
+ std::vector<std::shared_ptr<CPVRProvider>> GetProvidersList() const;
+
+protected:
+ void InsertEntry(const std::shared_ptr<CPVRProvider>& newProvider, ProviderUpdateMode updateMode);
+
+ mutable CCriticalSection m_critSection;
+ unsigned int m_iLastId = 0;
+ std::vector<std::shared_ptr<CPVRProvider>> m_providers;
+};
+
+class CPVRProviders : public CPVRProvidersContainer
+{
+public:
+ CPVRProviders() = default;
+ ~CPVRProviders() = default;
+
+ /**
+ * @brief (re)load the providers from the clients.
+ * @return True if loaded successfully, false otherwise.
+ */
+ bool Load();
+
+ /**
+ * @brief unload all providers.
+ */
+ void Unload();
+
+ /**
+ * @brief refresh the providers list from the clients.
+ */
+ bool Update();
+
+ /**
+ * @brief load the local providers from database.
+ * @return True if loaded successfully, false otherwise.
+ */
+ bool LoadFromDatabase();
+
+ /*!
+ * @brief Check if the entry exists in the container, if it does update it otherwise add it
+ * @param newProvider The provider entry to update/add in/to the container
+ * @param updateMode update as Client (respect User set values) or DB (update all values)
+ * @return The provider if updated or added, otherwise an empty object (nullptr)
+ */
+ std::shared_ptr<CPVRProvider> CheckAndAddEntry(const std::shared_ptr<CPVRProvider>& newProvider,
+ ProviderUpdateMode updateMode);
+
+ /*!
+ * @brief Check if the entry exists in the container, if it does update it and persist
+ * it in the DB otherwise add it and persist it in the DB.
+ * @param newProvider The provider entry to update/add in/to the container and DB
+ * @param updateMode update as Client (respect User set values) or DB (update all values)
+ * @return The provider if updated or added, otherwise an empty object (nullptr)
+ */
+ std::shared_ptr<CPVRProvider> CheckAndPersistEntry(
+ const std::shared_ptr<CPVRProvider>& newProvider, ProviderUpdateMode updateMode);
+
+ /**
+ * @brief Persist user changes to the current state of the providers in the DB.
+ */
+ bool PersistUserChanges(const std::vector<std::shared_ptr<CPVRProvider>>& providers);
+
+ /*!
+ * @brief Get a provider given it's database ID
+ * @param iProviderId The ID to find
+ * @return The provider, or an empty one when not found
+ */
+ std::shared_ptr<CPVRProvider> GetById(unsigned int iProviderId) const;
+
+private:
+ void RemoveEntry(const std::shared_ptr<CPVRProvider>& provider);
+ bool UpdateDefaultEntries(const CPVRProvidersContainer& newProviders);
+ bool UpdateClientEntries(const CPVRProvidersContainer& newProviders,
+ const std::vector<int>& failedClients,
+ const std::vector<int>& disabledClients);
+
+ bool m_bIsUpdating = false;
+};
+} // namespace PVR
diff --git a/xbmc/windows/GUIWindowSystemInfo.cpp b/xbmc/windows/GUIWindowSystemInfo.cpp
index a9d847a47e..2e54d521ab 100644
--- a/xbmc/windows/GUIWindowSystemInfo.cpp
+++ b/xbmc/windows/GUIWindowSystemInfo.cpp
@@ -217,6 +217,8 @@ void CGUIWindowSystemInfo::FrameMove()
SetControlLabel(i++, "{}: {}", 19114, PVR_BACKEND_VERSION);
SetControlLabel(i++, "{}: {}", 19115, PVR_BACKEND_HOST);
SetControlLabel(i++, "{}: {}", 19116, PVR_BACKEND_DISKSPACE);
+ SetControlLabel(i++, "%s: %s", 19334, PVR_BACKEND_PROVIDERS);
+ SetControlLabel(i++, "%s: %s", 19042, PVR_BACKEND_CHANNEL_GROUPS);
SetControlLabel(i++, "{}: {}", 19019, PVR_BACKEND_CHANNELS);
SetControlLabel(i++, "{}: {}", 19163, PVR_BACKEND_RECORDINGS);
SetControlLabel(i++, "{}: {}", 19168,