1 /*
2  *  Copyright (C) 2012-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "PVRManager.h"
10 
11 #include "ServiceBroker.h"
12 #include "guilib/LocalizeStrings.h"
13 #include "interfaces/AnnouncementManager.h"
14 #include "messaging/ApplicationMessenger.h"
15 #include "pvr/PVRDatabase.h"
16 #include "pvr/PVRPlaybackState.h"
17 #include "pvr/addons/PVRClient.h"
18 #include "pvr/addons/PVRClients.h"
19 #include "pvr/channels/PVRChannel.h"
20 #include "pvr/channels/PVRChannelGroup.h"
21 #include "pvr/channels/PVRChannelGroupInternal.h"
22 #include "pvr/channels/PVRChannelGroups.h"
23 #include "pvr/channels/PVRChannelGroupsContainer.h"
24 #include "pvr/epg/EpgInfoTag.h"
25 #include "pvr/guilib/PVRGUIActions.h"
26 #include "pvr/guilib/PVRGUIChannelIconUpdater.h"
27 #include "pvr/guilib/PVRGUIProgressHandler.h"
28 #include "pvr/guilib/guiinfo/PVRGUIInfo.h"
29 #include "pvr/recordings/PVRRecording.h"
30 #include "pvr/recordings/PVRRecordings.h"
31 #include "pvr/timers/PVRTimerInfoTag.h"
32 #include "pvr/timers/PVRTimers.h"
33 #include "settings/Settings.h"
34 #include "utils/JobManager.h"
35 #include "utils/Stopwatch.h"
36 #include "utils/StringUtils.h"
37 #include "utils/URIUtils.h"
38 #include "utils/log.h"
39 
40 #include <memory>
41 #include <string>
42 #include <utility>
43 #include <vector>
44 
45 using namespace PVR;
46 using namespace KODI::MESSAGING;
47 
48 namespace PVR
49 {
50 template<typename F>
51 class CPVRLambdaJob : public CJob
52 {
53 public:
54   CPVRLambdaJob() = delete;
CPVRLambdaJob(const std::string & type,F && f)55   CPVRLambdaJob(const std::string& type, F&& f) : m_type(type), m_f(std::forward<F>(f)) {}
56 
DoWork()57   bool DoWork() override
58   {
59     m_f();
60     return true;
61   }
62 
GetType() const63   const char* GetType() const override
64   {
65     return m_type.c_str();
66   }
67 
68 private:
69   std::string m_type;
70   F m_f;
71 };
72 
73 class CPVRManagerJobQueue
74 {
75 public:
CPVRManagerJobQueue()76   CPVRManagerJobQueue() : m_triggerEvent(false) {}
77 
78   void Start();
79   void Stop();
80   void Clear();
81 
82   template<typename F>
Append(const std::string & type,F && f)83   void Append(const std::string& type, F&& f)
84   {
85     AppendJob(new CPVRLambdaJob<F>(type, std::forward<F>(f)));
86   }
87 
88   void ExecutePendingJobs();
89 
WaitForJobs(unsigned int milliSeconds)90   bool WaitForJobs(unsigned int milliSeconds)
91   {
92     return m_triggerEvent.WaitMSec(milliSeconds);
93   }
94 
95 
96 private:
97   void AppendJob(CJob* job);
98 
99   CCriticalSection m_critSection;
100   CEvent m_triggerEvent;
101   std::vector<CJob*> m_pendingUpdates;
102   bool m_bStopped = true;
103 };
104 } // namespace PVR
105 
Start()106 void CPVRManagerJobQueue::Start()
107 {
108   CSingleLock lock(m_critSection);
109   m_bStopped = false;
110   m_triggerEvent.Set();
111 }
112 
Stop()113 void CPVRManagerJobQueue::Stop()
114 {
115   CSingleLock lock(m_critSection);
116   m_bStopped = true;
117   m_triggerEvent.Reset();
118 }
119 
Clear()120 void CPVRManagerJobQueue::Clear()
121 {
122   CSingleLock lock(m_critSection);
123   for (CJob* updateJob : m_pendingUpdates)
124     delete updateJob;
125 
126   m_pendingUpdates.clear();
127   m_triggerEvent.Set();
128 }
129 
AppendJob(CJob * job)130 void CPVRManagerJobQueue::AppendJob(CJob* job)
131 {
132   CSingleLock lock(m_critSection);
133 
134   // check for another pending job of given type...
135   for (CJob* updateJob : m_pendingUpdates)
136   {
137     if (!strcmp(updateJob->GetType(), job->GetType()))
138     {
139       delete job;
140       return;
141     }
142   }
143 
144   m_pendingUpdates.push_back(job);
145   m_triggerEvent.Set();
146 }
147 
ExecutePendingJobs()148 void CPVRManagerJobQueue::ExecutePendingJobs()
149 {
150   std::vector<CJob*> pendingUpdates;
151 
152   {
153     CSingleLock lock(m_critSection);
154 
155     if (m_bStopped)
156       return;
157 
158     pendingUpdates = std::move(m_pendingUpdates);
159     m_triggerEvent.Reset();
160   }
161 
162   CJob* job = nullptr;
163   while (!pendingUpdates.empty())
164   {
165     job = pendingUpdates.front();
166     pendingUpdates.erase(pendingUpdates.begin());
167 
168     job->DoWork();
169     delete job;
170   }
171 }
172 
CPVRManager()173 CPVRManager::CPVRManager() :
174     CThread("PVRManager"),
175     m_channelGroups(new CPVRChannelGroupsContainer),
176     m_recordings(new CPVRRecordings),
177     m_timers(new CPVRTimers),
178     m_addons(new CPVRClients),
179     m_guiInfo(new CPVRGUIInfo),
180     m_guiActions(new CPVRGUIActions),
181     m_pendingUpdates(new CPVRManagerJobQueue),
182     m_database(new CPVRDatabase),
183     m_parentalTimer(new CStopWatch),
184     m_playbackState(new CPVRPlaybackState),
185     m_settings({
186       CSettings::SETTING_PVRPOWERMANAGEMENT_ENABLED,
187       CSettings::SETTING_PVRPOWERMANAGEMENT_SETWAKEUPCMD,
188       CSettings::SETTING_PVRPARENTAL_ENABLED,
189       CSettings::SETTING_PVRPARENTAL_DURATION
190     })
191 {
192   CServiceBroker::GetAnnouncementManager()->AddAnnouncer(this);
193   m_actionListener.Init(*this);
194 
195   CLog::LogFC(LOGDEBUG, LOGPVR, "PVR Manager instance created");
196 }
197 
~CPVRManager()198 CPVRManager::~CPVRManager()
199 {
200   m_actionListener.Deinit(*this);
201   CServiceBroker::GetAnnouncementManager()->RemoveAnnouncer(this);
202 
203   CLog::LogFC(LOGDEBUG, LOGPVR, "PVR Manager instance destroyed");
204 }
205 
Announce(ANNOUNCEMENT::AnnouncementFlag flag,const std::string & sender,const std::string & message,const CVariant & data)206 void CPVRManager::Announce(ANNOUNCEMENT::AnnouncementFlag flag,
207                            const std::string& sender,
208                            const std::string& message,
209                            const CVariant& data)
210 {
211   if (!IsStarted())
212     return;
213 
214   if ((flag & (ANNOUNCEMENT::GUI)))
215   {
216     if (message == "OnScreensaverActivated")
217       m_addons->OnPowerSavingActivated();
218     else if (message == "OnScreensaverDeactivated")
219       m_addons->OnPowerSavingDeactivated();
220   }
221 }
222 
GetTVDatabase() const223 std::shared_ptr<CPVRDatabase> CPVRManager::GetTVDatabase() const
224 {
225   CSingleLock lock(m_critSection);
226   if (!m_database || !m_database->IsOpen())
227     CLog::LogF(LOGERROR, "Failed to open the PVR database");
228 
229   return m_database;
230 }
231 
ChannelGroups() const232 std::shared_ptr<CPVRChannelGroupsContainer> CPVRManager::ChannelGroups() const
233 {
234   CSingleLock lock(m_critSection);
235   return m_channelGroups;
236 }
237 
Recordings() const238 std::shared_ptr<CPVRRecordings> CPVRManager::Recordings() const
239 {
240   CSingleLock lock(m_critSection);
241   return m_recordings;
242 }
243 
Timers() const244 std::shared_ptr<CPVRTimers> CPVRManager::Timers() const
245 {
246   CSingleLock lock(m_critSection);
247   return m_timers;
248 }
249 
Clients() const250 std::shared_ptr<CPVRClients> CPVRManager::Clients() const
251 {
252   // note: m_addons is const (only set/reset in ctor/dtor). no need for a lock here.
253   return m_addons;
254 }
255 
GetClient(const CFileItem & item) const256 std::shared_ptr<CPVRClient> CPVRManager::GetClient(const CFileItem& item) const
257 {
258   int iClientID = PVR_INVALID_CLIENT_ID;
259 
260   if (item.HasPVRChannelInfoTag())
261     iClientID = item.GetPVRChannelInfoTag()->ClientID();
262   else if (item.HasPVRRecordingInfoTag())
263     iClientID = item.GetPVRRecordingInfoTag()->m_iClientId;
264   else if (item.HasPVRTimerInfoTag())
265     iClientID = item.GetPVRTimerInfoTag()->m_iClientId;
266   else if (item.HasEPGInfoTag())
267     iClientID = item.GetEPGInfoTag()->ClientID();
268   else if (URIUtils::IsPVRChannel(item.GetPath()))
269   {
270     const std::shared_ptr<CPVRChannel> channel = m_channelGroups->GetByPath(item.GetPath());
271     if (channel)
272       iClientID = channel->ClientID();
273   }
274   else if (URIUtils::IsPVRRecording(item.GetPath()))
275   {
276     const std::shared_ptr<CPVRRecording> recording = m_recordings->GetByPath(item.GetPath());
277     if (recording)
278       iClientID = recording->ClientID();
279   }
280   return GetClient(iClientID);
281 }
282 
GetClient(int iClientId) const283 std::shared_ptr<CPVRClient> CPVRManager::GetClient(int iClientId) const
284 {
285   std::shared_ptr<CPVRClient> client;
286   if (iClientId != PVR_INVALID_CLIENT_ID)
287     m_addons->GetCreatedClient(iClientId, client);
288 
289   return client;
290 }
291 
GUIActions() const292 std::shared_ptr<CPVRGUIActions> CPVRManager::GUIActions() const
293 {
294   // note: m_guiActions is const (only set/reset in ctor/dtor). no need for a lock here.
295   return m_guiActions;
296 }
297 
PlaybackState() const298 std::shared_ptr<CPVRPlaybackState> CPVRManager::PlaybackState() const
299 {
300   // note: m_playbackState is const (only set/reset in ctor/dtor). no need for a lock here.
301   return m_playbackState;
302 }
303 
EpgContainer()304 CPVREpgContainer& CPVRManager::EpgContainer()
305 {
306   // note: m_epgContainer is const (only set/reset in ctor/dtor). no need for a lock here.
307   return m_epgContainer;
308 }
309 
Clear()310 void CPVRManager::Clear()
311 {
312   m_playbackState->Clear();
313   m_pendingUpdates->Clear();
314 
315   CSingleLock lock(m_critSection);
316 
317   m_guiInfo.reset();
318   m_timers.reset();
319   m_recordings.reset();
320   m_channelGroups.reset();
321   m_parentalTimer.reset();
322   m_database.reset();
323 
324   m_bEpgsCreated = false;
325 }
326 
ResetProperties()327 void CPVRManager::ResetProperties()
328 {
329   CSingleLock lock(m_critSection);
330   Clear();
331 
332   m_database.reset(new CPVRDatabase);
333   m_channelGroups.reset(new CPVRChannelGroupsContainer);
334   m_recordings.reset(new CPVRRecordings);
335   m_timers.reset(new CPVRTimers);
336   m_guiInfo.reset(new CPVRGUIInfo);
337   m_parentalTimer.reset(new CStopWatch);
338 }
339 
Init()340 void CPVRManager::Init()
341 {
342   // initial check for enabled addons
343   // if at least one pvr addon is enabled, PVRManager start up
344   CJobManager::GetInstance().Submit([this] {
345     Clients()->Start();
346     return true;
347   });
348 }
349 
Start()350 void CPVRManager::Start()
351 {
352   CSingleLock initLock(m_startStopMutex);
353 
354   // Prevent concurrent starts
355   if (IsInitialising())
356     return;
357 
358   // Note: Stop() must not be called while holding pvr manager's mutex. Stop() calls
359   // StopThread() which can deadlock if the worker thread tries to acquire pvr manager's
360   // lock while StopThread() is waiting for the worker to exit. Thus, we introduce another
361   // lock here (m_startStopMutex), which only gets hold while starting/restarting pvr manager.
362   Stop(true);
363 
364   if (!m_addons->HasCreatedClients())
365     return;
366 
367   CLog::Log(LOGINFO, "PVR Manager: Starting");
368   SetState(ManagerStateStarting);
369 
370   /* create the pvrmanager thread, which will ensure that all data will be loaded */
371   Create();
372   SetPriority(-1);
373 }
374 
Stop(bool bRestart)375 void CPVRManager::Stop(bool bRestart /* = false */)
376 {
377   CSingleLock initLock(m_startStopMutex);
378 
379   // Prevent concurrent stops
380   if (IsStopped())
381     return;
382 
383   /* stop playback if needed */
384   if (!bRestart && m_playbackState->IsPlaying())
385   {
386     CLog::LogFC(LOGDEBUG, LOGPVR, "Stopping PVR playback");
387     CApplicationMessenger::GetInstance().SendMsg(TMSG_MEDIA_STOP);
388   }
389 
390   CLog::Log(LOGINFO, "PVR Manager: Stopping");
391   SetState(ManagerStateStopping);
392 
393   m_addons->Stop();
394   m_pendingUpdates->Stop();
395   m_timers->Stop();
396   m_epgContainer.Stop();
397   m_guiInfo->Stop();
398 
399   StopThread();
400 
401   SetState(ManagerStateInterrupted);
402 
403   UnloadComponents();
404   m_database->Close();
405 
406   ResetProperties();
407 
408   CLog::Log(LOGINFO, "PVR Manager: Stopped");
409   SetState(ManagerStateStopped);
410 }
411 
Unload()412 void CPVRManager::Unload()
413 {
414   // stop pvr manager thread and clear all pvr data
415   Stop();
416   Clear();
417 }
418 
Deinit()419 void CPVRManager::Deinit()
420 {
421   SetWakeupCommand();
422   Unload();
423 
424   // release addons
425   m_addons.reset();
426 }
427 
GetState() const428 CPVRManager::ManagerState CPVRManager::GetState() const
429 {
430   CSingleLock lock(m_managerStateMutex);
431   return m_managerState;
432 }
433 
SetState(CPVRManager::ManagerState state)434 void CPVRManager::SetState(CPVRManager::ManagerState state)
435 {
436   {
437     CSingleLock lock(m_managerStateMutex);
438     if (m_managerState == state)
439       return;
440 
441     m_managerState = state;
442   }
443 
444   PVREvent event;
445   switch (state)
446   {
447     case ManagerStateError:
448       event = PVREvent::ManagerError;
449       break;
450     case ManagerStateStopped:
451       event = PVREvent::ManagerStopped;
452       break;
453     case ManagerStateStarting:
454       event = PVREvent::ManagerStarting;
455       break;
456     case ManagerStateStopping:
457       event = PVREvent::ManagerStopped;
458       break;
459     case ManagerStateInterrupted:
460       event = PVREvent::ManagerInterrupted;
461       break;
462     case ManagerStateStarted:
463       event = PVREvent::ManagerStarted;
464       break;
465     default:
466       return;
467   }
468 
469   PublishEvent(event);
470 }
471 
PublishEvent(PVREvent event)472 void CPVRManager::PublishEvent(PVREvent event)
473 {
474   m_events.Publish(event);
475 }
476 
Process()477 void CPVRManager::Process()
478 {
479   m_addons->Continue();
480   m_database->Open();
481 
482   /* load the pvr data from the db and clients if it's not already loaded */
483   XbmcThreads::EndTime progressTimeout(30000); // 30 secs
484   CPVRGUIProgressHandler* progressHandler = new CPVRGUIProgressHandler(g_localizeStrings.Get(19235)); // PVR manager is starting up
485   while (!LoadComponents(progressHandler) && IsInitialising())
486   {
487     CLog::Log(LOGWARNING, "PVR Manager failed to load data, retrying");
488     CThread::Sleep(1000);
489 
490     if (progressHandler && progressTimeout.IsTimePast())
491     {
492       progressHandler->DestroyProgress();
493       progressHandler = nullptr; // no delete, instance is deleting itself
494     }
495   }
496 
497   if (progressHandler)
498   {
499     progressHandler->DestroyProgress();
500     progressHandler = nullptr; // no delete, instance is deleting itself
501   }
502 
503   if (!IsInitialising())
504   {
505     CLog::Log(LOGINFO, "PVR Manager: Start aborted");
506     return;
507   }
508 
509   // Load EPGs from database.
510   m_epgContainer.Load();
511 
512   // Reinit playbackstate
513   m_playbackState->ReInit();
514 
515   m_guiInfo->Start();
516   m_epgContainer.Start();
517   m_timers->Start();
518   m_pendingUpdates->Start();
519 
520   SetState(ManagerStateStarted);
521   CLog::Log(LOGINFO, "PVR Manager: Started");
522 
523   /* main loop */
524   CLog::LogFC(LOGDEBUG, LOGPVR, "PVR Manager entering main loop");
525 
526   bool bRestart(false);
527   while (IsStarted() && m_addons->HasCreatedClients() && !bRestart)
528   {
529     /* first startup */
530     if (m_bFirstStart)
531     {
532       {
533         CSingleLock lock(m_critSection);
534         m_bFirstStart = false;
535       }
536 
537       /* start job to search for missing channel icons */
538       TriggerSearchMissingChannelIcons();
539 
540       /* try to play channel on startup */
541       TriggerPlayChannelOnStartup();
542     }
543 
544     if (m_addons->AnyClientSupportingRecordingsSize())
545       TriggerRecordingsSizeInProgressUpdate();
546 
547     /* execute the next pending jobs if there are any */
548     try
549     {
550       m_pendingUpdates->ExecutePendingJobs();
551     }
552     catch (...)
553     {
554       CLog::LogF(LOGERROR, "An error occurred while trying to execute the last PVR update job, trying to recover");
555       bRestart = true;
556     }
557 
558     if (IsStarted() && !bRestart)
559       m_pendingUpdates->WaitForJobs(1000);
560   }
561 
562   CLog::LogFC(LOGDEBUG, LOGPVR, "PVR Manager leaving main loop");
563 }
564 
SetWakeupCommand()565 bool CPVRManager::SetWakeupCommand()
566 {
567 #if !defined(TARGET_DARWIN_EMBEDDED) && !defined(TARGET_WINDOWS_STORE)
568   if (!m_settings.GetBoolValue(CSettings::SETTING_PVRPOWERMANAGEMENT_ENABLED))
569     return false;
570 
571   const std::string strWakeupCommand(m_settings.GetStringValue(CSettings::SETTING_PVRPOWERMANAGEMENT_SETWAKEUPCMD));
572   if (!strWakeupCommand.empty() && m_timers)
573   {
574     time_t iWakeupTime;
575     const CDateTime nextEvent = m_timers->GetNextEventTime();
576     if (nextEvent.IsValid())
577     {
578       nextEvent.GetAsTime(iWakeupTime);
579 
580       std::string strExecCommand = StringUtils::Format("%s %ld", strWakeupCommand.c_str(), iWakeupTime);
581 
582       const int iReturn = system(strExecCommand.c_str());
583       if (iReturn != 0)
584         CLog::LogF(LOGERROR, "PVR Manager failed to execute wakeup command '{}': {} ({})",
585                    strExecCommand, strerror(iReturn), iReturn);
586 
587       return iReturn == 0;
588     }
589   }
590 #endif
591   return false;
592 }
593 
OnSleep()594 void CPVRManager::OnSleep()
595 {
596   PublishEvent(PVREvent::SystemSleep);
597 
598   SetWakeupCommand();
599 
600   m_epgContainer.OnSystemSleep();
601 
602   m_addons->OnSystemSleep();
603 }
604 
OnWake()605 void CPVRManager::OnWake()
606 {
607   m_addons->OnSystemWake();
608 
609   m_epgContainer.OnSystemWake();
610 
611   PublishEvent(PVREvent::SystemWake);
612 
613   /* start job to search for missing channel icons */
614   TriggerSearchMissingChannelIcons();
615 
616   /* try to play channel on startup */
617   TriggerPlayChannelOnStartup();
618 
619   /* trigger PVR data updates */
620   TriggerChannelGroupsUpdate();
621   TriggerChannelsUpdate();
622   TriggerRecordingsUpdate();
623   TriggerEpgsCreate();
624   TriggerTimersUpdate();
625 }
626 
LoadComponents(CPVRGUIProgressHandler * progressHandler)627 bool CPVRManager::LoadComponents(CPVRGUIProgressHandler* progressHandler)
628 {
629   /* load at least one client */
630   while (IsInitialising() && m_addons && !m_addons->HasCreatedClients())
631     CThread::Sleep(50);
632 
633   if (!IsInitialising() || !m_addons->HasCreatedClients())
634     return false;
635 
636   CLog::LogFC(LOGDEBUG, LOGPVR, "PVR Manager found active clients. Continuing startup");
637 
638   /* load all channels and groups */
639   if (progressHandler)
640     progressHandler->UpdateProgress(g_localizeStrings.Get(19236), 0); // Loading channels from clients
641 
642   if (!m_channelGroups->Load() || !IsInitialising())
643     return false;
644 
645   PublishEvent(PVREvent::ChannelGroupsLoaded);
646 
647   /* get timers from the backends */
648   if (progressHandler)
649     progressHandler->UpdateProgress(g_localizeStrings.Get(19237), 50); // Loading timers from clients
650 
651   m_timers->Load();
652 
653   /* get recordings from the backend */
654   if (progressHandler)
655     progressHandler->UpdateProgress(g_localizeStrings.Get(19238), 75); // Loading recordings from clients
656 
657   m_recordings->Load();
658 
659   if (!IsInitialising())
660     return false;
661 
662   /* start the other pvr related update threads */
663   if (progressHandler)
664     progressHandler->UpdateProgress(g_localizeStrings.Get(19239), 85); // Starting background threads
665 
666   return true;
667 }
668 
UnloadComponents()669 void CPVRManager::UnloadComponents()
670 {
671   m_recordings->Unload();
672   m_timers->Unload();
673   m_channelGroups->Unload();
674   m_epgContainer.Unload();
675 }
676 
TriggerPlayChannelOnStartup()677 void CPVRManager::TriggerPlayChannelOnStartup()
678 {
679   if (IsStarted())
680   {
681     CJobManager::GetInstance().Submit([this] {
682       return GUIActions()->PlayChannelOnStartup();
683     });
684   }
685 }
686 
RestartParentalTimer()687 void CPVRManager::RestartParentalTimer()
688 {
689   if (m_parentalTimer)
690     m_parentalTimer->StartZero();
691 }
692 
IsParentalLocked(const std::shared_ptr<CPVREpgInfoTag> & epgTag) const693 bool CPVRManager::IsParentalLocked(const std::shared_ptr<CPVREpgInfoTag>& epgTag) const
694 {
695   return m_channelGroups &&
696          epgTag &&
697          IsCurrentlyParentalLocked(m_channelGroups->GetByUniqueID(epgTag->UniqueChannelID(), epgTag->ClientID()),
698                                    epgTag->IsParentalLocked());
699 }
700 
IsParentalLocked(const std::shared_ptr<CPVRChannel> & channel) const701 bool CPVRManager::IsParentalLocked(const std::shared_ptr<CPVRChannel>& channel) const
702 {
703   return channel &&
704          IsCurrentlyParentalLocked(channel, channel->IsLocked());
705 }
706 
IsCurrentlyParentalLocked(const std::shared_ptr<CPVRChannel> & channel,bool bGenerallyLocked) const707 bool CPVRManager::IsCurrentlyParentalLocked(const std::shared_ptr<CPVRChannel>& channel, bool bGenerallyLocked) const
708 {
709   bool bReturn = false;
710 
711   if (!channel || !bGenerallyLocked)
712     return bReturn;
713 
714   const std::shared_ptr<CPVRChannel> currentChannel = m_playbackState->GetPlayingChannel();
715 
716   if (// if channel in question is currently playing it must be currently unlocked.
717       (!currentChannel || channel != currentChannel) &&
718       // parental control enabled
719       m_settings.GetBoolValue(CSettings::SETTING_PVRPARENTAL_ENABLED))
720   {
721     float parentalDurationMs = m_settings.GetIntValue(CSettings::SETTING_PVRPARENTAL_DURATION) * 1000.0f;
722     bReturn = m_parentalTimer &&
723         (!m_parentalTimer->IsRunning() ||
724           m_parentalTimer->GetElapsedMilliseconds() > parentalDurationMs);
725   }
726 
727   return bReturn;
728 }
729 
OnPlaybackStarted(const CFileItemPtr & item)730 void CPVRManager::OnPlaybackStarted(const CFileItemPtr& item)
731 {
732   m_playbackState->OnPlaybackStarted(item);
733   m_guiActions->OnPlaybackStarted(item);
734   m_epgContainer.OnPlaybackStarted();
735 }
736 
OnPlaybackStopped(const CFileItemPtr & item)737 void CPVRManager::OnPlaybackStopped(const CFileItemPtr& item)
738 {
739   // Playback ended due to user interaction
740   if (m_playbackState->OnPlaybackStopped(item))
741     PublishEvent(PVREvent::ChannelPlaybackStopped);
742 
743   m_guiActions->OnPlaybackStopped(item);
744   m_epgContainer.OnPlaybackStopped();
745 }
746 
OnPlaybackEnded(const CFileItemPtr & item)747 void CPVRManager::OnPlaybackEnded(const CFileItemPtr& item)
748 {
749   // Playback ended, but not due to user interaction
750   OnPlaybackStopped(item);
751 }
752 
LocalizationChanged()753 void CPVRManager::LocalizationChanged()
754 {
755   CSingleLock lock(m_critSection);
756   if (IsStarted())
757   {
758     static_cast<CPVRChannelGroupInternal *>(m_channelGroups->GetGroupAllRadio().get())->CheckGroupName();
759     static_cast<CPVRChannelGroupInternal *>(m_channelGroups->GetGroupAllTV().get())->CheckGroupName();
760   }
761 }
762 
EpgsCreated() const763 bool CPVRManager::EpgsCreated() const
764 {
765   CSingleLock lock(m_critSection);
766   return m_bEpgsCreated;
767 }
768 
TriggerEpgsCreate()769 void CPVRManager::TriggerEpgsCreate()
770 {
771   m_pendingUpdates->Append("pvr-create-epgs", [this]() {
772     return CreateChannelEpgs();
773   });
774 }
775 
TriggerRecordingsUpdate()776 void CPVRManager::TriggerRecordingsUpdate()
777 {
778   m_pendingUpdates->Append("pvr-update-recordings", [this]() {
779     return Recordings()->Update();
780   });
781 }
782 
TriggerRecordingsSizeInProgressUpdate()783 void CPVRManager::TriggerRecordingsSizeInProgressUpdate()
784 {
785   m_pendingUpdates->Append("pvr-update-recordings-size", [this]() {
786     return Recordings()->UpdateInProgressSize();
787   });
788 }
789 
TriggerTimersUpdate()790 void CPVRManager::TriggerTimersUpdate()
791 {
792   m_pendingUpdates->Append("pvr-update-timers", [this]() {
793     return Timers()->Update();
794   });
795 }
796 
TriggerChannelsUpdate()797 void CPVRManager::TriggerChannelsUpdate()
798 {
799   m_pendingUpdates->Append("pvr-update-channels", [this]() {
800     return ChannelGroups()->Update(true);
801   });
802 }
803 
TriggerChannelGroupsUpdate()804 void CPVRManager::TriggerChannelGroupsUpdate()
805 {
806   m_pendingUpdates->Append("pvr-update-channelgroups", [this]() {
807     return ChannelGroups()->Update(false);
808   });
809 }
810 
TriggerSearchMissingChannelIcons()811 void CPVRManager::TriggerSearchMissingChannelIcons()
812 {
813   m_pendingUpdates->Append("pvr-search-missing-channel-icons", [this]() {
814     CPVRGUIChannelIconUpdater updater(
815         {ChannelGroups()->GetGroupAllTV(), ChannelGroups()->GetGroupAllRadio()}, true);
816     updater.SearchAndUpdateMissingChannelIcons();
817     return true;
818   });
819 }
820 
TriggerSearchMissingChannelIcons(const std::shared_ptr<CPVRChannelGroup> & group)821 void CPVRManager::TriggerSearchMissingChannelIcons(const std::shared_ptr<CPVRChannelGroup>& group)
822 {
823   m_pendingUpdates->Append("pvr-search-missing-channel-icons-" + std::to_string(group->GroupID()),
824                            [group]() {
825                              CPVRGUIChannelIconUpdater updater({group}, false);
826                              updater.SearchAndUpdateMissingChannelIcons();
827                              return true;
828                            });
829 }
830 
ConnectionStateChange(CPVRClient * client,const std::string & connectString,PVR_CONNECTION_STATE state,const std::string & message)831 void CPVRManager::ConnectionStateChange(CPVRClient* client,
832                                         const std::string& connectString,
833                                         PVR_CONNECTION_STATE state,
834                                         const std::string& message)
835 {
836   CJobManager::GetInstance().Submit([this, client, connectString, state, message] {
837     Clients()->ConnectionStateChange(client, connectString, state, message);
838 
839     if (state == PVR_CONNECTION_STATE_CONNECTED)
840       Start(); // start over
841 
842     return true;
843   });
844 }
845 
CreateChannelEpgs()846 bool CPVRManager::CreateChannelEpgs()
847 {
848   if (EpgsCreated())
849     return true;
850 
851   bool bEpgsCreated = m_channelGroups->CreateChannelEpgs();
852 
853   CSingleLock lock(m_critSection);
854   m_bEpgsCreated = bEpgsCreated;
855   return m_bEpgsCreated;
856 }
857