FIX: [droid] set "remote as keyboard" default to true
[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/lib/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   while (!m_bStop && !g_application.m_bStop)
263   {
264     CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow);
265     {
266       CSingleLock lock(m_critSection);
267       bUpdateEpg = (iNow >= m_iNextEpgUpdate);
268     }
269
270     /* update the EPG */
271     if (!InterruptUpdate() && bUpdateEpg && g_PVRManager.EpgsCreated() && UpdateEPG())
272       m_bIsInitialising = false;
273
274     /* clean up old entries */
275     if (!m_bStop && iNow >= m_iLastEpgCleanup)
276       RemoveOldEntries();
277
278     /* check for pending manual EPG updates */
279     if (!m_bStop)
280     {
281       {
282         CSingleLock lock(m_critSection);
283         bHasPendingUpdates = m_bHasPendingUpdates;
284       }
285
286       if (bHasPendingUpdates)
287         UpdateEPG(true);
288     }
289
290     /* check for updated active tag */
291     if (!m_bStop)
292       CheckPlayingEvents();
293
294     /* check for changes that need to be saved every 60 seconds */
295     if (iNow - iLastSave > 60)
296     {
297       PersistAll();
298       iLastSave = iNow;
299     }
300
301     Sleep(1000);
302   }
303 }
304
305 CEpg *CEpgContainer::GetById(int iEpgId) const
306 {
307   if (iEpgId < 0)
308     return NULL;
309
310   CSingleLock lock(m_critSection);
311   map<unsigned int, CEpg *>::const_iterator it = m_epgs.find((unsigned int) iEpgId);
312   return it != m_epgs.end() ? it->second : NULL;
313 }
314
315 CEpg *CEpgContainer::GetByChannel(const CPVRChannel &channel) const
316 {
317   CSingleLock lock(m_critSection);
318   for (map<unsigned int, CEpg *>::const_iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
319     if (channel.ChannelID() == it->second->ChannelID())
320       return it->second;
321
322   return NULL;
323 }
324
325 void CEpgContainer::InsertFromDatabase(int iEpgID, const CStdString &strName, const CStdString &strScraperName)
326 {
327   // table might already have been created when pvr channels were loaded
328   CEpg* epg = GetById(iEpgID);
329   if (epg)
330   {
331     if (!epg->Name().Equals(strName) || !epg->ScraperName().Equals(strScraperName))
332     {
333       // current table data differs from the info in the db
334       epg->SetChanged();
335       SetChanged();
336     }
337   }
338   else
339   {
340     // create a new epg table
341     epg = new CEpg(iEpgID, strName, strScraperName, true);
342     if (epg)
343     {
344       m_epgs.insert(make_pair(iEpgID, epg));
345       SetChanged();
346       epg->RegisterObserver(this);
347     }
348   }
349 }
350
351 CEpg *CEpgContainer::CreateChannelEpg(CPVRChannelPtr channel)
352 {
353   if (!channel)
354     return NULL;
355
356   WaitForUpdateFinish(true);
357   LoadFromDB();
358
359   CEpg *epg(NULL);
360   if (channel->EpgID() > 0)
361     epg = GetById(channel->EpgID());
362
363   if (!epg)
364   {
365     channel->SetEpgID(NextEpgId());
366     epg = new CEpg(channel, false);
367
368     CSingleLock lock(m_critSection);
369     m_epgs.insert(make_pair((unsigned int)epg->EpgID(), epg));
370     SetChanged();
371     epg->RegisterObserver(this);
372   }
373
374   epg->SetChannel(channel);
375
376   {
377     CSingleLock lock(m_critSection);
378     m_bPreventUpdates = false;
379     CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgUpdate);
380   }
381
382   NotifyObservers(ObservableMessageEpgContainer);
383
384   return epg;
385 }
386
387 bool CEpgContainer::LoadSettings(void)
388 {
389   m_bIgnoreDbForClient = CSettings::Get().GetBool("epg.ignoredbforclient");
390   m_iUpdateTime        = CSettings::Get().GetInt ("epg.epgupdate") * 60;
391   m_iDisplayTime       = CSettings::Get().GetInt ("epg.daystodisplay") * 24 * 60 * 60;
392
393   return true;
394 }
395
396 bool CEpgContainer::RemoveOldEntries(void)
397 {
398   CDateTime now = CDateTime::GetUTCDateTime() -
399       CDateTimeSpan(0, g_advancedSettings.m_iEpgLingerTime / 60, g_advancedSettings.m_iEpgLingerTime % 60, 0);
400
401   /* call Cleanup() on all known EPG tables */
402   for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
403     it->second->Cleanup(now);
404
405   /* remove the old entries from the database */
406   if (!m_bIgnoreDbForClient && m_database.IsOpen())
407     m_database.DeleteOldEpgEntries();
408
409   CSingleLock lock(m_critSection);
410   CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iLastEpgCleanup);
411   m_iLastEpgCleanup += g_advancedSettings.m_iEpgCleanupInterval;
412
413   return true;
414 }
415
416 bool CEpgContainer::DeleteEpg(const CEpg &epg, bool bDeleteFromDatabase /* = false */)
417 {
418   if (epg.EpgID() < 0)
419     return false;
420
421   CSingleLock lock(m_critSection);
422
423   map<unsigned int, CEpg *>::iterator it = m_epgs.find((unsigned int)epg.EpgID());
424   if (it == m_epgs.end())
425     return false;
426
427   CLog::Log(LOGDEBUG, "deleting EPG table %s (%d)", epg.Name().c_str(), epg.EpgID());
428   if (bDeleteFromDatabase && !m_bIgnoreDbForClient && m_database.IsOpen())
429     m_database.Delete(*it->second);
430
431   it->second->UnregisterObserver(this);
432   delete it->second;
433   m_epgs.erase(it);
434
435   return true;
436 }
437
438 void CEpgContainer::CloseProgressDialog(void)
439 {
440   if (m_progressHandle)
441   {
442     m_progressHandle->MarkFinished();
443     m_progressHandle = NULL;
444   }
445 }
446
447 void CEpgContainer::ShowProgressDialog(bool bUpdating /* = true */)
448 {
449   if (!m_progressHandle)
450   {
451     CGUIDialogExtendedProgressBar *progressDialog = (CGUIDialogExtendedProgressBar *)g_windowManager.GetWindow(WINDOW_DIALOG_EXT_PROGRESS);
452     if (progressDialog)
453       m_progressHandle = progressDialog->GetHandle(bUpdating ? g_localizeStrings.Get(19004) : g_localizeStrings.Get(19250));
454   }
455 }
456
457 void CEpgContainer::UpdateProgressDialog(int iCurrent, int iMax, const CStdString &strText)
458 {
459   if (!m_progressHandle)
460     ShowProgressDialog();
461
462   if (m_progressHandle)
463   {
464     m_progressHandle->SetProgress(iCurrent, iMax);
465     m_progressHandle->SetText(strText);
466   }
467 }
468
469 bool CEpgContainer::InterruptUpdate(void) const
470 {
471   bool bReturn(false);
472   CSingleLock lock(m_critSection);
473   bReturn = g_application.m_bStop || m_bStop || m_bPreventUpdates;
474
475   return bReturn ||
476     (CSettings::Get().GetBool("epg.preventupdateswhileplayingtv") &&
477      g_application.m_pPlayer && g_application.m_pPlayer->IsPlaying());
478 }
479
480 void CEpgContainer::WaitForUpdateFinish(bool bInterrupt /* = true */)
481 {
482   {
483     CSingleLock lock(m_critSection);
484     if (bInterrupt)
485       m_bPreventUpdates = true;
486
487     if (!m_bIsUpdating)
488       return;
489
490     m_updateEvent.Reset();
491   }
492
493   m_updateEvent.Wait();
494 }
495
496 bool CEpgContainer::UpdateEPG(bool bOnlyPending /* = false */)
497 {
498   bool bInterrupted(false);
499   unsigned int iUpdatedTables(0);
500   bool bShowProgress(false);
501
502   /* set start and end time */
503   time_t start;
504   time_t end;
505   CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(start);
506   end = start + m_iDisplayTime;
507   start -= g_advancedSettings.m_iEpgLingerTime * 60;
508   bShowProgress = g_advancedSettings.m_bEpgDisplayUpdatePopup && (m_bIsInitialising || g_advancedSettings.m_bEpgDisplayIncrementalUpdatePopup);
509
510   {
511     CSingleLock lock(m_critSection);
512     if (m_bIsUpdating || InterruptUpdate())
513       return false;
514     m_bIsUpdating = true;
515   }
516
517   if (bShowProgress && !bOnlyPending)
518     ShowProgressDialog();
519
520   if (!m_bIgnoreDbForClient && !m_database.IsOpen())
521   {
522     CLog::Log(LOGERROR, "EpgContainer - %s - could not open the database", __FUNCTION__);
523
524     CSingleLock lock(m_critSection);
525     m_bIsUpdating = false;
526     m_updateEvent.Set();
527
528     if (bShowProgress && !bOnlyPending)
529       CloseProgressDialog();
530
531     return false;
532   }
533
534   vector<CEpg*> invalidTables;
535
536   /* load or update all EPG tables */
537   CEpg *epg;
538   unsigned int iCounter(0);
539   for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
540   {
541     if (InterruptUpdate())
542     {
543       bInterrupted = true;
544       break;
545     }
546
547     epg = it->second;
548     if (!epg)
549       continue;
550
551     if (bShowProgress && !bOnlyPending)
552       UpdateProgressDialog(++iCounter, m_epgs.size(), epg->Name());
553
554     // we currently only support update via pvr add-ons. skip update when the pvr manager isn't started
555     if (!g_PVRManager.IsStarted())
556       continue;
557
558     // check the pvr manager when the channel pointer isn't set
559     if (!epg->Channel())
560     {
561       CPVRChannelPtr channel = g_PVRChannelGroups->GetChannelByEpgId(epg->EpgID());
562       if (channel)
563         epg->SetChannel(channel);
564     }
565
566     if ((!bOnlyPending || epg->UpdatePending()) && epg->Update(start, end, m_iUpdateTime, bOnlyPending))
567       iUpdatedTables++;
568     else if (!epg->IsValid())
569       invalidTables.push_back(epg);
570   }
571
572   for (vector<CEpg*>::iterator it = invalidTables.begin(); it != invalidTables.end(); it++)
573     DeleteEpg(**it, true);
574
575   if (bInterrupted)
576   {
577     /* the update has been interrupted. try again later */
578     time_t iNow;
579     CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow);
580     m_iNextEpgUpdate = iNow + g_advancedSettings.m_iEpgRetryInterruptedUpdateInterval;
581   }
582   else
583   {
584     CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgUpdate);
585     m_iNextEpgUpdate += g_advancedSettings.m_iEpgUpdateCheckInterval;
586     m_bHasPendingUpdates = false;
587   }
588
589   if (bShowProgress && !bOnlyPending)
590     CloseProgressDialog();
591
592   /* notify observers */
593   if (iUpdatedTables > 0)
594   {
595     SetChanged();
596     NotifyObservers(ObservableMessageEpgContainer);
597   }
598
599   CSingleLock lock(m_critSection);
600   m_bIsUpdating = false;
601   m_updateEvent.Set();
602
603   return !bInterrupted;
604 }
605
606 int CEpgContainer::GetEPGAll(CFileItemList &results)
607 {
608   int iInitialSize = results.Size();
609
610   CSingleLock lock(m_critSection);
611   for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
612     it->second->Get(results);
613
614   return results.Size() - iInitialSize;
615 }
616
617 const CDateTime CEpgContainer::GetFirstEPGDate(void)
618 {
619   CDateTime returnValue;
620
621   CSingleLock lock(m_critSection);
622   for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
623   {
624     lock.Leave();
625     CDateTime entry = it->second->GetFirstDate();
626     if (entry.IsValid() && (!returnValue.IsValid() || entry < returnValue))
627       returnValue = entry;
628     lock.Enter();
629   }
630
631   return returnValue;
632 }
633
634 const CDateTime CEpgContainer::GetLastEPGDate(void)
635 {
636   CDateTime returnValue;
637
638   CSingleLock lock(m_critSection);
639   for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
640   {
641     lock.Leave();
642     CDateTime entry = it->second->GetLastDate();
643     if (entry.IsValid() && (!returnValue.IsValid() || entry > returnValue))
644       returnValue = entry;
645     lock.Enter();
646   }
647
648   return returnValue;
649 }
650
651 int CEpgContainer::GetEPGSearch(CFileItemList &results, const EpgSearchFilter &filter)
652 {
653   int iInitialSize = results.Size();
654
655   /* get filtered results from all tables */
656   {
657     CSingleLock lock(m_critSection);
658     for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
659       it->second->Get(results, filter);
660   }
661
662   /* remove duplicate entries */
663   if (filter.m_bPreventRepeats)
664     EpgSearchFilter::RemoveDuplicates(results);
665
666   return results.Size() - iInitialSize;
667 }
668
669 bool CEpgContainer::CheckPlayingEvents(void)
670 {
671   bool bReturn(false);
672   time_t iNow;
673   CSingleLock lock(m_critSection);
674
675   CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow);
676   if (iNow >= m_iNextEpgActiveTagCheck)
677   {
678     bool bFoundChanges(false);
679     CSingleLock lock(m_critSection);
680
681     for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++)
682       bFoundChanges = it->second->CheckPlayingEvent() || bFoundChanges;
683     CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgActiveTagCheck);
684     m_iNextEpgActiveTagCheck += g_advancedSettings.m_iEpgActiveTagCheckInterval;
685
686     if (bFoundChanges)
687     {
688       SetChanged();
689       NotifyObservers(ObservableMessageEpgActiveItem);
690     }
691
692     /* pvr tags always start on the full minute */
693     if (g_PVRManager.IsStarted())
694       m_iNextEpgActiveTagCheck -= m_iNextEpgActiveTagCheck % 60;
695
696     bReturn = true;
697   }
698
699   return bReturn;
700 }
701
702 bool CEpgContainer::IsInitialising(void) const
703 {
704   CSingleLock lock(m_critSection);
705   return m_bIsInitialising;
706 }
707
708 void CEpgContainer::SetHasPendingUpdates(bool bHasPendingUpdates /* = true */)
709 {
710   CSingleLock lock(m_critSection);
711   m_bHasPendingUpdates = bHasPendingUpdates;
712 }