[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / pvr / timers / PVRTimers.cpp
1 /*
2  *      Copyright (C) 2012-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "Application.h"
22 #include "FileItem.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"
30 #include "URL.h"
31
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"
37
38 using namespace std;
39 using namespace PVR;
40 using namespace EPG;
41
42 CPVRTimers::CPVRTimers(void)
43 {
44   m_bIsUpdating = false;
45 }
46
47 CPVRTimers::~CPVRTimers(void)
48 {
49   Unload();
50 }
51
52 bool CPVRTimers::Load(void)
53 {
54   // unload previous timers
55   Unload();
56
57   // (re)register observer
58   g_EpgContainer.RegisterObserver(this);
59
60   // update from clients
61   return Update();
62 }
63
64 void CPVRTimers::Unload()
65 {
66   // unregister observer
67   g_EpgContainer.UnregisterObserver(this);
68
69   // remove all tags
70   CSingleLock lock(m_critSection);
71   m_tags.clear();
72 }
73
74 bool CPVRTimers::Update(void)
75 {
76   {
77     CSingleLock lock(m_critSection);
78     if (m_bIsUpdating)
79       return false;
80     m_bIsUpdating = true;
81   }
82
83   CLog::Log(LOGDEBUG, "CPVRTimers - %s - updating timers", __FUNCTION__);
84   CPVRTimers newTimerList;
85   g_PVRClients->GetTimers(&newTimerList);
86   return UpdateEntries(newTimerList);
87 }
88
89 bool CPVRTimers::IsRecording(void) const
90 {
91   CSingleLock lock(m_critSection);
92
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())
96         return true;
97
98   return false;
99 }
100
101 bool CPVRTimers::UpdateEntries(const CPVRTimers &timers)
102 {
103   bool bChanged(false);
104   bool bAddedOrDeleted(false);
105   vector<CStdString> timerNotifications;
106
107   CSingleLock lock(m_critSection);
108
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++)
111   {
112     for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
113     {
114       /* check if this timer is present in this container */
115       CPVRTimerInfoTagPtr existingTimer = GetByClient((*timerIt)->m_iClientId, (*timerIt)->m_iClientIndex);
116       if (existingTimer)
117       {
118         /* if it's present, update the current tag */
119         bool bStateChanged(existingTimer->m_state != (*timerIt)->m_state);
120         if (existingTimer->UpdateEntry(*(*timerIt)))
121         {
122           bChanged = true;
123           UpdateEpgEvent(existingTimer);
124
125           if (bStateChanged && g_PVRManager.IsStarted())
126           {
127             CStdString strMessage;
128             existingTimer->GetNotificationText(strMessage);
129             timerNotifications.push_back(strMessage);
130           }
131
132           CLog::Log(LOGDEBUG,"PVRTimers - %s - updated timer %d on client %d",
133               __FUNCTION__, (*timerIt)->m_iClientIndex, (*timerIt)->m_iClientId);
134         }
135       }
136       else
137       {
138         /* new timer */
139         CPVRTimerInfoTagPtr newTimer = CPVRTimerInfoTagPtr(new CPVRTimerInfoTag);
140         newTimer->UpdateEntry(*(*timerIt));
141         UpdateEpgEvent(newTimer);
142
143         vector<CPVRTimerInfoTagPtr>* addEntry = NULL;
144         map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::iterator itr = m_tags.find(newTimer->StartAsUTC());
145         if (itr == m_tags.end())
146         {
147           addEntry = new vector<CPVRTimerInfoTagPtr>;
148           m_tags.insert(make_pair(newTimer->StartAsUTC(), addEntry));
149         }
150         else
151         {
152           addEntry = itr->second;
153         }
154
155         addEntry->push_back(newTimer);
156         UpdateEpgEvent(newTimer);
157         bChanged = true;
158         bAddedOrDeleted = true;
159
160         if (g_PVRManager.IsStarted())
161         {
162           CStdString strMessage;
163           newTimer->GetNotificationText(strMessage);
164           timerNotifications.push_back(strMessage);
165         }
166
167         CLog::Log(LOGDEBUG,"PVRTimers - %s - added timer %d on client %d",
168             __FUNCTION__, (*timerIt)->m_iClientIndex, (*timerIt)->m_iClientId);
169       }
170     }
171   }
172
173   /* to collect timer with changed starting time */
174   vector<CPVRTimerInfoTagPtr> timersToMove;
175   
176   /* check for deleted timers */
177   for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::iterator it = m_tags.begin(); it != m_tags.end();)
178   {
179     for (int iTimerPtr = it->second->size() - 1; iTimerPtr >= 0; iTimerPtr--)
180     {
181       CPVRTimerInfoTagPtr timer = it->second->at(iTimerPtr);
182       if (!timers.GetByClient(timer->m_iClientId, timer->m_iClientIndex))
183       {
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);
187
188         if (g_PVRManager.IsStarted())
189         {
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);
197         }
198
199         it->second->erase(it->second->begin() + iTimerPtr);
200
201         bChanged = true;
202         bAddedOrDeleted = true;
203       }
204       else if (timer->StartAsUTC() != it->first)
205       {
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);
209
210         timer->ClearEpgTag();
211
212         /* remember timer */
213         timersToMove.push_back(timer);
214         
215         /* remove timer for now, reinsert later */
216         it->second->erase(it->second->begin() + iTimerPtr);
217
218         bChanged = true;
219         bAddedOrDeleted = true;
220       }
221     }
222     if (it->second->size() == 0)
223       m_tags.erase(it++);
224     else
225       ++it;
226   }
227
228   /* reinsert timers with changed timer start */
229   for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = timersToMove.begin(); timerIt != timersToMove.end(); timerIt++)
230   {
231       vector<CPVRTimerInfoTagPtr>* addEntry = NULL;
232       map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator itr = m_tags.find((*timerIt)->StartAsUTC());
233       if (itr == m_tags.end())
234       {
235         addEntry = new vector<CPVRTimerInfoTagPtr>;
236         m_tags.insert(make_pair((*timerIt)->StartAsUTC(), addEntry));
237       }
238       else
239       {
240         addEntry = itr->second;
241       }
242
243       addEntry->push_back(*timerIt);
244       UpdateEpgEvent(*timerIt);
245   }
246
247   m_bIsUpdating = false;
248   if (bChanged)
249   {
250     UpdateChannels();
251     SetChanged();
252     lock.Leave();
253
254     NotifyObservers(bAddedOrDeleted ? ObservableMessageTimersReset : ObservableMessageTimers);
255
256     if (g_guiSettings.GetBool("pvrrecord.timernotifications"))
257     {
258       /* queue notifications */
259       for (unsigned int iNotificationPtr = 0; iNotificationPtr < timerNotifications.size(); iNotificationPtr++)
260       {
261         CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info,
262             g_localizeStrings.Get(19166),
263             timerNotifications.at(iNotificationPtr));
264       }
265     }
266   }
267
268   return bChanged;
269 }
270
271 bool CPVRTimers::UpdateFromClient(const CPVRTimerInfoTag &timer)
272 {
273   CSingleLock lock(m_critSection);
274   CPVRTimerInfoTagPtr tag = GetByClient(timer.m_iClientId, timer.m_iClientIndex);
275   if (!tag)
276   {
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())
281     {
282       addEntry = new vector<CPVRTimerInfoTagPtr>;
283       m_tags.insert(make_pair(timer.StartAsUTC(), addEntry));
284     }
285     else
286     {
287       addEntry = itr->second;
288     }
289     addEntry->push_back(tag);
290   }
291
292   UpdateEpgEvent(tag);
293
294   return tag->UpdateEntry(timer);
295 }
296
297 /********** getters **********/
298
299 CFileItemPtr CPVRTimers::GetNextActiveTimer(void) const
300 {
301   CSingleLock lock(m_critSection);
302   for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
303   {
304     for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
305     {
306       CPVRTimerInfoTagPtr current = *timerIt;
307       if (current->IsActive() && !current->IsRecording())
308       {
309         CFileItemPtr fileItem(new CFileItem(*current));
310         return fileItem;
311       }
312     }
313   }
314
315   CFileItemPtr fileItem;
316   return fileItem;
317 }
318
319 vector<CFileItemPtr> CPVRTimers::GetActiveTimers(void) const
320 {
321   vector<CFileItemPtr> tags;
322   CSingleLock lock(m_critSection);
323
324   for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
325   {
326     for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
327     {
328       CPVRTimerInfoTagPtr current = *timerIt;
329       if (current->IsActive())
330       {
331         CFileItemPtr fileItem(new CFileItem(*current));
332         tags.push_back(fileItem);
333       }
334     }
335   }
336
337   return tags;
338 }
339
340 int CPVRTimers::AmountActiveTimers(void) const
341 {
342   int iReturn(0);
343   CSingleLock lock(m_critSection);
344
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())
348         ++iReturn;
349
350   return iReturn;
351 }
352
353 std::vector<CFileItemPtr> CPVRTimers::GetActiveRecordings(void) const
354 {
355   std::vector<CFileItemPtr> tags;
356   CSingleLock lock(m_critSection);
357
358   for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
359   {
360     for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
361     {
362       CPVRTimerInfoTagPtr current = *timerIt;
363       if (current->IsRecording())
364       {
365         CFileItemPtr fileItem(new CFileItem(*current));
366         tags.push_back(fileItem);
367       }
368     }
369   }
370
371   return tags;
372 }
373
374 int CPVRTimers::AmountActiveRecordings(void) const
375 {
376   int iReturn(0);
377   CSingleLock lock(m_critSection);
378
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())
382         ++iReturn;
383
384   return iReturn;
385 }
386
387 bool CPVRTimers::HasActiveTimers(void) const
388 {
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())
393         return true;
394
395   return false;
396 }
397
398 bool CPVRTimers::GetDirectory(const CStdString& strPath, CFileItemList &items) const
399 {
400   CStdString base(strPath);
401   URIUtils::RemoveSlashAtEnd(base);
402
403   CURL url(strPath);
404   CStdString fileName = url.GetFileName();
405   URIUtils::RemoveSlashAtEnd(fileName);
406
407   if (fileName == "timers")
408   {
409     CFileItemPtr item;
410
411     item.reset(new CFileItem(base + "/add.timer", false));
412     item->SetLabel(g_localizeStrings.Get(19026));
413     item->SetLabelPreformated(true);
414     items.Add(item);
415
416     CSingleLock lock(m_critSection);
417     for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
418     {
419       for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
420       {
421         CPVRTimerInfoTagPtr current = *timerIt;
422         item.reset(new CFileItem(*current));
423         items.Add(item);
424       }
425     }
426
427     return true;
428   }
429   return false;
430 }
431
432 /********** channel methods **********/
433
434 bool CPVRTimers::DeleteTimersOnChannel(const CPVRChannel &channel, bool bDeleteRepeating /* = true */, bool bCurrentlyActiveOnly /* = false */)
435 {
436   bool bReturn = false;
437   {
438     CSingleLock lock(m_critSection);
439
440     for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::reverse_iterator it = m_tags.rbegin(); it != m_tags.rend(); it++)
441     {
442       for (vector<CPVRTimerInfoTagPtr>::iterator timerIt = it->second->begin(); timerIt != it->second->end(); )
443       {
444         bool bDeleteActiveItem = !bCurrentlyActiveOnly || (*timerIt)->IsRecording();
445         bool bDeleteRepeatingItem = bDeleteRepeating || !(*timerIt)->m_bIsRepeating;
446         bool bChannelsMatch = *(*timerIt)->ChannelTag() == channel;
447
448         if (bDeleteActiveItem && bDeleteRepeatingItem && bChannelsMatch)
449         {
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);
453           SetChanged();
454         }
455         else
456         {
457           ++timerIt;
458         }
459       }
460     }
461   }
462
463   NotifyObservers(ObservableMessageTimersReset);
464
465   return bReturn;
466 }
467
468 bool CPVRTimers::InstantTimer(const CPVRChannel &channel)
469 {
470   if (!g_PVRManager.CheckParentalLock(channel))
471     return false;
472
473   CEpgInfoTag epgTag;
474   bool bHasEpgNow = channel.GetEPGNow(epgTag);
475   CPVRTimerInfoTag *newTimer = bHasEpgNow ? CPVRTimerInfoTag::CreateFromEpg(epgTag) : NULL;
476   if (!newTimer)
477   {
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();
487
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));
495   }
496
497   CDateTime startTime(0);
498   newTimer->SetStartFromUTC(startTime);
499   newTimer->m_iMarginStart = 0; /* set the start margin to 0 for instant timers */
500
501   int iDuration = g_guiSettings.GetInt("pvrrecord.instantrecordtime");
502   CDateTime endTime = CDateTime::GetUTCDateTime() + CDateTimeSpan(0, 0, iDuration ? iDuration : 120, 0);
503   newTimer->SetEndFromUTC(endTime);
504
505   /* unused only for reference */
506   newTimer->m_strFileNameAndPath = "pvr://timers/new";
507
508   bool bReturn = newTimer->AddToClient();
509   if (!bReturn)
510     CLog::Log(LOGERROR, "PVRTimers - %s - unable to add an instant timer on the client", __FUNCTION__);
511
512   delete newTimer;
513
514   return bReturn;
515 }
516
517 /********** static methods **********/
518
519 bool CPVRTimers::AddTimer(const CPVRTimerInfoTag &item)
520 {
521   if (!item.m_channel)
522   {
523     CLog::Log(LOGERROR, "PVRTimers - %s - no channel given", __FUNCTION__);
524     CGUIDialogOK::ShowAndGetInput(19033,0,19109,0); // Couldn't save timer
525     return false;
526   }
527
528   if (!g_PVRClients->SupportsTimers(item.m_iClientId))
529   {
530     CGUIDialogOK::ShowAndGetInput(19033,0,19215,0);
531     return false;
532   }
533
534   if (!g_PVRManager.CheckParentalLock(*item.m_channel))
535     return false;
536
537   return item.AddToClient();
538 }
539
540 bool CPVRTimers::DeleteTimer(const CFileItem &item, bool bForce /* = false */)
541 {
542   /* Check if a CPVRTimerInfoTag is inside file item */
543   if (!item.IsPVRTimer())
544   {
545     CLog::Log(LOGERROR, "PVRTimers - %s - no TimerInfoTag given", __FUNCTION__);
546     return false;
547   }
548
549   const CPVRTimerInfoTag *tag = item.GetPVRTimerInfoTag();
550   if (!tag)
551     return false;
552
553   return tag->DeleteFromClient(bForce);
554 }
555
556 bool CPVRTimers::RenameTimer(CFileItem &item, const CStdString &strNewName)
557 {
558   /* Check if a CPVRTimerInfoTag is inside file item */
559   if (!item.IsPVRTimer())
560   {
561     CLog::Log(LOGERROR, "PVRTimers - %s - no TimerInfoTag given", __FUNCTION__);
562     return false;
563   }
564
565   CPVRTimerInfoTag *tag = item.GetPVRTimerInfoTag();
566   if (!tag)
567     return false;
568
569   return tag->RenameOnClient(strNewName);
570 }
571
572 bool CPVRTimers::UpdateTimer(CFileItem &item)
573 {
574   /* Check if a CPVRTimerInfoTag is inside file item */
575   if (!item.IsPVRTimer())
576   {
577     CLog::Log(LOGERROR, "PVRTimers - %s - no TimerInfoTag given", __FUNCTION__);
578     return false;
579   }
580
581   CPVRTimerInfoTag *tag = item.GetPVRTimerInfoTag();
582   if (!tag)
583     return false;
584
585   return tag->UpdateOnClient();
586 }
587
588 CPVRTimerInfoTagPtr CPVRTimers::GetByClient(int iClientId, int iClientTimerId) const
589 {
590   CSingleLock lock(m_critSection);
591
592   for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
593   {
594     for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
595     {
596       if ((*timerIt)->m_iClientId == iClientId &&
597           (*timerIt)->m_iClientIndex == iClientTimerId)
598         return *timerIt;
599     }
600   }
601
602   CPVRTimerInfoTagPtr empty;
603   return empty;
604 }
605
606 bool CPVRTimers::IsRecordingOnChannel(const CPVRChannel &channel) const
607 {
608   CSingleLock lock(m_critSection);
609
610   for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
611   {
612     for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
613     {
614       if ((*timerIt)->IsRecording() &&
615           (*timerIt)->m_iClientChannelUid == channel.UniqueID() &&
616           (*timerIt)->m_iClientId == channel.ClientID())
617         return true;
618     }
619   }
620
621   return false;
622 }
623
624 CFileItemPtr CPVRTimers::GetTimerForEpgTag(const CFileItem *item) const
625 {
626   if (item && item->HasEPGInfoTag() && item->GetEPGInfoTag()->ChannelTag())
627   {
628     const CEpgInfoTag *epgTag = item->GetEPGInfoTag();
629     const CPVRChannelPtr channel = epgTag->ChannelTag();
630     CSingleLock lock(m_critSection);
631
632     for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++)
633     {
634       for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
635       {
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())
641         {
642           CFileItemPtr fileItem(new CFileItem(*timer));
643           return fileItem;
644         }
645       }
646     }
647   }
648
649   CFileItemPtr fileItem;
650   return fileItem;
651 }
652
653 void CPVRTimers::Notify(const Observable &obs, const ObservableMessage msg)
654 {
655   if (msg == ObservableMessageEpgContainer)
656     g_PVRManager.TriggerTimersUpdate();
657 }
658
659 CDateTime CPVRTimers::GetNextEventTime(void) const
660 {
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);
665
666   CDateTime wakeuptime;
667
668   /* Check next active time */
669   CFileItemPtr item = GetNextActiveTimer();
670   if (item && item->HasPVRTimerInfoTag())
671   {
672     const CDateTime start = item->GetPVRTimerInfoTag()->StartAsUTC();
673     wakeuptime = ((start - idle) > now) ?
674         start - prewakeup:
675         now + idle;
676   }
677
678   /* check daily wake up */
679   if (dailywakup)
680   {
681     CDateTime dailywakeuptime;
682     dailywakeuptime.SetFromDBTime(g_guiSettings.GetString("pvrpowermanagement.dailywakeuptime", false));
683     dailywakeuptime = dailywakeuptime.GetAsUTCDateTime();
684
685     dailywakeuptime.SetDateTime(
686       now.GetYear(), now.GetMonth(), now.GetDay(),
687       dailywakeuptime.GetHour(), dailywakeuptime.GetMinute(), dailywakeuptime.GetSecond()
688     );
689
690     if ((dailywakeuptime - idle) < now)
691     {
692       const CDateTimeSpan oneDay(1,0,0,0);
693       dailywakeuptime += oneDay;
694     }
695     if (!wakeuptime.IsValid() || dailywakeuptime < wakeuptime)
696       wakeuptime = dailywakeuptime;
697   }
698
699   const CDateTime retVal(wakeuptime);
700   return retVal;
701 }
702
703 void CPVRTimers::UpdateEpgEvent(CPVRTimerInfoTagPtr timer)
704 {
705   CSingleLock lock(timer->m_critSection);
706
707   /* already got an epg event set */
708   if (timer->m_epgTag)
709     return;
710
711   /* try to get the channel */
712   CPVRChannelPtr channel = g_PVRChannelGroups->GetByUniqueID(timer->m_iClientChannelUid, timer->m_iClientId);
713   if (!channel)
714     return;
715
716   /* try to get the EPG table */
717   CEpg *epg = channel->GetEPG();
718   if (!epg)
719     return;
720
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));
723   if (!epgTag)
724     epgTag = epg->GetTagAround(timer->StartAsUTC());
725
726   if (epgTag)
727   {
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);
733   }
734 }
735
736 void CPVRTimers::UpdateChannels(void)
737 {
738   CSingleLock lock(m_critSection);
739   for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::iterator it = m_tags.begin(); it != m_tags.end(); it++)
740   {
741     for (vector<CPVRTimerInfoTagPtr>::iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++)
742       (*timerIt)->UpdateChannel();
743   }
744 }