1 /*
2  *  Copyright (C) 2012-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 //! @todo use Observable here, so we can use event driven operations later
10 
11 #include "PVRChannelGroup.h"
12 
13 #include "ServiceBroker.h"
14 #include "addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_channel_groups.h"
15 #include "pvr/PVRDatabase.h"
16 #include "pvr/PVRManager.h"
17 #include "pvr/addons/PVRClient.h"
18 #include "pvr/addons/PVRClients.h"
19 #include "pvr/channels/PVRChannel.h"
20 #include "pvr/channels/PVRChannelsPath.h"
21 #include "pvr/epg/Epg.h"
22 #include "pvr/epg/EpgChannelData.h"
23 #include "pvr/epg/EpgInfoTag.h"
24 #include "settings/Settings.h"
25 #include "settings/SettingsComponent.h"
26 #include "settings/lib/Setting.h"
27 #include "threads/SingleLock.h"
28 #include "utils/StringUtils.h"
29 #include "utils/log.h"
30 
31 #include <algorithm>
32 #include <memory>
33 #include <string>
34 #include <utility>
35 #include <vector>
36 
37 using namespace PVR;
38 
CPVRChannelGroup(const CPVRChannelsPath & path,int iGroupId,const std::shared_ptr<CPVRChannelGroup> & allChannelsGroup)39 CPVRChannelGroup::CPVRChannelGroup(const CPVRChannelsPath& path,
40                                    int iGroupId /* = INVALID_GROUP_ID */,
41                                    const std::shared_ptr<CPVRChannelGroup>& allChannelsGroup /* = {} */)
42   : m_iGroupId(iGroupId)
43   , m_allChannelsGroup(allChannelsGroup)
44   , m_path(path)
45 {
46   OnInit();
47 }
48 
CPVRChannelGroup(const PVR_CHANNEL_GROUP & group,const std::shared_ptr<CPVRChannelGroup> & allChannelsGroup)49 CPVRChannelGroup::CPVRChannelGroup(const PVR_CHANNEL_GROUP& group,
50                                    const std::shared_ptr<CPVRChannelGroup>& allChannelsGroup)
51   : m_iPosition(group.iPosition)
52   , m_allChannelsGroup(allChannelsGroup)
53   , m_path(group.bIsRadio, group.strGroupName)
54 {
55   OnInit();
56 }
57 
~CPVRChannelGroup()58 CPVRChannelGroup::~CPVRChannelGroup()
59 {
60   CServiceBroker::GetSettingsComponent()->GetSettings()->UnregisterCallback(this);
61   Unload();
62 }
63 
operator ==(const CPVRChannelGroup & right) const64 bool CPVRChannelGroup::operator==(const CPVRChannelGroup& right) const
65 {
66   return (m_iGroupType == right.m_iGroupType &&
67           m_iGroupId == right.m_iGroupId &&
68           m_iPosition == right.m_iPosition &&
69           m_path == right.m_path);
70 }
71 
operator !=(const CPVRChannelGroup & right) const72 bool CPVRChannelGroup::operator!=(const CPVRChannelGroup& right) const
73 {
74   return !(*this == right);
75 }
76 
77 std::shared_ptr<PVRChannelGroupMember> CPVRChannelGroup::EmptyMember = std::make_shared<PVRChannelGroupMember>();
78 
OnInit()79 void CPVRChannelGroup::OnInit()
80 {
81   CServiceBroker::GetSettingsComponent()->GetSettings()->RegisterCallback(this, {
82     CSettings::SETTING_PVRMANAGER_BACKENDCHANNELORDER,
83     CSettings::SETTING_PVRMANAGER_USEBACKENDCHANNELNUMBERS,
84     CSettings::SETTING_PVRMANAGER_USEBACKENDCHANNELNUMBERSALWAYS,
85     CSettings::SETTING_PVRMANAGER_STARTGROUPCHANNELNUMBERSFROMONE
86   });
87 }
88 
89 namespace
90 {
91 
UsingBackendChannelNumbers(const std::shared_ptr<CSettings> & settings)92 bool UsingBackendChannelNumbers(const std::shared_ptr<CSettings>& settings)
93 {
94   int enabledClientAmount = CServiceBroker::GetPVRManager().Clients()->EnabledClientAmount();
95   return settings->GetBool(CSettings::SETTING_PVRMANAGER_USEBACKENDCHANNELNUMBERS) &&
96          (enabledClientAmount == 1 ||
97           (settings->GetBool(CSettings::SETTING_PVRMANAGER_USEBACKENDCHANNELNUMBERSALWAYS) &&
98            enabledClientAmount > 1));
99 }
100 
101 } // unnamed namespace
102 
Load(std::vector<std::shared_ptr<CPVRChannel>> & channelsToRemove)103 bool CPVRChannelGroup::Load(std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove)
104 {
105   /* make sure this container is empty before loading */
106   Unload();
107 
108   const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
109   m_bSyncChannelGroups = settings->GetBool(CSettings::SETTING_PVRMANAGER_SYNCCHANNELGROUPS);
110   m_bUsingBackendChannelOrder = settings->GetBool(CSettings::SETTING_PVRMANAGER_BACKENDCHANNELORDER);
111   m_bUsingBackendChannelNumbers = UsingBackendChannelNumbers(settings);
112   m_bStartGroupChannelNumbersFromOne = settings->GetBool(CSettings::SETTING_PVRMANAGER_STARTGROUPCHANNELNUMBERSFROMONE) && !m_bUsingBackendChannelNumbers;
113 
114   int iChannelCount = m_iGroupId > 0 ? LoadFromDb() : 0;
115   CLog::LogFC(LOGDEBUG, LOGPVR, "{} channels loaded from the database for group '{}'",
116               iChannelCount, GroupName());
117 
118   if (!Update(channelsToRemove))
119   {
120     CLog::LogF(LOGERROR, "Failed to update channels for group '{}'", GroupName());
121     return false;
122   }
123 
124   if (Size() - iChannelCount > 0)
125   {
126     CLog::LogFC(LOGDEBUG, LOGPVR, "{} channels added from clients to group '{}'",
127                 static_cast<int>(Size() - iChannelCount), GroupName());
128   }
129 
130   SortAndRenumber();
131 
132   m_bLoaded = true;
133 
134   return true;
135 }
136 
Unload()137 void CPVRChannelGroup::Unload()
138 {
139   CSingleLock lock(m_critSection);
140   m_sortedMembers.clear();
141   m_members.clear();
142   m_failedClients.clear();
143 }
144 
Update(std::vector<std::shared_ptr<CPVRChannel>> & channelsToRemove)145 bool CPVRChannelGroup::Update(std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove)
146 {
147   if (GroupType() == PVR_GROUP_TYPE_USER_DEFINED ||
148       !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_PVRMANAGER_SYNCCHANNELGROUPS))
149     return true;
150 
151   CPVRChannelGroup PVRChannels_tmp(m_path, m_iGroupId, m_allChannelsGroup);
152   PVRChannels_tmp.SetPreventSortAndRenumber();
153   PVRChannels_tmp.LoadFromClients();
154   m_failedClients = PVRChannels_tmp.m_failedClients;
155   return UpdateGroupEntries(PVRChannels_tmp, channelsToRemove);
156 }
157 
GetPath() const158 const CPVRChannelsPath& CPVRChannelGroup::GetPath() const
159 {
160   CSingleLock lock(m_critSection);
161   return m_path;
162 }
163 
SetPath(const CPVRChannelsPath & path)164 void CPVRChannelGroup::SetPath(const CPVRChannelsPath& path)
165 {
166   CSingleLock lock(m_critSection);
167   if (m_path != path)
168   {
169     m_path = path;
170     m_bChanged = true;
171 
172     if (m_bLoaded)
173       Persist();
174   }
175 }
176 
SetChannelNumber(const std::shared_ptr<CPVRChannel> & channel,const CPVRChannelNumber & channelNumber)177 bool CPVRChannelGroup::SetChannelNumber(const std::shared_ptr<CPVRChannel>& channel, const CPVRChannelNumber& channelNumber)
178 {
179   bool bReturn(false);
180   CSingleLock lock(m_critSection);
181 
182   for (auto& member : m_sortedMembers)
183   {
184     if (*member->channel == *channel)
185     {
186       if (member->channelNumber != channelNumber)
187       {
188         m_bChanged = true;
189         bReturn = true;
190         member->channelNumber = channelNumber;
191       }
192       break;
193     }
194   }
195 
196   return bReturn;
197 }
198 
199 /********** sort methods **********/
200 
201 struct sortByClientChannelNumber
202 {
operator ()sortByClientChannelNumber203   bool operator()(const std::shared_ptr<PVRChannelGroupMember>& channel1, const std::shared_ptr<PVRChannelGroupMember>& channel2) const
204   {
205     if (channel1->iClientPriority == channel2->iClientPriority)
206     {
207       if (channel1->clientChannelNumber == channel2->clientChannelNumber)
208         return channel1->channel->ChannelName() < channel2->channel->ChannelName();
209 
210       return channel1->clientChannelNumber < channel2->clientChannelNumber;
211     }
212     return channel1->iClientPriority > channel2->iClientPriority;
213   }
214 };
215 
216 struct sortByChannelNumber
217 {
operator ()sortByChannelNumber218   bool operator()(const std::shared_ptr<PVRChannelGroupMember>& channel1, const std::shared_ptr<PVRChannelGroupMember>& channel2) const
219   {
220     return channel1->channelNumber < channel2->channelNumber;
221   }
222 };
223 
Sort()224 void CPVRChannelGroup::Sort()
225 {
226   if (m_bUsingBackendChannelOrder)
227     SortByClientChannelNumber();
228   else
229     SortByChannelNumber();
230 }
231 
SortAndRenumber()232 bool CPVRChannelGroup::SortAndRenumber()
233 {
234   if (PreventSortAndRenumber())
235     return true;
236 
237   CSingleLock lock(m_critSection);
238   Sort();
239 
240   bool bReturn = Renumber();
241   return bReturn;
242 }
243 
SortByClientChannelNumber()244 void CPVRChannelGroup::SortByClientChannelNumber()
245 {
246   CSingleLock lock(m_critSection);
247   if (!PreventSortAndRenumber())
248     sort(m_sortedMembers.begin(), m_sortedMembers.end(), sortByClientChannelNumber());
249 }
250 
SortByChannelNumber()251 void CPVRChannelGroup::SortByChannelNumber()
252 {
253   CSingleLock lock(m_critSection);
254   if (!PreventSortAndRenumber())
255     sort(m_sortedMembers.begin(), m_sortedMembers.end(), sortByChannelNumber());
256 }
257 
UpdateClientPriorities()258 bool CPVRChannelGroup::UpdateClientPriorities()
259 {
260   const std::shared_ptr<CPVRClients> clients = CServiceBroker::GetPVRManager().Clients();
261   bool bChanged = false;
262 
263   CSingleLock lock(m_critSection);
264 
265   for (auto& member : m_sortedMembers)
266   {
267     int iNewPriority = 0;
268 
269     if (m_bUsingBackendChannelOrder)
270     {
271       std::shared_ptr<CPVRClient> client;
272       if (!clients->GetCreatedClient(member->channel->ClientID(), client))
273         continue;
274 
275       iNewPriority = client->GetPriority();
276     }
277     else
278     {
279       iNewPriority = 0;
280     }
281 
282     bChanged |= (member->iClientPriority != iNewPriority);
283     member->iClientPriority = iNewPriority;
284   }
285 
286   return bChanged;
287 }
288 
289 /********** getters **********/
GetByUniqueID(const std::pair<int,int> & id)290 std::shared_ptr<PVRChannelGroupMember>& CPVRChannelGroup::GetByUniqueID(const std::pair<int, int>& id)
291 {
292   CSingleLock lock(m_critSection);
293   const auto it = m_members.find(id);
294   return it != m_members.end() ? it->second : CPVRChannelGroup::EmptyMember;
295 }
296 
GetByUniqueID(const std::pair<int,int> & id) const297 const std::shared_ptr<PVRChannelGroupMember>& CPVRChannelGroup::GetByUniqueID(const std::pair<int, int>& id) const
298 {
299   CSingleLock lock(m_critSection);
300   const auto it = m_members.find(id);
301   return it != m_members.end() ? it->second : CPVRChannelGroup::EmptyMember;
302 }
303 
GetByUniqueID(int iUniqueChannelId,int iClientID) const304 std::shared_ptr<CPVRChannel> CPVRChannelGroup::GetByUniqueID(int iUniqueChannelId, int iClientID) const
305 {
306   return GetByUniqueID(std::make_pair(iClientID, iUniqueChannelId))->channel;
307 }
308 
GetByChannelID(int iChannelID) const309 std::shared_ptr<CPVRChannel> CPVRChannelGroup::GetByChannelID(int iChannelID) const
310 {
311   CSingleLock lock(m_critSection);
312 
313   for (const auto& memberPair : m_members)
314   {
315     if (memberPair.second->channel->ChannelID() == iChannelID)
316       return memberPair.second->channel;
317   }
318 
319   return {};
320 }
321 
GetByChannelEpgID(int iEpgID) const322 std::shared_ptr<CPVRChannel> CPVRChannelGroup::GetByChannelEpgID(int iEpgID) const
323 {
324   CSingleLock lock(m_critSection);
325 
326   for (const auto& memberPair : m_members)
327   {
328     if (memberPair.second->channel->EpgID() == iEpgID)
329       return memberPair.second->channel;
330   }
331 
332   return {};
333 }
334 
GetLastPlayedChannel(int iCurrentChannel) const335 std::shared_ptr<CPVRChannel> CPVRChannelGroup::GetLastPlayedChannel(int iCurrentChannel /* = -1 */) const
336 {
337   CSingleLock lock(m_critSection);
338 
339   std::shared_ptr<CPVRChannel> returnChannel, channel;
340   for (const auto& memberPair : m_members)
341   {
342     channel = memberPair.second->channel;
343     if (channel->ChannelID() != iCurrentChannel &&
344         CServiceBroker::GetPVRManager().Clients()->IsCreatedClient(channel->ClientID()) &&
345         channel->LastWatched() > 0 &&
346         (!returnChannel || channel->LastWatched() > returnChannel->LastWatched()))
347     {
348       returnChannel = channel;
349     }
350   }
351 
352   return returnChannel;
353 }
354 
GetChannelNumber(const std::shared_ptr<CPVRChannel> & channel) const355 CPVRChannelNumber CPVRChannelGroup::GetChannelNumber(const std::shared_ptr<CPVRChannel>& channel) const
356 {
357   CSingleLock lock(m_critSection);
358   const std::shared_ptr<PVRChannelGroupMember>& member = GetByUniqueID(channel->StorageId());
359   return member->channelNumber;
360 }
361 
GetClientChannelNumber(const std::shared_ptr<CPVRChannel> & channel) const362 CPVRChannelNumber CPVRChannelGroup::GetClientChannelNumber(const std::shared_ptr<CPVRChannel>& channel) const
363 {
364   CSingleLock lock(m_critSection);
365   const std::shared_ptr<PVRChannelGroupMember>& member = GetByUniqueID(channel->StorageId());
366   return member->clientChannelNumber;
367 }
368 
GetByChannelNumber(const CPVRChannelNumber & channelNumber) const369 std::shared_ptr<CPVRChannel> CPVRChannelGroup::GetByChannelNumber(const CPVRChannelNumber& channelNumber) const
370 {
371   CSingleLock lock(m_critSection);
372 
373   for (const auto& member : m_sortedMembers)
374   {
375     CPVRChannelNumber activeChannelNumber = m_bUsingBackendChannelNumbers ? member->clientChannelNumber : member->channelNumber;
376     if (activeChannelNumber == channelNumber)
377       return member->channel;
378   }
379 
380   return {};
381 }
382 
GetNextChannel(const std::shared_ptr<CPVRChannel> & channel) const383 std::shared_ptr<CPVRChannel> CPVRChannelGroup::GetNextChannel(const std::shared_ptr<CPVRChannel>& channel) const
384 {
385   std::shared_ptr<CPVRChannel> nextChannel;
386 
387   if (channel)
388   {
389     CSingleLock lock(m_critSection);
390     for (auto it = m_sortedMembers.cbegin(); !nextChannel && it != m_sortedMembers.cend(); ++it)
391     {
392       if ((*it)->channel == channel)
393       {
394         do
395         {
396           if ((++it) == m_sortedMembers.end())
397             it = m_sortedMembers.begin();
398           if ((*it)->channel && !(*it)->channel->IsHidden())
399             nextChannel = (*it)->channel;
400         } while (!nextChannel && (*it)->channel != channel);
401 
402         break;
403       }
404     }
405   }
406 
407   return nextChannel;
408 }
409 
GetPreviousChannel(const std::shared_ptr<CPVRChannel> & channel) const410 std::shared_ptr<CPVRChannel> CPVRChannelGroup::GetPreviousChannel(const std::shared_ptr<CPVRChannel>& channel) const
411 {
412   std::shared_ptr<CPVRChannel> previousChannel;
413 
414   if (channel)
415   {
416     CSingleLock lock(m_critSection);
417     for (auto it = m_sortedMembers.rbegin(); !previousChannel && it != m_sortedMembers.rend(); ++it)
418     {
419       if ((*it)->channel == channel)
420       {
421         do
422         {
423           if ((++it) == m_sortedMembers.rend())
424             it = m_sortedMembers.rbegin();
425           if ((*it)->channel && !(*it)->channel->IsHidden())
426             previousChannel = (*it)->channel;
427         } while (!previousChannel && (*it)->channel != channel);
428 
429         break;
430       }
431     }
432   }
433   return previousChannel;
434 }
435 
GetMembers(Include eFilter) const436 std::vector<std::shared_ptr<PVRChannelGroupMember>> CPVRChannelGroup::GetMembers(Include eFilter /* = Include::ALL */) const
437 {
438   CSingleLock lock(m_critSection);
439   if (eFilter == Include::ALL)
440     return m_sortedMembers;
441 
442   std::vector<std::shared_ptr<PVRChannelGroupMember>> members;
443   for (const auto& member : m_sortedMembers)
444   {
445     switch (eFilter)
446     {
447       case Include::ONLY_HIDDEN:
448         if (!member->channel->IsHidden())
449           continue;
450         break;
451       case Include::ONLY_VISIBLE:
452         if (member->channel->IsHidden())
453           continue;
454        break;
455       default:
456         break;
457     }
458 
459     members.emplace_back(member);
460   }
461 
462   return members;
463 }
464 
GetChannelNumbers(std::vector<std::string> & channelNumbers) const465 void CPVRChannelGroup::GetChannelNumbers(std::vector<std::string>& channelNumbers) const
466 {
467   CSingleLock lock(m_critSection);
468   for (const auto& member : m_sortedMembers)
469   {
470     CPVRChannelNumber activeChannelNumber = m_bUsingBackendChannelNumbers ? member->clientChannelNumber : member->channelNumber;
471     channelNumbers.emplace_back(activeChannelNumber.FormattedChannelNumber());
472   }
473 }
474 
LoadFromDb(bool bCompress)475 int CPVRChannelGroup::LoadFromDb(bool bCompress /* = false */)
476 {
477   const std::shared_ptr<CPVRDatabase> database(CServiceBroker::GetPVRManager().GetTVDatabase());
478   if (!database)
479     return -1;
480 
481   int iChannelCount = Size();
482 
483   database->Get(*this, *m_allChannelsGroup);
484 
485   return Size() - iChannelCount;
486 }
487 
LoadFromClients()488 bool CPVRChannelGroup::LoadFromClients()
489 {
490   /* get the channels from the backends */
491   return CServiceBroker::GetPVRManager().Clients()->GetChannelGroupMembers(this, m_failedClients) ==
492          PVR_ERROR_NO_ERROR;
493 }
494 
AddAndUpdateChannels(const CPVRChannelGroup & channels,bool bUseBackendChannelNumbers)495 bool CPVRChannelGroup::AddAndUpdateChannels(const CPVRChannelGroup& channels, bool bUseBackendChannelNumbers)
496 {
497   bool bReturn(false);
498 
499   /* go through the channel list and check for new channels.
500      channels will only by updated in CPVRChannelGroupInternal to prevent dupe updates */
501   for (const auto& newMemberPair : channels.m_members)
502   {
503     /* check whether this channel is known in the internal group */
504     const std::shared_ptr<PVRChannelGroupMember>& existingAllChannelsMember = m_allChannelsGroup->GetByUniqueID(newMemberPair.first);
505     if (!existingAllChannelsMember->channel)
506       continue;
507 
508     const std::shared_ptr<PVRChannelGroupMember>& newMember = newMemberPair.second;
509     /* if it's found, add the channel to this group */
510     if (!IsGroupMember(existingAllChannelsMember->channel))
511     {
512       AddToGroup(existingAllChannelsMember->channel,
513                  newMember->channelNumber,
514                  newMember->iOrder,
515                  bUseBackendChannelNumbers, newMember->clientChannelNumber);
516 
517       bReturn = true;
518       CLog::LogFC(LOGDEBUG, LOGPVR, "Added {} channel '{}' to group '{}'",
519                   IsRadio() ? "radio" : "TV", existingAllChannelsMember->channel->ChannelName(),
520                   GroupName());
521     }
522     else
523     {
524       CSingleLock lock(m_critSection);
525       std::shared_ptr<PVRChannelGroupMember>& existingMember = GetByUniqueID(newMemberPair.first);
526 
527       if ((existingMember->channelNumber != newMember->channelNumber && !m_bStartGroupChannelNumbersFromOne) ||
528           existingMember->clientChannelNumber != newMember->clientChannelNumber ||
529           existingMember->iOrder != newMember->iOrder)
530       {
531         existingMember->channelNumber = newMember->channelNumber;
532         existingMember->clientChannelNumber = newMember->clientChannelNumber;
533         existingMember->iOrder = newMember->iOrder;
534         bReturn = true;
535       }
536 
537       CLog::LogFC(LOGDEBUG, LOGPVR, "Updated {} channel '{}' in group '{}'",
538                   IsRadio() ? "radio" : "TV", existingMember->channel->ChannelName(), GroupName());
539     }
540   }
541 
542   SortAndRenumber();
543 
544   return bReturn;
545 }
546 
HasValidDataFromClient(int iClientId) const547 bool CPVRChannelGroup::HasValidDataFromClient(int iClientId) const
548 {
549   return std::find(m_failedClients.begin(), m_failedClients.end(), iClientId) ==
550          m_failedClients.end();
551 }
552 
UpdateClientOrder()553 void CPVRChannelGroup::UpdateClientOrder()
554 {
555   CSingleLock lock(m_critSection);
556 
557   for (const auto& member : m_sortedMembers)
558     member->channel->SetClientOrder(member->iOrder);
559 }
560 
UpdateChannelNumbers()561 void CPVRChannelGroup::UpdateChannelNumbers()
562 {
563   CSingleLock lock(m_critSection);
564 
565   for (const auto& member : m_sortedMembers)
566   {
567     member->channel->SetChannelNumber(m_bUsingBackendChannelNumbers ? member->clientChannelNumber : member->channelNumber);
568     member->channel->SetClientChannelNumber(member->clientChannelNumber);
569   }
570 }
571 
UpdateChannelNumbersFromAllChannelsGroup()572 bool CPVRChannelGroup::UpdateChannelNumbersFromAllChannelsGroup()
573 {
574   CSingleLock lock(m_critSection);
575 
576   bool bChanged = false;
577 
578   if (!IsInternalGroup())
579   {
580     // If we don't sync channel groups make sure the channel numbers are set from
581     // the all channels group using the non default renumber call before sorting
582     if (Renumber(IGNORE_NUMBERING_FROM_ONE) || SortAndRenumber())
583     {
584       Persist();
585       bChanged = true;
586     }
587   }
588 
589   m_events.Publish(IsInternalGroup() || bChanged ? PVREvent::ChannelGroupInvalidated
590                                                  : PVREvent::ChannelGroup);
591 
592   return bChanged;
593 }
594 
RemoveDeletedChannels(const CPVRChannelGroup & channels)595 std::vector<std::shared_ptr<CPVRChannel>> CPVRChannelGroup::RemoveDeletedChannels(const CPVRChannelGroup& channels)
596 {
597   std::vector<std::shared_ptr<CPVRChannel>> removedChannels;
598   CSingleLock lock(m_critSection);
599 
600   /* check for deleted channels */
601   for (auto it = m_sortedMembers.begin(); it != m_sortedMembers.end();)
602   {
603     const std::shared_ptr<CPVRChannel> channel = (*it)->channel;
604     if (channels.m_members.find(channel->StorageId()) == channels.m_members.end())
605     {
606       m_members.erase(channel->StorageId());
607       it = m_sortedMembers.erase(it);
608 
609       if (HasValidDataFromClient(channel->ClientID()))
610       {
611         CLog::Log(LOGINFO, "Removed stale {} channel '{}' from group '{}'",
612                   IsRadio() ? "radio" : "TV", channel->ChannelName(), GroupName());
613 
614         removedChannels.emplace_back(channel);
615         m_bChanged = true;
616       }
617     }
618     else
619     {
620       ++it;
621     }
622   }
623 
624   return removedChannels;
625 }
626 
UpdateGroupEntries(const CPVRChannelGroup & channels,std::vector<std::shared_ptr<CPVRChannel>> & channelsToRemove)627 bool CPVRChannelGroup::UpdateGroupEntries(const CPVRChannelGroup& channels, std::vector<std::shared_ptr<CPVRChannel>>& channelsToRemove)
628 {
629   bool bReturn(false);
630   bool bChanged(false);
631   bool bRemoved(false);
632 
633   CSingleLock lock(m_critSection);
634   /* sort by client channel number if this is the first time or if SETTING_PVRMANAGER_BACKENDCHANNELORDER is true */
635   bool bUseBackendChannelNumbers(m_members.empty() || m_bUsingBackendChannelOrder);
636 
637   SetPreventSortAndRenumber(true);
638   channelsToRemove = RemoveDeletedChannels(channels);
639   bRemoved = !channelsToRemove.empty();
640   bChanged = AddAndUpdateChannels(channels, bUseBackendChannelNumbers) || bRemoved;
641   SetPreventSortAndRenumber(false);
642 
643   bChanged |= UpdateClientPriorities();
644 
645   if (bChanged)
646   {
647     /* renumber to make sure all channels have a channel number.
648        new channels were added at the back, so they'll get the highest numbers */
649     bool bRenumbered = SortAndRenumber();
650 
651     m_bChanged = true;
652     bReturn = Persist();
653 
654     m_events.Publish(HasNewChannels() || bRemoved || bRenumbered ? PVREvent::ChannelGroupInvalidated : PVREvent::ChannelGroup);
655   }
656   else
657   {
658     bReturn = true;
659   }
660 
661   return bReturn;
662 }
663 
RemoveFromGroup(const std::shared_ptr<CPVRChannel> & channel)664 bool CPVRChannelGroup::RemoveFromGroup(const std::shared_ptr<CPVRChannel>& channel)
665 {
666   bool bReturn(false);
667   CSingleLock lock(m_critSection);
668 
669   for (std::vector<std::shared_ptr<PVRChannelGroupMember>>::iterator it = m_sortedMembers.begin(); it != m_sortedMembers.end();)
670   {
671     if (*channel == *((*it)->channel))
672     {
673       //! @todo notify observers
674       m_members.erase((*it)->channel->StorageId());
675       it = m_sortedMembers.erase(it);
676       bReturn = true;
677       m_bChanged = true;
678       break;
679     }
680     else
681     {
682       ++it;
683     }
684   }
685 
686   // no need to renumber if nothing was removed
687   if (bReturn)
688     Renumber();
689 
690   return bReturn;
691 }
692 
AddToGroup(const std::shared_ptr<CPVRChannel> & channel,const CPVRChannelNumber & channelNumber,int iOrder,bool bUseBackendChannelNumbers,const CPVRChannelNumber & clientChannelNumber)693 bool CPVRChannelGroup::AddToGroup(const std::shared_ptr<CPVRChannel>& channel, const CPVRChannelNumber& channelNumber, int iOrder, bool bUseBackendChannelNumbers, const CPVRChannelNumber& clientChannelNumber /* = {} */)
694 {
695   bool bReturn(false);
696   CSingleLock lock(m_critSection);
697 
698   if (!CPVRChannelGroup::IsGroupMember(channel))
699   {
700     const std::shared_ptr<PVRChannelGroupMember>& realMember = IsInternalGroup() ?
701         GetByUniqueID(channel->StorageId()) :
702         m_allChannelsGroup->GetByUniqueID(channel->StorageId());
703 
704     if (realMember->channel)
705     {
706       unsigned int iChannelNumber = channelNumber.GetChannelNumber();
707       if (!channelNumber.IsValid())
708         iChannelNumber = realMember->channelNumber.GetChannelNumber();
709 
710       CPVRChannelNumber clientChannelNumberToUse = clientChannelNumber;
711       if (!clientChannelNumber.IsValid())
712         clientChannelNumberToUse = realMember->clientChannelNumber;
713 
714       auto newMember = std::make_shared<PVRChannelGroupMember>(realMember->channel, CPVRChannelNumber(iChannelNumber, channelNumber.GetSubChannelNumber()), realMember->iClientPriority, iOrder, clientChannelNumberToUse);
715       m_sortedMembers.emplace_back(newMember);
716       m_members.insert(std::make_pair(realMember->channel->StorageId(), newMember));
717       m_bChanged = true;
718 
719       SortAndRenumber();
720 
721       //! @todo notify observers
722       bReturn = true;
723     }
724   }
725 
726   return bReturn;
727 }
728 
AppendToGroup(const std::shared_ptr<CPVRChannel> & channel)729 bool CPVRChannelGroup::AppendToGroup(const std::shared_ptr<CPVRChannel>& channel)
730 {
731   CSingleLock lock(m_critSection);
732 
733   unsigned int channelNumberMax = 0;
734   for (const auto& member : m_sortedMembers)
735   {
736     if (member->channelNumber.GetChannelNumber() > channelNumberMax)
737       channelNumberMax = member->channelNumber.GetChannelNumber();
738   }
739 
740   return AddToGroup(channel, CPVRChannelNumber(channelNumberMax + 1, 0), 0, false);
741 }
742 
IsGroupMember(const std::shared_ptr<CPVRChannel> & channel) const743 bool CPVRChannelGroup::IsGroupMember(const std::shared_ptr<CPVRChannel>& channel) const
744 {
745   CSingleLock lock(m_critSection);
746   return m_members.find(channel->StorageId()) != m_members.end();
747 }
748 
IsGroupMember(int iChannelId) const749 bool CPVRChannelGroup::IsGroupMember(int iChannelId) const
750 {
751   CSingleLock lock(m_critSection);
752 
753   for (const auto& memberPair : m_members)
754   {
755     if (iChannelId == memberPair.second->channel->ChannelID())
756       return true;
757   }
758 
759   return false;
760 }
761 
Persist()762 bool CPVRChannelGroup::Persist()
763 {
764   bool bReturn(true);
765   const std::shared_ptr<CPVRDatabase> database(CServiceBroker::GetPVRManager().GetTVDatabase());
766 
767   CSingleLock lock(m_critSection);
768 
769   /* only persist if the group has changes and is fully loaded or never has been saved before */
770   if (!HasChanges() || (!m_bLoaded && m_iGroupId != INVALID_GROUP_ID))
771     return bReturn;
772 
773   // Mark newly created groups as loaded so future updates will also be persisted...
774   if (m_iGroupId == INVALID_GROUP_ID)
775     m_bLoaded = true;
776 
777   if (database)
778   {
779     CLog::LogFC(LOGDEBUG, LOGPVR, "Persisting channel group '{}' with {} channels", GroupName(),
780                 static_cast<int>(m_members.size()));
781     m_bChanged = false;
782 
783     bReturn = database->Persist(*this);
784   }
785   else
786   {
787     bReturn = false;
788   }
789 
790   return bReturn;
791 }
792 
Renumber(RenumberMode mode)793 bool CPVRChannelGroup::Renumber(RenumberMode mode /* = NORMAL */)
794 {
795   if (PreventSortAndRenumber())
796     return true;
797 
798   bool bReturn(false);
799   unsigned int iChannelNumber(0);
800   const auto& settings = CServiceBroker::GetSettingsComponent()->GetSettings();
801   bool bUsingBackendChannelNumbers = UsingBackendChannelNumbers(settings);
802   bool bStartGroupChannelNumbersFromOne =
803       settings->GetBool(CSettings::SETTING_PVRMANAGER_STARTGROUPCHANNELNUMBERSFROMONE) &&
804       !bUsingBackendChannelNumbers;
805 
806   CSingleLock lock(m_critSection);
807 
808   CPVRChannelNumber currentChannelNumber;
809   CPVRChannelNumber currentClientChannelNumber;
810   for (auto& sortedMember : m_sortedMembers)
811   {
812     currentClientChannelNumber = sortedMember->clientChannelNumber;
813 
814     if (sortedMember->channel->IsHidden())
815     {
816       currentChannelNumber = CPVRChannelNumber(0, 0);
817     }
818     else
819     {
820       if (IsInternalGroup())
821       {
822         currentChannelNumber = CPVRChannelNumber(++iChannelNumber, 0);
823       }
824       else
825       {
826         if (bStartGroupChannelNumbersFromOne && mode != IGNORE_NUMBERING_FROM_ONE)
827           currentChannelNumber = CPVRChannelNumber(++iChannelNumber, 0);
828         else
829           currentChannelNumber = m_allChannelsGroup->GetChannelNumber(sortedMember->channel);
830 
831         if (!sortedMember->clientChannelNumber.IsValid())
832           currentClientChannelNumber = m_allChannelsGroup->GetClientChannelNumber(sortedMember->channel);
833       }
834     }
835 
836     if (sortedMember->channelNumber != currentChannelNumber || sortedMember->clientChannelNumber != currentClientChannelNumber)
837     {
838       bReturn = true;
839       m_bChanged = true;
840       sortedMember->channelNumber = currentChannelNumber;
841       sortedMember->clientChannelNumber = currentClientChannelNumber;
842 
843       auto& unsortedMember = GetByUniqueID(sortedMember->channel->StorageId());
844       unsortedMember->channelNumber = sortedMember->channelNumber;
845       unsortedMember->clientChannelNumber = sortedMember->clientChannelNumber;
846     }
847   }
848 
849   Sort();
850 
851   return bReturn;
852 }
853 
HasChangedChannels() const854 bool CPVRChannelGroup::HasChangedChannels() const
855 {
856   CSingleLock lock(m_critSection);
857 
858   for (const auto& memberPair : m_members)
859   {
860     if (memberPair.second->channel->IsChanged())
861       return true;
862   }
863 
864   return false;
865 }
866 
HasNewChannels() const867 bool CPVRChannelGroup::HasNewChannels() const
868 {
869   CSingleLock lock(m_critSection);
870 
871   for (const auto& memberPair : m_members)
872   {
873     if (memberPair.second->channel->ChannelID() <= 0)
874       return true;
875   }
876 
877   return false;
878 }
879 
HasChanges() const880 bool CPVRChannelGroup::HasChanges() const
881 {
882   CSingleLock lock(m_critSection);
883   return m_bChanged || HasNewChannels() || HasChangedChannels();
884 }
885 
OnSettingChanged(const std::shared_ptr<const CSetting> & setting)886 void CPVRChannelGroup::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
887 {
888   if (setting == NULL)
889     return;
890 
891   //! @todo while pvr manager is starting up do accept setting changes.
892   if(!CServiceBroker::GetPVRManager().IsStarted())
893   {
894     CLog::Log(LOGWARNING, "Channel group setting change ignored while PVR Manager is starting");
895     return;
896   }
897 
898   const std::string& settingId = setting->GetId();
899   if (settingId == CSettings::SETTING_PVRMANAGER_SYNCCHANNELGROUPS ||
900       settingId == CSettings::SETTING_PVRMANAGER_BACKENDCHANNELORDER ||
901       settingId == CSettings::SETTING_PVRMANAGER_USEBACKENDCHANNELNUMBERS ||
902       settingId == CSettings::SETTING_PVRMANAGER_STARTGROUPCHANNELNUMBERSFROMONE)
903   {
904     const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
905     m_bSyncChannelGroups = settings->GetBool(CSettings::SETTING_PVRMANAGER_SYNCCHANNELGROUPS);
906     bool bUsingBackendChannelOrder = settings->GetBool(CSettings::SETTING_PVRMANAGER_BACKENDCHANNELORDER);
907     bool bUsingBackendChannelNumbers = UsingBackendChannelNumbers(settings);
908     bool bStartGroupChannelNumbersFromOne = settings->GetBool(CSettings::SETTING_PVRMANAGER_STARTGROUPCHANNELNUMBERSFROMONE) && !bUsingBackendChannelNumbers;
909 
910     CSingleLock lock(m_critSection);
911 
912     bool bChannelNumbersChanged = m_bUsingBackendChannelNumbers != bUsingBackendChannelNumbers;
913     bool bChannelOrderChanged = m_bUsingBackendChannelOrder != bUsingBackendChannelOrder;
914     bool bGroupChannelNumbersFromOneChanged = m_bStartGroupChannelNumbersFromOne != bStartGroupChannelNumbersFromOne;
915 
916     m_bUsingBackendChannelOrder = bUsingBackendChannelOrder;
917     m_bUsingBackendChannelNumbers = bUsingBackendChannelNumbers;
918     m_bStartGroupChannelNumbersFromOne = bStartGroupChannelNumbersFromOne;
919 
920     /* check whether this channel group has to be renumbered */
921     if (bChannelOrderChanged || bChannelNumbersChanged || bGroupChannelNumbersFromOneChanged)
922     {
923       CLog::LogFC(LOGDEBUG, LOGPVR,
924                   "Renumbering channel group '{}' to use the backend channel order and/or numbers",
925                   GroupName());
926 
927       if (bChannelOrderChanged)
928         UpdateClientPriorities();
929 
930       // If we don't sync channel groups make sure the channel numbers are set from
931       // the all channels group using the non default renumber call before sorting
932       if (!m_bSyncChannelGroups)
933         Renumber(IGNORE_NUMBERING_FROM_ONE);
934 
935       bool bRenumbered = SortAndRenumber();
936       Persist();
937 
938       if (m_bIsSelectedGroup)
939       {
940         for (const auto& member : m_sortedMembers)
941         {
942           member->channel->SetClientOrder(member->iOrder);
943           member->channel->SetChannelNumber(m_bUsingBackendChannelNumbers ? member->clientChannelNumber : member->channelNumber);
944           member->channel->SetClientChannelNumber(member->clientChannelNumber);
945         }
946       }
947 
948       m_events.Publish(bRenumbered ? PVREvent::ChannelGroupInvalidated : PVREvent::ChannelGroup);
949     }
950   }
951 }
952 
GetEPGDate(EpgDateType epgDateType) const953 CDateTime CPVRChannelGroup::GetEPGDate(EpgDateType epgDateType) const
954 {
955   CDateTime date;
956   std::shared_ptr<CPVREpg> epg;
957   std::shared_ptr<CPVRChannel> channel;
958   CSingleLock lock(m_critSection);
959 
960   for (const auto& memberPair : m_members)
961   {
962     channel = memberPair.second->channel;
963     if (!channel->IsHidden() && (epg = channel->GetEPG()))
964     {
965       CDateTime epgDate;
966       switch (epgDateType)
967       {
968         case EPG_FIRST_DATE:
969           epgDate = epg->GetFirstDate();
970           if (epgDate.IsValid() && (!date.IsValid() || epgDate < date))
971             date = epgDate;
972           break;
973 
974         case EPG_LAST_DATE:
975           epgDate = epg->GetLastDate();
976           if (epgDate.IsValid() && (!date.IsValid() || epgDate > date))
977             date = epgDate;
978           break;
979       }
980     }
981   }
982 
983   return date;
984 }
985 
GetFirstEPGDate() const986 CDateTime CPVRChannelGroup::GetFirstEPGDate() const
987 {
988   return GetEPGDate(EPG_FIRST_DATE);
989 }
990 
GetLastEPGDate() const991 CDateTime CPVRChannelGroup::GetLastEPGDate() const
992 {
993   return GetEPGDate(EPG_LAST_DATE);
994 }
995 
GroupID() const996 int CPVRChannelGroup::GroupID() const
997 {
998   return m_iGroupId;
999 }
1000 
SetGroupID(int iGroupId)1001 void CPVRChannelGroup::SetGroupID(int iGroupId)
1002 {
1003   if (iGroupId >= 0)
1004     m_iGroupId = iGroupId;
1005 }
1006 
SetGroupType(int iGroupType)1007 void CPVRChannelGroup::SetGroupType(int iGroupType)
1008 {
1009   m_iGroupType = iGroupType;
1010 }
1011 
GroupType() const1012 int CPVRChannelGroup::GroupType() const
1013 {
1014   return m_iGroupType;
1015 }
1016 
GroupName() const1017 std::string CPVRChannelGroup::GroupName() const
1018 {
1019   CSingleLock lock(m_critSection);
1020   return m_path.GetGroupName();
1021 }
1022 
SetGroupName(const std::string & strGroupName)1023 void CPVRChannelGroup::SetGroupName(const std::string& strGroupName)
1024 {
1025   CSingleLock lock(m_critSection);
1026   if (m_path.GetGroupName() != strGroupName)
1027   {
1028     m_path = CPVRChannelsPath(m_path.IsRadio(), strGroupName);
1029     m_bChanged = true;
1030 
1031     if (m_bLoaded)
1032       Persist();
1033   }
1034 }
1035 
IsRadio() const1036 bool CPVRChannelGroup::IsRadio() const
1037 {
1038   CSingleLock lock(m_critSection);
1039   return m_path.IsRadio();
1040 }
1041 
LastWatched() const1042 time_t CPVRChannelGroup::LastWatched() const
1043 {
1044   CSingleLock lock(m_critSection);
1045   return m_iLastWatched;
1046 }
1047 
SetLastWatched(time_t iLastWatched)1048 bool CPVRChannelGroup::SetLastWatched(time_t iLastWatched)
1049 {
1050   const std::shared_ptr<CPVRDatabase> database(CServiceBroker::GetPVRManager().GetTVDatabase());
1051 
1052   CSingleLock lock(m_critSection);
1053 
1054   if (m_iLastWatched != iLastWatched)
1055   {
1056     m_iLastWatched = iLastWatched;
1057 
1058     /* update the database immediately */
1059     if (database)
1060       return database->UpdateLastWatched(*this);
1061   }
1062 
1063   return false;
1064 }
1065 
LastOpened() const1066 uint64_t CPVRChannelGroup::LastOpened() const
1067 {
1068   CSingleLock lock(m_critSection);
1069   return m_iLastOpened;
1070 }
1071 
SetLastOpened(uint64_t iLastOpened)1072 bool CPVRChannelGroup::SetLastOpened(uint64_t iLastOpened)
1073 {
1074   const std::shared_ptr<CPVRDatabase> database(CServiceBroker::GetPVRManager().GetTVDatabase());
1075 
1076   CSingleLock lock(m_critSection);
1077 
1078   if (m_iLastOpened != iLastOpened)
1079   {
1080     m_iLastOpened = iLastOpened;
1081 
1082     /* update the database immediately */
1083     if (database)
1084       return database->UpdateLastOpened(*this);
1085   }
1086 
1087   return false;
1088 }
1089 
PreventSortAndRenumber() const1090 bool CPVRChannelGroup::PreventSortAndRenumber() const
1091 {
1092   CSingleLock lock(m_critSection);
1093   return m_bPreventSortAndRenumber;
1094 }
1095 
SetPreventSortAndRenumber(bool bPreventSortAndRenumber)1096 void CPVRChannelGroup::SetPreventSortAndRenumber(bool bPreventSortAndRenumber /* = true */)
1097 {
1098   CSingleLock lock(m_critSection);
1099   m_bPreventSortAndRenumber = bPreventSortAndRenumber;
1100 }
1101 
UpdateChannel(const std::pair<int,int> & storageId,const std::string & strChannelName,const std::string & strIconPath,int iEPGSource,int iChannelNumber,bool bHidden,bool bEPGEnabled,bool bParentalLocked,bool bUserSetIcon)1102 bool CPVRChannelGroup::UpdateChannel(const std::pair<int, int>& storageId,
1103                                      const std::string& strChannelName,
1104                                      const std::string& strIconPath,
1105                                      int iEPGSource,
1106                                      int iChannelNumber,
1107                                      bool bHidden,
1108                                      bool bEPGEnabled,
1109                                      bool bParentalLocked,
1110                                      bool bUserSetIcon)
1111 {
1112   CSingleLock lock(m_critSection);
1113 
1114   /* get the real channel from the group */
1115   const std::shared_ptr<PVRChannelGroupMember>& member = GetByUniqueID(storageId);
1116   if (!member->channel)
1117     return false;
1118 
1119   member->channel->SetChannelName(strChannelName, true);
1120   member->channel->SetHidden(bHidden);
1121   member->channel->SetLocked(bParentalLocked);
1122   member->channel->SetIconPath(strIconPath, bUserSetIcon);
1123 
1124   if (iEPGSource == 0)
1125     member->channel->SetEPGScraper("client");
1126 
1127   //! @todo add other scrapers
1128   member->channel->SetEPGEnabled(bEPGEnabled);
1129 
1130   /* set new values in the channel tag */
1131   if (bHidden)
1132   {
1133     // sort or previous changes will be overwritten
1134     Sort();
1135 
1136     RemoveFromGroup(member->channel);
1137   }
1138   else
1139   {
1140     SetChannelNumber(member->channel, CPVRChannelNumber(iChannelNumber, 0));
1141   }
1142 
1143   return true;
1144 }
1145 
Size() const1146 size_t CPVRChannelGroup::Size() const
1147 {
1148   CSingleLock lock(m_critSection);
1149   return m_members.size();
1150 }
1151 
HasChannels() const1152 bool CPVRChannelGroup::HasChannels() const
1153 {
1154   CSingleLock lock(m_critSection);
1155   return !m_members.empty();
1156 }
1157 
CreateChannelEpgs(bool bForce)1158 bool CPVRChannelGroup::CreateChannelEpgs(bool bForce /* = false */)
1159 {
1160   /* used only by internal channel groups */
1161   return true;
1162 }
1163 
SetHidden(bool bHidden)1164 bool CPVRChannelGroup::SetHidden(bool bHidden)
1165 {
1166   CSingleLock lock(m_critSection);
1167 
1168   if (m_bHidden != bHidden)
1169   {
1170     m_bHidden = bHidden;
1171     m_bChanged = true;
1172   }
1173 
1174   return m_bChanged;
1175 }
1176 
IsHidden() const1177 bool CPVRChannelGroup::IsHidden() const
1178 {
1179   CSingleLock lock(m_critSection);
1180   return m_bHidden;
1181 }
1182 
GetPosition() const1183 int CPVRChannelGroup::GetPosition() const
1184 {
1185   CSingleLock lock(m_critSection);
1186   return m_iPosition;
1187 }
1188 
SetPosition(int iPosition)1189 void CPVRChannelGroup::SetPosition(int iPosition)
1190 {
1191   CSingleLock lock(m_critSection);
1192 
1193   if (m_iPosition != iPosition)
1194   {
1195     m_iPosition = iPosition;
1196     m_bChanged = true;
1197   }
1198 }
1199