CPPMyth
Library to interoperate with MythTV server
mythwsapi.cpp
1 /*
2  * Copyright (C) 2014 Jean-Luc Barriere
3  *
4  * This Program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2, or (at your option)
7  * any later version.
8  *
9  * This Program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; see the file COPYING. If not, write to
16  * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
17  * MA 02110-1301 USA
18  * http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21 
22 #include "mythwsapi.h"
23 #include "private/debug.h"
24 #include "private/socket.h"
25 #include "private/wsrequest.h"
26 #include "private/wsresponse.h"
27 #include "private/jsonparser.h"
28 #include "private/mythjsonbinder.h"
29 #include "private/os/threads/mutex.h"
30 #include "private/cppdef.h"
31 #include "private/builtin.h"
32 #include "private/uriparser.h"
33 
34 #define BOOLSTR(a) ((a) ? "true" : "false")
35 #define FETCHSIZE 100
36 #define FETCHSIZE_L 1000
37 
38 using namespace Myth;
39 
40 #define WS_ROOT_MYTH "/Myth"
41 #define WS_ROOT_CAPTURE "/Capture"
42 #define WS_ROOT_CHANNEL "/Channel"
43 #define WS_ROOT_GUIDE "/Guide"
44 #define WS_ROOT_CONTENT "/Content"
45 #define WS_ROOT_DVR "/Dvr"
46 
47 static std::string encodeParam(const std::string& str);
48 
49 WSAPI::WSAPI(const std::string& server, unsigned port, const std::string& securityPin)
50 : m_mutex(new OS::CMutex)
51 , m_server(server)
52 , m_port(port)
53 , m_securityPin(securityPin)
54 , m_checked(false)
55 , m_version()
56 , m_serverHostName()
57 {
58  m_checked = InitWSAPI();
59 }
60 
61 WSAPI::~WSAPI()
62 {
63  SAFE_DELETE(m_mutex);
64 }
65 
66 bool WSAPI::InitWSAPI()
67 {
68  bool status = false;
69  // Reset array of version
70  memset(m_serviceVersion, 0, sizeof(m_serviceVersion));
71  // Check the core service Myth
72  WSServiceVersion_t& mythwsv = m_serviceVersion[WS_Myth];
73  if (!GetServiceVersion(WS_Myth, mythwsv))
74  {
75  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
76  return false;
77  }
78  if (mythwsv.ranking > MYTH_API_VERSION_MAX_RANKING) {}
79  else if (mythwsv.ranking >= 0x00020000)
80  status = CheckServerHostName2_0() & CheckVersion2_0();
81 
82  // If everything is fine then check other services
83  if (status)
84  {
85  if (GetServiceVersion(WS_Capture, m_serviceVersion[WS_Capture]) &&
86  GetServiceVersion(WS_Channel, m_serviceVersion[WS_Channel]) &&
87  GetServiceVersion(WS_Guide, m_serviceVersion[WS_Guide]) &&
88  GetServiceVersion(WS_Content, m_serviceVersion[WS_Content]) &&
89  GetServiceVersion(WS_Dvr, m_serviceVersion[WS_Dvr]))
90  {
91  DBG(DBG_INFO, "%s: MythTV API service is available: %s:%d(%s) protocol(%d) schema(%d)\n",
92  __FUNCTION__, m_serverHostName.c_str(), m_port, m_version.version.c_str(),
93  (unsigned)m_version.protocol, (unsigned)m_version.schema);
94  return true;
95  }
96  }
97  DBG(DBG_ERROR, "%s: MythTV API service is not supported or unavailable: %s:%d (%u.%u)\n",
98  __FUNCTION__, m_server.c_str(), m_port, mythwsv.major, mythwsv.minor);
99  return false;
100 }
101 
102 bool WSAPI::GetServiceVersion(WSServiceId_t id, WSServiceVersion_t& wsv)
103 {
104  static const char * WSServiceRoot[WS_INVALID + 1] =
105  {
106  WS_ROOT_MYTH,
107  WS_ROOT_CAPTURE,
108  WS_ROOT_CHANNEL,
109  WS_ROOT_GUIDE,
110  WS_ROOT_CONTENT,
111  WS_ROOT_DVR,
112  "/?",
113  };
114  std::string url(WSServiceRoot[id]);
115  url.append("/version");
116  WSRequest req = WSRequest(m_server, m_port);
117  req.RequestAccept(CT_JSON);
118  req.RequestService(url);
119  WSResponse resp(req);
120  if (resp.IsSuccessful())
121  {
122  // Parse content response
123  const JSON::Document json(resp);
124  const JSON::Node& root = json.GetRoot();
125  if (json.IsValid() && root.IsObject())
126  {
127  const JSON::Node& field = root.GetObjectValue("String");
128  if (field.IsString())
129  {
130  const std::string& val = field.GetStringValue();
131  if (sscanf(val.c_str(), "%d.%d", &(wsv.major), &(wsv.minor)) == 2)
132  {
133  wsv.ranking = ((wsv.major & 0xFFFF) << 16) | (wsv.minor & 0xFFFF);
134  return true;
135  }
136  }
137  }
138  }
139  wsv.major = 0;
140  wsv.minor = 0;
141  wsv.ranking = 0;
142  return false;
143 }
144 
145 bool WSAPI::CheckServerHostName2_0()
146 {
147  m_serverHostName.clear();
148 
149  WSRequest req = WSRequest(m_server, m_port);
150  req.RequestAccept(CT_JSON);
151  req.RequestService("/Myth/GetHostName");
152  WSResponse resp(req);
153  if (!resp.IsSuccessful())
154  {
155  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
156  return false;
157  }
158  // Parse content response
159  const JSON::Document json(resp);
160  const JSON::Node& root = json.GetRoot();
161  if (json.IsValid() && root.IsObject())
162  {
163  const JSON::Node& field = root.GetObjectValue("String");
164  if (field.IsString())
165  {
166  const std::string& val = field.GetStringValue();
167  m_serverHostName = val;
168  m_namedCache[val] = m_server;
169  return true;
170  }
171  }
172  return false;
173 }
174 
175 bool WSAPI::CheckVersion2_0()
176 {
177  m_version.protocol = 0;
178  m_version.schema = 0;
179  m_version.version.clear();
180  WSServiceVersion_t& wsv = m_serviceVersion[WS_Myth];
181 
182  WSRequest req = WSRequest(m_server, m_port);
183  req.RequestAccept(CT_JSON);
184  req.RequestService("/Myth/GetConnectionInfo");
185  if (!m_securityPin.empty())
186  {
187  // Skip if null or empty
188  req.SetContentParam("Pin", m_securityPin);
189  }
190  WSResponse resp(req);
191  if (!resp.IsSuccessful())
192  {
193  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
194  return false;
195  }
196  // Parse content response
197  const JSON::Document json(resp);
198  const JSON::Node& root = json.GetRoot();
199  if (json.IsValid() && root.IsObject())
200  {
201  const JSON::Node& con = root.GetObjectValue("ConnectionInfo");
202  if (con.IsObject())
203  {
204  const JSON::Node& ver = con.GetObjectValue("Version");
205  JSON::BindObject(ver, &m_version, MythDTO::getVersionBindArray(wsv.ranking));
206  if (m_version.protocol)
207  return true;
208  }
209  }
210  return false;
211 }
212 
213 unsigned WSAPI::CheckService()
214 {
215  OS::CLockGuard lock(*m_mutex);
216  if (m_checked || (m_checked = InitWSAPI()))
217  return (unsigned)m_version.protocol;
218  return 0;
219 }
220 
221 WSServiceVersion_t WSAPI::CheckService(WSServiceId_t id)
222 {
223  OS::CLockGuard lock(*m_mutex);
224  if (m_checked || (m_checked = InitWSAPI()))
225  return m_serviceVersion[id];
226  return m_serviceVersion[WS_INVALID];
227 }
228 
229 void WSAPI::InvalidateService()
230 {
231  if (m_checked)
232  m_checked = false;
233 }
234 
235 std::string WSAPI::GetServerHostName()
236 {
237  return m_serverHostName;
238 }
239 
240 VersionPtr WSAPI::GetVersion()
241 {
242  return VersionPtr(new Version(m_version));
243 }
244 
245 std::string WSAPI::ResolveHostName(const std::string& hostname)
246 {
247  OS::CLockGuard lock(*m_mutex);
248  std::map<std::string, std::string>::const_iterator it = m_namedCache.find(hostname);
249  if (it != m_namedCache.end())
250  return it->second;
251  Myth::SettingPtr addr = this->GetSetting("BackendServerIP6", hostname);
252  if (addr && !addr->value.empty() && addr->value != "::1")
253  {
254  std::string& ret = m_namedCache[hostname];
255  ret.assign(addr->value);
256  DBG(DBG_DEBUG, "%s: resolving hostname %s as %s\n", __FUNCTION__, hostname.c_str(), ret.c_str());
257  return ret;
258  }
259  addr = this->GetSetting("BackendServerIP", hostname);
260  if (addr && !addr->value.empty())
261  {
262  std::string& ret = m_namedCache[hostname];
263  ret.assign(addr->value);
264  DBG(DBG_DEBUG, "%s: resolving hostname %s as %s\n", __FUNCTION__, hostname.c_str(), ret.c_str());
265  return ret;
266  }
267  DBG(DBG_ERROR, "%s: unknown host (%s)\n", __FUNCTION__, hostname.c_str());
268  return std::string();
269 }
270 
275 
276 SettingPtr WSAPI::GetSetting2_0(const std::string& key, const std::string& hostname)
277 {
278  SettingPtr ret;
279 
280  // Initialize request header
281  WSRequest req = WSRequest(m_server, m_port);
282  req.RequestAccept(CT_JSON);
283  req.RequestService("/Myth/GetSetting");
284  req.SetContentParam("HostName", hostname);
285  req.SetContentParam("Key", key);
286  WSResponse resp(req);
287  if (!resp.IsSuccessful())
288  {
289  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
290  return ret;
291  }
292  const JSON::Document json(resp);
293  const JSON::Node& root = json.GetRoot();
294  if (!json.IsValid() || !root.IsObject())
295  {
296  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
297  return ret;
298  }
299  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
300 
301  // Object: SettingList
302  const JSON::Node& slist = root.GetObjectValue("SettingList");
303  // Object: Settings
304  const JSON::Node& sts = slist.GetObjectValue("Settings");
305  if (sts.IsObject())
306  {
307  if (sts.Size())
308  {
309  const JSON::Node& val = sts.GetObjectValue(static_cast<size_t>(0));
310  if (val.IsString())
311  {
312  ret.reset(new Setting()); // Using default constructor
313  ret->key = sts.GetObjectKey(0);
314  ret->value = val.GetStringValue();
315  }
316  }
317  }
318  return ret;
319 }
320 
321 SettingPtr WSAPI::GetSetting5_0(const std::string& key, const std::string& hostname)
322 {
323  SettingPtr ret;
324 
325  // Initialize request header
326  WSRequest req = WSRequest(m_server, m_port);
327  req.RequestAccept(CT_JSON);
328  req.RequestService("/Myth/GetSetting");
329  req.SetContentParam("HostName", hostname);
330  req.SetContentParam("Key", key);
331  WSResponse resp(req);
332  if (!resp.IsSuccessful())
333  {
334  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
335  return ret;
336  }
337  const JSON::Document json(resp);
338  const JSON::Node& root = json.GetRoot();
339  if (!json.IsValid() || !root.IsObject())
340  {
341  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
342  return ret;
343  }
344  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
345 
346  // Object: String
347  const JSON::Node& val = root.GetObjectValue("String");
348  if (val.IsString())
349  {
350  ret.reset(new Setting()); // Using default constructor
351  ret->key = key;
352  ret->value = val.GetStringValue();
353  }
354  return ret;
355 }
356 
357 SettingPtr WSAPI::GetSetting(const std::string& key, bool myhost)
358 {
359  std::string hostname;
360  if (myhost)
361  hostname = TcpSocket::GetMyHostName();
362  return GetSetting(key, hostname);
363 }
364 
365 SettingMapPtr WSAPI::GetSettings2_0(const std::string& hostname)
366 {
367  SettingMapPtr ret(new SettingMap);
368 
369  // Initialize request header
370  WSRequest req = WSRequest(m_server, m_port);
371  req.RequestAccept(CT_JSON);
372  req.RequestService("/Myth/GetSetting");
373  req.SetContentParam("HostName", hostname);
374  WSResponse resp(req);
375  if (!resp.IsSuccessful())
376  {
377  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
378  return ret;
379  }
380  const JSON::Document json(resp);
381  const JSON::Node& root = json.GetRoot();
382  if (!json.IsValid() || !root.IsObject())
383  {
384  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
385  return ret;
386  }
387  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
388 
389  // Object: SettingList
390  const JSON::Node& slist = root.GetObjectValue("SettingList");
391  // Object: Settings
392  const JSON::Node& sts = slist.GetObjectValue("Settings");
393  if (sts.IsObject())
394  {
395  size_t s = sts.Size();
396  for (size_t i = 0; i < s; ++i)
397  {
398  const JSON::Node& val = sts.GetObjectValue(i);
399  if (val.IsString())
400  {
401  SettingPtr setting(new Setting()); // Using default constructor
402  setting->key = sts.GetObjectKey(i);
403  setting->value = val.GetStringValue();
404  ret->insert(SettingMap::value_type(setting->key, setting));
405  }
406  }
407  }
408  return ret;
409 }
410 
411 SettingMapPtr WSAPI::GetSettings5_0(const std::string& hostname)
412 {
413  SettingMapPtr ret(new SettingMap);
414 
415  // Initialize request header
416  WSRequest req = WSRequest(m_server, m_port);
417  req.RequestAccept(CT_JSON);
418  req.RequestService("/Myth/GetSettingList");
419  req.SetContentParam("HostName", hostname);
420  WSResponse resp(req);
421  if (!resp.IsSuccessful())
422  {
423  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
424  return ret;
425  }
426  const JSON::Document json(resp);
427  const JSON::Node& root = json.GetRoot();
428  if (!json.IsValid() || !root.IsObject())
429  {
430  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
431  return ret;
432  }
433  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
434 
435  // Object: SettingList
436  const JSON::Node& slist = root.GetObjectValue("SettingList");
437  // Object: Settings
438  const JSON::Node& sts = slist.GetObjectValue("Settings");
439  if (sts.IsObject())
440  {
441  size_t s = sts.Size();
442  for (size_t i = 0; i < s; ++i)
443  {
444  const JSON::Node& val = sts.GetObjectValue(i);
445  if (val.IsString())
446  {
447  SettingPtr setting(new Setting()); // Using default constructor
448  setting->key = sts.GetObjectKey(i);
449  setting->value = val.GetStringValue();
450  ret->insert(SettingMap::value_type(setting->key, setting));
451  }
452  }
453  }
454  return ret;
455 }
456 
457 SettingMapPtr WSAPI::GetSettings(bool myhost)
458 {
459  std::string hostname;
460  if (myhost)
461  hostname = TcpSocket::GetMyHostName();
462  return GetSettings(hostname);
463 }
464 
465 bool WSAPI::PutSetting2_0(const std::string& key, const std::string& value, bool myhost)
466 {
467  // Initialize request header
468  WSRequest req = WSRequest(m_server, m_port);
469  req.RequestAccept(CT_JSON);
470  req.RequestService("/Myth/PutSetting", HRM_POST);
471  std::string hostname;
472  if (myhost)
473  hostname = TcpSocket::GetMyHostName();
474  req.SetContentParam("HostName", hostname);
475  req.SetContentParam("Key", key);
476  req.SetContentParam("Value", value);
477  WSResponse resp(req);
478  if (!resp.IsSuccessful())
479  {
480  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
481  return false;
482  }
483  const JSON::Document json(resp);
484  const JSON::Node& root = json.GetRoot();
485  if (!json.IsValid() || !root.IsObject())
486  {
487  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
488  return false;
489  }
490  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
491 
492  const JSON::Node& field = root.GetObjectValue("bool");
493  if (!field.IsString() || strcmp(field.GetStringValue().c_str(), "true"))
494  return false;
495  return true;
496 }
497 
502 CaptureCardListPtr WSAPI::GetCaptureCardList1_4()
503 {
504  CaptureCardListPtr ret(new CaptureCardList);
505  unsigned proto = (unsigned)m_version.protocol;
506 
507  // Get bindings for protocol version
508  const bindings_t *bindcard = MythDTO::getCaptureCardBindArray(proto);
509 
510  // Initialize request header
511  WSRequest req = WSRequest(m_server, m_port);
512  req.RequestAccept(CT_JSON);
513  req.RequestService("/Capture/GetCaptureCardList");
514  req.SetContentParam("HostName", m_serverHostName.c_str());
515  WSResponse resp(req);
516  if (!resp.IsSuccessful())
517  {
518  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
519  return ret;
520  }
521  const JSON::Document json(resp);
522  const JSON::Node& root = json.GetRoot();
523  if (!json.IsValid() || !root.IsObject())
524  {
525  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
526  return ret;
527  }
528  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
529 
530  // Object: CaptureCardList
531  const JSON::Node& clist = root.GetObjectValue("CaptureCardList");
532  // Object: CaptureCards[]
533  const JSON::Node& cards = clist.GetObjectValue("CaptureCards");
534  // Iterates over the sequence elements.
535  size_t cs = cards.Size();
536  for (size_t ci = 0; ci < cs; ++ci)
537  {
538  const JSON::Node& card = cards.GetArrayElement(ci);
539  CaptureCardPtr captureCard(new CaptureCard()); // Using default constructor
540  // Bind the new captureCard
541  JSON::BindObject(card, captureCard.get(), bindcard);
542  ret->push_back(captureCard);
543  }
544  return ret;
545 }
546 
551 VideoSourceListPtr WSAPI::GetVideoSourceList1_2()
552 {
553  VideoSourceListPtr ret(new VideoSourceList);
554  unsigned proto = (unsigned)m_version.protocol;
555 
556  // Get bindings for protocol version
557  const bindings_t *bindvsrc = MythDTO::getVideoSourceBindArray(proto);
558 
559  // Initialize request header
560  WSRequest req = WSRequest(m_server, m_port);
561  req.RequestAccept(CT_JSON);
562  req.RequestService("/Channel/GetVideoSourceList");
563  WSResponse resp(req);
564  if (!resp.IsSuccessful())
565  {
566  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
567  return ret;
568  }
569  const JSON::Document json(resp);
570  const JSON::Node& root = json.GetRoot();
571  if (!json.IsValid() || !root.IsObject())
572  {
573  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
574  return ret;
575  }
576  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
577 
578  // Object: VideoSourceList
579  const JSON::Node& slist = root.GetObjectValue("VideoSourceList");
580  // Object: VideoSources[]
581  const JSON::Node& vsrcs = slist.GetObjectValue("VideoSources");
582  // Iterates over the sequence elements.
583  size_t vs = vsrcs.Size();
584  for (size_t vi = 0; vi < vs; ++vi)
585  {
586  const JSON::Node& vsrc = vsrcs.GetArrayElement(vi);
587  VideoSourcePtr videoSource(new VideoSource()); // Using default constructor
588  // Bind the new videoSource
589  JSON::BindObject(vsrc, videoSource.get(), bindvsrc);
590  ret->push_back(videoSource);
591  }
592  return ret;
593 }
594 
595 ChannelListPtr WSAPI::GetChannelList1_2(uint32_t sourceid, bool onlyVisible)
596 {
597  ChannelListPtr ret(new ChannelList);
598  char buf[32];
599  int32_t req_index = 0, req_count = FETCHSIZE, count = 0;
600  unsigned proto = (unsigned)m_version.protocol;
601 
602  // Get bindings for protocol version
603  const bindings_t *bindlist = MythDTO::getListBindArray(proto);
604  const bindings_t *bindchan = MythDTO::getChannelBindArray(proto);
605 
606  // Initialize request header
607  WSRequest req = WSRequest(m_server, m_port);
608  req.RequestAccept(CT_JSON);
609  req.RequestService("/Channel/GetChannelInfoList");
610 
611  do
612  {
613  req.ClearContent();
614  uint32_to_string(sourceid, buf);
615  req.SetContentParam("SourceID", buf);
616  int32_to_string(req_index, buf);
617  req.SetContentParam("StartIndex", buf);
618  int32_to_string(req_count, buf);
619  req.SetContentParam("Count", buf);
620 
621  DBG(DBG_DEBUG, "%s: request index(%d) count(%d)\n", __FUNCTION__, req_index, req_count);
622  WSResponse resp(req);
623  if (!resp.IsSuccessful())
624  {
625  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
626  break;
627  }
628  const JSON::Document json(resp);
629  const JSON::Node& root = json.GetRoot();
630  if (!json.IsValid() || !root.IsObject())
631  {
632  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
633  break;
634  }
635  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
636 
637  // Object: ChannelInfoList
638  const JSON::Node& clist = root.GetObjectValue("ChannelInfoList");
639  ItemList list = ItemList(); // Using default constructor
640  JSON::BindObject(clist, &list, bindlist);
641  // List has ProtoVer. Check it or sound alarm
642  if (list.protoVer != proto)
643  {
644  InvalidateService();
645  break;
646  }
647  count = 0;
648  // Object: ChannelInfos[]
649  const JSON::Node& chans = clist.GetObjectValue("ChannelInfos");
650  // Iterates over the sequence elements.
651  size_t cs = chans.Size();
652  for (size_t ci = 0; ci < cs; ++ci)
653  {
654  ++count;
655  const JSON::Node& chan = chans.GetArrayElement(ci);
656  ChannelPtr channel(new Channel()); // Using default constructor
657  // Bind the new channel
658  JSON::BindObject(chan, channel.get(), bindchan);
659  if (channel->chanId && (!onlyVisible || channel->visible))
660  ret->push_back(channel);
661  }
662  DBG(DBG_DEBUG, "%s: received count(%d)\n", __FUNCTION__, count);
663  req_index += count; // Set next requested index
664  }
665  while (count == req_count);
666 
667  return ret;
668 }
669 
670 ChannelListPtr WSAPI::GetChannelList1_5(uint32_t sourceid, bool onlyVisible)
671 {
672  ChannelListPtr ret(new ChannelList);
673  char buf[32];
674  int32_t req_index = 0, /*req_count = FETCHSIZE,*/ count = 0;
675  unsigned proto = (unsigned)m_version.protocol;
676 
677  // Get bindings for protocol version
678  const bindings_t *bindlist = MythDTO::getListBindArray(proto);
679  const bindings_t *bindchan = MythDTO::getChannelBindArray(proto);
680 
681  // Initialize request header
682  WSRequest req = WSRequest(m_server, m_port);
683  req.RequestAccept(CT_JSON);
684  req.RequestService("/Channel/GetChannelInfoList");
685 
686  do
687  {
688  req.ClearContent();
689  req.SetContentParam("Details", "true");
690  req.SetContentParam("OnlyVisible", BOOLSTR(onlyVisible));
691  uint32_to_string(sourceid, buf);
692  req.SetContentParam("SourceID", buf);
693  // W.A. for bug tracked by ticket 12461
694  //int32_to_string(req_index, buf);
695  //req.SetContentParam("StartIndex", buf);
696  //int32_to_string(req_count, buf);
697  //req.SetContentParam("Count", buf);
698 
699  //DBG(DBG_DEBUG, "%s: request index(%d) count(%d)\n", __FUNCTION__, req_index, req_count);
700  WSResponse resp(req);
701  if (!resp.IsSuccessful())
702  {
703  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
704  break;
705  }
706  const JSON::Document json(resp);
707  const JSON::Node& root = json.GetRoot();
708  if (!json.IsValid() || !root.IsObject())
709  {
710  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
711  break;
712  }
713  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
714 
715  // Object: ChannelInfoList
716  const JSON::Node& clist = root.GetObjectValue("ChannelInfoList");
717  ItemList list = ItemList(); // Using default constructor
718  JSON::BindObject(clist, &list, bindlist);
719  // List has ProtoVer. Check it or sound alarm
720  if (list.protoVer != proto)
721  {
722  InvalidateService();
723  break;
724  }
725  count = 0;
726  // Object: ChannelInfos[]
727  const JSON::Node& chans = clist.GetObjectValue("ChannelInfos");
728  // Iterates over the sequence elements.
729  size_t cs = chans.Size();
730  for (size_t ci = 0; ci < cs; ++ci)
731  {
732  ++count;
733  const JSON::Node& chan = chans.GetArrayElement(ci);
734  ChannelPtr channel(new Channel()); // Using default constructor
735  // Bind the new channel
736  JSON::BindObject(chan, channel.get(), bindchan);
737  if (channel->chanId)
738  ret->push_back(channel);
739  }
740  DBG(DBG_DEBUG, "%s: received count(%d)\n", __FUNCTION__, count);
741  req_index += count; // Set next requested index
742  }
743  //while (count == req_count);
744  while (false); // W.A. for bug tracked by ticket 12461
745 
746  return ret;
747 }
748 
749 ChannelPtr WSAPI::GetChannel1_2(uint32_t chanid)
750 {
751  ChannelPtr ret;
752  char buf[32];
753  unsigned proto = (unsigned)m_version.protocol;
754 
755  // Get bindings for protocol version
756  const bindings_t *bindchan = MythDTO::getChannelBindArray(proto);
757 
758  // Initialize request header
759  WSRequest req = WSRequest(m_server, m_port);
760  req.RequestAccept(CT_JSON);
761  req.RequestService("/Channel/GetChannelInfo");
762  uint32_to_string(chanid, buf);
763  req.SetContentParam("ChanID", buf);
764 
765  WSResponse resp(req);
766  if (!resp.IsSuccessful())
767  {
768  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
769  return ret;
770  }
771  const JSON::Document json(resp);
772  const JSON::Node& root = json.GetRoot();
773  if (!json.IsValid() || !root.IsObject())
774  {
775  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
776  return ret;
777  }
778  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
779 
780  // Object: ChannelInfo
781  const JSON::Node& chan = root.GetObjectValue("ChannelInfo");
782  ChannelPtr channel(new Channel()); // Using default constructor
783  // Bind the new channel
784  JSON::BindObject(chan, channel.get(), bindchan);
785  if (channel->chanId == chanid)
786  ret = channel;
787  return ret;
788 }
789 
794 std::map<uint32_t, ProgramMapPtr> WSAPI::GetProgramGuide1_0(time_t starttime, time_t endtime)
795 {
796  std::map<uint32_t, ProgramMapPtr> ret;
797  char buf[32];
798  int32_t count = 0;
799  unsigned proto = (unsigned)m_version.protocol;
800 
801  // Get bindings for protocol version
802  const bindings_t *bindlist = MythDTO::getListBindArray(proto);
803  const bindings_t *bindchan = MythDTO::getChannelBindArray(proto);
804  const bindings_t *bindprog = MythDTO::getProgramBindArray(proto);
805 
806  // Initialize request header
807  WSRequest req = WSRequest(m_server, m_port);
808  req.RequestAccept(CT_JSON);
809  req.RequestService("/Guide/GetProgramGuide");
810  req.SetContentParam("StartChanId", "0");
811  req.SetContentParam("NumChannels", "0");
812  time_to_iso8601utc(starttime, buf);
813  req.SetContentParam("StartTime", buf);
814  time_to_iso8601utc(endtime, buf);
815  req.SetContentParam("EndTime", buf);
816  req.SetContentParam("Details", "true");
817 
818  WSResponse resp(req);
819  if (!resp.IsSuccessful())
820  {
821  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
822  return ret;
823  }
824  const JSON::Document json(resp);
825  const JSON::Node& root = json.GetRoot();
826  if (!json.IsValid() || !root.IsObject())
827  {
828  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
829  return ret;
830  }
831  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
832 
833  // Object: ProgramGuide
834  const JSON::Node& glist = root.GetObjectValue("ProgramGuide");
835  ItemList list = ItemList(); // Using default constructor
836  JSON::BindObject(glist, &list, bindlist);
837  // List has ProtoVer. Check it or sound alarm
838  if (list.protoVer != proto)
839  {
840  InvalidateService();
841  return ret;
842  }
843  // Object: Channels[]
844  const JSON::Node& chans = glist.GetObjectValue("Channels");
845  // Iterates over the sequence elements.
846  size_t cs = chans.Size();
847  for (size_t ci = 0; ci < cs; ++ci)
848  {
849  const JSON::Node& chan = chans.GetArrayElement(ci);
850  Channel channel;
851  JSON::BindObject(chan, &channel, bindchan);
852  ProgramMapPtr pmap(new ProgramMap);
853  ret.insert(std::make_pair(channel.chanId, pmap));
854  // Object: Programs[]
855  const JSON::Node& progs = chan.GetObjectValue("Programs");
856  // Iterates over the sequence elements.
857  size_t ps = progs.Size();
858  for (size_t pi = 0; pi < ps; ++pi)
859  {
860  ++count;
861  const JSON::Node& prog = progs.GetArrayElement(pi);
862  ProgramPtr program(new Program()); // Using default constructor
863  // Bind the new program
864  JSON::BindObject(prog, program.get(), bindprog);
865  program->channel = channel;
866  pmap->insert(std::make_pair(program->startTime, program));
867  }
868  }
869  DBG(DBG_DEBUG, "%s: received count(%d)\n", __FUNCTION__, count);
870 
871  return ret;
872 }
873 
874 ProgramMapPtr WSAPI::GetProgramGuide1_0(uint32_t chanid, time_t starttime, time_t endtime)
875 {
876  ProgramMapPtr ret(new ProgramMap);
877  char buf[32];
878  int32_t count = 0;
879  unsigned proto = (unsigned)m_version.protocol;
880 
881  // Get bindings for protocol version
882  const bindings_t *bindlist = MythDTO::getListBindArray(proto);
883  const bindings_t *bindchan = MythDTO::getChannelBindArray(proto);
884  const bindings_t *bindprog = MythDTO::getProgramBindArray(proto);
885 
886  // Initialize request header
887  WSRequest req = WSRequest(m_server, m_port);
888  req.RequestAccept(CT_JSON);
889  req.RequestService("/Guide/GetProgramGuide");
890  uint32_to_string(chanid, buf);
891  req.SetContentParam("StartChanId", buf);
892  req.SetContentParam("NumChannels", "1");
893  time_to_iso8601utc(starttime, buf);
894  req.SetContentParam("StartTime", buf);
895  time_to_iso8601utc(endtime, buf);
896  req.SetContentParam("EndTime", buf);
897  req.SetContentParam("Details", "true");
898 
899  WSResponse resp(req);
900  if (!resp.IsSuccessful())
901  {
902  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
903  return ret;
904  }
905  const JSON::Document json(resp);
906  const JSON::Node& root = json.GetRoot();
907  if (!json.IsValid() || !root.IsObject())
908  {
909  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
910  return ret;
911  }
912  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
913 
914  // Object: ProgramGuide
915  const JSON::Node& glist = root.GetObjectValue("ProgramGuide");
916  ItemList list = ItemList(); // Using default constructor
917  JSON::BindObject(glist, &list, bindlist);
918  // List has ProtoVer. Check it or sound alarm
919  if (list.protoVer != proto)
920  {
921  InvalidateService();
922  return ret;
923  }
924  // Object: Channels[]
925  const JSON::Node& chans = glist.GetObjectValue("Channels");
926  // Iterates over the sequence elements.
927  size_t cs = chans.Size();
928  for (size_t ci = 0; ci < cs; ++ci)
929  {
930  const JSON::Node& chan = chans.GetArrayElement(ci);
931  Channel channel;
932  JSON::BindObject(chan, &channel, bindchan);
933  if (channel.chanId != chanid)
934  continue;
935  // Object: Programs[]
936  const JSON::Node& progs = chan.GetObjectValue("Programs");
937  // Iterates over the sequence elements.
938  size_t ps = progs.Size();
939  for (size_t pi = 0; pi < ps; ++pi)
940  {
941  ++count;
942  const JSON::Node& prog = progs.GetArrayElement(pi);
943  ProgramPtr program(new Program()); // Using default constructor
944  // Bind the new program
945  JSON::BindObject(prog, program.get(), bindprog);
946  program->channel = channel;
947  ret->insert(std::make_pair(program->startTime, program));
948  }
949  break;
950  }
951  DBG(DBG_DEBUG, "%s: received count(%d)\n", __FUNCTION__, count);
952 
953  return ret;
954 }
955 
956 std::map<uint32_t, ProgramMapPtr> WSAPI::GetProgramGuide2_2(time_t starttime, time_t endtime)
957 {
958  std::map<uint32_t, ProgramMapPtr> ret;
959  char buf[32];
960  uint32_t req_index = 0, req_count = FETCHSIZE, count = 0, total = 0;
961  unsigned proto = (unsigned)m_version.protocol;
962 
963  // Adjust the fetch count according to the number of requested days
964  double d = difftime(endtime, starttime);
965  if (d > 0)
966  req_count = FETCHSIZE / (int)(1.0 + d / (3 * 86400));
967 
968  // Get bindings for protocol version
969  const bindings_t *bindlist = MythDTO::getListBindArray(proto);
970  const bindings_t *bindprog = MythDTO::getProgramBindArray(proto);
971  const bindings_t *bindchan = MythDTO::getChannelBindArray(proto);
972 
973  // Initialize request header
974  WSRequest req = WSRequest(m_server, m_port);
975  req.RequestAccept(CT_JSON);
976  req.RequestService("/Guide/GetProgramGuide");
977 
978  do
979  {
980  req.ClearContent();
981  uint32_to_string(req_index, buf);
982  req.SetContentParam("StartIndex", buf);
983  uint32_to_string(req_count, buf);
984  req.SetContentParam("Count", buf);
985  time_to_iso8601utc(starttime, buf);
986  req.SetContentParam("StartTime", buf);
987  time_to_iso8601utc(endtime, buf);
988  req.SetContentParam("EndTime", buf);
989  req.SetContentParam("Details", "true");
990 
991  DBG(DBG_DEBUG, "%s: request index(%d) count(%d)\n", __FUNCTION__, req_index, req_count);
992  WSResponse resp(req);
993  if (!resp.IsSuccessful())
994  {
995  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
996  break;
997  }
998  const JSON::Document json(resp);
999  const JSON::Node& root = json.GetRoot();
1000  if (!json.IsValid() || !root.IsObject())
1001  {
1002  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1003  break;
1004  }
1005  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1006 
1007  // Object: ProgramGuide
1008  const JSON::Node& glist = root.GetObjectValue("ProgramGuide");
1009  ItemList list = ItemList(); // Using default constructor
1010  JSON::BindObject(glist, &list, bindlist);
1011  // List has ProtoVer. Check it or sound alarm
1012  if (list.protoVer != proto)
1013  {
1014  InvalidateService();
1015  break;
1016  }
1017  count = 0;
1018  // Object: Channels[]
1019  const JSON::Node& chans = glist.GetObjectValue("Channels");
1020  // Iterates over the sequence elements.
1021  size_t cs = chans.Size();
1022  for (size_t ci = 0; ci < cs; ++ci)
1023  {
1024  ++count;
1025  const JSON::Node& chan = chans.GetArrayElement(ci);
1026  Channel channel;
1027  JSON::BindObject(chan, &channel, bindchan);
1028  ProgramMapPtr pmap(new ProgramMap);
1029  ret.insert(std::make_pair(channel.chanId, pmap));
1030  // Object: Programs[]
1031  const JSON::Node& progs = chan.GetObjectValue("Programs");
1032  // Iterates over the sequence elements.
1033  size_t ps = progs.Size();
1034  for (size_t pi = 0; pi < ps; ++pi)
1035  {
1036  const JSON::Node& prog = progs.GetArrayElement(pi);
1037  ProgramPtr program(new Program()); // Using default constructor
1038  // Bind the new program
1039  JSON::BindObject(prog, program.get(), bindprog);
1040  program->channel = channel;
1041  pmap->insert(std::make_pair(program->startTime, program));
1042  }
1043  ++total;
1044  }
1045  DBG(DBG_DEBUG, "%s: received count(%d)\n", __FUNCTION__, count);
1046  req_index += count; // Set next requested index
1047  }
1048  while (count == req_count);
1049 
1050  return ret;
1051 }
1052 
1053 ProgramMapPtr WSAPI::GetProgramList2_2(uint32_t chanid, time_t starttime, time_t endtime)
1054 {
1055  ProgramMapPtr ret(new ProgramMap);
1056  char buf[32];
1057  uint32_t req_index = 0, req_count = FETCHSIZE_L, count = 0, total = 0;
1058  unsigned proto = (unsigned)m_version.protocol;
1059 
1060  // Get bindings for protocol version
1061  const bindings_t *bindlist = MythDTO::getListBindArray(proto);
1062  const bindings_t *bindprog = MythDTO::getProgramBindArray(proto);
1063  const bindings_t *bindchan = MythDTO::getChannelBindArray(proto);
1064 
1065  // Initialize request header
1066  WSRequest req = WSRequest(m_server, m_port);
1067  req.RequestAccept(CT_JSON);
1068  req.RequestService("/Guide/GetProgramList");
1069 
1070  do
1071  {
1072  req.ClearContent();
1073  uint32_to_string(req_index, buf);
1074  req.SetContentParam("StartIndex", buf);
1075  uint32_to_string(req_count, buf);
1076  req.SetContentParam("Count", buf);
1077  uint32_to_string(chanid, buf);
1078  req.SetContentParam("ChanId", buf);
1079  time_to_iso8601utc(starttime, buf);
1080  req.SetContentParam("StartTime", buf);
1081  time_to_iso8601utc(endtime, buf);
1082  req.SetContentParam("EndTime", buf);
1083  req.SetContentParam("Details", "true");
1084 
1085  DBG(DBG_DEBUG, "%s: request index(%d) count(%d)\n", __FUNCTION__, req_index, req_count);
1086  WSResponse resp(req);
1087  if (!resp.IsSuccessful())
1088  {
1089  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1090  break;
1091  }
1092  const JSON::Document json(resp);
1093  const JSON::Node& root = json.GetRoot();
1094  if (!json.IsValid() || !root.IsObject())
1095  {
1096  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1097  break;
1098  }
1099  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1100 
1101  // Object: ProgramList
1102  const JSON::Node& plist = root.GetObjectValue("ProgramList");
1103  ItemList list = ItemList(); // Using default constructor
1104  JSON::BindObject(plist, &list, bindlist);
1105  // List has ProtoVer. Check it or sound alarm
1106  if (list.protoVer != proto)
1107  {
1108  InvalidateService();
1109  break;
1110  }
1111  count = 0;
1112  // Object: Programs[]
1113  const JSON::Node& progs = plist.GetObjectValue("Programs");
1114  // Iterates over the sequence elements.
1115  size_t ps = progs.Size();
1116  for (size_t pi = 0; pi < ps; ++pi)
1117  {
1118  ++count;
1119  const JSON::Node& prog = progs.GetArrayElement(pi);
1120  ProgramPtr program(new Program()); // Using default constructor
1121  // Bind the new program
1122  JSON::BindObject(prog, program.get(), bindprog);
1123  // Bind channel of program
1124  const JSON::Node& chan = prog.GetObjectValue("Channel");
1125  JSON::BindObject(chan, &(program->channel), bindchan);
1126  ret->insert(std::make_pair(program->startTime, program));
1127  ++total;
1128  }
1129  DBG(DBG_DEBUG, "%s: received count(%d)\n", __FUNCTION__, count);
1130  req_index += count; // Set next requested index
1131  }
1132  while (count == req_count);
1133 
1134  return ret;
1135 }
1136 
1141 ProgramListPtr WSAPI::GetRecordedList1_5(unsigned n, bool descending)
1142 {
1143  ProgramListPtr ret(new ProgramList);
1144  char buf[32];
1145  uint32_t req_index = 0, req_count = FETCHSIZE, count = 0, total = 0;
1146  unsigned proto = (unsigned)m_version.protocol;
1147 
1148  // Get bindings for protocol version
1149  const bindings_t *bindlist = MythDTO::getListBindArray(proto);
1150  const bindings_t *bindprog = MythDTO::getProgramBindArray(proto);
1151  const bindings_t *bindchan = MythDTO::getChannelBindArray(proto);
1152  const bindings_t *bindreco = MythDTO::getRecordingBindArray(proto);
1153  const bindings_t *bindartw = MythDTO::getArtworkBindArray(proto);
1154 
1155  // Initialize request header
1156  WSRequest req = WSRequest(m_server, m_port);
1157  req.RequestAccept(CT_JSON);
1158  req.RequestService("/Dvr/GetRecordedList");
1159 
1160  do
1161  {
1162  // Adjust the packet size
1163  if (n && req_count > (n - total))
1164  req_count = (n - total);
1165 
1166  req.ClearContent();
1167  uint32_to_string(req_index, buf);
1168  req.SetContentParam("StartIndex", buf);
1169  uint32_to_string(req_count, buf);
1170  req.SetContentParam("Count", buf);
1171  req.SetContentParam("Descending", BOOLSTR(descending));
1172 
1173  DBG(DBG_DEBUG, "%s: request index(%d) count(%d)\n", __FUNCTION__, req_index, req_count);
1174  WSResponse resp(req);
1175  if (!resp.IsSuccessful())
1176  {
1177  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1178  break;
1179  }
1180  const JSON::Document json(resp);
1181  const JSON::Node& root = json.GetRoot();
1182  if (!json.IsValid() || !root.IsObject())
1183  {
1184  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1185  break;
1186  }
1187  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1188 
1189  // Object: ProgramList
1190  const JSON::Node& plist = root.GetObjectValue("ProgramList");
1191  ItemList list = ItemList(); // Using default constructor
1192  JSON::BindObject(plist, &list, bindlist);
1193  // List has ProtoVer. Check it or sound alarm
1194  if (list.protoVer != proto)
1195  {
1196  InvalidateService();
1197  break;
1198  }
1199  count = 0;
1200  // Object: Programs[]
1201  const JSON::Node& progs = plist.GetObjectValue("Programs");
1202  // Iterates over the sequence elements.
1203  size_t ps = progs.Size();
1204  for (size_t pi = 0; pi < ps; ++pi)
1205  {
1206  ++count;
1207  const JSON::Node& prog = progs.GetArrayElement(pi);
1208  ProgramPtr program(new Program()); // Using default constructor
1209  // Bind the new program
1210  JSON::BindObject(prog, program.get(), bindprog);
1211  // Bind channel of program
1212  const JSON::Node& chan = prog.GetObjectValue("Channel");
1213  JSON::BindObject(chan, &(program->channel), bindchan);
1214  // Bind recording of program
1215  const JSON::Node& reco = prog.GetObjectValue("Recording");
1216  JSON::BindObject(reco, &(program->recording), bindreco);
1217  // Bind artwork list of program
1218  const JSON::Node& arts = prog.GetObjectValue("Artwork").GetObjectValue("ArtworkInfos");
1219  size_t as = arts.Size();
1220  for (size_t pa = 0; pa < as; ++pa)
1221  {
1222  const JSON::Node& artw = arts.GetArrayElement(pa);
1223  Artwork artwork = Artwork(); // Using default constructor
1224  JSON::BindObject(artw, &artwork, bindartw);
1225  program->artwork.push_back(artwork);
1226  }
1227  ret->push_back(program);
1228  ++total;
1229  }
1230  DBG(DBG_DEBUG, "%s: received count(%d)\n", __FUNCTION__, count);
1231  req_index += count; // Set next requested index
1232  }
1233  while (count == req_count && (!n || n > total));
1234 
1235  return ret;
1236 }
1237 
1238 ProgramPtr WSAPI::GetRecorded1_5(uint32_t chanid, time_t recstartts)
1239 {
1240  ProgramPtr ret;
1241  char buf[32];
1242  unsigned proto = (unsigned)m_version.protocol;
1243 
1244  // Get bindings for protocol version
1245  const bindings_t *bindprog = MythDTO::getProgramBindArray(proto);
1246  const bindings_t *bindchan = MythDTO::getChannelBindArray(proto);
1247  const bindings_t *bindreco = MythDTO::getRecordingBindArray(proto);
1248  const bindings_t *bindartw = MythDTO::getArtworkBindArray(proto);
1249 
1250  WSRequest req = WSRequest(m_server, m_port);
1251  req.RequestAccept(CT_JSON);
1252  req.RequestService("/Dvr/GetRecorded");
1253  uint32_to_string(chanid, buf);
1254  req.SetContentParam("ChanId", buf);
1255  time_to_iso8601utc(recstartts, buf);
1256  req.SetContentParam("StartTime", buf);
1257  WSResponse resp(req);
1258  if (!resp.IsSuccessful())
1259  {
1260  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1261  return ret;
1262  }
1263  const JSON::Document json(resp);
1264  const JSON::Node& root = json.GetRoot();
1265  if (!json.IsValid() || !root.IsObject())
1266  {
1267  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1268  return ret;
1269  }
1270  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1271 
1272  const JSON::Node& prog = root.GetObjectValue("Program");
1273  ProgramPtr program(new Program()); // Using default constructor
1274  // Bind the new program
1275  JSON::BindObject(prog, program.get(), bindprog);
1276  // Bind channel of program
1277  const JSON::Node& chan = prog.GetObjectValue("Channel");
1278  JSON::BindObject(chan, &(program->channel), bindchan);
1279  // Bind recording of program
1280  const JSON::Node& reco = prog.GetObjectValue("Recording");
1281  JSON::BindObject(reco, &(program->recording), bindreco);
1282  // Bind artwork list of program
1283  const JSON::Node& arts = prog.GetObjectValue("Artwork").GetObjectValue("ArtworkInfos");
1284  size_t as = arts.Size();
1285  for (size_t pa = 0; pa < as; ++pa)
1286  {
1287  const JSON::Node& artw = arts.GetArrayElement(pa);
1288  Artwork artwork = Artwork(); // Using default constructor
1289  JSON::BindObject(artw, &artwork, bindartw);
1290  program->artwork.push_back(artwork);
1291  }
1292  // Return valid program
1293  if (program->recording.startTs != INVALID_TIME)
1294  ret = program;
1295  return ret;
1296 }
1297 
1298 ProgramPtr WSAPI::GetRecorded6_0(uint32_t recordedid)
1299 {
1300  ProgramPtr ret;
1301  char buf[32];
1302  unsigned proto = (unsigned)m_version.protocol;
1303 
1304  // Get bindings for protocol version
1305  const bindings_t *bindprog = MythDTO::getProgramBindArray(proto);
1306  const bindings_t *bindchan = MythDTO::getChannelBindArray(proto);
1307  const bindings_t *bindreco = MythDTO::getRecordingBindArray(proto);
1308  const bindings_t *bindartw = MythDTO::getArtworkBindArray(proto);
1309 
1310  WSRequest req = WSRequest(m_server, m_port);
1311  req.RequestAccept(CT_JSON);
1312  req.RequestService("/Dvr/GetRecorded");
1313  uint32_to_string(recordedid, buf);
1314  req.SetContentParam("RecordedId", buf);
1315  WSResponse resp(req);
1316  if (!resp.IsSuccessful())
1317  {
1318  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1319  return ret;
1320  }
1321  const JSON::Document json(resp);
1322  const JSON::Node& root = json.GetRoot();
1323  if (!json.IsValid() || !root.IsObject())
1324  {
1325  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1326  return ret;
1327  }
1328  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1329 
1330  const JSON::Node& prog = root.GetObjectValue("Program");
1331  ProgramPtr program(new Program()); // Using default constructor
1332  // Bind the new program
1333  JSON::BindObject(prog, program.get(), bindprog);
1334  // Bind channel of program
1335  const JSON::Node& chan = prog.GetObjectValue("Channel");
1336  JSON::BindObject(chan, &(program->channel), bindchan);
1337  // Bind recording of program
1338  const JSON::Node& reco = prog.GetObjectValue("Recording");
1339  JSON::BindObject(reco, &(program->recording), bindreco);
1340  // Bind artwork list of program
1341  const JSON::Node& arts = prog.GetObjectValue("Artwork").GetObjectValue("ArtworkInfos");
1342  size_t as = arts.Size();
1343  for (size_t pa = 0; pa < as; ++pa)
1344  {
1345  const JSON::Node& artw = arts.GetArrayElement(pa);
1346  Artwork artwork = Artwork(); // Using default constructor
1347  JSON::BindObject(artw, &artwork, bindartw);
1348  program->artwork.push_back(artwork);
1349  }
1350  // Return valid program
1351  if (program->recording.startTs != INVALID_TIME)
1352  ret = program;
1353  return ret;
1354 }
1355 
1356 bool WSAPI::DeleteRecording2_1(uint32_t chanid, time_t recstartts, bool forceDelete, bool allowRerecord)
1357 {
1358  char buf[32];
1359 
1360  // Initialize request header
1361  WSRequest req = WSRequest(m_server, m_port);
1362  req.RequestAccept(CT_JSON);
1363  req.RequestService("/Dvr/DeleteRecording", HRM_POST);
1364  uint32_to_string(chanid, buf);
1365  req.SetContentParam("ChanId", buf);
1366  time_to_iso8601utc(recstartts, buf);
1367  req.SetContentParam("StartTime", buf);
1368  req.SetContentParam("ForceDelete", BOOLSTR(forceDelete));
1369  req.SetContentParam("AllowRerecord", BOOLSTR(allowRerecord));
1370  WSResponse resp(req);
1371  if (!resp.IsSuccessful())
1372  {
1373  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1374  return false;
1375  }
1376  const JSON::Document json(resp);
1377  const JSON::Node& root = json.GetRoot();
1378  if (!json.IsValid() || !root.IsObject())
1379  {
1380  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1381  return false;
1382  }
1383  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1384 
1385  const JSON::Node& field = root.GetObjectValue("bool");
1386  if (!field.IsString() || strcmp(field.GetStringValue().c_str(), "true"))
1387  return false;
1388  return true;
1389 }
1390 
1391 bool WSAPI::DeleteRecording6_0(uint32_t recordedid, bool forceDelete, bool allowRerecord)
1392 {
1393  char buf[32];
1394 
1395  // Initialize request header
1396  WSRequest req = WSRequest(m_server, m_port);
1397  req.RequestAccept(CT_JSON);
1398  req.RequestService("/Dvr/DeleteRecording", HRM_POST);
1399  uint32_to_string(recordedid, buf);
1400  req.SetContentParam("RecordedId", buf);
1401  req.SetContentParam("ForceDelete", BOOLSTR(forceDelete));
1402  req.SetContentParam("AllowRerecord", BOOLSTR(allowRerecord));
1403  WSResponse resp(req);
1404  if (!resp.IsSuccessful())
1405  {
1406  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1407  return false;
1408  }
1409  const JSON::Document json(resp);
1410  const JSON::Node& root = json.GetRoot();
1411  if (!json.IsValid() || !root.IsObject())
1412  {
1413  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1414  return false;
1415  }
1416  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1417 
1418  const JSON::Node& field = root.GetObjectValue("bool");
1419  if (!field.IsString() || strcmp(field.GetStringValue().c_str(), "true"))
1420  return false;
1421  return true;
1422 }
1423 
1424 bool WSAPI::UnDeleteRecording2_1(uint32_t chanid, time_t recstartts)
1425 {
1426  char buf[32];
1427 
1428  // Initialize request header
1429  WSRequest req = WSRequest(m_server, m_port);
1430  req.RequestAccept(CT_JSON);
1431  req.RequestService("/Dvr/UnDeleteRecording", HRM_POST);
1432  uint32_to_string(chanid, buf);
1433  req.SetContentParam("ChanId", buf);
1434  time_to_iso8601utc(recstartts, buf);
1435  req.SetContentParam("StartTime", buf);
1436  WSResponse resp(req);
1437  if (!resp.IsSuccessful())
1438  {
1439  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1440  return false;
1441  }
1442  const JSON::Document json(resp);
1443  const JSON::Node& root = json.GetRoot();
1444  if (!json.IsValid() || !root.IsObject())
1445  {
1446  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1447  return false;
1448  }
1449  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1450 
1451  const JSON::Node& field = root.GetObjectValue("bool");
1452  if (!field.IsString() || strcmp(field.GetStringValue().c_str(), "true"))
1453  return false;
1454  return true;
1455 }
1456 
1457 bool WSAPI::UnDeleteRecording6_0(uint32_t recordedid)
1458 {
1459  char buf[32];
1460 
1461  // Initialize request header
1462  WSRequest req = WSRequest(m_server, m_port);
1463  req.RequestAccept(CT_JSON);
1464  req.RequestService("/Dvr/UnDeleteRecording", HRM_POST);
1465  uint32_to_string(recordedid, buf);
1466  req.SetContentParam("RecordedId", buf);
1467  WSResponse resp(req);
1468  if (!resp.IsSuccessful())
1469  {
1470  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1471  return false;
1472  }
1473  const JSON::Document json(resp);
1474  const JSON::Node& root = json.GetRoot();
1475  if (!json.IsValid() || !root.IsObject())
1476  {
1477  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1478  return false;
1479  }
1480  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1481 
1482  const JSON::Node& field = root.GetObjectValue("bool");
1483  if (!field.IsString() || strcmp(field.GetStringValue().c_str(), "true"))
1484  return false;
1485  return true;
1486 }
1487 
1488 bool WSAPI::UpdateRecordedWatchedStatus4_5(uint32_t chanid, time_t recstartts, bool watched)
1489 {
1490  char buf[32];
1491 
1492  // Initialize request header
1493  WSRequest req = WSRequest(m_server, m_port);
1494  req.RequestAccept(CT_JSON);
1495  req.RequestService("/Dvr/UpdateRecordedWatchedStatus", HRM_POST);
1496  uint32_to_string(chanid, buf);
1497  req.SetContentParam("ChanId", buf);
1498  time_to_iso8601utc(recstartts, buf);
1499  req.SetContentParam("StartTime", buf);
1500  req.SetContentParam("Watched", BOOLSTR(watched));
1501  WSResponse resp(req);
1502  if (!resp.IsSuccessful())
1503  {
1504  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1505  return false;
1506  }
1507  const JSON::Document json(resp);
1508  const JSON::Node& root = json.GetRoot();
1509  if (!json.IsValid() || !root.IsObject())
1510  {
1511  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1512  return false;
1513  }
1514  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1515 
1516  const JSON::Node& field = root.GetObjectValue("bool");
1517  if (!field.IsString() || strcmp(field.GetStringValue().c_str(), "true"))
1518  return false;
1519  return true;
1520 }
1521 
1522 bool WSAPI::UpdateRecordedWatchedStatus6_0(uint32_t recordedid, bool watched)
1523 {
1524  char buf[32];
1525 
1526  // Initialize request header
1527  WSRequest req = WSRequest(m_server, m_port);
1528  req.RequestAccept(CT_JSON);
1529  req.RequestService("/Dvr/UpdateRecordedWatchedStatus", HRM_POST);
1530  uint32_to_string(recordedid, buf);
1531  req.SetContentParam("RecordedId", buf);
1532  req.SetContentParam("Watched", BOOLSTR(watched));
1533  WSResponse resp(req);
1534  if (!resp.IsSuccessful())
1535  {
1536  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1537  return false;
1538  }
1539  const JSON::Document json(resp);
1540  const JSON::Node& root = json.GetRoot();
1541  if (!json.IsValid() || !root.IsObject())
1542  {
1543  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1544  return false;
1545  }
1546  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1547 
1548  const JSON::Node& field = root.GetObjectValue("bool");
1549  if (!field.IsString() || strcmp(field.GetStringValue().c_str(), "true"))
1550  return false;
1551  return true;
1552 }
1553 
1554 MarkListPtr WSAPI::GetRecordedCommBreak6_1(uint32_t recordedid, int unit)
1555 {
1556  char buf[32];
1557  MarkListPtr ret(new MarkList);
1558  unsigned proto = (unsigned)m_version.protocol;
1559 
1560  // Get bindings for protocol version
1561  const bindings_t *bindcut = MythDTO::getCuttingBindArray(proto);
1562 
1563  // Initialize request header
1564  WSRequest req = WSRequest(m_server, m_port);
1565  req.RequestAccept(CT_JSON);
1566  req.RequestService("/Dvr/GetRecordedCommBreak");
1567  uint32_to_string(recordedid, buf);
1568  req.SetContentParam("RecordedId", buf);
1569  if (unit == 1)
1570  req.SetContentParam("OffsetType", "Position");
1571  else if (unit == 2)
1572  req.SetContentParam("OffsetType", "Duration");
1573  WSResponse resp(req);
1574  if (!resp.IsSuccessful())
1575  {
1576  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1577  return ret;
1578  }
1579  const JSON::Document json(resp);
1580  const JSON::Node& root = json.GetRoot();
1581  if (!json.IsValid() || !root.IsObject())
1582  {
1583  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1584  return ret;
1585  }
1586  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1587 
1588  // Object: CutList
1589  const JSON::Node& slist = root.GetObjectValue("CutList");
1590  // Object: Cuttings[]
1591  const JSON::Node& vcuts = slist.GetObjectValue("Cuttings");
1592  // Iterates over the sequence elements.
1593  size_t vs = vcuts.Size();
1594  for (size_t vi = 0; vi < vs; ++vi)
1595  {
1596  const JSON::Node& vcut = vcuts.GetArrayElement(vi);
1597  MarkPtr mark(new Mark()); // Using default constructor
1598  // Bind the new mark
1599  JSON::BindObject(vcut, mark.get(), bindcut);
1600  ret->push_back(mark);
1601  }
1602  return ret;
1603 }
1604 
1605 MarkListPtr WSAPI::GetRecordedCutList6_1(uint32_t recordedid, int unit)
1606 {
1607  char buf[32];
1608  MarkListPtr ret(new MarkList);
1609  unsigned proto = (unsigned)m_version.protocol;
1610 
1611  // Get bindings for protocol version
1612  const bindings_t *bindcut = MythDTO::getCuttingBindArray(proto);
1613 
1614  // Initialize request header
1615  WSRequest req = WSRequest(m_server, m_port);
1616  req.RequestAccept(CT_JSON);
1617  req.RequestService("/Dvr/GetRecordedCutList");
1618  uint32_to_string(recordedid, buf);
1619  req.SetContentParam("RecordedId", buf);
1620  if (unit == 1)
1621  req.SetContentParam("OffsetType", "Position");
1622  else if (unit == 2)
1623  req.SetContentParam("OffsetType", "Duration");
1624  WSResponse resp(req);
1625  if (!resp.IsSuccessful())
1626  {
1627  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1628  return ret;
1629  }
1630  const JSON::Document json(resp);
1631  const JSON::Node& root = json.GetRoot();
1632  if (!json.IsValid() || !root.IsObject())
1633  {
1634  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1635  return ret;
1636  }
1637  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1638 
1639  // Object: CutList
1640  const JSON::Node& slist = root.GetObjectValue("CutList");
1641  // Object: Cuttings[]
1642  const JSON::Node& vcuts = slist.GetObjectValue("Cuttings");
1643  // Iterates over the sequence elements.
1644  size_t vs = vcuts.Size();
1645  for (size_t vi = 0; vi < vs; ++vi)
1646  {
1647  const JSON::Node& vcut = vcuts.GetArrayElement(vi);
1648  MarkPtr mark(new Mark()); // Using default constructor
1649  // Bind the new mark
1650  JSON::BindObject(vcut, mark.get(), bindcut);
1651  ret->push_back(mark);
1652  }
1653  return ret;
1654 }
1655 
1656 bool WSAPI::SetSavedBookmark6_2(uint32_t recordedid, int unit, int64_t value)
1657 {
1658  char buf[32];
1659 
1660  // Initialize request header
1661  WSRequest req = WSRequest(m_server, m_port);
1662  req.RequestAccept(CT_JSON);
1663  req.RequestService("/Dvr/SetSavedBookmark", HRM_POST);
1664  uint32_to_string(recordedid, buf);
1665  req.SetContentParam("RecordedId", buf);
1666  if (unit == 2)
1667  req.SetContentParam("OffsetType", "Duration");
1668  else
1669  req.SetContentParam("OffsetType", "Position");
1670  int64_to_string(value, buf);
1671  req.SetContentParam("Offset", buf);
1672  WSResponse resp(req);
1673  if (!resp.IsSuccessful())
1674  {
1675  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1676  return false;
1677  }
1678  const JSON::Document json(resp);
1679  const JSON::Node& root = json.GetRoot();
1680  if (!json.IsValid() || !root.IsObject())
1681  {
1682  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1683  return false;
1684  }
1685  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1686 
1687  const JSON::Node& field = root.GetObjectValue("bool");
1688  if (!field.IsString() || strcmp(field.GetStringValue().c_str(), "true"))
1689  return false;
1690  return true;
1691 }
1692 
1693 int64_t WSAPI::GetSavedBookmark6_2(uint32_t recordedid, int unit)
1694 {
1695  char buf[32];
1696 
1697  // Initialize request header
1698  WSRequest req = WSRequest(m_server, m_port);
1699  req.RequestAccept(CT_JSON);
1700  req.RequestService("/Dvr/GetSavedBookmark", HRM_POST);
1701  uint32_to_string(recordedid, buf);
1702  req.SetContentParam("RecordedId", buf);
1703  if (unit == 2)
1704  req.SetContentParam("OffsetType", "Duration");
1705  else
1706  req.SetContentParam("OffsetType", "Position");
1707  WSResponse resp(req);
1708  if (!resp.IsSuccessful())
1709  {
1710  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1711  return false;
1712  }
1713  const JSON::Document json(resp);
1714  const JSON::Node& root = json.GetRoot();
1715  if (!json.IsValid() || !root.IsObject())
1716  {
1717  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1718  return false;
1719  }
1720  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1721 
1722  int64_t value = 0;
1723  const JSON::Node& field = root.GetObjectValue("long");
1724  if (!field.IsString() || string_to_int64(field.GetStringValue().c_str(), &value))
1725  return -1;
1726  return value;
1727 }
1728 
1729 static void ProcessRecordIN(unsigned proto, RecordSchedule& record)
1730 {
1731  // Converting API codes to internal types
1732  record.type_t = RuleTypeFromString(proto, record.type);
1733  record.searchType_t = SearchTypeFromString(proto, record.searchType);
1734  record.dupMethod_t = DupMethodFromString(proto, record.dupMethod);
1735  record.dupIn_t = DupInFromString(proto, record.dupIn);
1736 }
1737 
1738 RecordScheduleListPtr WSAPI::GetRecordScheduleList1_5()
1739 {
1740  RecordScheduleListPtr ret(new RecordScheduleList);
1741  char buf[32];
1742  int32_t req_index = 0, req_count = FETCHSIZE, count = 0;
1743  unsigned proto = (unsigned)m_version.protocol;
1744 
1745  // Get bindings for protocol version
1746  const bindings_t *bindlist = MythDTO::getListBindArray(proto);
1747  const bindings_t *bindrec = MythDTO::getRecordScheduleBindArray(proto);
1748 
1749  // Initialize request header
1750  WSRequest req = WSRequest(m_server, m_port);
1751  req.RequestAccept(CT_JSON);
1752  req.RequestService("/Dvr/GetRecordScheduleList");
1753 
1754  do
1755  {
1756  req.ClearContent();
1757  int32_to_string(req_index, buf);
1758  req.SetContentParam("StartIndex", buf);
1759  int32_to_string(req_count, buf);
1760  req.SetContentParam("Count", buf);
1761 
1762  DBG(DBG_DEBUG, "%s: request index(%d) count(%d)\n", __FUNCTION__, req_index, req_count);
1763  WSResponse resp(req);
1764  if (!resp.IsSuccessful())
1765  {
1766  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1767  break;
1768  }
1769  const JSON::Document json(resp);
1770  const JSON::Node& root = json.GetRoot();
1771  if (!json.IsValid() || !root.IsObject())
1772  {
1773  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1774  break;
1775  }
1776  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1777 
1778  // Object: RecRuleList
1779  const JSON::Node& rlist = root.GetObjectValue("RecRuleList");
1780  ItemList list = ItemList(); // Using default constructor
1781  JSON::BindObject(rlist, &list, bindlist);
1782  // List has ProtoVer. Check it or sound alarm
1783  if (list.protoVer != proto)
1784  {
1785  InvalidateService();
1786  break;
1787  }
1788  count = 0;
1789  // Object: RecRules[]
1790  const JSON::Node& recs = rlist.GetObjectValue("RecRules");
1791  // Iterates over the sequence elements.
1792  size_t rs = recs.Size();
1793  for (size_t ri = 0; ri < rs; ++ri)
1794  {
1795  ++count;
1796  const JSON::Node& rec = recs.GetArrayElement(ri);
1797  RecordSchedulePtr record(new RecordSchedule()); // Using default constructor
1798  // Bind the new record
1799  JSON::BindObject(rec, record.get(), bindrec);
1800  ProcessRecordIN(proto, *record);
1801  ret->push_back(record);
1802  }
1803  DBG(DBG_DEBUG, "%s: received count(%d)\n", __FUNCTION__, count);
1804  req_index += count; // Set next requested index
1805  }
1806  while (count == req_count);
1807 
1808  return ret;
1809 }
1810 
1811 RecordSchedulePtr WSAPI::GetRecordSchedule1_5(uint32_t recordid)
1812 {
1813  RecordSchedulePtr ret;
1814  char buf[32];
1815  unsigned proto = (unsigned)m_version.protocol;
1816 
1817  // Get bindings for protocol version
1818  const bindings_t *bindrec = MythDTO::getRecordScheduleBindArray(proto);
1819 
1820  WSRequest req = WSRequest(m_server, m_port);
1821  req.RequestAccept(CT_JSON);
1822  req.RequestService("/Dvr/GetRecordSchedule");
1823  uint32_to_string(recordid, buf);
1824  req.SetContentParam("RecordId", buf);
1825  WSResponse resp(req);
1826  if (!resp.IsSuccessful())
1827  {
1828  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1829  return ret;
1830  }
1831  const JSON::Document json(resp);
1832  const JSON::Node& root = json.GetRoot();
1833  if (!json.IsValid() || !root.IsObject())
1834  {
1835  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1836  return ret;
1837  }
1838  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1839 
1840  const JSON::Node& rec = root.GetObjectValue("RecRule");
1841  RecordSchedulePtr record(new RecordSchedule()); // Using default constructor
1842  // Bind the new record
1843  JSON::BindObject(rec, record.get(), bindrec);
1844  // Return valid record
1845  if (record->recordId > 0)
1846  {
1847  ProcessRecordIN(proto, *record);
1848  ret = record;
1849  }
1850  return ret;
1851 }
1852 
1853 static void ProcessRecordOUT(unsigned proto, RecordSchedule& record)
1854 {
1855  char buf[10];
1856  struct tm stm;
1857  time_t st = record.startTime;
1858  localtime_r(&st, &stm);
1859  // Set find time & day
1860  sprintf(buf, "%.2d:%.2d:%.2d", stm.tm_hour, stm.tm_min, stm.tm_sec);
1861  record.findTime = buf;
1862  record.findDay = (stm.tm_wday + 1) % 7;
1863  // Converting internal types to API codes
1864  record.type = RuleTypeToString(proto, record.type_t);
1865  record.searchType = SearchTypeToString(proto, record.searchType_t);
1866  record.dupMethod = DupMethodToString(proto, record.dupMethod_t);
1867  record.dupIn = DupInToString(proto, record.dupIn_t);
1868 }
1869 
1870 bool WSAPI::AddRecordSchedule1_5(RecordSchedule& record)
1871 {
1872  char buf[32];
1873  uint32_t recordid;
1874  unsigned proto = (unsigned)m_version.protocol;
1875 
1876  ProcessRecordOUT(proto, record);
1877 
1878  // Initialize request header
1879  WSRequest req = WSRequest(m_server, m_port);
1880  req.RequestAccept(CT_JSON);
1881  req.RequestService("/Dvr/AddRecordSchedule", HRM_POST);
1882 
1883  req.SetContentParam("Title", record.title);
1884  req.SetContentParam("Subtitle", record.subtitle);
1885  req.SetContentParam("Description", record.description);
1886  req.SetContentParam("Category", record.category);
1887  time_to_iso8601utc(record.startTime, buf);
1888  req.SetContentParam("StartTime", buf);
1889  time_to_iso8601utc(record.endTime, buf);
1890  req.SetContentParam("EndTime", buf);
1891  req.SetContentParam("SeriesId", record.seriesId);
1892  req.SetContentParam("ProgramId", record.programId);
1893  uint32_to_string(record.chanId, buf);
1894  req.SetContentParam("ChanId", buf);
1895  uint32_to_string(record.parentId, buf);
1896  req.SetContentParam("ParentId", buf);
1897  req.SetContentParam("Inactive", BOOLSTR(record.inactive));
1898  uint16_to_string(record.season, buf);
1899  req.SetContentParam("Season", buf);
1900  uint16_to_string(record.episode, buf);
1901  req.SetContentParam("Episode", buf);
1902  req.SetContentParam("Inetref", record.inetref);
1903  req.SetContentParam("Type", record.type);
1904  req.SetContentParam("SearchType", record.searchType);
1905  int8_to_string(record.recPriority, buf);
1906  req.SetContentParam("RecPriority", buf);
1907  uint32_to_string(record.preferredInput, buf);
1908  req.SetContentParam("PreferredInput", buf);
1909  uint8_to_string(record.startOffset, buf);
1910  req.SetContentParam("StartOffset", buf);
1911  uint8_to_string(record.endOffset, buf);
1912  req.SetContentParam("EndOffset", buf);
1913  req.SetContentParam("DupMethod", record.dupMethod);
1914  req.SetContentParam("DupIn", record.dupIn);
1915  uint32_to_string(record.filter, buf);
1916  req.SetContentParam("Filter", buf);
1917  req.SetContentParam("RecProfile", record.recProfile);
1918  req.SetContentParam("RecGroup", record.recGroup);
1919  req.SetContentParam("StorageGroup", record.storageGroup);
1920  req.SetContentParam("PlayGroup", record.playGroup);
1921  req.SetContentParam("AutoExpire", BOOLSTR(record.autoExpire));
1922  uint32_to_string(record.maxEpisodes, buf);
1923  req.SetContentParam("MaxEpisodes", buf);
1924  req.SetContentParam("MaxNewest", BOOLSTR(record.maxNewest));
1925  req.SetContentParam("AutoCommflag", BOOLSTR(record.autoCommflag));
1926  req.SetContentParam("AutoTranscode", BOOLSTR(record.autoTranscode));
1927  req.SetContentParam("AutoMetaLookup", BOOLSTR(record.autoMetaLookup));
1928  req.SetContentParam("AutoUserJob1", BOOLSTR(record.autoUserJob1));
1929  req.SetContentParam("AutoUserJob2", BOOLSTR(record.autoUserJob2));
1930  req.SetContentParam("AutoUserJob3", BOOLSTR(record.autoUserJob3));
1931  req.SetContentParam("AutoUserJob4", BOOLSTR(record.autoUserJob4));
1932  uint32_to_string(record.transcoder, buf);
1933  req.SetContentParam("Transcoder", buf);
1934 
1935  WSResponse resp(req);
1936  if (!resp.IsSuccessful())
1937  {
1938  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
1939  return false;
1940  }
1941  const JSON::Document json(resp);
1942  const JSON::Node& root = json.GetRoot();
1943  if (!json.IsValid() || !root.IsObject())
1944  {
1945  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
1946  return false;
1947  }
1948  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
1949 
1950  const JSON::Node& field = root.GetObjectValue("int");
1951  if (!field.IsString() || string_to_uint32(field.GetStringValue().c_str(), &recordid))
1952  return false;
1953  record.recordId = recordid;
1954  return true;
1955 }
1956 
1957 bool WSAPI::AddRecordSchedule1_7(RecordSchedule& record)
1958 {
1959  char buf[32];
1960  uint32_t recordid;
1961  unsigned proto = (unsigned)m_version.protocol;
1962 
1963  ProcessRecordOUT(proto, record);
1964 
1965  // Initialize request header
1966  WSRequest req = WSRequest(m_server, m_port);
1967  req.RequestAccept(CT_JSON);
1968  req.RequestService("/Dvr/AddRecordSchedule", HRM_POST);
1969 
1970  req.SetContentParam("Title", record.title);
1971  req.SetContentParam("Subtitle", record.subtitle);
1972  req.SetContentParam("Description", record.description);
1973  req.SetContentParam("Category", record.category);
1974  time_to_iso8601utc(record.startTime, buf);
1975  req.SetContentParam("StartTime", buf);
1976  time_to_iso8601utc(record.endTime, buf);
1977  req.SetContentParam("EndTime", buf);
1978  req.SetContentParam("SeriesId", record.seriesId);
1979  req.SetContentParam("ProgramId", record.programId);
1980  uint32_to_string(record.chanId, buf);
1981  req.SetContentParam("ChanId", buf);
1982  req.SetContentParam("Station", record.callSign);
1983  int8_to_string(record.findDay, buf);
1984  req.SetContentParam("FindDay", buf);
1985  req.SetContentParam("FindTime", record.findTime);
1986  uint32_to_string(record.parentId, buf);
1987  req.SetContentParam("ParentId", buf);
1988  req.SetContentParam("Inactive", BOOLSTR(record.inactive));
1989  uint16_to_string(record.season, buf);
1990  req.SetContentParam("Season", buf);
1991  uint16_to_string(record.episode, buf);
1992  req.SetContentParam("Episode", buf);
1993  req.SetContentParam("Inetref", record.inetref);
1994  req.SetContentParam("Type", record.type);
1995  req.SetContentParam("SearchType", record.searchType);
1996  int8_to_string(record.recPriority, buf);
1997  req.SetContentParam("RecPriority", buf);
1998  uint32_to_string(record.preferredInput, buf);
1999  req.SetContentParam("PreferredInput", buf);
2000  uint8_to_string(record.startOffset, buf);
2001  req.SetContentParam("StartOffset", buf);
2002  uint8_to_string(record.endOffset, buf);
2003  req.SetContentParam("EndOffset", buf);
2004  req.SetContentParam("DupMethod", record.dupMethod);
2005  req.SetContentParam("DupIn", record.dupIn);
2006  uint32_to_string(record.filter, buf);
2007  req.SetContentParam("Filter", buf);
2008  req.SetContentParam("RecProfile", record.recProfile);
2009  req.SetContentParam("RecGroup", record.recGroup);
2010  req.SetContentParam("StorageGroup", record.storageGroup);
2011  req.SetContentParam("PlayGroup", record.playGroup);
2012  req.SetContentParam("AutoExpire", BOOLSTR(record.autoExpire));
2013  uint32_to_string(record.maxEpisodes, buf);
2014  req.SetContentParam("MaxEpisodes", buf);
2015  req.SetContentParam("MaxNewest", BOOLSTR(record.maxNewest));
2016  req.SetContentParam("AutoCommflag", BOOLSTR(record.autoCommflag));
2017  req.SetContentParam("AutoTranscode", BOOLSTR(record.autoTranscode));
2018  req.SetContentParam("AutoMetaLookup", BOOLSTR(record.autoMetaLookup));
2019  req.SetContentParam("AutoUserJob1", BOOLSTR(record.autoUserJob1));
2020  req.SetContentParam("AutoUserJob2", BOOLSTR(record.autoUserJob2));
2021  req.SetContentParam("AutoUserJob3", BOOLSTR(record.autoUserJob3));
2022  req.SetContentParam("AutoUserJob4", BOOLSTR(record.autoUserJob4));
2023  uint32_to_string(record.transcoder, buf);
2024  req.SetContentParam("Transcoder", buf);
2025 
2026  WSResponse resp(req);
2027  if (!resp.IsSuccessful())
2028  {
2029  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
2030  return false;
2031  }
2032  const JSON::Document json(resp);
2033  const JSON::Node& root = json.GetRoot();
2034  if (!json.IsValid() || !root.IsObject())
2035  {
2036  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
2037  return false;
2038  }
2039  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
2040 
2041  const JSON::Node& field = root.GetObjectValue("uint");
2042  if (!field.IsString() || string_to_uint32(field.GetStringValue().c_str(), &recordid))
2043  return false;
2044  record.recordId = recordid;
2045  return true;
2046 }
2047 
2048 bool WSAPI::UpdateRecordSchedule1_7(RecordSchedule& record)
2049 {
2050  char buf[32];
2051  unsigned proto = (unsigned)m_version.protocol;
2052 
2053  ProcessRecordOUT(proto, record);
2054 
2055  // Initialize request header
2056  WSRequest req = WSRequest(m_server, m_port);
2057  req.RequestAccept(CT_JSON);
2058  req.RequestService("/Dvr/UpdateRecordSchedule", HRM_POST);
2059 
2060  uint32_to_string(record.recordId, buf);
2061  req.SetContentParam("RecordId", buf);
2062  req.SetContentParam("Title", record.title);
2063  req.SetContentParam("Subtitle", record.subtitle);
2064  req.SetContentParam("Description", record.description);
2065  req.SetContentParam("Category", record.category);
2066  time_to_iso8601utc(record.startTime, buf);
2067  req.SetContentParam("StartTime", buf);
2068  time_to_iso8601utc(record.endTime, buf);
2069  req.SetContentParam("EndTime", buf);
2070  req.SetContentParam("SeriesId", record.seriesId);
2071  req.SetContentParam("ProgramId", record.programId);
2072  uint32_to_string(record.chanId, buf);
2073  req.SetContentParam("ChanId", buf);
2074  req.SetContentParam("Station", record.callSign);
2075  int8_to_string(record.findDay, buf);
2076  req.SetContentParam("FindDay", buf);
2077  req.SetContentParam("FindTime", record.findTime);
2078  uint32_to_string(record.parentId, buf);
2079  req.SetContentParam("ParentId", buf);
2080  req.SetContentParam("Inactive", BOOLSTR(record.inactive));
2081  uint16_to_string(record.season, buf);
2082  req.SetContentParam("Season", buf);
2083  uint16_to_string(record.episode, buf);
2084  req.SetContentParam("Episode", buf);
2085  req.SetContentParam("Inetref", record.inetref);
2086  req.SetContentParam("Type", record.type);
2087  req.SetContentParam("SearchType", record.searchType);
2088  int8_to_string(record.recPriority, buf);
2089  req.SetContentParam("RecPriority", buf);
2090  uint32_to_string(record.preferredInput, buf);
2091  req.SetContentParam("PreferredInput", buf);
2092  uint8_to_string(record.startOffset, buf);
2093  req.SetContentParam("StartOffset", buf);
2094  uint8_to_string(record.endOffset, buf);
2095  req.SetContentParam("EndOffset", buf);
2096  req.SetContentParam("DupMethod", record.dupMethod);
2097  req.SetContentParam("DupIn", record.dupIn);
2098  uint32_to_string(record.filter, buf);
2099  req.SetContentParam("Filter", buf);
2100  req.SetContentParam("RecProfile", record.recProfile);
2101  req.SetContentParam("RecGroup", record.recGroup);
2102  req.SetContentParam("StorageGroup", record.storageGroup);
2103  req.SetContentParam("PlayGroup", record.playGroup);
2104  req.SetContentParam("AutoExpire", BOOLSTR(record.autoExpire));
2105  uint32_to_string(record.maxEpisodes, buf);
2106  req.SetContentParam("MaxEpisodes", buf);
2107  req.SetContentParam("MaxNewest", BOOLSTR(record.maxNewest));
2108  req.SetContentParam("AutoCommflag", BOOLSTR(record.autoCommflag));
2109  req.SetContentParam("AutoTranscode", BOOLSTR(record.autoTranscode));
2110  req.SetContentParam("AutoMetaLookup", BOOLSTR(record.autoMetaLookup));
2111  req.SetContentParam("AutoUserJob1", BOOLSTR(record.autoUserJob1));
2112  req.SetContentParam("AutoUserJob2", BOOLSTR(record.autoUserJob2));
2113  req.SetContentParam("AutoUserJob3", BOOLSTR(record.autoUserJob3));
2114  req.SetContentParam("AutoUserJob4", BOOLSTR(record.autoUserJob4));
2115  uint32_to_string(record.transcoder, buf);
2116  req.SetContentParam("Transcoder", buf);
2117 
2118  WSResponse resp(req);
2119  if (!resp.IsSuccessful())
2120  {
2121  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
2122  return false;
2123  }
2124  const JSON::Document json(resp);
2125  const JSON::Node& root = json.GetRoot();
2126  if (!json.IsValid() || !root.IsObject())
2127  {
2128  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
2129  return false;
2130  }
2131  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
2132 
2133  const JSON::Node& field = root.GetObjectValue("bool");
2134  if (!field.IsString() || strcmp(field.GetStringValue().c_str(), "true"))
2135  return false;
2136  return true;
2137 }
2138 
2139 bool WSAPI::DisableRecordSchedule1_5(uint32_t recordid)
2140 {
2141  char buf[32];
2142 
2143  // Initialize request header
2144  WSRequest req = WSRequest(m_server, m_port);
2145  req.RequestAccept(CT_JSON);
2146  req.RequestService("/Dvr/DisableRecordSchedule", HRM_POST);
2147 
2148  uint32_to_string(recordid, buf);
2149  req.SetContentParam("RecordId", buf);
2150 
2151  WSResponse resp(req);
2152  if (!resp.IsSuccessful())
2153  {
2154  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
2155  return false;
2156  }
2157  const JSON::Document json(resp);
2158  const JSON::Node& root = json.GetRoot();
2159  if (!json.IsValid() || !root.IsObject())
2160  {
2161  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
2162  return false;
2163  }
2164  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
2165 
2166  const JSON::Node& field = root.GetObjectValue("bool");
2167  if (!field.IsString() || strcmp(field.GetStringValue().c_str(), "true"))
2168  return false;
2169  return true;
2170 }
2171 
2172 bool WSAPI::EnableRecordSchedule1_5(uint32_t recordid)
2173 {
2174  char buf[32];
2175 
2176  // Initialize request header
2177  WSRequest req = WSRequest(m_server, m_port);
2178  req.RequestAccept(CT_JSON);
2179  req.RequestService("/Dvr/EnableRecordSchedule", HRM_POST);
2180 
2181  uint32_to_string(recordid, buf);
2182  req.SetContentParam("RecordId", buf);
2183 
2184  WSResponse resp(req);
2185  if (!resp.IsSuccessful())
2186  {
2187  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
2188  return false;
2189  }
2190  const JSON::Document json(resp);
2191  const JSON::Node& root = json.GetRoot();
2192  if (!json.IsValid() || !root.IsObject())
2193  {
2194  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
2195  return false;
2196  }
2197  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
2198 
2199  const JSON::Node& field = root.GetObjectValue("bool");
2200  if (!field.IsString() || strcmp(field.GetStringValue().c_str(), "true"))
2201  return false;
2202  return true;
2203 }
2204 
2205 bool WSAPI::RemoveRecordSchedule1_5(uint32_t recordid)
2206 {
2207  char buf[32];
2208 
2209  // Initialize request header
2210  WSRequest req = WSRequest(m_server, m_port);
2211  req.RequestAccept(CT_JSON);
2212  req.RequestService("/Dvr/RemoveRecordSchedule", HRM_POST);
2213 
2214  uint32_to_string(recordid, buf);
2215  req.SetContentParam("RecordId", buf);
2216 
2217  WSResponse resp(req);
2218  if (!resp.IsSuccessful())
2219  {
2220  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
2221  return false;
2222  }
2223  const JSON::Document json(resp);
2224  const JSON::Node& root = json.GetRoot();
2225  if (!json.IsValid() || !root.IsObject())
2226  {
2227  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
2228  return false;
2229  }
2230  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
2231 
2232  const JSON::Node& field = root.GetObjectValue("bool");
2233  if (!field.IsString() || strcmp(field.GetStringValue().c_str(), "true"))
2234  return false;
2235  return true;
2236 }
2237 
2238 ProgramListPtr WSAPI::GetUpcomingList1_5()
2239 {
2240  // Only for backward compatibility (0.27)
2241  ProgramListPtr ret = GetUpcomingList2_2();
2242  // Add being recorded (https://code.mythtv.org/trac/changeset/3084ebc/mythtv)
2243  ProgramListPtr recordings = GetRecordedList(20, true);
2244  for (Myth::ProgramList::iterator it = recordings->begin(); it != recordings->end(); ++it)
2245  {
2246  if ((*it)->recording.status == RS_RECORDING)
2247  ret->push_back(*it);
2248  }
2249  return ret;
2250 }
2251 
2252 ProgramListPtr WSAPI::GetUpcomingList2_2()
2253 {
2254  ProgramListPtr ret(new ProgramList);
2255  char buf[32];
2256  int32_t req_index = 0, req_count = FETCHSIZE, count = 0;
2257  unsigned proto = (unsigned)m_version.protocol;
2258 
2259  // Get bindings for protocol version
2260  const bindings_t *bindlist = MythDTO::getListBindArray(proto);
2261  const bindings_t *bindprog = MythDTO::getProgramBindArray(proto);
2262  const bindings_t *bindchan = MythDTO::getChannelBindArray(proto);
2263  const bindings_t *bindreco = MythDTO::getRecordingBindArray(proto);
2264 
2265  // Initialize request header
2266  WSRequest req = WSRequest(m_server, m_port);
2267  req.RequestAccept(CT_JSON);
2268  req.RequestService("/Dvr/GetUpcomingList");
2269 
2270  do
2271  {
2272  req.ClearContent();
2273  int32_to_string(req_index, buf);
2274  req.SetContentParam("StartIndex", buf);
2275  int32_to_string(req_count, buf);
2276  req.SetContentParam("Count", buf);
2277  req.SetContentParam("ShowAll", "true");
2278 
2279  DBG(DBG_DEBUG, "%s: request index(%d) count(%d)\n", __FUNCTION__, req_index, req_count);
2280  WSResponse resp(req);
2281  if (!resp.IsSuccessful())
2282  {
2283  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
2284  break;
2285  }
2286  const JSON::Document json(resp);
2287  const JSON::Node& root = json.GetRoot();
2288  if (!json.IsValid() || !root.IsObject())
2289  {
2290  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
2291  break;
2292  }
2293  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
2294 
2295  // Object: ProgramList
2296  const JSON::Node& plist = root.GetObjectValue("ProgramList");
2297  ItemList list = ItemList(); // Using default constructor
2298  JSON::BindObject(plist, &list, bindlist);
2299  // List has ProtoVer. Check it or sound alarm
2300  if (list.protoVer != proto)
2301  {
2302  InvalidateService();
2303  break;
2304  }
2305  count = 0;
2306  // Object: Programs[]
2307  const JSON::Node& progs = plist.GetObjectValue("Programs");
2308  // Iterates over the sequence elements.
2309  size_t ps = progs.Size();
2310  for (size_t pi = 0; pi < ps; ++pi)
2311  {
2312  ++count;
2313  const JSON::Node& prog = progs.GetArrayElement(pi);
2314  ProgramPtr program(new Program()); // Using default constructor
2315  // Bind the new program
2316  JSON::BindObject(prog, program.get(), bindprog);
2317  // Bind channel of program
2318  const JSON::Node& chan = prog.GetObjectValue("Channel");
2319  JSON::BindObject(chan, &(program->channel), bindchan);
2320  // Bind recording of program
2321  const JSON::Node& reco = prog.GetObjectValue("Recording");
2322  JSON::BindObject(reco, &(program->recording), bindreco);
2323  ret->push_back(program);
2324  }
2325  DBG(DBG_DEBUG, "%s: received count(%d)\n", __FUNCTION__, count);
2326  req_index += count; // Set next requested index
2327  }
2328  while (count == req_count);
2329 
2330  return ret;
2331 }
2332 
2333 ProgramListPtr WSAPI::GetConflictList1_5()
2334 {
2335  ProgramListPtr ret(new ProgramList);
2336  char buf[32];
2337  int32_t req_index = 0, req_count = FETCHSIZE, count = 0;
2338  unsigned proto = (unsigned)m_version.protocol;
2339 
2340  // Get bindings for protocol version
2341  const bindings_t *bindlist = MythDTO::getListBindArray(proto);
2342  const bindings_t *bindprog = MythDTO::getProgramBindArray(proto);
2343  const bindings_t *bindchan = MythDTO::getChannelBindArray(proto);
2344  const bindings_t *bindreco = MythDTO::getRecordingBindArray(proto);
2345 
2346  // Initialize request header
2347  WSRequest req = WSRequest(m_server, m_port);
2348  req.RequestAccept(CT_JSON);
2349  req.RequestService("/Dvr/GetConflictList");
2350 
2351  do
2352  {
2353  req.ClearContent();
2354  int32_to_string(req_index, buf);
2355  req.SetContentParam("StartIndex", buf);
2356  int32_to_string(req_count, buf);
2357  req.SetContentParam("Count", buf);
2358 
2359  DBG(DBG_DEBUG, "%s: request index(%d) count(%d)\n", __FUNCTION__, req_index, req_count);
2360  WSResponse resp(req);
2361  if (!resp.IsSuccessful())
2362  {
2363  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
2364  break;
2365  }
2366  const JSON::Document json(resp);
2367  const JSON::Node& root = json.GetRoot();
2368  if (!json.IsValid() || !root.IsObject())
2369  {
2370  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
2371  break;
2372  }
2373  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
2374 
2375  // Object: ProgramList
2376  const JSON::Node& plist = root.GetObjectValue("ProgramList");
2377  ItemList list = ItemList(); // Using default constructor
2378  JSON::BindObject(plist, &list, bindlist);
2379  // List has ProtoVer. Check it or sound alarm
2380  if (list.protoVer != proto)
2381  {
2382  InvalidateService();
2383  break;
2384  }
2385  count = 0;
2386  // Object: Programs[]
2387  const JSON::Node& progs = plist.GetObjectValue("Programs");
2388  // Iterates over the sequence elements.
2389  size_t ps = progs.Size();
2390  for (size_t pi = 0; pi < ps; ++pi)
2391  {
2392  ++count;
2393  const JSON::Node& prog = progs.GetArrayElement(pi);
2394  ProgramPtr program(new Program()); // Using default constructor
2395  // Bind the new program
2396  JSON::BindObject(prog, program.get(), bindprog);
2397  // Bind channel of program
2398  const JSON::Node& chan = prog.GetObjectValue("Channel");
2399  JSON::BindObject(chan, &(program->channel), bindchan);
2400  // Bind recording of program
2401  const JSON::Node& reco = prog.GetObjectValue("Recording");
2402  JSON::BindObject(reco, &(program->recording), bindreco);
2403  ret->push_back(program);
2404  }
2405  DBG(DBG_DEBUG, "%s: received count(%d)\n", __FUNCTION__, count);
2406  req_index += count; // Set next requested index
2407  }
2408  while (count == req_count);
2409 
2410  return ret;
2411 }
2412 
2413 ProgramListPtr WSAPI::GetExpiringList1_5()
2414 {
2415  ProgramListPtr ret(new ProgramList);
2416  char buf[32];
2417  int32_t req_index = 0, req_count = FETCHSIZE, count = 0;
2418  unsigned proto = (unsigned)m_version.protocol;
2419 
2420  // Get bindings for protocol version
2421  const bindings_t *bindlist = MythDTO::getListBindArray(proto);
2422  const bindings_t *bindprog = MythDTO::getProgramBindArray(proto);
2423  const bindings_t *bindchan = MythDTO::getChannelBindArray(proto);
2424  const bindings_t *bindreco = MythDTO::getRecordingBindArray(proto);
2425 
2426  // Initialize request header
2427  WSRequest req = WSRequest(m_server, m_port);
2428  req.RequestAccept(CT_JSON);
2429  req.RequestService("/Dvr/GetExpiringList");
2430 
2431  do
2432  {
2433  req.ClearContent();
2434  int32_to_string(req_index, buf);
2435  req.SetContentParam("StartIndex", buf);
2436  int32_to_string(req_count, buf);
2437  req.SetContentParam("Count", buf);
2438 
2439  DBG(DBG_DEBUG, "%s: request index(%d) count(%d)\n", __FUNCTION__, req_index, req_count);
2440  WSResponse resp(req);
2441  if (!resp.IsSuccessful())
2442  {
2443  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
2444  break;
2445  }
2446  const JSON::Document json(resp);
2447  const JSON::Node& root = json.GetRoot();
2448  if (!json.IsValid() || !root.IsObject())
2449  {
2450  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
2451  break;
2452  }
2453  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
2454 
2455  // Object: ProgramList
2456  const JSON::Node& plist = root.GetObjectValue("ProgramList");
2457  ItemList list = ItemList(); // Using default constructor
2458  JSON::BindObject(plist, &list, bindlist);
2459  // List has ProtoVer. Check it or sound alarm
2460  if (list.protoVer != proto)
2461  {
2462  InvalidateService();
2463  break;
2464  }
2465  count = 0;
2466  // Object: Programs[]
2467  const JSON::Node& progs = plist.GetObjectValue("Programs");
2468  // Iterates over the sequence elements.
2469  size_t ps = progs.Size();
2470  for (size_t pi = 0; pi < ps; ++pi)
2471  {
2472  ++count;
2473  const JSON::Node& prog = progs.GetArrayElement(pi);
2474  ProgramPtr program(new Program()); // Using default constructor
2475  // Bind the new program
2476  JSON::BindObject(prog, program.get(), bindprog);
2477  // Bind channel of program
2478  const JSON::Node& chan = prog.GetObjectValue("Channel");
2479  JSON::BindObject(chan, &(program->channel), bindchan);
2480  // Bind recording of program
2481  const JSON::Node& reco = prog.GetObjectValue("Recording");
2482  JSON::BindObject(reco, &(program->recording), bindreco);
2483  ret->push_back(program);
2484  }
2485  DBG(DBG_DEBUG, "%s: received count(%d)\n", __FUNCTION__, count);
2486  req_index += count; // Set next requested index
2487  }
2488  while (count == req_count);
2489 
2490  return ret;
2491 }
2492 
2493 StringListPtr WSAPI::GetRecGroupList1_5()
2494 {
2495  StringListPtr ret(new StringList);
2496 
2497  // Initialize request header
2498  WSRequest req = WSRequest(m_server, m_port);
2499  req.RequestAccept(CT_JSON);
2500  req.RequestService("/Dvr/GetRecGroupList");
2501  WSResponse resp(req);
2502  if (!resp.IsSuccessful())
2503  {
2504  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
2505  return ret;
2506  }
2507  const JSON::Document json(resp);
2508  const JSON::Node& root = json.GetRoot();
2509  if (!json.IsValid() || !root.IsObject())
2510  {
2511  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
2512  return ret;
2513  }
2514  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
2515 
2516  // Object: Strings
2517  const JSON::Node& list = root.GetObjectValue("StringList");
2518  if (list.IsArray())
2519  {
2520  size_t s = list.Size();
2521  for (size_t i = 0; i < s; ++i)
2522  {
2523  const JSON::Node& val = list.GetArrayElement(i);
2524  if (val.IsString())
2525  {
2526  ret->push_back(val.GetStringValue());
2527  }
2528  }
2529  }
2530  return ret;
2531 }
2532 
2537 WSStreamPtr WSAPI::GetFile1_32(const std::string& filename, const std::string& sgname)
2538 {
2539  WSStreamPtr ret;
2540 
2541  // Initialize request header
2542  WSRequest req = WSRequest(m_server, m_port);
2543  req.RequestService("/Content/GetFile");
2544  req.SetContentParam("StorageGroup", sgname);
2545  req.SetContentParam("FileName", filename);
2546  WSResponse *resp = new WSResponse(req);
2547  /* try redirection if any */
2548  if (resp->GetStatusCode() == 301 && !resp->Redirection().empty())
2549  {
2550  URIParser uri(resp->Redirection());
2551  WSRequest rreq(ResolveHostName(uri.Host()), uri.Port());
2552  rreq.RequestService(std::string("/").append(uri.Path()));
2553  delete resp;
2554  resp = new WSResponse(rreq);
2555  }
2556  if (!resp->IsSuccessful())
2557  {
2558  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
2559  delete resp;
2560  return ret;
2561  }
2562  ret.reset(new WSStream(resp));
2563  return ret;
2564 }
2565 
2566 WSStreamPtr WSAPI::GetChannelIcon1_32(uint32_t chanid, unsigned width, unsigned height)
2567 {
2568  WSStreamPtr ret;
2569  char buf[32];
2570 
2571  // Initialize request header
2572  WSRequest req = WSRequest(m_server, m_port);
2573  req.RequestService("/Guide/GetChannelIcon");
2574  uint32_to_string(chanid, buf);
2575  req.SetContentParam("ChanId", buf);
2576  if (width)
2577  {
2578  uint32_to_string(width, buf);
2579  req.SetContentParam("Width", buf);
2580  }
2581  if (height)
2582  {
2583  uint32_to_string(height, buf);
2584  req.SetContentParam("Height", buf);
2585  }
2586  WSResponse *resp = new WSResponse(req);
2587  /* try redirection if any */
2588  if (resp->GetStatusCode() == 301 && !resp->Redirection().empty())
2589  {
2590  URIParser uri(resp->Redirection());
2591  WSRequest rreq(ResolveHostName(uri.Host()), uri.Port());
2592  rreq.RequestService(std::string("/").append(uri.Path()));
2593  delete resp;
2594  resp = new WSResponse(rreq);
2595  }
2596  if (!resp->IsSuccessful())
2597  {
2598  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
2599  delete resp;
2600  return ret;
2601  }
2602  ret.reset(new WSStream(resp));
2603  return ret;
2604 }
2605 
2606 std::string WSAPI::GetChannelIconUrl1_32(uint32_t chanid, unsigned width, unsigned height)
2607 {
2608  char buf[32];
2609  std::string uri;
2610  uri.reserve(95);
2611  uri.append("http://").append(m_server);
2612  if (m_port != 80)
2613  {
2614  uint32_to_string(m_port, buf);
2615  uri.append(":").append(buf);
2616  }
2617  uri.append("/Guide/GetChannelIcon");
2618  uint32_to_string(chanid, buf);
2619  uri.append("?ChanId=").append(buf);
2620  if (width)
2621  {
2622  uint32_to_string(width, buf);
2623  uri.append("&Width=").append(buf);
2624  }
2625  if (height)
2626  {
2627  uint32_to_string(height, buf);
2628  uri.append("&Height=").append(buf);
2629  }
2630  return uri;
2631 }
2632 
2633 WSStreamPtr WSAPI::GetPreviewImage1_32(uint32_t chanid, time_t recstartts, unsigned width, unsigned height)
2634 {
2635  WSStreamPtr ret;
2636  char buf[32];
2637 
2638  // Initialize request header
2639  WSRequest req = WSRequest(m_server, m_port);
2640  req.RequestService("/Content/GetPreviewImage");
2641  uint32_to_string(chanid, buf);
2642  req.SetContentParam("ChanId", buf);
2643  time_to_iso8601utc(recstartts, buf);
2644  req.SetContentParam("StartTime", buf);
2645  if (width)
2646  {
2647  uint32_to_string(width, buf);
2648  req.SetContentParam("Width", buf);
2649  }
2650  if (height)
2651  {
2652  uint32_to_string(height, buf);
2653  req.SetContentParam("Height", buf);
2654  }
2655  WSResponse *resp = new WSResponse(req);
2656  /* try redirection if any */
2657  if (resp->GetStatusCode() == 301 && !resp->Redirection().empty())
2658  {
2659  URIParser uri(resp->Redirection());
2660  WSRequest rreq(ResolveHostName(uri.Host()), uri.Port());
2661  rreq.RequestService(std::string("/").append(uri.Path()));
2662  delete resp;
2663  resp = new WSResponse(rreq);
2664  }
2665  if (!resp->IsSuccessful())
2666  {
2667  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
2668  delete resp;
2669  return ret;
2670  }
2671  ret.reset(new WSStream(resp));
2672  return ret;
2673 }
2674 
2675 std::string WSAPI::GetPreviewImageUrl1_32(uint32_t chanid, time_t recstartts, unsigned width, unsigned height)
2676 {
2677  char buf[32];
2678  std::string uri;
2679  uri.reserve(95);
2680  uri.append("http://").append(m_server);
2681  if (m_port != 80)
2682  {
2683  uint32_to_string(m_port, buf);
2684  uri.append(":").append(buf);
2685  }
2686  uri.append("/Content/GetPreviewImage");
2687  uint32_to_string(chanid, buf);
2688  uri.append("?ChanId=").append(buf);
2689  time_to_iso8601utc(recstartts, buf);
2690  uri.append("&StartTime=").append(encodeParam(buf));
2691  if (width)
2692  {
2693  uint32_to_string(width, buf);
2694  uri.append("&Width=").append(buf);
2695  }
2696  if (height)
2697  {
2698  uint32_to_string(height, buf);
2699  uri.append("&Height=").append(buf);
2700  }
2701  return uri;
2702 }
2703 
2704 WSStreamPtr WSAPI::GetRecordingArtwork1_32(const std::string& type, const std::string& inetref, uint16_t season, unsigned width, unsigned height)
2705 {
2706  WSStreamPtr ret;
2707  char buf[32];
2708 
2709  // Initialize request header
2710  WSRequest req = WSRequest(m_server, m_port);
2711  req.RequestService("/Content/GetRecordingArtwork");
2712  req.SetContentParam("Type", type.c_str());
2713  req.SetContentParam("Inetref", inetref.c_str());
2714  uint16_to_string(season, buf);
2715  req.SetContentParam("Season", buf);
2716  if (width)
2717  {
2718  uint32_to_string(width, buf);
2719  req.SetContentParam("Width", buf);
2720  }
2721  if (height)
2722  {
2723  uint32_to_string(height, buf);
2724  req.SetContentParam("Height", buf);
2725  }
2726  WSResponse *resp = new WSResponse(req);
2727  /* try redirection if any */
2728  if (resp->GetStatusCode() == 301 && !resp->Redirection().empty())
2729  {
2730  URIParser uri(resp->Redirection());
2731  WSRequest rreq(ResolveHostName(uri.Host()), uri.Port());
2732  rreq.RequestService(std::string("/").append(uri.Path()));
2733  delete resp;
2734  resp = new WSResponse(rreq);
2735  }
2736  if (!resp->IsSuccessful())
2737  {
2738  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
2739  delete resp;
2740  return ret;
2741  }
2742  ret.reset(new WSStream(resp));
2743  return ret;
2744 }
2745 
2746 std::string WSAPI::GetRecordingArtworkUrl1_32(const std::string& type, const std::string& inetref, uint16_t season, unsigned width, unsigned height)
2747 {
2748  char buf[32];
2749  std::string uri;
2750  uri.reserve(127);
2751  uri.append("http://").append(m_server);
2752  if (m_port != 80)
2753  {
2754  uint32_to_string(m_port, buf);
2755  uri.append(":").append(buf);
2756  }
2757  uri.append("/Content/GetRecordingArtwork");
2758  uri.append("?Type=").append(encodeParam(type));
2759  uri.append("&Inetref=").append(encodeParam(inetref));
2760  uint16_to_string(season, buf);
2761  uri.append("&Season=").append(buf);
2762  if (width)
2763  {
2764  uint32_to_string(width, buf);
2765  uri.append("&Width=").append(buf);
2766  }
2767  if (height)
2768  {
2769  uint32_to_string(height, buf);
2770  uri.append("&Height=").append(buf);
2771  }
2772  return uri;
2773 }
2774 
2775 ArtworkListPtr WSAPI::GetRecordingArtworkList1_32(uint32_t chanid, time_t recstartts)
2776 {
2777  ArtworkListPtr ret(new ArtworkList);
2778  char buf[32];
2779  unsigned proto = (unsigned)m_version.protocol;
2780 
2781  // Get bindings for protocol version
2782  const bindings_t *bindartw = MythDTO::getArtworkBindArray(proto);
2783 
2784  WSRequest req = WSRequest(m_server, m_port);
2785  req.RequestAccept(CT_JSON);
2786  req.RequestService("/Content/GetRecordingArtworkList");
2787  uint32_to_string(chanid, buf);
2788  req.SetContentParam("ChanId", buf);
2789  time_to_iso8601utc(recstartts, buf);
2790  req.SetContentParam("StartTime", buf);
2791  WSResponse resp(req);
2792  if (!resp.IsSuccessful())
2793  {
2794  DBG(DBG_ERROR, "%s: invalid response\n", __FUNCTION__);
2795  return ret;
2796  }
2797  const JSON::Document json(resp);
2798  const JSON::Node& root = json.GetRoot();
2799  if (!json.IsValid() || !root.IsObject())
2800  {
2801  DBG(DBG_ERROR, "%s: unexpected content\n", __FUNCTION__);
2802  return ret;
2803  }
2804  DBG(DBG_DEBUG, "%s: content parsed\n", __FUNCTION__);
2805 
2806  const JSON::Node& list = root.GetObjectValue("ArtworkInfoList");
2807  // Bind artwork list
2808  const JSON::Node& arts = list.GetObjectValue("ArtworkInfos");
2809  size_t as = arts.Size();
2810  for (size_t pa = 0; pa < as; ++pa)
2811  {
2812  const JSON::Node& artw = arts.GetArrayElement(pa);
2813  ArtworkPtr artwork(new Artwork()); // Using default constructor
2814  JSON::BindObject(artw, artwork.get(), bindartw);
2815  ret->push_back(artwork);
2816  }
2817  return ret;
2818 }
2819 
2820 // Internal
2821 #include "private/urlencoder.h"
2822 
2823 static std::string encodeParam(const std::string& str)
2824 {
2825  return urlencode(str);
2826 }
const bindings_t * getRecordingBindArray(unsigned proto)
Returns bindings for Myth::Recording.
Definition: mythdto.cpp:49
SettingMapPtr GetSettings(const std::string &hostname)
GET Myth/GetSetting.
Definition: mythwsapi.h:89
const bindings_t * getProgramBindArray(unsigned proto)
Returns bindings for Myth::Program.
Definition: mythdto.cpp:65
const bindings_t * getListBindArray(unsigned proto)
Returns bindings for Myth::List.
Definition: mythdto.cpp:36
const bindings_t * getCuttingBindArray(unsigned proto)
Returns bindings for Myth::Mark.
Definition: mythdto.cpp:95
const bindings_t * getArtworkBindArray(unsigned proto)
Returns bindings for Myth::Artwork.
Definition: mythdto.cpp:58
Brings together all attribute bindings of an object.
Definition: mythdto.h:66
bool GetServiceVersion(WSServiceId_t id, WSServiceVersion_t &version)
Definition: mythwsapi.cpp:102
const bindings_t * getChannelBindArray(unsigned proto)
Returns bindings for Myth::Channel.
Definition: mythdto.cpp:42
const bindings_t * getCaptureCardBindArray(unsigned proto)
Returns bindings for Myth::CaptureCard.
Definition: mythdto.cpp:72
const bindings_t * getVideoSourceBindArray(unsigned proto)
Returns bindings for Myth::VideoSource.
Definition: mythdto.cpp:79
This is the main namespace that encloses all public classes.
Definition: mythcontrol.h:29
const bindings_t * getVersionBindArray(unsigned ranking)
Returns bindings for Myth::Version.
Definition: mythdto.cpp:30
SettingPtr GetSetting(const std::string &key, const std::string &hostname)
GET Myth/GetSetting.
Definition: mythwsapi.h:73
const bindings_t * getRecordScheduleBindArray(unsigned proto)
Returns bindings for Myth::RecordSchedule.
Definition: mythdto.cpp:86