2 * Copyright (C) 2012 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 "Application.h"
23 #include "settings/GUISettings.h"
24 #include "dialogs/GUIDialogKaiToast.h"
25 #include "dialogs/GUIDialogOK.h"
26 #include "threads/SingleLock.h"
27 #include "utils/log.h"
28 #include "utils/URIUtils.h"
29 #include "utils/StringUtils.h"
32 #include "PVRTimers.h"
33 #include "pvr/PVRManager.h"
34 #include "pvr/channels/PVRChannelGroupsContainer.h"
35 #include "epg/EpgContainer.h"
36 #include "pvr/addons/PVRClients.h"
42 CPVRTimers::CPVRTimers(void)
44 m_bIsUpdating = false;
47 CPVRTimers::~CPVRTimers(void)
52 bool CPVRTimers::Load(void)
54 // unload previous timers
57 // (re)register observer
58 g_EpgContainer.RegisterObserver(this);
60 // update from clients
64 void CPVRTimers::Unload()
66 // unregister observer
67 g_EpgContainer.UnregisterObserver(this);
70 CSingleLock lock(m_critSection);
74 bool CPVRTimers::Update(void)
77 CSingleLock lock(m_critSection);
83 CLog::Log(LOGDEBUG, "CPVRTimers - %s - updating timers", __FUNCTION__);
84 CPVRTimers newTimerList;
85 g_PVRClients->GetTimers(&newTimerList);
86 return UpdateEntries(newTimerList);
89 bool CPVRTimers::IsRecording(void) const
91 CSingleLock lock(m_critSection);
93 for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
94 for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
95 if ((*timerIt)->IsRecording())
101 bool CPVRTimers::UpdateEntries(const CPVRTimers &timers)
103 bool bChanged(false);
104 bool bAddedOrDeleted(false);
105 vector<CStdString> timerNotifications;
107 CSingleLock lock(m_critSection);
109 /* go through the timer list and check for updated or new timers */
110 for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = timers.m_tags.begin(); it != timers.m_tags.end(); it++)
112 for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
114 /* check if this timer is present in this container */
115 CPVRTimerInfoTagPtr existingTimer = GetByClient((*timerIt)->m_iClientId, (*timerIt)->m_iClientIndex);
118 /* if it's present, update the current tag */
119 bool bStateChanged(existingTimer->m_state != (*timerIt)->m_state);
120 if (existingTimer->UpdateEntry(*(*timerIt)))
123 UpdateEpgEvent(existingTimer);
125 if (bStateChanged && g_PVRManager.IsStarted())
127 CStdString strMessage;
128 existingTimer->GetNotificationText(strMessage);
129 timerNotifications.push_back(strMessage);
132 CLog::Log(LOGDEBUG,"PVRTimers - %s - updated timer %d on client %d",
133 __FUNCTION__, (*timerIt)->m_iClientIndex, (*timerIt)->m_iClientId);
139 CPVRTimerInfoTagPtr newTimer = CPVRTimerInfoTagPtr(new CPVRTimerInfoTag);
140 newTimer->UpdateEntry(*(*timerIt));
141 UpdateEpgEvent(newTimer);
143 vector<CPVRTimerInfoTagPtr>* addEntry = NULL;
144 map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::iterator itr = m_tags.find(newTimer->StartAsUTC());
145 if (itr == m_tags.end())
147 addEntry = new vector<CPVRTimerInfoTagPtr>;
148 m_tags.insert(make_pair(newTimer->StartAsUTC(), addEntry));
152 addEntry = itr->second;
155 addEntry->push_back(newTimer);
156 UpdateEpgEvent(newTimer);
158 bAddedOrDeleted = true;
160 if (g_PVRManager.IsStarted())
162 CStdString strMessage;
163 newTimer->GetNotificationText(strMessage);
164 timerNotifications.push_back(strMessage);
167 CLog::Log(LOGDEBUG,"PVRTimers - %s - added timer %d on client %d",
168 __FUNCTION__, (*timerIt)->m_iClientIndex, (*timerIt)->m_iClientId);
173 /* to collect timer with changed starting time */
174 vector<CPVRTimerInfoTagPtr> timersToMove;
176 /* check for deleted timers */
177 for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::iterator it = m_tags.begin(); it != m_tags.end();)
179 for (int iTimerPtr = it->second->size() - 1; iTimerPtr >= 0; iTimerPtr--)
181 CPVRTimerInfoTagPtr timer = it->second->at(iTimerPtr);
182 if (!timers.GetByClient(timer->m_iClientId, timer->m_iClientIndex))
184 /* timer was not found */
185 CLog::Log(LOGDEBUG,"PVRTimers - %s - deleted timer %d on client %d",
186 __FUNCTION__, timer->m_iClientIndex, timer->m_iClientId);
188 if (g_PVRManager.IsStarted())
190 CStdString strMessage;
191 strMessage.Format("%s: '%s'",
192 (timer->EndAsUTC() <= CDateTime::GetCurrentDateTime().GetAsUTCDateTime()) ?
193 g_localizeStrings.Get(19227) :
194 g_localizeStrings.Get(19228),
195 timer->m_strTitle.c_str());
196 timerNotifications.push_back(strMessage);
199 it->second->erase(it->second->begin() + iTimerPtr);
202 bAddedOrDeleted = true;
204 else if (timer->StartAsUTC() != it->first)
206 /* timer start has changed */
207 CLog::Log(LOGDEBUG,"PVRTimers - %s - changed start time timer %d on client %d",
208 __FUNCTION__, timer->m_iClientIndex, timer->m_iClientId);
210 timer->ClearEpgTag();
213 timersToMove.push_back(timer);
215 /* remove timer for now, reinsert later */
216 it->second->erase(it->second->begin() + iTimerPtr);
219 bAddedOrDeleted = true;
222 if (it->second->size() == 0)
228 /* reinsert timers with changed timer start */
229 for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = timersToMove.begin(); timerIt != timersToMove.end(); timerIt++)
231 vector<CPVRTimerInfoTagPtr>* addEntry = NULL;
232 map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator itr = m_tags.find((*timerIt)->StartAsUTC());
233 if (itr == m_tags.end())
235 addEntry = new vector<CPVRTimerInfoTagPtr>;
236 m_tags.insert(make_pair((*timerIt)->StartAsUTC(), addEntry));
240 addEntry = itr->second;
243 addEntry->push_back(*timerIt);
244 UpdateEpgEvent(*timerIt);
247 m_bIsUpdating = false;
254 NotifyObservers(bAddedOrDeleted ? ObservableMessageTimersReset : ObservableMessageTimers);
256 if (g_guiSettings.GetBool("pvrrecord.timernotifications"))
258 /* queue notifications */
259 for (unsigned int iNotificationPtr = 0; iNotificationPtr < timerNotifications.size(); iNotificationPtr++)
261 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info,
262 g_localizeStrings.Get(19166),
263 timerNotifications.at(iNotificationPtr));
271 bool CPVRTimers::UpdateFromClient(const CPVRTimerInfoTag &timer)
273 CSingleLock lock(m_critSection);
274 CPVRTimerInfoTagPtr tag = GetByClient(timer.m_iClientId, timer.m_iClientIndex);
277 tag = CPVRTimerInfoTagPtr(new CPVRTimerInfoTag());
278 vector<CPVRTimerInfoTagPtr>* addEntry = NULL;
279 map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::iterator itr = m_tags.find(timer.StartAsUTC());
280 if (itr == m_tags.end())
282 addEntry = new vector<CPVRTimerInfoTagPtr>;
283 m_tags.insert(make_pair(timer.StartAsUTC(), addEntry));
287 addEntry = itr->second;
289 addEntry->push_back(tag);
294 return tag->UpdateEntry(timer);
297 /********** getters **********/
299 CFileItemPtr CPVRTimers::GetNextActiveTimer(void) const
301 CSingleLock lock(m_critSection);
302 for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
304 for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
306 CPVRTimerInfoTagPtr current = *timerIt;
307 if (current->IsActive() && !current->IsRecording())
309 CFileItemPtr fileItem(new CFileItem(*current));
315 CFileItemPtr fileItem;
319 vector<CFileItemPtr> CPVRTimers::GetActiveTimers(void) const
321 vector<CFileItemPtr> tags;
322 CSingleLock lock(m_critSection);
324 for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
326 for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
328 CPVRTimerInfoTagPtr current = *timerIt;
329 if (current->IsActive())
331 CFileItemPtr fileItem(new CFileItem(*current));
332 tags.push_back(fileItem);
340 int CPVRTimers::AmountActiveTimers(void) const
343 CSingleLock lock(m_critSection);
345 for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
346 for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
347 if ((*timerIt)->IsActive())
353 std::vector<CFileItemPtr> CPVRTimers::GetActiveRecordings(void) const
355 std::vector<CFileItemPtr> tags;
356 CSingleLock lock(m_critSection);
358 for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
360 for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
362 CPVRTimerInfoTagPtr current = *timerIt;
363 if (current->IsRecording())
365 CFileItemPtr fileItem(new CFileItem(*current));
366 tags.push_back(fileItem);
374 int CPVRTimers::AmountActiveRecordings(void) const
377 CSingleLock lock(m_critSection);
379 for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
380 for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
381 if ((*timerIt)->IsRecording())
387 bool CPVRTimers::HasActiveTimers(void) const
389 CSingleLock lock(m_critSection);
390 for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
391 for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
392 if ((*timerIt)->IsActive())
398 bool CPVRTimers::GetDirectory(const CStdString& strPath, CFileItemList &items) const
400 CStdString base(strPath);
401 URIUtils::RemoveSlashAtEnd(base);
404 CStdString fileName = url.GetFileName();
405 URIUtils::RemoveSlashAtEnd(fileName);
407 if (fileName == "timers")
411 item.reset(new CFileItem(base + "/add.timer", false));
412 item->SetLabel(g_localizeStrings.Get(19026));
413 item->SetLabelPreformated(true);
416 CSingleLock lock(m_critSection);
417 for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
419 for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
421 CPVRTimerInfoTagPtr current = *timerIt;
422 item.reset(new CFileItem(*current));
432 /********** channel methods **********/
434 bool CPVRTimers::DeleteTimersOnChannel(const CPVRChannel &channel, bool bDeleteRepeating /* = true */, bool bCurrentlyActiveOnly /* = false */)
436 bool bReturn = false;
438 CSingleLock lock(m_critSection);
440 for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::reverse_iterator it = m_tags.rbegin(); it != m_tags.rend(); it++)
442 for (vector<CPVRTimerInfoTagPtr>::iterator timerIt = it->second->begin(); timerIt != it->second->end(); )
444 bool bDeleteActiveItem = !bCurrentlyActiveOnly || (*timerIt)->IsRecording();
445 bool bDeleteRepeatingItem = bDeleteRepeating || !(*timerIt)->m_bIsRepeating;
446 bool bChannelsMatch = *(*timerIt)->ChannelTag() == channel;
448 if (bDeleteActiveItem && bDeleteRepeatingItem && bChannelsMatch)
450 CLog::Log(LOGDEBUG,"PVRTimers - %s - deleted timer %d on client %d", __FUNCTION__, (*timerIt)->m_iClientIndex, (*timerIt)->m_iClientId);
451 bReturn = (*timerIt)->DeleteFromClient(true) || bReturn;
452 timerIt = it->second->erase(timerIt);
463 NotifyObservers(ObservableMessageTimersReset);
468 bool CPVRTimers::InstantTimer(const CPVRChannel &channel)
470 if (!g_PVRManager.CheckParentalLock(channel))
474 bool bHasEpgNow = channel.GetEPGNow(epgTag);
475 CPVRTimerInfoTag *newTimer = bHasEpgNow ? CPVRTimerInfoTag::CreateFromEpg(epgTag) : NULL;
478 newTimer = new CPVRTimerInfoTag;
479 /* set the timer data */
480 newTimer->m_iClientIndex = -1;
481 newTimer->m_strTitle = channel.ChannelName();
482 newTimer->m_strSummary = g_localizeStrings.Get(19056);
483 newTimer->m_iChannelNumber = channel.ChannelNumber();
484 newTimer->m_iClientChannelUid = channel.UniqueID();
485 newTimer->m_iClientId = channel.ClientID();
486 newTimer->m_bIsRadio = channel.IsRadio();
488 /* generate summary string */
489 newTimer->m_strSummary.Format("%s %s %s %s %s",
490 newTimer->StartAsLocalTime().GetAsLocalizedDate(),
491 g_localizeStrings.Get(19159),
492 newTimer->StartAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false),
493 g_localizeStrings.Get(19160),
494 newTimer->EndAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false));
497 CDateTime startTime(0);
498 newTimer->SetStartFromUTC(startTime);
499 newTimer->m_iMarginStart = 0; /* set the start margin to 0 for instant timers */
501 int iDuration = g_guiSettings.GetInt("pvrrecord.instantrecordtime");
502 CDateTime endTime = CDateTime::GetUTCDateTime() + CDateTimeSpan(0, 0, iDuration ? iDuration : 120, 0);
503 newTimer->SetEndFromUTC(endTime);
505 /* unused only for reference */
506 newTimer->m_strFileNameAndPath = "pvr://timers/new";
508 bool bReturn = newTimer->AddToClient();
510 CLog::Log(LOGERROR, "PVRTimers - %s - unable to add an instant timer on the client", __FUNCTION__);
517 /********** static methods **********/
519 bool CPVRTimers::AddTimer(const CPVRTimerInfoTag &item)
523 CLog::Log(LOGERROR, "PVRTimers - %s - no channel given", __FUNCTION__);
524 CGUIDialogOK::ShowAndGetInput(19033,0,19109,0); // Couldn't save timer
528 if (!g_PVRClients->SupportsTimers(item.m_iClientId))
530 CGUIDialogOK::ShowAndGetInput(19033,0,19215,0);
534 if (!g_PVRManager.CheckParentalLock(*item.m_channel))
537 return item.AddToClient();
540 bool CPVRTimers::DeleteTimer(const CFileItem &item, bool bForce /* = false */)
542 /* Check if a CPVRTimerInfoTag is inside file item */
543 if (!item.IsPVRTimer())
545 CLog::Log(LOGERROR, "PVRTimers - %s - no TimerInfoTag given", __FUNCTION__);
549 const CPVRTimerInfoTag *tag = item.GetPVRTimerInfoTag();
553 return tag->DeleteFromClient(bForce);
556 bool CPVRTimers::RenameTimer(CFileItem &item, const CStdString &strNewName)
558 /* Check if a CPVRTimerInfoTag is inside file item */
559 if (!item.IsPVRTimer())
561 CLog::Log(LOGERROR, "PVRTimers - %s - no TimerInfoTag given", __FUNCTION__);
565 CPVRTimerInfoTag *tag = item.GetPVRTimerInfoTag();
569 return tag->RenameOnClient(strNewName);
572 bool CPVRTimers::UpdateTimer(CFileItem &item)
574 /* Check if a CPVRTimerInfoTag is inside file item */
575 if (!item.IsPVRTimer())
577 CLog::Log(LOGERROR, "PVRTimers - %s - no TimerInfoTag given", __FUNCTION__);
581 CPVRTimerInfoTag *tag = item.GetPVRTimerInfoTag();
585 return tag->UpdateOnClient();
588 CPVRTimerInfoTagPtr CPVRTimers::GetByClient(int iClientId, int iClientTimerId) const
590 CSingleLock lock(m_critSection);
592 for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
594 for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
596 if ((*timerIt)->m_iClientId == iClientId &&
597 (*timerIt)->m_iClientIndex == iClientTimerId)
602 CPVRTimerInfoTagPtr empty;
606 bool CPVRTimers::IsRecordingOnChannel(const CPVRChannel &channel) const
608 CSingleLock lock(m_critSection);
610 for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
612 for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
614 if ((*timerIt)->IsRecording() &&
615 (*timerIt)->m_iClientChannelUid == channel.UniqueID() &&
616 (*timerIt)->m_iClientId == channel.ClientID())
624 CFileItemPtr CPVRTimers::GetTimerForEpgTag(const CFileItem *item) const
626 if (item && item->HasEPGInfoTag() && item->GetEPGInfoTag()->ChannelTag())
628 const CEpgInfoTag *epgTag = item->GetEPGInfoTag();
629 const CPVRChannelPtr channel = epgTag->ChannelTag();
630 CSingleLock lock(m_critSection);
632 for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
634 for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
636 CPVRTimerInfoTagPtr timer = *timerIt;
637 if (timer->m_iClientChannelUid == channel->UniqueID() &&
638 timer->m_bIsRadio == channel->IsRadio() &&
639 timer->StartAsUTC() <= epgTag->StartAsUTC() &&
640 timer->EndAsUTC() >= epgTag->EndAsUTC())
642 CFileItemPtr fileItem(new CFileItem(*timer));
649 CFileItemPtr fileItem;
653 void CPVRTimers::Notify(const Observable &obs, const ObservableMessage msg)
655 if (msg == ObservableMessageEpgContainer)
656 g_PVRManager.TriggerTimersUpdate();
659 CDateTime CPVRTimers::GetNextEventTime(void) const
661 const bool dailywakup = g_guiSettings.GetBool("pvrpowermanagement.dailywakeup");
662 const CDateTime now = CDateTime::GetUTCDateTime();
663 const CDateTimeSpan prewakeup(0, 0, g_guiSettings.GetInt("pvrpowermanagement.prewakeup"), 0);
664 const CDateTimeSpan idle(0, 0, g_guiSettings.GetInt("pvrpowermanagement.backendidletime"), 0);
666 CDateTime wakeuptime;
668 /* Check next active time */
669 CFileItemPtr item = GetNextActiveTimer();
670 if (item && item->HasPVRTimerInfoTag())
672 const CDateTime start = item->GetPVRTimerInfoTag()->StartAsUTC();
673 wakeuptime = ((start - idle) > now) ?
678 /* check daily wake up */
681 CDateTime dailywakeuptime;
682 dailywakeuptime.SetFromDBTime(g_guiSettings.GetString("pvrpowermanagement.dailywakeuptime", false));
683 dailywakeuptime = dailywakeuptime.GetAsUTCDateTime();
685 dailywakeuptime.SetDateTime(
686 now.GetYear(), now.GetMonth(), now.GetDay(),
687 dailywakeuptime.GetHour(), dailywakeuptime.GetMinute(), dailywakeuptime.GetSecond()
690 if ((dailywakeuptime - idle) < now)
692 const CDateTimeSpan oneDay(1,0,0,0);
693 dailywakeuptime += oneDay;
695 if (!wakeuptime.IsValid() || dailywakeuptime < wakeuptime)
696 wakeuptime = dailywakeuptime;
699 const CDateTime retVal(wakeuptime);
703 void CPVRTimers::UpdateEpgEvent(CPVRTimerInfoTagPtr timer)
705 CSingleLock lock(timer->m_critSection);
707 /* already got an epg event set */
711 /* try to get the channel */
712 CPVRChannelPtr channel = g_PVRChannelGroups->GetByUniqueID(timer->m_iClientChannelUid, timer->m_iClientId);
716 /* try to get the EPG table */
717 CEpg *epg = channel->GetEPG();
721 /* try to set the timer on the epg tag that matches with a 2 minute margin */
722 CEpgInfoTagPtr epgTag = epg->GetTagBetween(timer->StartAsUTC() - CDateTimeSpan(0, 0, 2, 0), timer->EndAsUTC() + CDateTimeSpan(0, 0, 2, 0));
724 epgTag = epg->GetTagAround(timer->StartAsUTC());
728 timer->m_epgTag = epgTag;
729 timer->m_genre = epgTag->Genre();
730 timer->m_iGenreType = epgTag->GenreType();
731 timer->m_iGenreSubType = epgTag->GenreSubType();
732 epgTag->SetTimer(timer);
736 void CPVRTimers::UpdateChannels(void)
738 CSingleLock lock(m_critSection);
739 for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::iterator it = m_tags.begin(); it != m_tags.end(); it++)
741 for (vector<CPVRTimerInfoTagPtr>::iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
742 (*timerIt)->UpdateChannel();