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