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 "PVROperations.h"
10 
11 #include "ServiceBroker.h"
12 #include "pvr/PVRManager.h"
13 #include "pvr/PVRPlaybackState.h"
14 #include "pvr/addons/PVRClients.h"
15 #include "pvr/channels/PVRChannel.h"
16 #include "pvr/channels/PVRChannelGroups.h"
17 #include "pvr/channels/PVRChannelGroupsContainer.h"
18 #include "pvr/epg/Epg.h"
19 #include "pvr/epg/EpgContainer.h"
20 #include "pvr/epg/EpgInfoTag.h"
21 #include "pvr/guilib/PVRGUIActions.h"
22 #include "pvr/recordings/PVRRecordings.h"
23 #include "pvr/timers/PVRTimerInfoTag.h"
24 #include "pvr/timers/PVRTimers.h"
25 #include "utils/Variant.h"
26 
27 using namespace JSONRPC;
28 using namespace PVR;
29 using namespace KODI::MESSAGING;
30 
GetProperties(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)31 JSONRPC_STATUS CPVROperations::GetProperties(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
32 {
33   if (!CServiceBroker::GetPVRManager().IsStarted())
34     return FailedToExecute;
35 
36   CVariant properties = CVariant(CVariant::VariantTypeObject);
37   for (unsigned int index = 0; index < parameterObject["properties"].size(); index++)
38   {
39     std::string propertyName = parameterObject["properties"][index].asString();
40     CVariant property;
41     JSONRPC_STATUS ret;
42     if ((ret = GetPropertyValue(propertyName, property)) != OK)
43       return ret;
44 
45     properties[propertyName] = property;
46   }
47 
48   result = properties;
49 
50   return OK;
51 }
52 
GetChannelGroups(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)53 JSONRPC_STATUS CPVROperations::GetChannelGroups(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
54 {
55   if (!CServiceBroker::GetPVRManager().IsStarted())
56     return FailedToExecute;
57 
58   std::shared_ptr<CPVRChannelGroupsContainer> channelGroupContainer = CServiceBroker::GetPVRManager().ChannelGroups();
59   if (!channelGroupContainer)
60     return FailedToExecute;
61 
62   CPVRChannelGroups *channelGroups = channelGroupContainer->Get(parameterObject["channeltype"].asString().compare("radio") == 0);
63   if (channelGroups == NULL)
64     return FailedToExecute;
65 
66   int start, end;
67 
68   std::vector<std::shared_ptr<CPVRChannelGroup>> groupList = channelGroups->GetMembers(true);
69   HandleLimits(parameterObject, result, groupList.size(), start, end);
70   for (int index = start; index < end; index++)
71     FillChannelGroupDetails(groupList.at(index), parameterObject, result["channelgroups"], true);
72 
73   return OK;
74 }
75 
GetChannelGroupDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)76 JSONRPC_STATUS CPVROperations::GetChannelGroupDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
77 {
78   if (!CServiceBroker::GetPVRManager().IsStarted())
79     return FailedToExecute;
80 
81   std::shared_ptr<CPVRChannelGroupsContainer> channelGroupContainer = CServiceBroker::GetPVRManager().ChannelGroups();
82   if (!channelGroupContainer)
83     return FailedToExecute;
84 
85   std::shared_ptr<CPVRChannelGroup> channelGroup;
86   CVariant id = parameterObject["channelgroupid"];
87   if (id.isInteger())
88     channelGroup = channelGroupContainer->GetByIdFromAll((int)id.asInteger());
89   else if (id.isString())
90     channelGroup = channelGroupContainer->GetGroupAll(id.asString() == "allradio");
91 
92   if (channelGroup == NULL)
93     return InvalidParams;
94 
95   FillChannelGroupDetails(channelGroup, parameterObject, result["channelgroupdetails"], false);
96 
97   return OK;
98 }
99 
GetChannels(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)100 JSONRPC_STATUS CPVROperations::GetChannels(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
101 {
102   if (!CServiceBroker::GetPVRManager().IsStarted())
103     return FailedToExecute;
104 
105   std::shared_ptr<CPVRChannelGroupsContainer> channelGroupContainer = CServiceBroker::GetPVRManager().ChannelGroups();
106   if (!channelGroupContainer)
107     return FailedToExecute;
108 
109   std::shared_ptr<CPVRChannelGroup> channelGroup;
110   CVariant id = parameterObject["channelgroupid"];
111   if (id.isInteger())
112     channelGroup = channelGroupContainer->GetByIdFromAll((int)id.asInteger());
113   else if (id.isString())
114     channelGroup = channelGroupContainer->GetGroupAll(id.asString() == "allradio");
115 
116   if (channelGroup == NULL)
117     return InvalidParams;
118 
119   CFileItemList channels;
120   const std::vector<std::shared_ptr<PVRChannelGroupMember>> groupMembers = channelGroup->GetMembers(CPVRChannelGroup::Include::ONLY_VISIBLE);
121   for (const auto& groupMember : groupMembers)
122   {
123     channels.Add(std::make_shared<CFileItem>(groupMember->channel));
124   }
125 
126   HandleFileItemList("channelid", false, "channels", channels, parameterObject, result, true);
127 
128   return OK;
129 }
130 
GetChannelDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)131 JSONRPC_STATUS CPVROperations::GetChannelDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
132 {
133   if (!CServiceBroker::GetPVRManager().IsStarted())
134     return FailedToExecute;
135 
136   std::shared_ptr<CPVRChannelGroupsContainer> channelGroupContainer = CServiceBroker::GetPVRManager().ChannelGroups();
137   if (!channelGroupContainer)
138     return FailedToExecute;
139 
140   std::shared_ptr<CPVRChannel> channel = channelGroupContainer->GetChannelById((int)parameterObject["channelid"].asInteger());
141   if (channel == NULL)
142     return InvalidParams;
143 
144   HandleFileItem("channelid", false, "channeldetails", CFileItemPtr(new CFileItem(channel)), parameterObject, parameterObject["properties"], result, false);
145 
146   return OK;
147 }
148 
GetClients(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)149 JSONRPC_STATUS CPVROperations::GetClients(const std::string& method,
150                                           ITransportLayer* transport,
151                                           IClient* client,
152                                           const CVariant& parameterObject,
153                                           CVariant& result)
154 {
155   if (!CServiceBroker::GetPVRManager().IsStarted())
156     return FailedToExecute;
157 
158   int start, end;
159 
160   auto clientInfos = CServiceBroker::GetPVRManager().Clients()->GetEnabledClientInfos();
161   HandleLimits(parameterObject, result, clientInfos.size(), start, end);
162   for (int index = start; index < end; index++)
163     result["clients"].append(clientInfos[index]);
164 
165   return OK;
166 }
167 
GetBroadcasts(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)168 JSONRPC_STATUS CPVROperations::GetBroadcasts(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
169 {
170   if (!CServiceBroker::GetPVRManager().IsStarted())
171     return FailedToExecute;
172 
173   std::shared_ptr<CPVRChannelGroupsContainer> channelGroupContainer = CServiceBroker::GetPVRManager().ChannelGroups();
174   if (!channelGroupContainer)
175     return FailedToExecute;
176 
177   std::shared_ptr<CPVRChannel> channel = channelGroupContainer->GetChannelById((int)parameterObject["channelid"].asInteger());
178   if (channel == NULL)
179     return InvalidParams;
180 
181   std::shared_ptr<CPVREpg> channelEpg = channel->GetEPG();
182   if (!channelEpg)
183     return InternalError;
184 
185   CFileItemList programFull;
186 
187   const std::vector<std::shared_ptr<CPVREpgInfoTag>> tags = channelEpg->GetTags();
188   for (const auto& tag : tags)
189   {
190     programFull.Add(std::make_shared<CFileItem>(tag));
191   }
192 
193   HandleFileItemList("broadcastid", false, "broadcasts", programFull, parameterObject, result, programFull.Size(), true);
194 
195   return OK;
196 }
197 
GetBroadcastDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)198 JSONRPC_STATUS CPVROperations::GetBroadcastDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
199 {
200   if (!CServiceBroker::GetPVRManager().IsStarted())
201     return FailedToExecute;
202 
203   const std::shared_ptr<CPVREpgInfoTag> epgTag =
204       CServiceBroker::GetPVRManager().EpgContainer().GetTagByDatabaseId(
205           parameterObject["broadcastid"].asInteger());
206 
207   if (!epgTag)
208     return InvalidParams;
209 
210   HandleFileItem("broadcastid", false, "broadcastdetails", CFileItemPtr(new CFileItem(epgTag)), parameterObject, parameterObject["properties"], result, false);
211 
212   return OK;
213 }
214 
GetBroadcastIsPlayable(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)215 JSONRPC_STATUS CPVROperations::GetBroadcastIsPlayable(const std::string& method,
216                                                       ITransportLayer* transport,
217                                                       IClient* client,
218                                                       const CVariant& parameterObject,
219                                                       CVariant& result)
220 {
221   if (!CServiceBroker::GetPVRManager().IsStarted())
222     return FailedToExecute;
223 
224   const std::shared_ptr<CPVREpgInfoTag> epgTag =
225       CServiceBroker::GetPVRManager().EpgContainer().GetTagByDatabaseId(
226           parameterObject["broadcastid"].asInteger());
227 
228   if (!epgTag)
229     return InvalidParams;
230 
231   result = epgTag->IsPlayable();
232 
233   return OK;
234 }
235 
Record(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)236 JSONRPC_STATUS CPVROperations::Record(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
237 {
238   if (!CServiceBroker::GetPVRManager().IsStarted())
239     return FailedToExecute;
240 
241   std::shared_ptr<CPVRChannel> pChannel;
242   CVariant channel = parameterObject["channel"];
243   if (channel.isString() && channel.asString() == "current")
244   {
245     pChannel = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
246     if (!pChannel)
247       return InternalError;
248   }
249   else if (channel.isInteger())
250   {
251     std::shared_ptr<CPVRChannelGroupsContainer> channelGroupContainer = CServiceBroker::GetPVRManager().ChannelGroups();
252     if (!channelGroupContainer)
253       return FailedToExecute;
254 
255     pChannel = channelGroupContainer->GetChannelById((int)channel.asInteger());
256   }
257   else
258     return InvalidParams;
259 
260   if (pChannel == NULL)
261     return InvalidParams;
262   else if (!pChannel->CanRecord())
263     return FailedToExecute;
264 
265   CVariant record = parameterObject["record"];
266   bool bIsRecording = CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*pChannel);
267   bool toggle = true;
268   if (record.isBoolean() && record.asBoolean() == bIsRecording)
269     toggle = false;
270 
271   if (toggle)
272   {
273     if (!CServiceBroker::GetPVRManager().GUIActions()->SetRecordingOnChannel(pChannel, !bIsRecording))
274       return FailedToExecute;
275   }
276 
277   return ACK;
278 }
279 
Scan(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)280 JSONRPC_STATUS CPVROperations::Scan(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
281 {
282   if (!CServiceBroker::GetPVRManager().IsStarted())
283     return FailedToExecute;
284 
285   if (parameterObject.isMember("clientid"))
286   {
287     if (CServiceBroker::GetPVRManager().GUIActions()->StartChannelScan(
288             parameterObject["clientid"].asInteger()))
289       return ACK;
290   }
291   else
292   {
293     if (CServiceBroker::GetPVRManager().GUIActions()->StartChannelScan())
294       return ACK;
295   }
296 
297   return FailedToExecute;
298 }
299 
GetPropertyValue(const std::string & property,CVariant & result)300 JSONRPC_STATUS CPVROperations::GetPropertyValue(const std::string &property, CVariant &result)
301 {
302   bool started = CServiceBroker::GetPVRManager().IsStarted();
303 
304   if (property == "available")
305     result = started;
306   else if (property == "recording")
307   {
308     if (started)
309       result = CServiceBroker::GetPVRManager().PlaybackState()->IsRecording();
310     else
311       result = false;
312   }
313   else if (property == "scanning")
314   {
315     if (started)
316       result = CServiceBroker::GetPVRManager().GUIActions()->IsRunningChannelScan();
317     else
318       result = false;
319   }
320   else
321     return InvalidParams;
322 
323   return OK;
324 }
325 
FillChannelGroupDetails(const std::shared_ptr<CPVRChannelGroup> & channelGroup,const CVariant & parameterObject,CVariant & result,bool append)326 void CPVROperations::FillChannelGroupDetails(const std::shared_ptr<CPVRChannelGroup> &channelGroup, const CVariant &parameterObject, CVariant &result, bool append /* = false */)
327 {
328   if (channelGroup == NULL)
329     return;
330 
331   CVariant object(CVariant::VariantTypeObject);
332   object["channelgroupid"] = channelGroup->GroupID();
333   object["channeltype"] = channelGroup->IsRadio() ? "radio" : "tv";
334   object["label"] = channelGroup->GroupName();
335 
336   if (append)
337     result.append(object);
338   else
339   {
340     CFileItemList channels;
341     const std::vector<std::shared_ptr<PVRChannelGroupMember>> groupMembers = channelGroup->GetMembers(CPVRChannelGroup::Include::ONLY_VISIBLE);
342     for (const auto& groupMember : groupMembers)
343     {
344       channels.Add(std::make_shared<CFileItem>(groupMember->channel));
345     }
346 
347     object["channels"] = CVariant(CVariant::VariantTypeArray);
348     HandleFileItemList("channelid", false, "channels", channels, parameterObject["channels"], object, false);
349 
350     result = object;
351   }
352 }
353 
GetTimers(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)354 JSONRPC_STATUS CPVROperations::GetTimers(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
355 {
356   if (!CServiceBroker::GetPVRManager().IsStarted())
357     return FailedToExecute;
358 
359   std::shared_ptr<CPVRTimers> timers = CServiceBroker::GetPVRManager().Timers();
360   if (!timers)
361     return FailedToExecute;
362 
363   CFileItemList timerList;
364   const std::vector<std::shared_ptr<CPVRTimerInfoTag>> tags = timers->GetAll();
365   for (const auto& timer : tags)
366   {
367     timerList.Add(std::make_shared<CFileItem>(timer));
368   }
369 
370   HandleFileItemList("timerid", false, "timers", timerList, parameterObject, result, true);
371 
372   return OK;
373 }
374 
GetTimerDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)375 JSONRPC_STATUS CPVROperations::GetTimerDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
376 {
377   if (!CServiceBroker::GetPVRManager().IsStarted())
378     return FailedToExecute;
379 
380   std::shared_ptr<CPVRTimers> timers = CServiceBroker::GetPVRManager().Timers();
381   if (!timers)
382     return FailedToExecute;
383 
384   std::shared_ptr<CPVRTimerInfoTag> timer = timers->GetById((int)parameterObject["timerid"].asInteger());
385   if (!timer)
386     return InvalidParams;
387 
388   HandleFileItem("timerid", false, "timerdetails", CFileItemPtr(new CFileItem(timer)), parameterObject, parameterObject["properties"], result, false);
389 
390   return OK;
391 }
392 
AddTimer(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)393 JSONRPC_STATUS CPVROperations::AddTimer(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
394 {
395   if (!CServiceBroker::GetPVRManager().IsStarted())
396     return FailedToExecute;
397 
398   const std::shared_ptr<CPVREpgInfoTag> epgTag =
399       CServiceBroker::GetPVRManager().EpgContainer().GetTagByDatabaseId(
400           parameterObject["broadcastid"].asInteger());
401 
402   if (!epgTag)
403     return InvalidParams;
404 
405   if (CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epgTag))
406     return InvalidParams;
407 
408   const std::shared_ptr<CPVRTimerInfoTag> newTimer =
409       CPVRTimerInfoTag::CreateFromEpg(epgTag, parameterObject["timerrule"].asBoolean(false),
410                                       parameterObject["reminder"].asBoolean(false));
411   if (newTimer)
412   {
413     if (CServiceBroker::GetPVRManager().GUIActions()->AddTimer(newTimer))
414       return ACK;
415   }
416   return FailedToExecute;
417 }
418 
419 
DeleteTimer(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)420 JSONRPC_STATUS CPVROperations::DeleteTimer(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
421 {
422   if (!CServiceBroker::GetPVRManager().IsStarted())
423     return FailedToExecute;
424 
425   std::shared_ptr<CPVRTimers> timers = CServiceBroker::GetPVRManager().Timers();
426   if (!timers)
427     return FailedToExecute;
428 
429   std::shared_ptr<CPVRTimerInfoTag> timer = timers->GetById(parameterObject["timerid"].asInteger());
430   if (!timer)
431     return InvalidParams;
432 
433   if (timers->DeleteTimer(timer, timer->IsRecording(), false) == TimerOperationResult::OK)
434     return ACK;
435 
436   return FailedToExecute;
437 }
438 
ToggleTimer(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)439 JSONRPC_STATUS CPVROperations::ToggleTimer(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
440 {
441   if (!CServiceBroker::GetPVRManager().IsStarted())
442     return FailedToExecute;
443 
444   const std::shared_ptr<CPVREpgInfoTag> epgTag =
445       CServiceBroker::GetPVRManager().EpgContainer().GetTagByDatabaseId(
446           parameterObject["broadcastid"].asInteger());
447 
448   if (!epgTag)
449     return InvalidParams;
450 
451   bool timerrule = parameterObject["timerrule"].asBoolean(false);
452   bool sentOkay = false;
453   std::shared_ptr<CPVRTimerInfoTag> timer = CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epgTag);
454   if (timer)
455   {
456     if (timerrule)
457       timer = CServiceBroker::GetPVRManager().Timers()->GetTimerRule(timer);
458 
459     if (timer)
460       sentOkay = (CServiceBroker::GetPVRManager().Timers()->DeleteTimer(timer, timer->IsRecording(), false) == TimerOperationResult::OK);
461   }
462   else
463   {
464     timer = CPVRTimerInfoTag::CreateFromEpg(epgTag, timerrule);
465     if (!timer)
466       return InvalidParams;
467 
468     sentOkay = CServiceBroker::GetPVRManager().GUIActions()->AddTimer(timer);
469   }
470 
471   if (sentOkay)
472     return ACK;
473 
474   return FailedToExecute;
475 }
476 
GetRecordings(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)477 JSONRPC_STATUS CPVROperations::GetRecordings(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
478 {
479   if (!CServiceBroker::GetPVRManager().IsStarted())
480     return FailedToExecute;
481 
482   std::shared_ptr<CPVRRecordings> recordings = CServiceBroker::GetPVRManager().Recordings();
483   if (!recordings)
484     return FailedToExecute;
485 
486   CFileItemList recordingsList;
487   const std::vector<std::shared_ptr<CPVRRecording>> recs = recordings->GetAll();
488   for (const auto& recording : recs)
489   {
490     recordingsList.Add(std::make_shared<CFileItem>(recording));
491   }
492 
493   HandleFileItemList("recordingid", true, "recordings", recordingsList, parameterObject, result, true);
494 
495   return OK;
496 }
497 
GetRecordingDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)498 JSONRPC_STATUS CPVROperations::GetRecordingDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
499 {
500   if (!CServiceBroker::GetPVRManager().IsStarted())
501     return FailedToExecute;
502 
503   std::shared_ptr<CPVRRecordings> recordings = CServiceBroker::GetPVRManager().Recordings();
504   if (!recordings)
505     return FailedToExecute;
506 
507   const std::shared_ptr<CPVRRecording> recording = recordings->GetById(static_cast<int>(parameterObject["recordingid"].asInteger()));
508   if (!recording)
509     return InvalidParams;
510 
511   HandleFileItem("recordingid", true, "recordingdetails", std::make_shared<CFileItem>(recording), parameterObject, parameterObject["properties"], result, false);
512 
513   return OK;
514 }
515