From 07f51558d8a03d997d1f6df46938c97094f1393f Mon Sep 17 00:00:00 2001 From: ksooo <3226626+ksooo@users.noreply.github.com> Date: Wed, 5 Jun 2024 23:27:43 +0200 Subject: [PVR] Groups: Automatically create a channel group for every client, containing all channels provided by the client. Example: 2 clients enabled, create two groups 'Client 1 [All channels]' containing all channels from client 1 and 'Client 2 [All channels]' containing all channels from client 2. --- .../resource.language.en_gb/resources/strings.po | 8 +- xbmc/pvr/channels/CMakeLists.txt | 2 + xbmc/pvr/channels/PVRChannelGroup.h | 16 +++- xbmc/pvr/channels/PVRChannelGroupAllChannels.h | 2 +- .../PVRChannelGroupAllChannelsSingleClient.cpp | 95 ++++++++++++++++++++++ .../PVRChannelGroupAllChannelsSingleClient.h | 95 ++++++++++++++++++++++ xbmc/pvr/channels/PVRChannelGroupFactory.cpp | 31 +++++-- xbmc/pvr/channels/PVRChannelGroupFactory.h | 11 +++ xbmc/pvr/channels/PVRChannelGroups.cpp | 37 +++++++-- xbmc/pvr/channels/PVRChannelGroups.h | 2 + 10 files changed, 286 insertions(+), 13 deletions(-) create mode 100644 xbmc/pvr/channels/PVRChannelGroupAllChannelsSingleClient.cpp create mode 100644 xbmc/pvr/channels/PVRChannelGroupAllChannelsSingleClient.h diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po index 53523fc9e6..ab0a84adac 100644 --- a/addons/resource.language.en_gb/resources/strings.po +++ b/addons/resource.language.en_gb/resources/strings.po @@ -3996,7 +3996,13 @@ msgctxt "#858" msgid "User" msgstr "" -#empty strings from id 859 to 996 +#. Label postfix for channel groups containing content from all channels (e.g. "PVR Client 1 [All channels]") +#: xbmc/pvr/channels/PVRChannelGroupAllChannelsSingleClient.cpp +msgctxt "#859" +msgid "{0:s} [All channels]" +msgstr "" + +#empty strings from id 860 to 996 #: xbmc/windows/GUIMediaWindow.cpp msgctxt "#997" diff --git a/xbmc/pvr/channels/CMakeLists.txt b/xbmc/pvr/channels/CMakeLists.txt index c1eb19f4df..7094e029e6 100644 --- a/xbmc/pvr/channels/CMakeLists.txt +++ b/xbmc/pvr/channels/CMakeLists.txt @@ -2,6 +2,7 @@ set(SOURCES PVRChannel.cpp PVRChannelGroup.cpp PVRChannelGroupAllChannels.cpp PVRChannelGroupFactory.cpp + PVRChannelGroupAllChannelsSingleClient.cpp PVRChannelGroupFromClient.cpp PVRChannelGroupMember.cpp PVRChannelGroupSettings.cpp @@ -15,6 +16,7 @@ set(HEADERS PVRChannel.h PVRChannelGroup.h PVRChannelGroupAllChannels.h PVRChannelGroupFactory.h + PVRChannelGroupAllChannelsSingleClient.h PVRChannelGroupFromClient.h PVRChannelGroupFromUser.h PVRChannelGroupMember.h diff --git a/xbmc/pvr/channels/PVRChannelGroup.h b/xbmc/pvr/channels/PVRChannelGroup.h index 4582a273a3..af521abf98 100644 --- a/xbmc/pvr/channels/PVRChannelGroup.h +++ b/xbmc/pvr/channels/PVRChannelGroup.h @@ -25,8 +25,9 @@ struct PVR_CHANNEL_GROUP; namespace PVR { static constexpr int PVR_GROUP_TYPE_CLIENT = 0; -static constexpr int PVR_GROUP_TYPE_SYSTEM_ALL_CHANNELS = 1; +static constexpr int PVR_GROUP_TYPE_SYSTEM_ALL_CHANNELS_ALL_CLIENTS = 1; static constexpr int PVR_GROUP_TYPE_USER = 2; +static constexpr int PVR_GROUP_TYPE_SYSTEM_ALL_CHANNELS_SINGLE_CLIENT = 3; static constexpr int PVR_GROUP_CLIENT_ID_UNKNOWN = -2; static constexpr int PVR_GROUP_CLIENT_ID_LOCAL = -1; @@ -526,6 +527,19 @@ public: virtual bool ShouldBeIgnored( const std::vector>& allChannelGroups) const; + /*! + * @brief Update all group members. + * @param allChannelsGroup The all channels group. + * @param allChannelGroups All available channel groups. + * @return True on success, false otherwise. + */ + virtual bool UpdateGroupMembers( + const std::shared_ptr& allChannelsGroup, + const std::vector>& allChannelGroups) + { + return true; + } + protected: /*! * @brief Remove deleted group members from this group. diff --git a/xbmc/pvr/channels/PVRChannelGroupAllChannels.h b/xbmc/pvr/channels/PVRChannelGroupAllChannels.h index a51aadae76..53e166c830 100644 --- a/xbmc/pvr/channels/PVRChannelGroupAllChannels.h +++ b/xbmc/pvr/channels/PVRChannelGroupAllChannels.h @@ -62,7 +62,7 @@ public: /*! * @brief Return the type of this group. */ - int GroupType() const override { return PVR_GROUP_TYPE_SYSTEM_ALL_CHANNELS; } + int GroupType() const override { return PVR_GROUP_TYPE_SYSTEM_ALL_CHANNELS_ALL_CLIENTS; } /*! * @brief Check whether this group could be deleted by the user. diff --git a/xbmc/pvr/channels/PVRChannelGroupAllChannelsSingleClient.cpp b/xbmc/pvr/channels/PVRChannelGroupAllChannelsSingleClient.cpp new file mode 100644 index 0000000000..40b1649ecc --- /dev/null +++ b/xbmc/pvr/channels/PVRChannelGroupAllChannelsSingleClient.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2024 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 "PVRChannelGroupAllChannelsSingleClient.h" + +#include "ServiceBroker.h" +#include "guilib/LocalizeStrings.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClient.h" +#include "pvr/channels/PVRChannelGroupMember.h" +#include "pvr/channels/PVRChannelsPath.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +#include +#include + +using namespace PVR; + +std::vector> CPVRChannelGroupAllChannelsSingleClient:: + CreateMissingGroups(const std::shared_ptr& allChannelsGroup, + const std::vector>& allChannelGroups) +{ + const std::vector> allGroupMembers{ + allChannelsGroup->GetMembers()}; + + // Create a unique list of active client ids from current members of the all channels list. + std::unordered_set clientIds; + for (const auto& member : allGroupMembers) + { + clientIds.insert(member->ChannelClientID()); + } + + // If only one client is active, global all channels group would be identical to the all + // channels group for the client. No need to create it. + if (clientIds.size() <= 1) + return {}; + + std::vector> addedGroups; + + for (int clientId : clientIds) + { + const std::shared_ptr client{ + CServiceBroker().GetPVRManager().GetClient(clientId)}; + if (!client) + { + CLog::LogFC(LOGERROR, LOGPVR, "Failed to get client instance for client id {}", clientId); + continue; + } + + // Create a group containg all channels for this client, if not yet existing. + const auto it = std::find_if(allChannelGroups.cbegin(), allChannelGroups.cend(), + [&client](const auto& group) + { + return (group->GroupType() == + PVR_GROUP_TYPE_SYSTEM_ALL_CHANNELS_SINGLE_CLIENT) && + (group->GetClientID() == client->GetID()); + }); + if (it == allChannelGroups.cend()) + { + const std::string name{ + StringUtils::Format(g_localizeStrings.Get(859), client->GetFullClientName())}; + const CPVRChannelsPath path{allChannelsGroup->IsRadio(), name, client->GetID()}; + addedGroups.emplace_back( + std::make_shared(path, allChannelsGroup)); + } + } + + return addedGroups; +} + +bool CPVRChannelGroupAllChannelsSingleClient::UpdateGroupMembers( + const std::shared_ptr& allChannelsGroup, + const std::vector>& allChannelGroups) +{ + std::vector> groupMembers; + + // Collect and populate matching members. + const auto allChannelsGroupMembers{allChannelsGroup->GetMembers()}; + for (const auto& member : allChannelsGroupMembers) + { + if (member->ChannelClientID() != GetClientID()) + continue; + + groupMembers.emplace_back(std::make_shared( + GroupID(), GroupName(), GetClientID(), member->Channel())); + } + + return UpdateGroupEntries(groupMembers); +} diff --git a/xbmc/pvr/channels/PVRChannelGroupAllChannelsSingleClient.h b/xbmc/pvr/channels/PVRChannelGroupAllChannelsSingleClient.h new file mode 100644 index 0000000000..9e096c9a27 --- /dev/null +++ b/xbmc/pvr/channels/PVRChannelGroupAllChannelsSingleClient.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2024 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 "pvr/channels/PVRChannelGroup.h" + +namespace PVR +{ + +class CPVRChannelGroupAllChannelsSingleClient : public CPVRChannelGroup +{ +public: + /*! + * @brief Create a new channel group instance. + * @param path The channel group path. + * @param allChannelsGroup The channel group containing all TV or radio channels. + */ + CPVRChannelGroupAllChannelsSingleClient( + const CPVRChannelsPath& path, const std::shared_ptr& allChannelsGroup) + : CPVRChannelGroup(path, allChannelsGroup) + { + } + + /*! + * @brief Get the group's origin. + * @return The origin. + */ + Origin GetOrigin() const override { return Origin::SYSTEM; } + + /*! + * @brief Return the type of this group. + */ + int GroupType() const override { return PVR_GROUP_TYPE_SYSTEM_ALL_CHANNELS_SINGLE_CLIENT; } + + /*! + * @brief Check whether this group could be deleted by the user. + * @return True if the group could be deleted, false otherwise. + */ + bool SupportsDelete() const override { return false; } + + /*! + * @brief Check whether members could be added to this group by the user. + * @return True if members could be added, false otherwise. + */ + bool SupportsMemberAdd() const override { return false; } + + /*! + * @brief Check whether members could be removed from this group by the user. + * @return True if members could be removed, false otherwise. + */ + bool SupportsMemberRemove() const override { return false; } + + /*! + * @brief Check whether this group is owner of the channel instances it contains. + * @return True if owner, false otherwise. + */ + bool IsChannelsOwner() const override { return false; } + + /*! + * @brief Update data with channel group members from the given clients, sync with local data. + * @param clients The clients to fetch data from. Leave empty to fetch data from all created clients. + * @return True on success, false otherwise. + */ + bool UpdateFromClients(const std::vector>& clients) override + { + return true; // Nothing to update from any client for locally managed groups. + } + + /*! + * @brief Create any missing channel groups (e.g. after an update of groups/members/clients). + * @param allChannelsGroup The all channels group. + * @param allChannelGroups All channel groups. + * @return The newly created groups, if any. + */ + static std::vector> CreateMissingGroups( + const std::shared_ptr& allChannelsGroup, + const std::vector>& allChannelGroups); + + /*! + * @brief Update all group members. + * @param allChannelsGroup The all channels group. + * @param allChannelGroups All available channel groups. + * @return True on success, false otherwise. + */ + bool UpdateGroupMembers( + const std::shared_ptr& allChannelsGroup, + const std::vector>& allChannelGroups) override; +}; +} // namespace PVR diff --git a/xbmc/pvr/channels/PVRChannelGroupFactory.cpp b/xbmc/pvr/channels/PVRChannelGroupFactory.cpp index a895b53554..0eec8b5e08 100644 --- a/xbmc/pvr/channels/PVRChannelGroupFactory.cpp +++ b/xbmc/pvr/channels/PVRChannelGroupFactory.cpp @@ -9,6 +9,7 @@ #include "PVRChannelGroupFactory.h" #include "pvr/channels/PVRChannelGroupAllChannels.h" +#include "pvr/channels/PVRChannelGroupAllChannelsSingleClient.h" #include "pvr/channels/PVRChannelGroupFromClient.h" #include "pvr/channels/PVRChannelGroupFromUser.h" #include "pvr/channels/PVRChannelsPath.h" @@ -45,10 +46,12 @@ std::shared_ptr CPVRChannelGroupFactory::CreateGroup( { switch (groupType) { - case PVR_GROUP_TYPE_SYSTEM_ALL_CHANNELS: + case PVR_GROUP_TYPE_SYSTEM_ALL_CHANNELS_ALL_CLIENTS: return std::make_shared(groupPath); case PVR_GROUP_TYPE_USER: return std::make_shared(groupPath, allChannels); + case PVR_GROUP_TYPE_SYSTEM_ALL_CHANNELS_SINGLE_CLIENT: + return std::make_shared(groupPath, allChannels); case PVR_GROUP_TYPE_CLIENT: return std::make_shared(groupPath, allChannels); default: @@ -63,13 +66,31 @@ int CPVRChannelGroupFactory::GetGroupTypePriority( { switch (group->GroupType()) { - case PVR_GROUP_TYPE_SYSTEM_ALL_CHANNELS: + // System groups, created and managed by Kodi + case PVR_GROUP_TYPE_SYSTEM_ALL_CHANNELS_ALL_CLIENTS: return 0; // highest - case PVR_GROUP_TYPE_USER: + case PVR_GROUP_TYPE_SYSTEM_ALL_CHANNELS_SINGLE_CLIENT: return 1; + + // User groups, created and managed by the user + case PVR_GROUP_TYPE_USER: + return 20; + + // Client groups, created and managed by a PVR client add-on case PVR_GROUP_TYPE_CLIENT: - return 2; + return 40; + default: - return 3; + CLog::LogFC(LOGWARNING, LOGPVR, "Using default priority for group '{}' with type {}'.", + group->GroupName(), group->GroupType()); + return 60; } } + +std::vector> CPVRChannelGroupFactory::CreateMissingGroups( + const std::shared_ptr& allChannelsGroup, + const std::vector>& allChannelGroups) +{ + return CPVRChannelGroupAllChannelsSingleClient::CreateMissingGroups(allChannelsGroup, + allChannelGroups); +} diff --git a/xbmc/pvr/channels/PVRChannelGroupFactory.h b/xbmc/pvr/channels/PVRChannelGroupFactory.h index 569eb9b332..8f126b09d3 100644 --- a/xbmc/pvr/channels/PVRChannelGroupFactory.h +++ b/xbmc/pvr/channels/PVRChannelGroupFactory.h @@ -9,6 +9,7 @@ #pragma once #include +#include struct PVR_CHANNEL_GROUP; @@ -73,5 +74,15 @@ public: * @return The group's type priority. */ int GetGroupTypePriority(const std::shared_ptr& group) const; + + /*! + * @brief Create any missing channel groups (e.g. after an update of groups/members/clients). + * @param allChannelsGroup The all channels group. + * @param allChannelGroups All channel groups. + * @return The newly created groups, if any. + */ + std::vector> CreateMissingGroups( + const std::shared_ptr& allChannelsGroup, + const std::vector>& allChannelGroups); }; } // namespace PVR diff --git a/xbmc/pvr/channels/PVRChannelGroups.cpp b/xbmc/pvr/channels/PVRChannelGroups.cpp index 45008d81dd..97033d73fd 100644 --- a/xbmc/pvr/channels/PVRChannelGroups.cpp +++ b/xbmc/pvr/channels/PVRChannelGroups.cpp @@ -359,15 +359,15 @@ bool CPVRChannelGroups::UpdateFromClients(const std::vectorGroupName()); bReturn = false; } - } - if (bReturn && group->IsChannelsOwner() && - CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bPVRChannelIconsAutoScan) - { - CServiceBroker::GetPVRManager().TriggerSearchMissingChannelIcons(group); + if (bReturn && group->IsChannelsOwner() && + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bPVRChannelIconsAutoScan) + CServiceBroker::GetPVRManager().TriggerSearchMissingChannelIcons(group); } } + UpdateSystemChannelGroups(); + if (bChannelsOnly) { // changes in the all channels group may require resorting/renumbering of other groups. @@ -381,6 +381,32 @@ bool CPVRChannelGroups::UpdateFromClients(const std::vector lock(m_critSection); + + // Update existing groups + for (const auto& group : m_groups) + { + group->UpdateGroupMembers(GetGroupAll(), m_groups); + } + + // Determine new groups needed + const std::vector> newGroups{ + GetGroupFactory()->CreateMissingGroups(GetGroupAll(), m_groups)}; + + // Update new groups + for (const auto& group : newGroups) + { + group->UpdateGroupMembers(GetGroupAll(), m_groups); + } + + if (!newGroups.empty()) + m_groups.insert(m_groups.end(), newGroups.cbegin(), newGroups.cend()); + + SortGroups(); +} + bool CPVRChannelGroups::UpdateChannelNumbersFromAllChannelsGroup() { std::unique_lock lock(m_critSection); @@ -616,6 +642,7 @@ void CPVRChannelGroups::GroupStateChanged(const std::shared_ptrPersist(); + UpdateSystemChannelGroups(); CServiceBroker::GetPVRManager().PublishEvent(PVREvent::ChannelGroupsInvalidated); } diff --git a/xbmc/pvr/channels/PVRChannelGroups.h b/xbmc/pvr/channels/PVRChannelGroups.h index 8df9c5c525..b14a602611 100644 --- a/xbmc/pvr/channels/PVRChannelGroups.h +++ b/xbmc/pvr/channels/PVRChannelGroups.h @@ -294,6 +294,8 @@ private: void GroupStateChanged(const std::shared_ptr& group, GroupState state = GroupState::CHANGED); + void UpdateSystemChannelGroups(); + bool m_bRadio{false}; std::vector> m_groups; mutable CCriticalSection m_critSection; -- cgit v1.2.3