2 * Copyright (C) 2012-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "GUIEPGGridContainerModel.h"
12 #include "FileItemList.h"
13 #include "ServiceBroker.h"
14 #include "pvr/PVRManager.h"
15 #include "pvr/channels/PVRChannel.h"
16 #include "pvr/epg/Epg.h"
17 #include "pvr/epg/EpgChannelData.h"
18 #include "pvr/epg/EpgContainer.h"
19 #include "pvr/epg/EpgInfoTag.h"
20 #include "utils/Variant.h"
21 #include "utils/log.h"
31 static const unsigned int GRID_START_PADDING
= 30; // minutes
33 void CGUIEPGGridContainerModel::SetInvalid()
35 for (const auto& gridItem
: m_gridIndex
)
36 gridItem
.second
.item
->SetInvalid();
37 for (const auto& channel
: m_channelItems
)
38 channel
->SetInvalid();
39 for (const auto& ruler
: m_rulerItems
)
43 std::shared_ptr
<CFileItem
> CGUIEPGGridContainerModel::CreateGapItem(int iChannel
) const
45 const std::shared_ptr
<const CPVRChannel
> channel
=
46 m_channelItems
[iChannel
]->GetPVRChannelInfoTag();
47 const std::shared_ptr
<CPVREpgInfoTag
> gapTag
= channel
->CreateEPGGapTag(m_gridStart
, m_gridEnd
);
48 return std::make_shared
<CFileItem
>(gapTag
);
51 std::vector
<std::shared_ptr
<CPVREpgInfoTag
>> CGUIEPGGridContainerModel::GetEPGTimeline(
52 int iChannel
, const CDateTime
& minEventEnd
, const CDateTime
& maxEventStart
) const
54 CDateTime min
= minEventEnd
- CDateTimeSpan(0, 0, MINSPERBLOCK
, 0) + CDateTimeSpan(0, 0, 0, 1);
55 CDateTime max
= maxEventStart
+ CDateTimeSpan(0, 0, MINSPERBLOCK
, 0);
57 if (min
< m_gridStart
)
63 return m_channelItems
[iChannel
]->GetPVRChannelInfoTag()->GetEPGTimeline(m_gridStart
, m_gridEnd
,
67 void CGUIEPGGridContainerModel::Initialize(const std::unique_ptr
<CFileItemList
>& items
,
68 const CDateTime
& gridStart
,
69 const CDateTime
& gridEnd
,
77 if (!m_channelItems
.empty())
79 CLog::LogF(LOGERROR
, "Already initialized!");
83 m_fBlockSize
= fBlockSize
;
85 ////////////////////////////////////////////////////////////////////////
86 // Create channel items
87 std::copy(items
->cbegin(), items
->cend(), std::back_inserter(m_channelItems
));
89 /* check for invalid start and end time */
90 if (gridStart
>= gridEnd
)
92 // default to start "now minus GRID_START_PADDING minutes" and end "start plus one page".
93 m_gridStart
= CDateTime::GetUTCDateTime() - CDateTimeSpan(0, 0, GetGridStartPadding(), 0);
94 m_gridEnd
= m_gridStart
+ CDateTimeSpan(0, 0, iBlocksPerPage
* MINSPERBLOCK
, 0);
97 (CDateTime::GetUTCDateTime() - CDateTimeSpan(0, 0, GetGridStartPadding(), 0)))
99 // adjust to start "now minus GRID_START_PADDING minutes".
100 m_gridStart
= CDateTime::GetUTCDateTime() - CDateTimeSpan(0, 0, GetGridStartPadding(), 0);
105 m_gridStart
= gridStart
;
110 m_gridStart
= CDateTime(m_gridStart
.GetYear(), m_gridStart
.GetMonth(), m_gridStart
.GetDay(),
111 m_gridStart
.GetHour(), m_gridStart
.GetMinute() >= 30 ? 30 : 0, 0);
112 m_gridEnd
= CDateTime(m_gridEnd
.GetYear(), m_gridEnd
.GetMonth(), m_gridEnd
.GetDay(),
113 m_gridEnd
.GetHour(), m_gridEnd
.GetMinute() >= 30 ? 30 : 0, 0);
115 m_blocks
= GetBlock(m_gridEnd
) + 1;
117 const int iBlocksLastPage
= m_blocks
% iBlocksPerPage
;
118 if (iBlocksLastPage
> 0)
120 m_gridEnd
+= CDateTimeSpan(0, 0, (iBlocksPerPage
- iBlocksLastPage
) * MINSPERBLOCK
, 0);
121 m_blocks
+= (iBlocksPerPage
- iBlocksLastPage
);
124 ////////////////////////////////////////////////////////////////////////
125 // Create ruler items
127 ruler
.SetFromUTCDateTime(m_gridStart
);
129 rulerEnd
.SetFromUTCDateTime(m_gridEnd
);
130 CFileItemPtr
rulerItem(new CFileItem(ruler
.GetAsLocalizedDate(true)));
131 rulerItem
->SetProperty("DateLabel", true);
132 m_rulerItems
.emplace_back(rulerItem
);
134 const CDateTimeSpan
unit(0, 0, iRulerUnit
* MINSPERBLOCK
, 0);
135 for (; ruler
< rulerEnd
; ruler
+= unit
)
137 rulerItem
= std::make_shared
<CFileItem
>(ruler
.GetAsLocalizedTime("", false));
138 rulerItem
->SetLabel2(ruler
.GetAsLocalizedDate(true));
139 m_rulerItems
.emplace_back(rulerItem
);
142 m_firstActiveChannel
= iFirstChannel
;
143 m_lastActiveChannel
= iFirstChannel
+ iChannelsPerPage
- 1;
144 m_firstActiveBlock
= iFirstBlock
;
145 m_lastActiveBlock
= iFirstBlock
+ iBlocksPerPage
- 1;
148 std::shared_ptr
<CFileItem
> CGUIEPGGridContainerModel::CreateEpgTags(int iChannel
, int iBlock
) const
150 std::shared_ptr
<CFileItem
> result
;
152 const int firstBlock
= iBlock
< m_firstActiveBlock
? iBlock
: m_firstActiveBlock
;
153 const int lastBlock
= iBlock
> m_lastActiveBlock
? iBlock
: m_lastActiveBlock
;
156 GetEPGTimeline(iChannel
, GetStartTimeForBlock(firstBlock
), GetStartTimeForBlock(lastBlock
));
158 const int firstResultBlock
= GetFirstEventBlock(tags
.front());
159 const int lastResultBlock
= GetLastEventBlock(tags
.back());
160 if (firstResultBlock
> lastResultBlock
)
163 auto it
= m_epgItems
.insert({iChannel
, EpgTags()}).first
;
164 EpgTags
& epgTags
= (*it
).second
;
166 epgTags
.firstBlock
= firstResultBlock
;
167 epgTags
.lastBlock
= lastResultBlock
;
169 for (const auto& tag
: tags
)
171 if (GetFirstEventBlock(tag
) > GetLastEventBlock(tag
))
174 const std::shared_ptr
<CFileItem
> item
= std::make_shared
<CFileItem
>(tag
);
175 if (!result
&& IsEventMemberOfBlock(tag
, iBlock
))
178 epgTags
.tags
.emplace_back(item
);
184 std::shared_ptr
<CFileItem
> CGUIEPGGridContainerModel::GetEpgTags(EpgTagsMap::iterator
& itEpg
,
188 std::shared_ptr
<CFileItem
> result
;
190 EpgTags
& epgTags
= (*itEpg
).second
;
192 if (iBlock
< epgTags
.firstBlock
)
194 result
= GetEpgTagsBefore(epgTags
, iChannel
, iBlock
);
196 else if (iBlock
> epgTags
.lastBlock
)
198 result
= GetEpgTagsAfter(epgTags
, iChannel
, iBlock
);
203 std::find_if(epgTags
.tags
.cbegin(), epgTags
.tags
.cend(), [this, iBlock
](const auto& item
) {
204 return IsEventMemberOfBlock(item
->GetEPGInfoTag(), iBlock
);
206 if (it
!= epgTags
.tags
.cend())
213 std::shared_ptr
<CFileItem
> CGUIEPGGridContainerModel::GetEpgTagsBefore(EpgTags
& epgTags
,
217 std::shared_ptr
<CFileItem
> result
;
219 int lastBlock
= epgTags
.firstBlock
- 1;
224 GetEPGTimeline(iChannel
, GetStartTimeForBlock(iBlock
), GetStartTimeForBlock(lastBlock
));
226 if (epgTags
.lastBlock
== -1)
227 epgTags
.lastBlock
= lastBlock
;
231 epgTags
.firstBlock
= iBlock
;
235 const int firstResultBlock
= GetFirstEventBlock(tags
.front());
236 const int lastResultBlock
= GetLastEventBlock(tags
.back());
237 if (firstResultBlock
> lastResultBlock
)
240 // insert before the existing tags
241 epgTags
.firstBlock
= firstResultBlock
;
243 auto it
= tags
.crbegin();
244 if (!epgTags
.tags
.empty())
246 // ptr comp does not work for gap tags!
247 // if ((*it) == epgTags.tags.front()->GetEPGInfoTag())
249 const std::shared_ptr
<const CPVREpgInfoTag
> t
= epgTags
.tags
.front()->GetEPGInfoTag();
250 if ((*it
)->StartAsUTC() == t
->StartAsUTC() && (*it
)->EndAsUTC() == t
->EndAsUTC())
252 if (!result
&& IsEventMemberOfBlock(*it
, iBlock
))
253 result
= epgTags
.tags
.front();
255 ++it
; // skip, because we already have that epg tag
259 for (; it
!= tags
.crend(); ++it
)
261 if (GetFirstEventBlock(*it
) > GetLastEventBlock(*it
))
264 const std::shared_ptr
<CFileItem
> item
= std::make_shared
<CFileItem
>(*it
);
265 if (!result
&& IsEventMemberOfBlock(*it
, iBlock
))
268 epgTags
.tags
.insert(epgTags
.tags
.begin(), item
);
275 std::shared_ptr
<CFileItem
> CGUIEPGGridContainerModel::GetEpgTagsAfter(EpgTags
& epgTags
,
279 std::shared_ptr
<CFileItem
> result
;
281 int firstBlock
= epgTags
.lastBlock
+ 1;
282 if (firstBlock
>= GetLastBlock())
283 firstBlock
= GetLastBlock();
286 GetEPGTimeline(iChannel
, GetStartTimeForBlock(firstBlock
), GetStartTimeForBlock(iBlock
));
288 if (epgTags
.firstBlock
== -1)
289 epgTags
.firstBlock
= firstBlock
;
293 epgTags
.lastBlock
= iBlock
;
297 const int firstResultBlock
= GetFirstEventBlock(tags
.front());
298 const int lastResultBlock
= GetLastEventBlock(tags
.back());
299 if (firstResultBlock
> lastResultBlock
)
302 // append to the existing tags
303 epgTags
.lastBlock
= lastResultBlock
;
305 auto it
= tags
.cbegin();
306 if (!epgTags
.tags
.empty())
308 // ptr comp does not work for gap tags!
309 // if ((*it) == epgTags.tags.back()->GetEPGInfoTag())
311 const std::shared_ptr
<const CPVREpgInfoTag
> t
= epgTags
.tags
.back()->GetEPGInfoTag();
312 if ((*it
)->StartAsUTC() == t
->StartAsUTC() && (*it
)->EndAsUTC() == t
->EndAsUTC())
314 if (!result
&& IsEventMemberOfBlock(*it
, iBlock
))
315 result
= epgTags
.tags
.back();
317 ++it
; // skip, because we already have that epg tag
321 for (; it
!= tags
.cend(); ++it
)
323 if (GetFirstEventBlock(*it
) > GetLastEventBlock(*it
))
326 const std::shared_ptr
<CFileItem
> item
= std::make_shared
<CFileItem
>(*it
);
327 if (!result
&& IsEventMemberOfBlock(*it
, iBlock
))
330 epgTags
.tags
.emplace_back(item
);
337 std::shared_ptr
<CFileItem
> CGUIEPGGridContainerModel::GetItem(int iChannel
, int iBlock
) const
339 std::shared_ptr
<CFileItem
> result
;
341 auto itEpg
= m_epgItems
.find(iChannel
);
342 if (itEpg
== m_epgItems
.end())
344 result
= CreateEpgTags(iChannel
, iBlock
);
348 result
= GetEpgTags(itEpg
, iChannel
, iBlock
);
353 // Must never happen. if it does, fix the root cause, don't tolerate nullptr!
354 CLog::LogF(LOGERROR
, "EPG tag ({}, {}) not found!", iChannel
, iBlock
);
360 void CGUIEPGGridContainerModel::FindChannelAndBlockIndex(int channelUid
,
361 unsigned int broadcastUid
,
363 int& newChannelIndex
,
364 int& newBlockIndex
) const
366 newChannelIndex
= INVALID_INDEX
;
367 newBlockIndex
= INVALID_INDEX
;
369 // find the new channel index
370 int iCurrentChannel
= 0;
371 for (const auto& channel
: m_channelItems
)
373 if (channel
->GetPVRChannelInfoTag()->UniqueID() == channelUid
)
375 newChannelIndex
= iCurrentChannel
;
377 // find the new block index
378 const std::shared_ptr
<const CPVREpg
> epg
= channel
->GetPVRChannelInfoTag()->GetEPG();
381 const std::shared_ptr
<const CPVREpgInfoTag
> tag
= epg
->GetTagByBroadcastId(broadcastUid
);
383 newBlockIndex
= GetFirstEventBlock(tag
) + eventOffset
;
391 GridItem
* CGUIEPGGridContainerModel::GetGridItemPtr(int iChannel
, int iBlock
) const
393 auto it
= m_gridIndex
.find({iChannel
, iBlock
});
394 if (it
== m_gridIndex
.end())
396 const CDateTime startTime
= GetStartTimeForBlock(iBlock
);
397 if (startTime
< m_gridStart
|| m_gridEnd
< startTime
)
399 CLog::LogF(LOGERROR
, "Requested EPG tag ({}, {}) outside grid boundaries!", iChannel
, iBlock
);
403 const std::shared_ptr
<CFileItem
> item
= GetItem(iChannel
, iBlock
);
406 CLog::LogF(LOGERROR
, "Got no EPG tag ({}, {})!", iChannel
, iBlock
);
410 const std::shared_ptr
<const CPVREpgInfoTag
> epgTag
= item
->GetEPGInfoTag();
412 const int startBlock
= GetFirstEventBlock(epgTag
);
413 const int endBlock
= GetLastEventBlock(epgTag
);
415 //! @todo it seems that this should be done somewhere else. CFileItem ctor maybe.
416 item
->SetProperty("GenreType", epgTag
->GenreType());
418 const float fItemWidth
= (endBlock
- startBlock
+ 1) * m_fBlockSize
;
419 it
= m_gridIndex
.insert({{iChannel
, iBlock
}, {item
, fItemWidth
, startBlock
, endBlock
}}).first
;
422 return &(*it
).second
;
425 bool CGUIEPGGridContainerModel::IsSameGridItem(int iChannel
, int iBlock1
, int iBlock2
) const
427 if (iBlock1
== iBlock2
)
430 const GridItem
* item1
= GetGridItemPtr(iChannel
, iBlock1
);
431 const GridItem
* item2
= GetGridItemPtr(iChannel
, iBlock2
);
433 // compare the instances, not instance pointers, pointers are not unique.
434 return *item1
== *item2
;
437 std::shared_ptr
<CFileItem
> CGUIEPGGridContainerModel::GetGridItem(int iChannel
, int iBlock
) const
439 return GetGridItemPtr(iChannel
, iBlock
)->item
;
442 int CGUIEPGGridContainerModel::GetGridItemStartBlock(int iChannel
, int iBlock
) const
444 return GetGridItemPtr(iChannel
, iBlock
)->startBlock
;
447 int CGUIEPGGridContainerModel::GetGridItemEndBlock(int iChannel
, int iBlock
) const
449 return GetGridItemPtr(iChannel
, iBlock
)->endBlock
;
452 CDateTime
CGUIEPGGridContainerModel::GetGridItemEndTime(int iChannel
, int iBlock
) const
454 return GetGridItemPtr(iChannel
, iBlock
)->item
->GetEPGInfoTag()->EndAsUTC();
457 float CGUIEPGGridContainerModel::GetGridItemWidth(int iChannel
, int iBlock
) const
459 return GetGridItemPtr(iChannel
, iBlock
)->width
;
462 float CGUIEPGGridContainerModel::GetGridItemOriginWidth(int iChannel
, int iBlock
) const
464 return GetGridItemPtr(iChannel
, iBlock
)->originWidth
;
467 void CGUIEPGGridContainerModel::DecreaseGridItemWidth(int iChannel
, int iBlock
, float fSize
)
469 auto it
= m_gridIndex
.find({iChannel
, iBlock
});
470 if (it
!= m_gridIndex
.end() && (*it
).second
.width
!= ((*it
).second
.originWidth
- fSize
))
471 (*it
).second
.width
= (*it
).second
.originWidth
- fSize
;
474 unsigned int CGUIEPGGridContainerModel::GetGridStartPadding() const
476 unsigned int iPastMinutes
=
477 CServiceBroker::GetPVRManager().EpgContainer().GetPastDaysToDisplay() * 24 * 60;
479 if (iPastMinutes
< GRID_START_PADDING
)
482 return GRID_START_PADDING
; // minutes
485 void CGUIEPGGridContainerModel::FreeChannelMemory(int keepStart
, int keepEnd
)
487 if (keepStart
< keepEnd
)
489 // remove before keepStart and after keepEnd
490 for (int i
= 0; i
< keepStart
&& i
< ChannelItemsSize(); ++i
)
491 m_channelItems
[i
]->FreeMemory();
492 for (int i
= keepEnd
+ 1; i
< ChannelItemsSize(); ++i
)
493 m_channelItems
[i
]->FreeMemory();
498 for (int i
= keepEnd
+ 1; i
< keepStart
&& i
< ChannelItemsSize(); ++i
)
499 m_channelItems
[i
]->FreeMemory();
503 bool CGUIEPGGridContainerModel::FreeProgrammeMemory(int firstChannel
,
508 const bool channelsChanged
=
509 (firstChannel
!= m_firstActiveChannel
|| lastChannel
!= m_lastActiveChannel
);
510 const bool blocksChanged
= (firstBlock
!= m_firstActiveBlock
|| lastBlock
!= m_lastActiveBlock
);
511 if (!channelsChanged
&& !blocksChanged
)
514 // clear the grid. it will be recreated on-demand.
517 bool newChannels
= false;
521 // purge epg tags for inactive channels
522 for (auto it
= m_epgItems
.begin(); it
!= m_epgItems
.end();)
524 if ((*it
).first
< firstChannel
|| (*it
).first
> lastChannel
)
526 it
= m_epgItems
.erase(it
);
527 continue; // next channel
532 newChannels
= (firstChannel
< m_firstActiveChannel
) || (lastChannel
> m_lastActiveChannel
);
535 if (blocksChanged
|| newChannels
)
537 // clear and refetch epg tags for active channels
538 const CDateTime maxEnd
= GetStartTimeForBlock(firstBlock
);
539 const CDateTime minStart
= GetStartTimeForBlock(lastBlock
);
540 std::vector
<std::shared_ptr
<CPVREpgInfoTag
>> tags
;
541 for (int i
= firstChannel
; i
<= lastChannel
; ++i
)
543 auto it
= m_epgItems
.find(i
);
544 if (it
== m_epgItems
.end())
545 it
= m_epgItems
.insert({i
, EpgTags()}).first
;
547 if (blocksChanged
|| i
< m_firstActiveChannel
|| i
> m_lastActiveChannel
)
549 EpgTags
& epgTags
= (*it
).second
;
551 (*it
).second
.tags
.clear();
553 tags
= GetEPGTimeline(i
, maxEnd
, minStart
);
554 const int firstResultBlock
= GetFirstEventBlock(tags
.front());
555 const int lastResultBlock
= GetLastEventBlock(tags
.back());
556 if (firstResultBlock
> lastResultBlock
)
559 epgTags
.firstBlock
= firstResultBlock
;
560 epgTags
.lastBlock
= lastResultBlock
;
562 for (const auto& tag
: tags
)
564 if (GetFirstEventBlock(tag
) > GetLastEventBlock(tag
))
567 epgTags
.tags
.emplace_back(std::make_shared
<CFileItem
>(tag
));
573 m_firstActiveChannel
= firstChannel
;
574 m_lastActiveChannel
= lastChannel
;
575 m_firstActiveBlock
= firstBlock
;
576 m_lastActiveBlock
= lastBlock
;
581 void CGUIEPGGridContainerModel::FreeRulerMemory(int keepStart
, int keepEnd
)
583 if (keepStart
< keepEnd
)
585 // remove before keepStart and after keepEnd
586 for (int i
= 1; i
< keepStart
&& i
< RulerItemsSize(); ++i
)
587 m_rulerItems
[i
]->FreeMemory();
588 for (int i
= keepEnd
+ 1; i
< RulerItemsSize(); ++i
)
589 m_rulerItems
[i
]->FreeMemory();
594 for (int i
= keepEnd
+ 1; i
< keepStart
&& i
< RulerItemsSize(); ++i
)
599 m_rulerItems
[i
]->FreeMemory();
604 unsigned int CGUIEPGGridContainerModel::GetPageNowOffset() const
606 return GetGridStartPadding() / MINSPERBLOCK
; // this is the 'now' block relative to page start
609 CDateTime
CGUIEPGGridContainerModel::GetStartTimeForBlock(int block
) const
613 else if (block
>= GridItemsSize())
614 block
= GetLastBlock();
616 return m_gridStart
+ CDateTimeSpan(0, 0, block
* MINSPERBLOCK
, 0);
619 int CGUIEPGGridContainerModel::GetBlock(const CDateTime
& datetime
) const
623 if (m_gridStart
== datetime
)
624 return 0; // block is at grid start
625 else if (m_gridStart
> datetime
)
626 diff
= -1 * (m_gridStart
- datetime
).GetSecondsTotal(); // block is before grid start
628 diff
= (datetime
- m_gridStart
).GetSecondsTotal(); // block is after grid start
630 // Note: Subtract 1 second from diff to ensure that events ending exactly at block boundary
631 // are unambiguous. Example: An event ending at 5:00:00 shall be mapped to block 9 and
632 // an event starting at 5:00:00 shall be mapped to block 10, not both at block 10.
633 // Only exception is grid end, because there is no successor.
634 if (datetime
>= m_gridEnd
)
635 return diff
/ 60 / MINSPERBLOCK
; // block is equal or after grid end
637 return (diff
- 1) / 60 / MINSPERBLOCK
;
640 int CGUIEPGGridContainerModel::GetNowBlock() const
642 return GetBlock(CDateTime::GetUTCDateTime()) - GetPageNowOffset();
645 int CGUIEPGGridContainerModel::GetFirstEventBlock(
646 const std::shared_ptr
<const CPVREpgInfoTag
>& event
) const
648 const CDateTime eventStart
= event
->StartAsUTC();
651 if (m_gridStart
== eventStart
)
652 return 0; // block is at grid start
653 else if (m_gridStart
> eventStart
)
654 diff
= -1 * (m_gridStart
- eventStart
).GetSecondsTotal();
656 diff
= (eventStart
- m_gridStart
).GetSecondsTotal();
658 // First block of a tag is always the block calculated using event's start time, rounded up.
659 float fBlockIndex
= diff
/ 60.0f
/ MINSPERBLOCK
;
660 return static_cast<int>(std::ceil(fBlockIndex
));
663 int CGUIEPGGridContainerModel::GetLastEventBlock(
664 const std::shared_ptr
<const CPVREpgInfoTag
>& event
) const
666 // Last block of a tag is always the block calculated using event's end time, not rounded up.
667 return GetBlock(event
->EndAsUTC());
670 bool CGUIEPGGridContainerModel::IsEventMemberOfBlock(
671 const std::shared_ptr
<const CPVREpgInfoTag
>& event
, int iBlock
) const
673 const int iFirstBlock
= GetFirstEventBlock(event
);
674 const int iLastBlock
= GetLastEventBlock(event
);
676 if (iFirstBlock
> iLastBlock
)
680 else if (iFirstBlock
== iBlock
)
684 else if (iFirstBlock
< iBlock
)
686 return (iBlock
<= iLastBlock
);
691 std::unique_ptr
<CFileItemList
> CGUIEPGGridContainerModel::GetCurrentTimeLineItems(
692 int firstChannel
, int numChannels
) const
694 // Note: No need to keep this in a member. Gets generally not called multiple times for the
695 // same timeline, but content must be synced with m_epgItems, which changes quite often.
697 std::unique_ptr
<CFileItemList
> items(new CFileItemList
);
699 if (numChannels
> ChannelItemsSize())
700 numChannels
= ChannelItemsSize();
703 for (int channel
= firstChannel
; channel
< (firstChannel
+ numChannels
); ++channel
)
705 // m_epgItems is not sorted, fileitemlist must be sorted, so we have to 'find' the channel
706 const auto itEpg
= m_epgItems
.find(channel
);
707 if (itEpg
!= m_epgItems
.end())
709 // tags are sorted, so we can iterate and append
710 for (const auto& tag
: (*itEpg
).second
.tags
)
712 tag
->SetProperty("TimelineIndex", i
);
720 const std::shared_ptr
<CFileItem
> tag
= CreateGapItem(channel
);
721 tag
->SetProperty("TimelineIndex", i
);