diff options
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, |