2 * Copyright (C) 2012-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "guilib/LocalizeStrings.h"
22 #include "settings/AdvancedSettings.h"
23 #include "settings/Settings.h"
24 #include "threads/SingleLock.h"
25 #include "utils/log.h"
26 #include "utils/TimeUtils.h"
28 #include "EpgDatabase.h"
29 #include "EpgContainer.h"
30 #include "pvr/PVRManager.h"
31 #include "pvr/addons/PVRClients.h"
32 #include "pvr/channels/PVRChannelGroupsContainer.h"
33 #include "utils/StringUtils.h"
35 #include "../addons/include/xbmc_epg_types.h"
41 CEpg::CEpg(int iEpgID, const CStdString &strName /* = "" */, const CStdString &strScraperName /* = "" */, bool bLoadedFromDb /* = false */) :
42 m_bChanged(!bLoadedFromDb),
43 m_bTagsChanged(false),
45 m_bUpdatePending(false),
48 m_strScraperName(strScraperName),
49 m_bUpdateLastScanTime(false)
55 CEpg::CEpg(CPVRChannelPtr channel, bool bLoadedFromDb /* = false */) :
56 m_bChanged(!bLoadedFromDb),
57 m_bTagsChanged(false),
59 m_bUpdatePending(false),
60 m_iEpgID(channel->EpgID()),
61 m_strName(channel->ChannelName()),
62 m_strScraperName(channel->EPGScraper()),
63 m_pvrChannel(channel),
64 m_bUpdateLastScanTime(false)
70 m_bTagsChanged(false),
72 m_bUpdatePending(false),
74 m_bUpdateLastScanTime(false)
85 CEpg &CEpg::operator =(const CEpg &right)
87 m_bChanged = right.m_bChanged;
88 m_bTagsChanged = right.m_bTagsChanged;
89 m_bLoaded = right.m_bLoaded;
90 m_bUpdatePending = right.m_bUpdatePending;
91 m_iEpgID = right.m_iEpgID;
92 m_strName = right.m_strName;
93 m_strScraperName = right.m_strScraperName;
94 m_nowActiveStart = right.m_nowActiveStart;
95 m_lastScanTime = right.m_lastScanTime;
96 m_pvrChannel = right.m_pvrChannel;
98 for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = right.m_tags.begin(); it != right.m_tags.end(); it++)
100 CEpgInfoTagPtr EITPtr (new CEpgInfoTag(*it->second));
101 m_tags.insert(make_pair(it->first, EITPtr));
107 /** @name Public methods */
110 void CEpg::SetName(const CStdString &strName)
112 CSingleLock lock(m_critSection);
114 if (!m_strName.Equals(strName))
121 void CEpg::SetScraperName(const CStdString &strScraperName)
123 CSingleLock lock(m_critSection);
125 if (!m_strScraperName.Equals(strScraperName))
128 m_strScraperName = strScraperName;
132 void CEpg::SetUpdatePending(bool bUpdatePending /* = true */)
135 CSingleLock lock(m_critSection);
136 m_bUpdatePending = bUpdatePending;
140 g_EpgContainer.SetHasPendingUpdates(true);
143 void CEpg::ForceUpdate(void)
148 bool CEpg::HasValidEntries(void) const
150 CSingleLock lock(m_critSection);
152 return (m_iEpgID > 0 && /* valid EPG ID */
153 m_tags.size() > 0 && /* contains at least 1 tag */
154 m_tags.rbegin()->second->EndAsUTC() >= CDateTime::GetCurrentDateTime().GetAsUTCDateTime()); /* the last end time hasn't passed yet */
157 void CEpg::Clear(void)
159 CSingleLock lock(m_critSection);
163 void CEpg::Cleanup(void)
165 CDateTime cleanupTime = CDateTime::GetCurrentDateTime().GetAsUTCDateTime() -
166 CDateTimeSpan(0, g_advancedSettings.m_iEpgLingerTime / 60, g_advancedSettings.m_iEpgLingerTime % 60, 0);
167 Cleanup(cleanupTime);
170 void CEpg::Cleanup(const CDateTime &Time)
172 CSingleLock lock(m_critSection);
173 for (map<CDateTime, CEpgInfoTagPtr>::iterator it = m_tags.begin(); it != m_tags.end(); it != m_tags.end() ? it++ : it)
175 if (it->second->EndAsUTC() < Time)
177 if (m_nowActiveStart == it->first)
178 m_nowActiveStart.SetValid(false);
180 it->second->ClearTimer();
186 bool CEpg::InfoTagNow(CEpgInfoTag &tag, bool bUpdateIfNeeded /* = true */)
188 CSingleLock lock(m_critSection);
189 if (m_nowActiveStart.IsValid())
191 map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.find(m_nowActiveStart);
192 if (it != m_tags.end() && it->second->IsActive())
201 CDateTime lastActiveTag;
203 /* one of the first items will always match if the list is sorted */
204 for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
206 if (it->second->IsActive())
208 m_nowActiveStart = it->first;
212 else if (it->second->WasActive())
213 lastActiveTag = it->first;
216 /* there might be a gap between the last and next event. just return the last if found */
217 map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.find(lastActiveTag);
218 if (it != m_tags.end())
228 bool CEpg::InfoTagNext(CEpgInfoTag &tag)
231 if (InfoTagNow(nowTag))
233 CSingleLock lock(m_critSection);
234 map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.find(nowTag.StartAsUTC());
235 if (it != m_tags.end() && ++it != m_tags.end())
243 /* return the first event that is in the future */
244 for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
246 if (it->second->InTheFuture())
257 bool CEpg::CheckPlayingEvent(void)
260 CEpgInfoTag previousTag, newTag;
261 bool bGotPreviousTag = InfoTagNow(previousTag, false);
262 bool bGotCurrentTag = InfoTagNow(newTag);
264 bool bTagChanged = bGotCurrentTag && (!bGotPreviousTag || previousTag != newTag);
265 bool bTagRemoved = !bGotCurrentTag && bGotPreviousTag;
266 if (bTagChanged || bTagRemoved)
268 NotifyObservers(ObservableMessageEpgActiveItem);
275 CEpgInfoTagPtr CEpg::GetTag(const CDateTime &StartTime) const
277 CSingleLock lock(m_critSection);
278 map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.find(StartTime);
279 if (it != m_tags.end())
284 CEpgInfoTagPtr empty;
288 CEpgInfoTagPtr CEpg::GetTagBetween(const CDateTime &beginTime, const CDateTime &endTime) const
290 CSingleLock lock(m_critSection);
291 for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
293 if (it->second->StartAsUTC() >= beginTime && it->second->EndAsUTC() <= endTime)
297 CEpgInfoTagPtr retVal;
301 CEpgInfoTagPtr CEpg::GetTagAround(const CDateTime &time) const
303 CSingleLock lock(m_critSection);
304 for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
306 if ((it->second->StartAsUTC() <= time) && (it->second->EndAsUTC() >= time))
310 CEpgInfoTagPtr retVal;
314 void CEpg::AddEntry(const CEpgInfoTag &tag)
316 CEpgInfoTagPtr newTag;
317 CSingleLock lock(m_critSection);
318 map<CDateTime, CEpgInfoTagPtr>::iterator itr = m_tags.find(tag.StartAsUTC());
319 if (itr != m_tags.end())
320 newTag = itr->second;
323 newTag = CEpgInfoTagPtr(new CEpgInfoTag(this, m_pvrChannel, m_strName, m_pvrChannel ? m_pvrChannel->IconPath() : StringUtils::EmptyString));
324 m_tags.insert(make_pair(tag.StartAsUTC(), newTag));
330 newTag->SetPVRChannel(m_pvrChannel);
331 newTag->m_epg = this;
332 newTag->m_bChanged = false;
336 bool CEpg::UpdateEntry(const CEpgInfoTag &tag, bool bUpdateDatabase /* = false */, bool bSort /* = true */)
338 CEpgInfoTagPtr infoTag;
339 CSingleLock lock(m_critSection);
340 map<CDateTime, CEpgInfoTagPtr>::iterator it = m_tags.find(tag.StartAsUTC());
342 if (it != m_tags.end())
344 infoTag = it->second;
348 /* create a new tag if no tag with this ID exists */
349 infoTag = CEpgInfoTagPtr(new CEpgInfoTag(this, m_pvrChannel, m_strName, m_pvrChannel ? m_pvrChannel->IconPath() : StringUtils::EmptyString));
350 infoTag->SetUniqueBroadcastID(tag.UniqueBroadcastID());
351 m_tags.insert(make_pair(tag.StartAsUTC(), infoTag));
355 infoTag->Update(tag, bNewTag);
356 infoTag->m_epg = this;
357 infoTag->m_pvrChannel = m_pvrChannel;
360 m_changedTags.insert(make_pair(infoTag->UniqueBroadcastID(), infoTag));
365 bool CEpg::Load(void)
368 CEpgDatabase *database = g_EpgContainer.GetDatabase();
370 if (!database || !database->IsOpen())
372 CLog::Log(LOGERROR, "EPG - %s - could not open the database", __FUNCTION__);
376 CSingleLock lock(m_critSection);
377 int iEntriesLoaded = database->Get(*this);
378 if (iEntriesLoaded <= 0)
380 CLog::Log(LOGDEBUG, "EPG - %s - no database entries found for table '%s'.", __FUNCTION__, m_strName.c_str());
384 m_lastScanTime = GetLastScanTime();
386 CLog::Log(LOGDEBUG, "EPG - %s - %d entries loaded for table '%s'.", __FUNCTION__, (int) m_tags.size(), m_strName.c_str());
396 bool CEpg::UpdateEntries(const CEpg &epg, bool bStoreInDb /* = true */)
398 CSingleLock lock(m_critSection);
400 CLog::Log(LOGDEBUG, "EPG - %s - %zu entries in memory before merging", __FUNCTION__, m_tags.size());
403 for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = epg.m_tags.begin(); it != epg.m_tags.end(); it++)
404 UpdateEntry(*it->second, bStoreInDb, false);
407 CLog::Log(LOGDEBUG, "EPG - %s - %zu entries in memory after merging and before fixing", __FUNCTION__, m_tags.size());
409 FixOverlappingEvents(bStoreInDb);
412 CLog::Log(LOGDEBUG, "EPG - %s - %zu entries in memory after fixing", __FUNCTION__, m_tags.size());
414 /* update the last scan time of this table */
415 m_lastScanTime = CDateTime::GetCurrentDateTime().GetAsUTCDateTime();
416 m_bUpdateLastScanTime = true;
418 NotifyObservers(ObservableMessageEpg);
423 CDateTime CEpg::GetLastScanTime(void)
425 CDateTime lastScanTime;
427 CSingleLock lock(m_critSection);
429 if (!m_lastScanTime.IsValid())
431 if (!CSettings::Get().GetBool("epg.ignoredbforclient"))
433 CEpgDatabase *database = g_EpgContainer.GetDatabase();
434 CDateTime dtReturn; dtReturn.SetValid(false);
436 if (database && database->IsOpen())
437 database->GetLastEpgScanTime(m_iEpgID, &m_lastScanTime);
440 if (!m_lastScanTime.IsValid())
442 m_lastScanTime.SetDateTime(0, 0, 0, 0, 0, 0);
443 m_lastScanTime.SetValid(true);
446 lastScanTime = m_lastScanTime;
449 return m_lastScanTime;
452 bool CEpg::Update(const time_t start, const time_t end, int iUpdateTime, bool bForceUpdate /* = false */)
454 bool bGrabSuccess(true);
457 /* load the entries from the db first */
458 if (!m_bLoaded && !g_EpgContainer.IgnoreDB())
461 /* clean up if needed */
465 /* get the last update time from the database */
466 CDateTime lastScanTime = GetLastScanTime();
468 /* enforce advanced settings update interval override for TV Channels with no EPG data */
469 if (m_tags.empty() && !bUpdate && ChannelID() > 0 && !Channel()->IsRadio())
470 iUpdateTime = g_advancedSettings.m_iEpgUpdateEmptyTagsInterval;
474 /* check if we have to update */
476 time_t iLastUpdate = 0;
477 CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow);
478 lastScanTime.GetAsTime(iLastUpdate);
479 bUpdate = (iNow > iLastUpdate + iUpdateTime);
485 bGrabSuccess = LoadFromClients(start, end);
489 CPVRChannelPtr channel;
490 if (g_PVRManager.GetCurrentChannel(channel) &&
491 channel->EpgID() == m_iEpgID)
492 g_PVRManager.ResetPlayingTag();
496 CLog::Log(LOGERROR, "EPG - %s - failed to update table '%s'", __FUNCTION__, Name().c_str());
498 CSingleLock lock(m_critSection);
499 m_bUpdatePending = false;
504 int CEpg::Get(CFileItemList &results) const
506 int iInitialSize = results.Size();
508 CSingleLock lock(m_critSection);
510 for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
511 results.Add(CFileItemPtr(new CFileItem(*it->second)));
513 return results.Size() - iInitialSize;
516 int CEpg::Get(CFileItemList &results, const EpgSearchFilter &filter) const
518 int iInitialSize = results.Size();
520 if (!HasValidEntries())
523 CSingleLock lock(m_critSection);
525 for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
527 if (filter.FilterEntry(*it->second))
528 results.Add(CFileItemPtr(new CFileItem(*it->second)));
531 return results.Size() - iInitialSize;
534 bool CEpg::Persist(void)
536 if (CSettings::Get().GetBool("epg.ignoredbforclient") || !NeedsSave())
540 CLog::Log(LOGDEBUG, "persist table '%s' (#%d) changed=%d deleted=%d", Name().c_str(), m_iEpgID, m_changedTags.size(), m_deletedTags.size());
543 CEpgDatabase *database = g_EpgContainer.GetDatabase();
544 if (!database || !database->IsOpen())
546 CLog::Log(LOGERROR, "EPG - %s - could not open the database", __FUNCTION__);
551 CSingleLock lock(m_critSection);
552 if (m_iEpgID <= 0 || m_bChanged)
554 int iId = database->Persist(*this, m_iEpgID > 0);
559 for (std::map<int, CEpgInfoTagPtr>::iterator it = m_deletedTags.begin(); it != m_deletedTags.end(); it++)
560 database->Delete(*it->second);
562 for (std::map<int, CEpgInfoTagPtr>::iterator it = m_changedTags.begin(); it != m_changedTags.end(); it++)
563 it->second->Persist(false);
565 if (m_bUpdateLastScanTime)
566 database->PersistLastEpgScanTime(m_iEpgID, true);
568 m_deletedTags.clear();
569 m_changedTags.clear();
571 m_bTagsChanged = false;
572 m_bUpdateLastScanTime = false;
575 return database->CommitInsertQueries();
578 CDateTime CEpg::GetFirstDate(void) const
582 CSingleLock lock(m_critSection);
583 if (m_tags.size() > 0)
584 first = m_tags.begin()->second->StartAsUTC();
589 CDateTime CEpg::GetLastDate(void) const
593 CSingleLock lock(m_critSection);
594 if (m_tags.size() > 0)
595 last = m_tags.rbegin()->second->StartAsUTC();
602 /** @name Private methods */
605 bool CEpg::FixOverlappingEvents(bool bUpdateDb /* = false */)
608 CEpgInfoTagPtr previousTag, currentTag;
610 for (map<CDateTime, CEpgInfoTagPtr>::iterator it = m_tags.begin(); it != m_tags.end(); it != m_tags.end() ? it++ : it)
614 previousTag = it->second;
617 currentTag = it->second;
619 if (previousTag->EndAsUTC() >= currentTag->EndAsUTC())
621 // delete the current tag. it's completely overlapped
623 m_deletedTags.insert(make_pair(currentTag->UniqueBroadcastID(), currentTag));
625 if (m_nowActiveStart == it->first)
626 m_nowActiveStart.SetValid(false);
628 it->second->ClearTimer();
631 else if (previousTag->EndAsUTC() > currentTag->StartAsUTC())
633 currentTag->SetStartFromUTC(previousTag->EndAsUTC());
635 m_changedTags.insert(make_pair(currentTag->UniqueBroadcastID(), currentTag));
637 previousTag = it->second;
639 else if (previousTag->EndAsUTC() < currentTag->StartAsUTC())
641 time_t start, end, middle;
642 previousTag->EndAsUTC().GetAsTime(start);
643 currentTag->StartAsUTC().GetAsTime(end);
644 middle = start + ((end - start) / 2);
645 CDateTime newTime(middle);
647 currentTag->SetStartFromUTC(newTime);
648 previousTag->SetEndFromUTC(newTime);
650 if (m_nowActiveStart == it->first)
651 m_nowActiveStart = currentTag->StartAsUTC();
655 m_changedTags.insert(make_pair(currentTag->UniqueBroadcastID(), currentTag));
656 m_changedTags.insert(make_pair(previousTag->UniqueBroadcastID(), previousTag));
659 previousTag = it->second;
663 previousTag = it->second;
670 bool CEpg::UpdateFromScraper(time_t start, time_t end)
672 bool bGrabSuccess = false;
673 if (ScraperName() == "client")
675 CPVRChannelPtr channel = Channel();
678 CLog::Log(LOGWARNING, "EPG - %s - channel not found, can't update", __FUNCTION__);
680 else if (!channel->EPGEnabled())
683 CLog::Log(LOGDEBUG, "EPG - %s - EPG updating disabled in the channel configuration", __FUNCTION__);
687 else if (channel->IsHidden())
690 CLog::Log(LOGDEBUG, "EPG - %s - channel '%s' on client '%i' is hidden", __FUNCTION__, channel->ChannelName().c_str(), channel->ClientID());
694 else if (!g_PVRClients->SupportsEPG(channel->ClientID()))
696 CLog::Log(LOGDEBUG, "EPG - %s - the backend for channel '%s' on client '%i' does not support EPGs", __FUNCTION__, channel->ChannelName().c_str(), channel->ClientID());
700 CLog::Log(LOGDEBUG, "EPG - %s - updating EPG for channel '%s' from client '%i'", __FUNCTION__, channel->ChannelName().c_str(), channel->ClientID());
701 bGrabSuccess = (g_PVRClients->GetEPGForChannel(*channel, this, start, end) == PVR_ERROR_NO_ERROR);
704 else if (m_strScraperName.IsEmpty()) /* no grabber defined */
705 CLog::Log(LOGWARNING, "EPG - %s - no EPG scraper defined for table '%s'", __FUNCTION__, m_strName.c_str());
708 CLog::Log(LOGINFO, "EPG - %s - updating EPG table '%s' with scraper '%s'", __FUNCTION__, m_strName.c_str(), m_strScraperName.c_str());
709 CLog::Log(LOGWARNING, "loading the EPG via scraper has not been implemented yet");
710 // TODO: Add Support for Web EPG Scrapers here
718 const CStdString &CEpg::ConvertGenreIdToString(int iID, int iSubID)
720 unsigned int iLabelId = 19499;
723 case EPG_EVENT_CONTENTMASK_MOVIEDRAMA:
724 iLabelId = (iSubID <= 8) ? 19500 + iSubID : 19500;
726 case EPG_EVENT_CONTENTMASK_NEWSCURRENTAFFAIRS:
727 iLabelId = (iSubID <= 4) ? 19516 + iSubID : 19516;
729 case EPG_EVENT_CONTENTMASK_SHOW:
730 iLabelId = (iSubID <= 3) ? 19532 + iSubID : 19532;
732 case EPG_EVENT_CONTENTMASK_SPORTS:
733 iLabelId = (iSubID <= 11) ? 19548 + iSubID : 19548;
735 case EPG_EVENT_CONTENTMASK_CHILDRENYOUTH:
736 iLabelId = (iSubID <= 5) ? 19564 + iSubID : 19564;
738 case EPG_EVENT_CONTENTMASK_MUSICBALLETDANCE:
739 iLabelId = (iSubID <= 6) ? 19580 + iSubID : 19580;
741 case EPG_EVENT_CONTENTMASK_ARTSCULTURE:
742 iLabelId = (iSubID <= 11) ? 19596 + iSubID : 19596;
744 case EPG_EVENT_CONTENTMASK_SOCIALPOLITICALECONOMICS:
745 iLabelId = (iSubID <= 3) ? 19612 + iSubID : 19612;
747 case EPG_EVENT_CONTENTMASK_EDUCATIONALSCIENCE:
748 iLabelId = (iSubID <= 7) ? 19628 + iSubID : 19628;
750 case EPG_EVENT_CONTENTMASK_LEISUREHOBBIES:
751 iLabelId = (iSubID <= 7) ? 19644 + iSubID : 19644;
753 case EPG_EVENT_CONTENTMASK_SPECIAL:
754 iLabelId = (iSubID <= 3) ? 19660 + iSubID : 19660;
756 case EPG_EVENT_CONTENTMASK_USERDEFINED:
757 iLabelId = (iSubID <= 8) ? 19676 + iSubID : 19676;
763 return g_localizeStrings.Get(iLabelId);
766 bool CEpg::UpdateEntry(const EPG_TAG *data, bool bUpdateDatabase /* = false */)
771 CEpgInfoTag tag(*data);
772 return UpdateEntry(tag, bUpdateDatabase);
775 bool CEpg::IsRadio(void) const
777 CPVRChannelPtr channel = Channel();
778 return channel ? channel->IsRadio() : false;
781 bool CEpg::IsRemovableTag(const CEpgInfoTag &tag) const
783 return !tag.HasTimer();
786 bool CEpg::LoadFromClients(time_t start, time_t end)
789 CPVRChannelPtr channel = Channel();
792 CEpg tmpEpg(channel);
793 if (tmpEpg.UpdateFromScraper(start, end))
794 bReturn = UpdateEntries(tmpEpg, !CSettings::Get().GetBool("epg.ignoredbforclient"));
798 CEpg tmpEpg(m_iEpgID, m_strName, m_strScraperName);
799 if (tmpEpg.UpdateFromScraper(start, end))
800 bReturn = UpdateEntries(tmpEpg, !CSettings::Get().GetBool("epg.ignoredbforclient"));
806 CEpgInfoTagPtr CEpg::GetNextEvent(const CEpgInfoTag& tag) const
808 CSingleLock lock(m_critSection);
809 map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.find(tag.StartAsUTC());
810 if (it != m_tags.end() && ++it != m_tags.end())
813 CEpgInfoTagPtr retVal;
817 CEpgInfoTagPtr CEpg::GetPreviousEvent(const CEpgInfoTag& tag) const
819 CSingleLock lock(m_critSection);
820 map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.find(tag.StartAsUTC());
821 if (it != m_tags.end() && it != m_tags.begin())
827 CEpgInfoTagPtr retVal;
831 CPVRChannelPtr CEpg::Channel(void) const
833 CSingleLock lock(m_critSection);
837 int CEpg::ChannelID(void) const
839 CSingleLock lock(m_critSection);
840 return m_pvrChannel ? m_pvrChannel->ChannelID() : -1;
843 int CEpg::ChannelNumber(void) const
845 CSingleLock lock(m_critSection);
846 return m_pvrChannel ? m_pvrChannel->ChannelNumber() : -1;
849 void CEpg::SetChannel(PVR::CPVRChannelPtr channel)
851 CSingleLock lock(m_critSection);
852 if (m_pvrChannel != channel)
856 SetName(channel->ChannelName());
857 channel->SetEpgID(m_iEpgID);
859 m_pvrChannel = channel;
860 for (map<CDateTime, CEpgInfoTagPtr>::iterator it = m_tags.begin(); it != m_tags.end(); it++)
861 it->second->SetPVRChannel(m_pvrChannel);
865 bool CEpg::HasPVRChannel(void) const
867 CSingleLock lock(m_critSection);
871 bool CEpg::UpdatePending(void) const
873 CSingleLock lock(m_critSection);
874 return m_bUpdatePending;
877 size_t CEpg::Size(void) const
879 CSingleLock lock(m_critSection);
880 return m_tags.size();
883 bool CEpg::NeedsSave(void) const
885 CSingleLock lock(m_critSection);
886 return !m_changedTags.empty() || !m_deletedTags.empty() || m_bChanged;
889 bool CEpg::IsValid(void) const
891 CSingleLock lock(m_critSection);
892 if (ScraperName() == "client")
893 return Channel().get() != NULL;