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