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