[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / epg / EpgContainer.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 "threads/SingleLock.h"
23 #include "settings/AdvancedSettings.h"
24 #include "settings/GUISettings.h"
25 #include "dialogs/GUIDialogExtendedProgressBar.h"
26 #include "dialogs/GUIDialogProgress.h"
27 #include "guilib/GUIWindowManager.h"
28 #include "guilib/LocalizeStrings.h"
29 #include "utils/log.h"
30 #include "pvr/PVRManager.h"
31 #include "pvr/channels/PVRChannelGroupsContainer.h"
32 #include "pvr/timers/PVRTimers.h"
33
34 #include "EpgContainer.h"
35 #include "Epg.h"
36 #include "EpgInfoTag.h"
37 #include "EpgSearchFilter.h"
38
39 using namespace std;
40 using namespace EPG;
41 using namespace PVR;
42
43 typedef std::map<int, CEpg*>::iterator EPGITR;
44
45 CEpgContainer::CEpgContainer(void) :
46     CThread("EPG updater")
47 {
48   m_progressHandle = NULL;
49   m_bStop = true;
50   m_bIsUpdating = false;
51   m_bIsInitialising = true;
52   m_iNextEpgId = 0;
53   m_bPreventUpdates = false;
54   m_updateEvent.Reset();
55   m_bLoaded = false;
56   m_bHasPendingUpdates = false;
57 }
58
59 CEpgContainer::~CEpgContainer(void)
60 {
61   Unload();
62 }
63
64 CEpgContainer &CEpgContainer::Get(void)
65 {
66   static CEpgContainer epgInstance;
67   return epgInstance;
68 }
69
70 void CEpgContainer::Unload(void)
71 {
72   Stop();
73   Clear(false);
74 }
75
76 unsigned int CEpgContainer::NextEpgId(void)
77 {
78   CSingleLock lock(m_critSection);
79   return ++m_iNextEpgId;
80 }
81
82 void CEpgContainer::Clear(bool bClearDb /* = false */)
83 {
84   /* make sure the update thread is stopped */
85   bool bThreadRunning = !m_bStop;
86   if (bThreadRunning && !Stop())
87   {
88     CLog::Log(LOGERROR, "%s - cannot stop the update thread", __FUNCTION__);
89     return;
90   }
91
92   {
93     CSingleLock lock(m_critSection);
94     /* clear all epg tables and remove pointers to epg tables on channels */
95     for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
96     {
97       it->second->UnregisterObserver(this);
98       delete it->second;
99     }
100     m_epgs.clear();
101     m_iNextEpgUpdate  = 0;
102     m_bIsInitialising = true;
103     m_iNextEpgId = 0;
104   }
105
106   /* clear the database entries */
107   if (bClearDb && !m_bIgnoreDbForClient)
108   {
109     if (!m_database.IsOpen())
110       m_database.Open();
111
112     if (m_database.IsOpen())
113       m_database.DeleteEpg();
114   }
115
116   SetChanged();
117   NotifyObservers(ObservableMessageEpgContainer);
118
119   if (bThreadRunning)
120     Start();
121 }
122
123 void CEpgContainer::Start(void)
124 {
125   Stop();
126
127   CSingleLock lock(m_critSection);
128
129   if (!m_database.IsOpen())
130     m_database.Open();
131
132   m_bIsInitialising = true;
133   m_bStop = false;
134   g_guiSettings.RegisterObserver(this);
135   LoadSettings();
136
137   m_iNextEpgUpdate  = 0;
138   m_iNextEpgActiveTagCheck = 0;
139
140   LoadFromDB();
141   CheckPlayingEvents();
142
143   Create();
144   SetPriority(-1);
145   CLog::Log(LOGNOTICE, "%s - EPG thread started", __FUNCTION__);
146 }
147
148 bool CEpgContainer::Stop(void)
149 {
150   StopThread();
151
152   if (m_database.IsOpen())
153     m_database.Close();
154
155   return true;
156 }
157
158 void CEpgContainer::Notify(const Observable &obs, const ObservableMessage msg)
159 {
160   /* settings were updated */
161   if (msg == ObservableMessageGuiSettings)
162     LoadSettings();
163   else
164   {
165     SetChanged();
166     NotifyObservers(msg);
167   }
168 }
169
170 void CEpgContainer::LoadFromDB(void)
171 {
172   if (m_bLoaded || m_bIgnoreDbForClient)
173     return;
174
175   if (!m_database.IsOpen())
176     m_database.Open();
177
178   m_iNextEpgId = m_database.GetLastEPGId();
179
180   bool bLoaded(true);
181   unsigned int iCounter(0);
182   if (m_database.IsOpen())
183   {
184     ShowProgressDialog(false);
185
186     m_database.DeleteOldEpgEntries();
187     m_database.Get(*this);
188
189     for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
190     {
191       UpdateProgressDialog(++iCounter, m_epgs.size(), it->second->Name());
192       it->second->Load();
193     }
194
195     CloseProgressDialog();
196   }
197
198   CSingleLock lock(m_critSection);
199   m_bLoaded = bLoaded;
200 }
201
202 bool CEpgContainer::PersistTables(void)
203 {
204   return m_database.Persist(*this);
205 }
206
207 bool CEpgContainer::PersistAll(void)
208 {
209   bool bReturn(true);
210   CSingleLock lock(m_critSection);
211   for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end() && !m_bStop; it++)
212   {
213     CEpg *epg = it->second;
214     if (epg && epg->NeedsSave())
215     {
216       lock.Leave();
217       bReturn &= epg->Persist();
218       lock.Enter();
219     }
220   }
221
222   return bReturn;
223 }
224
225 void CEpgContainer::Process(void)
226 {
227   time_t iNow(0), iLastSave(0);
228   bool bUpdateEpg(true);
229   bool bHasPendingUpdates(false);
230
231   if (!CPVRManager::Get().WaitUntilInitialised())
232   {
233     CLog::Log(LOGDEBUG, "EPG - %s - pvr manager failed to load - exiting", __FUNCTION__);
234     return;
235   }
236
237   while (!m_bStop && !g_application.m_bStop)
238   {
239     CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow);
240     {
241       CSingleLock lock(m_critSection);
242       bUpdateEpg = (iNow >= m_iNextEpgUpdate);
243     }
244
245     /* update the EPG */
246     if (!InterruptUpdate() && bUpdateEpg && UpdateEPG())
247       m_bIsInitialising = false;
248
249     /* clean up old entries */
250     if (!m_bStop && iNow >= m_iLastEpgCleanup)
251       RemoveOldEntries();
252
253     /* check for pending manual EPG updates */
254     if (!m_bStop)
255     {
256       {
257         CSingleLock lock(m_critSection);
258         bHasPendingUpdates = m_bHasPendingUpdates;
259       }
260
261       if (bHasPendingUpdates)
262         UpdateEPG(true);
263     }
264
265     /* check for updated active tag */
266     if (!m_bStop)
267       CheckPlayingEvents();
268
269     /* check for changes that need to be saved every 60 seconds */
270     if (iNow - iLastSave > 60)
271     {
272       PersistAll();
273       iLastSave = iNow;
274     }
275
276     Sleep(1000);
277   }
278
279   g_guiSettings.UnregisterObserver(this);
280 }
281
282 CEpg *CEpgContainer::GetById(int iEpgId) const
283 {
284   if (iEpgId < 0)
285     return NULL;
286
287   CSingleLock lock(m_critSection);
288   map<unsigned int, CEpg *>::const_iterator it = m_epgs.find((unsigned int) iEpgId);
289   return it != m_epgs.end() ? it->second : NULL;
290 }
291
292 CEpg *CEpgContainer::GetByChannel(const CPVRChannel &channel) const
293 {
294   CSingleLock lock(m_critSection);
295   for (map<unsigned int, CEpg *>::const_iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
296     if (channel.ChannelID() == it->second->ChannelID())
297       return it->second;
298
299   return NULL;
300 }
301
302 void CEpgContainer::InsertFromDatabase(int iEpgID, const CStdString &strName, const CStdString &strScraperName)
303 {
304   // table might already have been created when pvr channels were loaded
305   CEpg* epg = GetById(iEpgID);
306   if (epg)
307   {
308     if (!epg->Name().Equals(strName) || !epg->ScraperName().Equals(strScraperName))
309     {
310       // current table data differs from the info in the db
311       epg->SetChanged();
312       SetChanged();
313     }
314   }
315   else
316   {
317     // create a new epg table
318     epg = new CEpg(iEpgID, strName, strScraperName, true);
319     if (epg)
320     {
321       m_epgs.insert(make_pair(iEpgID, epg));
322       SetChanged();
323       epg->RegisterObserver(this);
324     }
325   }
326 }
327
328 CEpg *CEpgContainer::CreateChannelEpg(CPVRChannelPtr channel)
329 {
330   if (!channel)
331     return NULL;
332
333   WaitForUpdateFinish(true);
334   CSingleLock lock(m_critSection);
335   LoadFromDB();
336
337   CEpg *epg(NULL);
338   if (channel->EpgID() > 0)
339     epg = GetById(channel->EpgID());
340
341   if (!epg)
342   {
343     channel->SetEpgID(NextEpgId());
344     epg = new CEpg(channel, false);
345     m_epgs.insert(make_pair((unsigned int)epg->EpgID(), epg));
346     SetChanged();
347     epg->RegisterObserver(this);
348   }
349
350   epg->SetChannel(channel);
351
352   m_bPreventUpdates = false;
353   CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgUpdate);
354
355   NotifyObservers(ObservableMessageEpgContainer);
356
357   return epg;
358 }
359
360 bool CEpgContainer::LoadSettings(void)
361 {
362   m_bIgnoreDbForClient = g_guiSettings.GetBool("epg.ignoredbforclient");
363   m_iUpdateTime        = g_guiSettings.GetInt ("epg.epgupdate") * 60;
364   m_iDisplayTime       = g_guiSettings.GetInt ("epg.daystodisplay") * 24 * 60 * 60;
365
366   return true;
367 }
368
369 bool CEpgContainer::RemoveOldEntries(void)
370 {
371   CDateTime now = CDateTime::GetUTCDateTime() -
372       CDateTimeSpan(0, g_advancedSettings.m_iEpgLingerTime / 60, g_advancedSettings.m_iEpgLingerTime % 60, 0);
373
374   /* call Cleanup() on all known EPG tables */
375   for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
376     it->second->Cleanup(now);
377
378   /* remove the old entries from the database */
379   if (!m_bIgnoreDbForClient && m_database.IsOpen())
380     m_database.DeleteOldEpgEntries();
381
382   CSingleLock lock(m_critSection);
383   CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iLastEpgCleanup);
384   m_iLastEpgCleanup += g_advancedSettings.m_iEpgCleanupInterval;
385
386   return true;
387 }
388
389 bool CEpgContainer::DeleteEpg(const CEpg &epg, bool bDeleteFromDatabase /* = false */)
390 {
391   if (epg.EpgID() < 0)
392     return false;
393
394   CSingleLock lock(m_critSection);
395
396   map<unsigned int, CEpg *>::iterator it = m_epgs.find((unsigned int)epg.EpgID());
397   if (it == m_epgs.end())
398     return false;
399
400   CLog::Log(LOGDEBUG, "deleting EPG table %s (%d)", epg.Name().c_str(), epg.EpgID());
401   if (bDeleteFromDatabase && !m_bIgnoreDbForClient && m_database.IsOpen())
402     m_database.Delete(*it->second);
403
404   it->second->UnregisterObserver(this);
405   delete it->second;
406   m_epgs.erase(it);
407
408   return true;
409 }
410
411 void CEpgContainer::CloseProgressDialog(void)
412 {
413   if (m_progressHandle)
414   {
415     m_progressHandle->MarkFinished();
416     m_progressHandle = NULL;
417   }
418 }
419
420 void CEpgContainer::ShowProgressDialog(bool bUpdating /* = true */)
421 {
422   if (!m_progressHandle)
423   {
424     CGUIDialogExtendedProgressBar *progressDialog = (CGUIDialogExtendedProgressBar *)g_windowManager.GetWindow(WINDOW_DIALOG_EXT_PROGRESS);
425     if (progressDialog)
426       m_progressHandle = progressDialog->GetHandle(bUpdating ? g_localizeStrings.Get(19004) : g_localizeStrings.Get(19250));
427   }
428 }
429
430 void CEpgContainer::UpdateProgressDialog(int iCurrent, int iMax, const CStdString &strText)
431 {
432   if (!m_progressHandle)
433     ShowProgressDialog();
434
435   if (m_progressHandle)
436   {
437     m_progressHandle->SetProgress(iCurrent, iMax);
438     m_progressHandle->SetText(strText);
439   }
440 }
441
442 bool CEpgContainer::InterruptUpdate(void) const
443 {
444   bool bReturn(false);
445   CSingleLock lock(m_critSection);
446   bReturn = g_application.m_bStop || m_bStop || m_bPreventUpdates;
447   lock.Leave();
448
449   return bReturn ||
450     (g_guiSettings.GetBool("epg.preventupdateswhileplayingtv") &&
451      g_PVRManager.IsStarted() &&
452      g_PVRManager.IsPlaying());
453 }
454
455 void CEpgContainer::WaitForUpdateFinish(bool bInterrupt /* = true */)
456 {
457   {
458     CSingleLock lock(m_critSection);
459     if (bInterrupt)
460       m_bPreventUpdates = true;
461
462     if (!m_bIsUpdating)
463       return;
464
465     m_updateEvent.Reset();
466   }
467
468   m_updateEvent.Wait();
469 }
470
471 bool CEpgContainer::UpdateEPG(bool bOnlyPending /* = false */)
472 {
473   bool bInterrupted(false);
474   unsigned int iUpdatedTables(0);
475   bool bShowProgress(false);
476
477   /* set start and end time */
478   time_t start;
479   time_t end;
480   CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(start);
481   end = start + m_iDisplayTime;
482   start -= g_advancedSettings.m_iEpgLingerTime * 60;
483   bShowProgress = g_advancedSettings.m_bEpgDisplayUpdatePopup && (m_bIsInitialising || g_advancedSettings.m_bEpgDisplayIncrementalUpdatePopup);
484
485   {
486     CSingleLock lock(m_critSection);
487     if (m_bIsUpdating || InterruptUpdate())
488       return false;
489     m_bIsUpdating = true;
490   }
491
492   if (bShowProgress && !bOnlyPending)
493     ShowProgressDialog();
494
495   if (!m_bIgnoreDbForClient && !m_database.IsOpen())
496   {
497     CLog::Log(LOGERROR, "EpgContainer - %s - could not open the database", __FUNCTION__);
498
499     CSingleLock lock(m_critSection);
500     m_bIsUpdating = false;
501     m_updateEvent.Set();
502
503     if (bShowProgress && !bOnlyPending)
504       CloseProgressDialog();
505
506     return false;
507   }
508
509   vector<CEpg*> invalidTables;
510
511   /* load or update all EPG tables */
512   CEpg *epg;
513   unsigned int iCounter(0);
514   for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
515   {
516     if (InterruptUpdate())
517     {
518       bInterrupted = true;
519       break;
520     }
521
522     epg = it->second;
523     if (!epg)
524       continue;
525
526     if (bShowProgress && !bOnlyPending)
527       UpdateProgressDialog(++iCounter, m_epgs.size(), epg->Name());
528
529     // we currently only support update via pvr add-ons. skip update when the pvr manager isn't started
530     if (!g_PVRManager.IsStarted())
531       continue;
532
533     // check the pvr manager when the channel pointer isn't set
534     if (!epg->Channel())
535     {
536       CPVRChannelPtr channel = g_PVRChannelGroups->GetChannelByEpgId(epg->EpgID());
537       if (channel)
538         epg->SetChannel(channel);
539     }
540
541     if ((!bOnlyPending || epg->UpdatePending()) && epg->Update(start, end, m_iUpdateTime, bOnlyPending))
542       iUpdatedTables++;
543     else if (!epg->IsValid())
544       invalidTables.push_back(epg);
545   }
546
547   for (vector<CEpg*>::iterator it = invalidTables.begin(); it != invalidTables.end(); it++)
548     DeleteEpg(**it, true);
549
550   if (bInterrupted)
551   {
552     /* the update has been interrupted. try again later */
553     time_t iNow;
554     CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow);
555     m_iNextEpgUpdate = iNow + g_advancedSettings.m_iEpgRetryInterruptedUpdateInterval;
556   }
557   else
558   {
559     CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgUpdate);
560     m_iNextEpgUpdate += g_advancedSettings.m_iEpgUpdateCheckInterval;
561     m_bHasPendingUpdates = false;
562   }
563
564   if (bShowProgress && !bOnlyPending)
565     CloseProgressDialog();
566
567   /* notify observers */
568   if (iUpdatedTables > 0)
569   {
570     SetChanged();
571     NotifyObservers(ObservableMessageEpgContainer);
572   }
573
574   CSingleLock lock(m_critSection);
575   m_bIsUpdating = false;
576   m_updateEvent.Set();
577
578   return !bInterrupted;
579 }
580
581 int CEpgContainer::GetEPGAll(CFileItemList &results)
582 {
583   int iInitialSize = results.Size();
584
585   CSingleLock lock(m_critSection);
586   for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
587     it->second->Get(results);
588
589   return results.Size() - iInitialSize;
590 }
591
592 const CDateTime CEpgContainer::GetFirstEPGDate(void)
593 {
594   CDateTime returnValue;
595
596   CSingleLock lock(m_critSection);
597   for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
598   {
599     lock.Leave();
600     CDateTime entry = it->second->GetFirstDate();
601     if (entry.IsValid() && (!returnValue.IsValid() || entry < returnValue))
602       returnValue = entry;
603     lock.Enter();
604   }
605
606   return returnValue;
607 }
608
609 const CDateTime CEpgContainer::GetLastEPGDate(void)
610 {
611   CDateTime returnValue;
612
613   CSingleLock lock(m_critSection);
614   for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
615   {
616     lock.Leave();
617     CDateTime entry = it->second->GetLastDate();
618     if (entry.IsValid() && (!returnValue.IsValid() || entry > returnValue))
619       returnValue = entry;
620     lock.Enter();
621   }
622
623   return returnValue;
624 }
625
626 int CEpgContainer::GetEPGSearch(CFileItemList &results, const EpgSearchFilter &filter)
627 {
628   int iInitialSize = results.Size();
629
630   /* get filtered results from all tables */
631   {
632     CSingleLock lock(m_critSection);
633     for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
634       it->second->Get(results, filter);
635   }
636
637   /* remove duplicate entries */
638   if (filter.m_bPreventRepeats)
639     EpgSearchFilter::RemoveDuplicates(results);
640
641   return results.Size() - iInitialSize;
642 }
643
644 bool CEpgContainer::CheckPlayingEvents(void)
645 {
646   bool bReturn(false);
647   time_t iNow;
648   CSingleLock lock(m_critSection);
649
650   CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow);
651   if (iNow >= m_iNextEpgActiveTagCheck)
652   {
653     bool bFoundChanges(false);
654     CSingleLock lock(m_critSection);
655
656     for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
657       bFoundChanges = it->second->CheckPlayingEvent() || bFoundChanges;
658     CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgActiveTagCheck);
659     m_iNextEpgActiveTagCheck += g_advancedSettings.m_iEpgActiveTagCheckInterval;
660
661     if (bFoundChanges)
662     {
663       SetChanged();
664       NotifyObservers(ObservableMessageEpgActiveItem);
665     }
666
667     /* pvr tags always start on the full minute */
668     if (g_PVRManager.IsStarted())
669       m_iNextEpgActiveTagCheck -= m_iNextEpgActiveTagCheck % 60;
670
671     bReturn = true;
672   }
673
674   return bReturn;
675 }
676
677 bool CEpgContainer::IsInitialising(void) const
678 {
679   CSingleLock lock(m_critSection);
680   return m_bIsInitialising;
681 }
682
683 void CEpgContainer::SetHasPendingUpdates(bool bHasPendingUpdates /* = true */)
684 {
685   CSingleLock lock(m_critSection);
686   m_bHasPendingUpdates = bHasPendingUpdates;
687 }