MythTV  master
httpstatus.cpp
Go to the documentation of this file.
1 // Program Name: httpstatus.cpp
3 //
4 // Purpose - Html & XML status HttpServerExtension
5 //
6 // Created By : David Blain Created On : Oct. 24, 2005
7 // Modified By : Modified On:
8 //
10 
11 // POSIX headers
12 #include <unistd.h>
13 
14 // ANSI C headers
15 #include <cmath>
16 #include <cstdio>
17 #include <cstdlib>
18 
19 // Qt headers
20 #include <QTextStream>
21 #include <QRegExp>
22 
23 // MythTV headers
24 #include "httpstatus.h"
25 
26 #include "mythcorecontext.h"
27 #include "mythversion.h"
28 #include "mythdbcon.h"
29 #include "compat.h"
30 #include "mythconfig.h"
31 #include "autoexpire.h"
32 #include "tv.h"
33 #include "encoderlink.h"
34 #include "scheduler.h"
35 #include "mainserver.h"
36 #include "cardutil.h"
37 #include "mythsystemlegacy.h"
38 #include "exitcodes.h"
39 #include "jobqueue.h"
40 #include "upnp.h"
41 #include "mythdate.h"
42 #include "tv_rec.h"
43 
45 //
47 
48 HttpStatus::HttpStatus( QMap<int, EncoderLink *> *tvList, Scheduler *sched,
49  AutoExpire *expirer, bool bIsMaster )
50  : HttpServerExtension( "HttpStatus" , QString())
51 {
53  m_pSched = sched;
55  m_bIsMaster = bIsMaster;
56 
57  m_nPreRollSeconds = gCoreContext->GetNumSetting("RecordPreRoll", 0);
58 
59  m_pMainServer = nullptr;
60 }
61 
63 //
65 
67 {
68  if (sURI == "Status" ) return( HSM_GetStatusHTML );
69  if (sURI == "GetStatusHTML" ) return( HSM_GetStatusHTML );
70  if (sURI == "GetStatus" ) return( HSM_GetStatusXML );
71  if (sURI == "xml" ) return( HSM_GetStatusXML );
72 
73  return( HSM_Unknown );
74 }
75 
77 //
79 
81 {
82  return QStringList( "/Status" );
83 }
84 
86 //
88 
90 {
91  try
92  {
93  if (pRequest)
94  {
95  if ((pRequest->m_sBaseUrl != "/Status" ) &&
96  (pRequest->m_sResourceUrl != "/Status" ))
97  {
98  return( false );
99  }
100 
101  switch( GetMethod( pRequest->m_sMethod ))
102  {
103  case HSM_GetStatusXML : GetStatusXML ( pRequest ); return true;
104  case HSM_GetStatusHTML : GetStatusHTML ( pRequest ); return true;
105 
106  default:
107  {
108  pRequest->m_eResponseType = ResponseTypeHTML;
109  pRequest->m_nResponseStatus = 200;
110 
111  break;
112  }
113  }
114  }
115  }
116  catch( ... )
117  {
118  LOG(VB_GENERAL, LOG_ERR,
119  "HttpStatus::ProcessRequest() - Unexpected Exception");
120  }
121 
122  return( false );
123 }
124 
126 //
128 
130 {
131  QDomDocument doc( "Status" );
132 
133  // UTF-8 is the default, but good practice to specify it anyway
134  QDomProcessingInstruction encoding =
135  doc.createProcessingInstruction("xml",
136  "version=\"1.0\" encoding=\"UTF-8\"");
137  doc.appendChild(encoding);
138 
139  FillStatusXML( &doc );
140 
141  pRequest->m_eResponseType = ResponseTypeXML;
142  pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000";
143 
144  QTextStream stream( &pRequest->m_response );
145  stream.setCodec("UTF-8"); // Otherwise locale default is used.
146  stream << doc.toString();
147 }
148 
150 //
152 
154 {
155  pRequest->m_eResponseType = ResponseTypeHTML;
156  pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000";
157 
158  QDomDocument doc( "Status" );
159 
160  FillStatusXML( &doc );
161 
162  QTextStream stream( &pRequest->m_response );
163  PrintStatus( stream, &doc );
164 }
165 
166 static QString setting_to_localtime(const char *setting)
167 {
168  QString origDateString = gCoreContext->GetSetting(setting);
169  QDateTime origDate = MythDate::fromString(origDateString);
171 }
172 
173 void HttpStatus::FillStatusXML( QDomDocument *pDoc )
174 {
175  QDateTime qdtNow = MythDate::current();
176 
177  // Add Root Node.
178 
179  QDomElement root = pDoc->createElement("Status");
180  pDoc->appendChild(root);
181 
182  root.setAttribute("date" , MythDate::toString(
184  root.setAttribute("time" ,
186  root.setAttribute("ISODate" , qdtNow.toString(Qt::ISODate) );
187  root.setAttribute("version" , MYTH_BINARY_VERSION );
188  root.setAttribute("protoVer", MYTH_PROTO_VERSION );
189 
190  // Add all encoders, if any
191 
192  QDomElement encoders = pDoc->createElement("Encoders");
193  root.appendChild(encoders);
194 
195  int numencoders = 0;
196  bool isLocal = true;
197 
198  TVRec::s_inputsLock.lockForRead();
199 
200  foreach (auto elink, *m_pEncoders)
201  {
202  if (elink != nullptr)
203  {
204  TVState state = elink->GetState();
205  isLocal = elink->IsLocal();
206 
207  QDomElement encoder = pDoc->createElement("Encoder");
208  encoders.appendChild(encoder);
209 
210  encoder.setAttribute("id" , elink->GetInputID() );
211  encoder.setAttribute("local" , static_cast<int>(isLocal));
212  encoder.setAttribute("connected" , static_cast<int>(elink->IsConnected()));
213  encoder.setAttribute("state" , state );
214  encoder.setAttribute("sleepstatus" , elink->GetSleepStatus() );
215  //encoder.setAttribute("lowOnFreeSpace", elink->isLowOnFreeSpace());
216 
217  if (isLocal)
218  encoder.setAttribute("hostname", gCoreContext->GetHostName());
219  else
220  encoder.setAttribute("hostname", elink->GetHostName());
221 
222  encoder.setAttribute("devlabel",
223  CardUtil::GetDeviceLabel(elink->GetInputID()) );
224 
225  if (elink->IsConnected())
226  numencoders++;
227 
228  switch (state)
229  {
233  {
234  ProgramInfo *pInfo = elink->GetRecording();
235 
236  if (pInfo)
237  {
238  FillProgramInfo(pDoc, encoder, pInfo);
239  delete pInfo;
240  }
241 
242  break;
243  }
244 
245  default:
246  break;
247  }
248  }
249  }
250 
251  TVRec::s_inputsLock.unlock();
252 
253  encoders.setAttribute("count", numencoders);
254 
255  // Add upcoming shows
256 
257  QDomElement scheduled = pDoc->createElement("Scheduled");
258  root.appendChild(scheduled);
259 
260  RecList recordingList;
261 
262  if (m_pSched)
263  m_pSched->GetAllPending(recordingList);
264 
265  unsigned int iNum = 10;
266  unsigned int iNumRecordings = 0;
267 
268  RecConstIter itProg = recordingList.begin();
269  for (; (itProg != recordingList.end()) && iNumRecordings < iNum; ++itProg)
270  {
271  if (((*itProg)->GetRecordingStatus() <= RecStatus::WillRecord) &&
272  ((*itProg)->GetRecordingStartTime() >=
274  {
275  iNumRecordings++;
276  FillProgramInfo(pDoc, scheduled, *itProg);
277  }
278  }
279 
280  while (!recordingList.empty())
281  {
282  ProgramInfo *pginfo = recordingList.back();
283  delete pginfo;
284  recordingList.pop_back();
285  }
286 
287  scheduled.setAttribute("count", iNumRecordings);
288 
289  // Add known frontends
290 
291  QDomElement frontends = pDoc->createElement("Frontends");
292  root.appendChild(frontends);
293 
295  "urn:schemas-mythtv-org:service:MythFrontend:1");
296  if (fes)
297  {
298  EntryMap map;
299  fes->GetEntryMap(map);
300  fes->DecrRef();
301  fes = nullptr;
302 
303  frontends.setAttribute( "count", map.size() );
304  foreach (auto & entry, map)
305  {
306  QDomElement fe = pDoc->createElement("Frontend");
307  frontends.appendChild(fe);
308  QUrl url(entry->m_sLocation);
309  fe.setAttribute("name", url.host());
310  fe.setAttribute("url", url.toString(QUrl::RemovePath));
311  entry->DecrRef();
312  }
313  }
314 
315  // Other backends
316 
317  QDomElement backends = pDoc->createElement("Backends");
318  root.appendChild(backends);
319 
320  int numbes = 0;
322  {
323  numbes++;
324  QString masterhost = gCoreContext->GetMasterHostName();
325  QString masterip = gCoreContext->GetMasterServerIP();
326  int masterport = gCoreContext->GetMasterServerStatusPort();
327 
328  QDomElement mbe = pDoc->createElement("Backend");
329  backends.appendChild(mbe);
330  mbe.setAttribute("type", "Master");
331  mbe.setAttribute("name", masterhost);
332  mbe.setAttribute("url" , masterip + ":" + masterport);
333  }
334 
336  "urn:schemas-mythtv-org:device:SlaveMediaServer:1");
337  if (sbes)
338  {
339 
340  QString ipaddress = QString();
341  if (!UPnp::g_IPAddrList.isEmpty())
342  ipaddress = UPnp::g_IPAddrList.at(0).toString();
343 
344  EntryMap map;
345  sbes->GetEntryMap(map);
346  sbes->DecrRef();
347  sbes = nullptr;
348 
349  foreach (auto & entry, map)
350  {
351  QUrl url(entry->m_sLocation);
352  if (url.host() != ipaddress)
353  {
354  numbes++;
355  QDomElement mbe = pDoc->createElement("Backend");
356  backends.appendChild(mbe);
357  mbe.setAttribute("type", "Slave");
358  mbe.setAttribute("name", url.host());
359  mbe.setAttribute("url" , url.toString(QUrl::RemovePath));
360  }
361  entry->DecrRef();
362  }
363  }
364 
365  backends.setAttribute("count", numbes);
366 
367  // Add Job Queue Entries
368 
369  QDomElement jobqueue = pDoc->createElement("JobQueue");
370  root.appendChild(jobqueue);
371 
372  QMap<int, JobQueueEntry> jobs;
373  QMap<int, JobQueueEntry>::Iterator it;
374 
378 
379  for (it = jobs.begin(); it != jobs.end(); ++it)
380  {
381  ProgramInfo pginfo((*it).chanid, (*it).recstartts);
382  if (!pginfo.GetChanID())
383  continue;
384 
385  QDomElement job = pDoc->createElement("Job");
386  jobqueue.appendChild(job);
387 
388  job.setAttribute("id" , (*it).id );
389  job.setAttribute("chanId" , (*it).chanid );
390  job.setAttribute("startTime" ,
391  (*it).recstartts.toString(Qt::ISODate));
392  job.setAttribute("startTs" , (*it).startts );
393  job.setAttribute("insertTime",
394  (*it).inserttime.toString(Qt::ISODate));
395  job.setAttribute("type" , (*it).type );
396  job.setAttribute("cmds" , (*it).cmds );
397  job.setAttribute("flags" , (*it).flags );
398  job.setAttribute("status" , (*it).status );
399  job.setAttribute("statusTime",
400  (*it).statustime.toString(Qt::ISODate));
401  job.setAttribute("schedTime" ,
402  (*it).schedruntime.toString(Qt::ISODate));
403  job.setAttribute("args" , (*it).args );
404 
405  if ((*it).hostname.isEmpty())
406  job.setAttribute("hostname", QObject::tr("master"));
407  else
408  job.setAttribute("hostname",(*it).hostname);
409 
410  QDomText textNode = pDoc->createTextNode((*it).comment);
411  job.appendChild(textNode);
412 
413  FillProgramInfo(pDoc, job, &pginfo);
414  }
415 
416  jobqueue.setAttribute( "count", jobs.size() );
417 
418  // Add Machine information
419 
420  QDomElement mInfo = pDoc->createElement("MachineInfo");
421  QDomElement storage = pDoc->createElement("Storage" );
422  QDomElement load = pDoc->createElement("Load" );
423  QDomElement guide = pDoc->createElement("Guide" );
424 
425  root.appendChild (mInfo );
426  mInfo.appendChild(storage);
427  mInfo.appendChild(load );
428  mInfo.appendChild(guide );
429 
430  // drive space ---------------------
431 
432  QStringList strlist;
433  QString dirs;
434  QString hostname;
435  QString directory;
436  QString isLocalstr;
437  QString fsID;
438  QString ids;
439 
440  if (m_pMainServer)
442 
443  QDomElement total;
444 
445  // Make a temporary list to hold the per-filesystem elements so that the
446  // total is always the first element.
447  QList<QDomElement> fsXML;
448  QStringList::const_iterator sit = strlist.begin();
449  while (sit != strlist.end())
450  {
451  // cppcheck-suppress unreadVariable
452  hostname = *(sit++);
453  directory = *(sit++);
454  // cppcheck-suppress unreadVariable
455  isLocalstr = *(sit++);
456  fsID = *(sit++);
457  ++sit; // ignore dirID
458  ++sit; // ignore blocksize
459  long long iTotal = (*(sit++)).toLongLong();
460  long long iUsed = (*(sit++)).toLongLong();;
461  long long iAvail = iTotal - iUsed;
462 
463  if (fsID == "-2")
464  fsID = "total";
465 
466  QDomElement group = pDoc->createElement("Group");
467 
468  group.setAttribute("id" , fsID );
469  group.setAttribute("total", (int)(iTotal>>10) );
470  group.setAttribute("used" , (int)(iUsed>>10) );
471  group.setAttribute("free" , (int)(iAvail>>10) );
472  group.setAttribute("dir" , directory );
473 
474  if (fsID == "total")
475  {
476  long long iLiveTV = -1;
477  long long iDeleted = -1;
478  long long iExpirable = -1;
479  MSqlQuery query(MSqlQuery::InitCon());
480  query.prepare("SELECT SUM(filesize) FROM recorded "
481  " WHERE recgroup = :RECGROUP;");
482 
483  query.bindValue(":RECGROUP", "LiveTV");
484  if (query.exec() && query.next())
485  {
486  iLiveTV = query.value(0).toLongLong();
487  }
488  query.bindValue(":RECGROUP", "Deleted");
489  if (query.exec() && query.next())
490  {
491  iDeleted = query.value(0).toLongLong();
492  }
493  query.prepare("SELECT SUM(filesize) FROM recorded "
494  " WHERE autoexpire = 1 "
495  " AND recgroup NOT IN ('LiveTV', 'Deleted');");
496  if (query.exec() && query.next())
497  {
498  iExpirable = query.value(0).toLongLong();
499  }
500  group.setAttribute("livetv", (int)(iLiveTV>>20) );
501  group.setAttribute("deleted", (int)(iDeleted>>20) );
502  group.setAttribute("expirable", (int)(iExpirable>>20) );
503  total = group;
504  }
505  else
506  fsXML << group;
507  }
508 
509  storage.appendChild(total);
510  int num_elements = fsXML.size();
511  for (int fs_index = 0; fs_index < num_elements; fs_index++)
512  {
513  storage.appendChild(fsXML[fs_index]);
514  }
515 
516  // load average ---------------------
517 
518 #ifdef Q_OS_ANDROID
519  load.setAttribute("avg1", 0);
520  load.setAttribute("avg2", 1);
521  load.setAttribute("avg3", 2);
522 #else
523  double rgdAverages[3];
524 
525  if (getloadavg(rgdAverages, 3) != -1)
526  {
527  load.setAttribute("avg1", rgdAverages[0]);
528  load.setAttribute("avg2", rgdAverages[1]);
529  load.setAttribute("avg3", rgdAverages[2]);
530  }
531 #endif
532 
533  // Guide Data ---------------------
534 
535  QDateTime GuideDataThrough;
536 
537  MSqlQuery query(MSqlQuery::InitCon());
538  query.prepare("SELECT MAX(endtime) FROM program WHERE manualid = 0;");
539 
540  if (query.exec() && query.next())
541  {
542  GuideDataThrough = MythDate::fromString(query.value(0).toString());
543  }
544 
545  guide.setAttribute("start",
546  setting_to_localtime("mythfilldatabaseLastRunStart"));
547  guide.setAttribute("end",
548  setting_to_localtime("mythfilldatabaseLastRunEnd"));
549  guide.setAttribute("status",
550  gCoreContext->GetSetting("mythfilldatabaseLastRunStatus"));
551  if (gCoreContext->GetBoolSetting("MythFillGrabberSuggestsTime", false))
552  {
553  guide.setAttribute("next",
554  gCoreContext->GetSetting("MythFillSuggestedRunTime"));
555  }
556 
557  if (!GuideDataThrough.isNull())
558  {
559  guide.setAttribute("guideThru",
560  GuideDataThrough.toString(Qt::ISODate));
561  guide.setAttribute("guideDays", qdtNow.daysTo(GuideDataThrough));
562  }
563 
564  // Add Miscellaneous information
565 
566  QString info_script = gCoreContext->GetSetting("MiscStatusScript");
567  if ((!info_script.isEmpty()) && (info_script != "none"))
568  {
569  QDomElement misc = pDoc->createElement("Miscellaneous");
570  root.appendChild(misc);
571 
572  uint flags = kMSRunShell | kMSStdOut;
573  MythSystemLegacy ms(info_script, flags);
574  ms.Run(10);
575  if (ms.Wait() != GENERIC_EXIT_OK)
576  {
577  LOG(VB_GENERAL, LOG_ERR,
578  QString("Error running miscellaneous "
579  "status information script: %1").arg(info_script));
580  return;
581  }
582 
583  QByteArray input = ms.ReadAll();
584 
585  QStringList output = QString(input).split('\n',
586  QString::SkipEmptyParts);
587 
588  foreach (auto & line, output)
589  {
590  QDomElement info = pDoc->createElement("Information");
591 
592  QStringList list = line.split("[]:[]");
593  unsigned int size = list.size();
594  unsigned int hasAttributes = 0;
595 
596  if ((size > 0) && (!list[0].isEmpty()))
597  {
598  info.setAttribute("display", list[0]);
599  hasAttributes++;
600  }
601  if ((size > 1) && (!list[1].isEmpty()))
602  {
603  info.setAttribute("name", list[1]);
604  hasAttributes++;
605  }
606  if ((size > 2) && (!list[2].isEmpty()))
607  {
608  info.setAttribute("value", list[2]);
609  hasAttributes++;
610  }
611 
612  if (hasAttributes > 0)
613  misc.appendChild(info);
614  }
615  }
616 }
617 
619 //
621 
622 void HttpStatus::PrintStatus( QTextStream &os, QDomDocument *pDoc )
623 {
624  os.setCodec("UTF-8");
625 
626  QDateTime qdtNow = MythDate::current();
627 
628  QDomElement docElem = pDoc->documentElement();
629 
630  os << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
631  << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n"
632  << "<html xmlns=\"http://www.w3.org/1999/xhtml\""
633  << " xml:lang=\"en\" lang=\"en\">\r\n"
634  << "<head>\r\n"
635  << " <meta http-equiv=\"Content-Type\""
636  << "content=\"text/html; charset=UTF-8\" />\r\n"
637  << " <link rel=\"stylesheet\" href=\"/css/Status.css\" type=\"text/css\">\r\n"
638  << " <title>MythTV Status - "
639  << docElem.attribute( "date", MythDate::toString(qdtNow, MythDate::kDateShort) )
640  << " "
641  << docElem.attribute( "time", MythDate::toString(qdtNow, MythDate::kTime) ) << " - "
642  << docElem.attribute( "version", MYTH_BINARY_VERSION ) << "</title>\r\n"
643  << "</head>\r\n"
644  << "<body bgcolor=\"#fff\">\r\n"
645  << "<div class=\"status\">\r\n"
646  << " <h1 class=\"status\">MythTV Status</h1>\r\n";
647 
648  // encoder information ---------------------
649 
650  QDomNode node = docElem.namedItem( "Encoders" );
651 
652  if (!node.isNull())
653  PrintEncoderStatus( os, node.toElement() );
654 
655  // upcoming shows --------------------------
656 
657  node = docElem.namedItem( "Scheduled" );
658 
659  if (!node.isNull())
660  PrintScheduled( os, node.toElement());
661 
662  // Frontends
663 
664  node = docElem.namedItem( "Frontends" );
665 
666  if (!node.isNull())
667  PrintFrontends (os, node.toElement());
668 
669  // Backends
670 
671  node = docElem.namedItem( "Backends" );
672 
673  if (!node.isNull())
674  PrintBackends (os, node.toElement());
675 
676  // Job Queue Entries -----------------------
677 
678  node = docElem.namedItem( "JobQueue" );
679 
680  if (!node.isNull())
681  PrintJobQueue( os, node.toElement());
682 
683  // Machine information ---------------------
684 
685  node = docElem.namedItem( "MachineInfo" );
686 
687  if (!node.isNull())
688  PrintMachineInfo( os, node.toElement());
689 
690  // Miscellaneous information ---------------
691 
692  node = docElem.namedItem( "Miscellaneous" );
693 
694  if (!node.isNull())
695  PrintMiscellaneousInfo( os, node.toElement());
696 
697  os << "\r\n</div>\r\n</body>\r\n</html>\r\n";
698 
699 }
700 
702 //
704 
705 int HttpStatus::PrintEncoderStatus( QTextStream &os, const QDomElement& encoders )
706 {
707  int nNumEncoders = 0;
708 
709  if (encoders.isNull())
710  return 0;
711 
712  os << " <div class=\"content\">\r\n"
713  << " <h2 class=\"status\">Encoder Status</h2>\r\n";
714 
715  QDomNode node = encoders.firstChild();
716 
717  while (!node.isNull())
718  {
719  QDomElement e = node.toElement();
720 
721  if (!e.isNull())
722  {
723  if (e.tagName() == "Encoder")
724  {
725  QString sIsLocal = (e.attribute( "local" , "remote" )== "1")
726  ? "local" : "remote";
727  QString sCardId = e.attribute( "id" , "0" );
728  QString sHostName = e.attribute( "hostname" , "Unknown");
729  bool bConnected= static_cast<bool>(e.attribute( "connected", "0" ).toInt());
730 
731  bool bIsLowOnFreeSpace=static_cast<bool>(e.attribute( "lowOnFreeSpace", "0").toInt());
732 
733  QString sDevlabel = e.attribute( "devlabel", "[ UNKNOWN ]");
734 
735  os << " Encoder " << sCardId << " " << sDevlabel
736  << " is " << sIsLocal << " on " << sHostName;
737 
738  if ((sIsLocal == "remote") && !bConnected)
739  {
740  SleepStatus sleepStatus =
741  (SleepStatus) e.attribute("sleepstatus",
742  QString((int)sStatus_Undefined)).toInt();
743 
744  if (sleepStatus == sStatus_Asleep)
745  os << " (currently asleep).<br />";
746  else
747  os << " (currently not connected).<br />";
748 
749  node = node.nextSibling();
750  continue;
751  }
752 
753  nNumEncoders++;
754 
755  TVState encState = (TVState) e.attribute( "state", "0").toInt();
756 
757  switch( encState )
758  {
760  os << " and is watching Live TV";
761  break;
762 
765  os << " and is recording";
766  break;
767 
768  default:
769  os << " and is not recording.";
770  break;
771  }
772 
773  // Display first Program Element listed under the encoder
774 
775  QDomNode tmpNode = e.namedItem( "Program" );
776 
777  if (!tmpNode.isNull())
778  {
779  QDomElement program = tmpNode.toElement();
780 
781  if (!program.isNull())
782  {
783  os << " '" << program.attribute( "title", "Unknown" ) << "'";
784 
785  // Get Channel information
786 
787  tmpNode = program.namedItem( "Channel" );
788 
789  if (!tmpNode.isNull())
790  {
791  QDomElement channel = tmpNode.toElement();
792 
793  if (!channel.isNull())
794  os << " on "
795  << channel.attribute( "callSign", "unknown" );
796  }
797 
798  // Get Recording Information (if any)
799 
800  tmpNode = program.namedItem( "Recording" );
801 
802  if (!tmpNode.isNull())
803  {
804  QDomElement recording = tmpNode.toElement();
805 
806  if (!recording.isNull())
807  {
808  QDateTime endTs = MythDate::fromString(
809  recording.attribute( "recEndTs", "" ));
810 
811  os << ". This recording ";
812  if (endTs < MythDate::current())
813  os << "was ";
814  else
815  os << "is ";
816 
817  os << "scheduled to end at "
818  << MythDate::toString(endTs,
820  }
821  }
822  }
823 
824  os << ".";
825  }
826 
827  if (bIsLowOnFreeSpace)
828  {
829  os << " <strong>WARNING</strong>:"
830  << " This backend is low on free disk space!";
831  }
832 
833  os << "<br />\r\n";
834  }
835  }
836 
837  node = node.nextSibling();
838  }
839 
840  os << " </div>\r\n\r\n";
841 
842  return( nNumEncoders );
843 }
844 
846 //
848 
849 int HttpStatus::PrintScheduled( QTextStream &os, const QDomElement& scheduled )
850 {
851  QDateTime qdtNow = MythDate::current();
852 
853  if (scheduled.isNull())
854  return( 0 );
855 
856  int nNumRecordings= scheduled.attribute( "count", "0" ).toInt();
857 
858  os << " <div class=\"content\">\r\n"
859  << " <h2 class=\"status\">Schedule</h2>\r\n";
860 
861  if (nNumRecordings == 0)
862  {
863  os << " There are no shows scheduled for recording.\r\n"
864  << " </div>\r\n";
865  return( 0 );
866  }
867 
868  os << " The next " << nNumRecordings << " show" << (nNumRecordings == 1 ? "" : "s" )
869  << " that " << (nNumRecordings == 1 ? "is" : "are")
870  << " scheduled for recording:\r\n";
871 
872  os << " <div class=\"schedule\">\r\n";
873 
874  // Iterate through all scheduled programs
875 
876  QDomNode node = scheduled.firstChild();
877 
878  while (!node.isNull())
879  {
880  QDomElement e = node.toElement();
881 
882  if (!e.isNull())
883  {
884  QDomNode recNode = e.namedItem( "Recording" );
885  QDomNode chanNode = e.namedItem( "Channel" );
886 
887  if ((e.tagName() == "Program") && !recNode.isNull() &&
888  !chanNode.isNull())
889  {
890  QDomElement r = recNode.toElement();
891  QDomElement c = chanNode.toElement();
892 
893  QString sTitle = e.attribute( "title" , "" );
894  QString sSubTitle = e.attribute( "subTitle", "" );
895  QDateTime airDate = MythDate::fromString( e.attribute( "airdate" ,"" ));
896  QDateTime startTs = MythDate::fromString( e.attribute( "startTime" ,"" ));
897  QDateTime endTs = MythDate::fromString( e.attribute( "endTime" ,"" ));
898  QDateTime recStartTs = MythDate::fromString( r.attribute( "recStartTs","" ));
899 // QDateTime recEndTs = MythDate::fromString( r.attribute( "recEndTs" ,"" ));
900  int nPreRollSecs = r.attribute( "preRollSeconds", "0" ).toInt();
901  int nEncoderId = r.attribute( "encoderId" , "0" ).toInt();
902  QString sProfile = r.attribute( "recProfile" , "" );
903  QString sChanName = c.attribute( "channelName" , "" );
904  QString sDesc = "";
905 
906  QDomText text = e.firstChild().toText();
907  if (!text.isNull())
908  sDesc = text.nodeValue();
909 
910  // Build Time to recording start.
911 
912  int nTotalSecs = qdtNow.secsTo( recStartTs ) - nPreRollSecs;
913 
914  //since we're not displaying seconds
915 
916  nTotalSecs -= 60;
917 
918  int nTotalDays = nTotalSecs / 86400;
919  int nTotalHours = (nTotalSecs / 3600)
920  - (nTotalDays * 24);
921  int nTotalMins = (nTotalSecs / 60) % 60;
922 
923  QString sTimeToStart = "in";
924 
925  sTimeToStart += QObject::tr(" %n day(s),", "", nTotalDays );
926  sTimeToStart += QObject::tr(" %n hour(s) and", "", nTotalHours);
927  sTimeToStart += QObject::tr(" %n minute(s)", "", nTotalMins);
928 
929  if ( nTotalHours == 0 && nTotalMins == 0)
930  sTimeToStart = QObject::tr("within one minute", "Recording starting");
931 
932  if ( nTotalSecs < 0)
933  sTimeToStart = QObject::tr("soon", "Recording starting");
934 
935  // Output HTML
936 
937  os << " <a href=\"#\">";
938  os << MythDate::toString(recStartTs.addSecs(-nPreRollSecs),
940  MythDate::kSimplify) << " "
941  << MythDate::toString(recStartTs.addSecs(-nPreRollSecs),
942  MythDate::kTime) << " - ";
943 
944  if (nEncoderId > 0)
945  os << "Encoder " << nEncoderId << " - ";
946 
947  os << sChanName << " - " << sTitle << "<br />"
948  << "<span><strong>" << sTitle << "</strong> ("
949  << MythDate::toString(startTs, MythDate::kTime) << "-"
950  << MythDate::toString(endTs, MythDate::kTime) << ")<br />";
951 
952  if ( !sSubTitle.isEmpty())
953  os << "<em>" << sSubTitle << "</em><br /><br />";
954 
955  if ( airDate.isValid())
956  {
957  os << "Orig. Airdate: "
960  << "<br /><br />";
961  }
962 
963  os << sDesc << "<br /><br />"
964  << "This recording will start " << sTimeToStart
965  << " using encoder " << nEncoderId << " with the '"
966  << sProfile << "' profile.</span></a><hr />\r\n";
967  }
968  }
969 
970  node = node.nextSibling();
971  }
972  os << " </div>\r\n";
973  os << " </div>\r\n\r\n";
974 
975  return( nNumRecordings );
976 }
977 
979 //
981 
982 int HttpStatus::PrintFrontends( QTextStream &os, const QDomElement& frontends )
983 {
984  if (frontends.isNull())
985  return( 0 );
986 
987  int nNumFES= frontends.attribute( "count", "0" ).toInt();
988 
989  if (nNumFES < 1)
990  return( 0 );
991 
992 
993  os << " <div class=\"content\">\r\n"
994  << " <h2 class=\"status\">Frontends</h2>\r\n";
995 
996  QDomNode node = frontends.firstChild();
997  while (!node.isNull())
998  {
999  QDomElement e = node.toElement();
1000 
1001  if (!e.isNull())
1002  {
1003  QString name = e.attribute( "name" , "" );
1004  QString url = e.attribute( "url" , "" );
1005  os << name << "&nbsp(<a href=\"" << url << "\">Status page</a>)<br />";
1006  }
1007 
1008  node = node.nextSibling();
1009  }
1010 
1011  os << " </div>\r\n\r\n";
1012 
1013  return nNumFES;
1014 }
1015 
1017 //
1019 
1020 int HttpStatus::PrintBackends( QTextStream &os, const QDomElement& backends )
1021 {
1022  if (backends.isNull())
1023  return( 0 );
1024 
1025  int nNumBES= backends.attribute( "count", "0" ).toInt();
1026 
1027  if (nNumBES < 1)
1028  return( 0 );
1029 
1030 
1031  os << " <div class=\"content\">\r\n"
1032  << " <h2 class=\"status\">Other Backends</h2>\r\n";
1033 
1034  QDomNode node = backends.firstChild();
1035  while (!node.isNull())
1036  {
1037  QDomElement e = node.toElement();
1038 
1039  if (!e.isNull())
1040  {
1041  QString type = e.attribute( "type", "" );
1042  QString name = e.attribute( "name" , "" );
1043  QString url = e.attribute( "url" , "" );
1044  os << type << ": " << name << "&nbsp(<a href=\"" << url << "\">Status page</a>)<br />";
1045  }
1046 
1047  node = node.nextSibling();
1048  }
1049 
1050  os << " </div>\r\n\r\n";
1051 
1052  return nNumBES;
1053 }
1054 
1056 //
1058 
1059 int HttpStatus::PrintJobQueue( QTextStream &os, const QDomElement& jobs )
1060 {
1061  if (jobs.isNull())
1062  return( 0 );
1063 
1064  int nNumJobs= jobs.attribute( "count", "0" ).toInt();
1065 
1066  os << " <div class=\"content\">\r\n"
1067  << " <h2 class=\"status\">Job Queue</h2>\r\n";
1068 
1069  if (nNumJobs != 0)
1070  {
1071  QString statusColor;
1072  QString jobColor;
1073 
1074  os << " Jobs currently in Queue or recently ended:\r\n<br />"
1075  << " <div class=\"schedule\">\r\n";
1076 
1077 
1078  QDomNode node = jobs.firstChild();
1079 
1080  while (!node.isNull())
1081  {
1082  QDomElement e = node.toElement();
1083 
1084  if (!e.isNull())
1085  {
1086  QDomNode progNode = e.namedItem( "Program" );
1087 
1088  if ((e.tagName() == "Job") && !progNode.isNull() )
1089  {
1090  QDomElement p = progNode.toElement();
1091 
1092  QDomNode recNode = p.namedItem( "Recording" );
1093  QDomNode chanNode = p.namedItem( "Channel" );
1094 
1095  QDomElement r = recNode.toElement();
1096  QDomElement c = chanNode.toElement();
1097 
1098  int nType = e.attribute( "type" , "0" ).toInt();
1099  int nStatus = e.attribute( "status", "0" ).toInt();
1100 
1101  switch( nStatus )
1102  {
1103  case JOB_ABORTED:
1104  statusColor = " class=\"jobaborted\"";
1105  jobColor = "";
1106  break;
1107 
1108  case JOB_ERRORED:
1109  statusColor = " class=\"joberrored\"";
1110  jobColor = " class=\"joberrored\"";
1111  break;
1112 
1113  case JOB_FINISHED:
1114  statusColor = " class=\"jobfinished\"";
1115  jobColor = " class=\"jobfinished\"";
1116  break;
1117 
1118  case JOB_RUNNING:
1119  statusColor = " class=\"jobrunning\"";
1120  jobColor = " class=\"jobrunning\"";
1121  break;
1122 
1123  default:
1124  statusColor = " class=\"jobqueued\"";
1125  jobColor = " class=\"jobqueued\"";
1126  break;
1127  }
1128 
1129  QString sTitle = p.attribute( "title" , "" ); //.replace(QRegExp("\""), "&quot;");
1130  QString sSubTitle = p.attribute( "subTitle", "" );
1131  QDateTime startTs = MythDate::fromString( p.attribute( "startTime" ,"" ));
1132  QDateTime endTs = MythDate::fromString( p.attribute( "endTime" ,"" ));
1133  QDateTime recStartTs = MythDate::fromString( r.attribute( "recStartTs","" ));
1134  QDateTime statusTime = MythDate::fromString( e.attribute( "statusTime","" ));
1135  QDateTime schedRunTime = MythDate::fromString( e.attribute( "schedTime","" ));
1136  QString sHostname = e.attribute( "hostname", "master" );
1137  QString sComment = "";
1138 
1139  QDomText text = e.firstChild().toText();
1140  if (!text.isNull())
1141  sComment = text.nodeValue();
1142 
1143  os << "<a href=\"javascript:void(0)\">"
1144  << MythDate::toString(recStartTs, MythDate::kDateFull |
1146  << " - "
1147  << sTitle << " - <font" << jobColor << ">"
1148  << JobQueue::JobText( nType ) << "</font><br />"
1149  << "<span><strong>" << sTitle << "</strong> ("
1150  << MythDate::toString(startTs, MythDate::kTime) << "-"
1151  << MythDate::toString(endTs, MythDate::kTime) << ")<br />";
1152 
1153  if (!sSubTitle.isEmpty())
1154  os << "<em>" << sSubTitle << "</em><br /><br />";
1155 
1156  os << "Job: " << JobQueue::JobText( nType ) << "<br />";
1157 
1158  if (schedRunTime > MythDate::current())
1159  {
1160  os << "Scheduled Run Time: "
1161  << MythDate::toString(schedRunTime,
1164  << "<br />";
1165  }
1166 
1167  os << "Status: <font" << statusColor << ">"
1168  << JobQueue::StatusText( nStatus )
1169  << "</font><br />"
1170  << "Status Time: "
1171  << MythDate::toString(statusTime, MythDate::kDateFull |
1173  << "<br />";
1174 
1175  if ( nStatus != JOB_QUEUED)
1176  os << "Host: " << sHostname << "<br />";
1177 
1178  if (!sComment.isEmpty())
1179  os << "<br />Comments:<br />" << sComment << "<br />";
1180 
1181  os << "</span></a><hr />\r\n";
1182  }
1183  }
1184 
1185  node = node.nextSibling();
1186  }
1187  os << " </div>\r\n";
1188  }
1189  else
1190  os << " Job Queue is currently empty.\r\n\r\n";
1191 
1192  os << " </div>\r\n\r\n ";
1193 
1194  return( nNumJobs );
1195 
1196 }
1197 
1199 //
1201 
1202 int HttpStatus::PrintMachineInfo( QTextStream &os, const QDomElement& info )
1203 {
1204  QString sRep;
1205 
1206  if (info.isNull())
1207  return( 0 );
1208 
1209  os << "<div class=\"content\">\r\n"
1210  << " <h2 class=\"status\">Machine Information</h2>\r\n";
1211 
1212  // load average ---------------------
1213 
1214  QDomNode node = info.namedItem( "Load" );
1215 
1216  if (!node.isNull())
1217  {
1218  QDomElement e = node.toElement();
1219 
1220  if (!e.isNull())
1221  {
1222  double dAvg1 = e.attribute( "avg1" , "0" ).toDouble();
1223  double dAvg2 = e.attribute( "avg2" , "0" ).toDouble();
1224  double dAvg3 = e.attribute( "avg3" , "0" ).toDouble();
1225 
1226  os << " <div class=\"loadstatus\">\r\n"
1227  << " This machine's load average:"
1228  << "\r\n <ul>\r\n <li>"
1229  << "1 Minute: " << dAvg1 << "</li>\r\n"
1230  << " <li>5 Minutes: " << dAvg2 << "</li>\r\n"
1231  << " <li>15 Minutes: " << dAvg3
1232  << "</li>\r\n </ul>\r\n"
1233  << " </div>\r\n";
1234  }
1235  }
1236 
1237  // local drive space ---------------------
1238  node = info.namedItem( "Storage" );
1239  QDomElement storage = node.toElement();
1240  node = storage.firstChild();
1241 
1242  // Loop once until we find id == "total". This should be first, but a loop
1243  // separate from the per-filesystem details loop ensures total is first,
1244  // regardless.
1245  while (!node.isNull())
1246  {
1247  QDomElement g = node.toElement();
1248 
1249  if (!g.isNull() && g.tagName() == "Group")
1250  {
1251  QString id = g.attribute("id", "" );
1252 
1253  if (id == "total")
1254  {
1255  int nFree = g.attribute("free" , "0" ).toInt();
1256  int nTotal = g.attribute("total", "0" ).toInt();
1257  int nUsed = g.attribute("used" , "0" ).toInt();
1258  int nLiveTV = g.attribute("livetv" , "0" ).toInt();
1259  int nDeleted = g.attribute("deleted", "0" ).toInt();
1260  int nExpirable = g.attribute("expirable" , "0" ).toInt();
1261  QString nDir = g.attribute("dir" , "" );
1262 
1263  nDir.replace(QRegExp(","), ", ");
1264 
1265  os << " Disk Usage Summary:<br />\r\n";
1266  os << " <ul>\r\n";
1267 
1268  os << " <li>Total Disk Space:\r\n"
1269  << " <ul>\r\n";
1270 
1271  os << " <li>Total Space: ";
1272  sRep = QString("%L1").arg(nTotal) + " MB";
1273  os << sRep << "</li>\r\n";
1274 
1275  os << " <li>Space Used: ";
1276  sRep = QString("%L1").arg(nUsed) + " MB";
1277  os << sRep << "</li>\r\n";
1278 
1279  os << " <li>Space Free: ";
1280  sRep = QString("%L1").arg(nFree) + " MB";
1281  os << sRep << "</li>\r\n";
1282 
1283  if ((nLiveTV + nDeleted + nExpirable) > 0)
1284  {
1285  os << " <li>Space Available "
1286  "After Auto-expire: ";
1287  sRep = QString("%L1").arg(nUsed) + " MB";
1288  sRep = QString("%L1").arg(nFree + nLiveTV +
1289  nDeleted + nExpirable) + " MB";
1290  os << sRep << "\r\n";
1291  os << " <ul>\r\n";
1292  os << " <li>Space Used by LiveTV: ";
1293  sRep = QString("%L1").arg(nLiveTV) + " MB";
1294  os << sRep << "</li>\r\n";
1295  os << " <li>Space Used by "
1296  "Deleted Recordings: ";
1297  sRep = QString("%L1").arg(nDeleted) + " MB";
1298  os << sRep << "</li>\r\n";
1299  os << " <li>Space Used by "
1300  "Auto-expirable Recordings: ";
1301  sRep = QString("%L1").arg(nExpirable) + " MB";
1302  os << sRep << "</li>\r\n";
1303  os << " </ul>\r\n";
1304  os << " </li>\r\n";
1305  }
1306 
1307  os << " </ul>\r\n"
1308  << " </li>\r\n";
1309 
1310  os << " </ul>\r\n";
1311  break;
1312  }
1313  }
1314 
1315  node = node.nextSibling();
1316  }
1317 
1318  // Loop again to handle per-filesystem details.
1319  node = storage.firstChild();
1320 
1321  os << " Disk Usage Details:<br />\r\n";
1322  os << " <ul>\r\n";
1323 
1324 
1325  while (!node.isNull())
1326  {
1327  QDomElement g = node.toElement();
1328 
1329  if (!g.isNull() && g.tagName() == "Group")
1330  {
1331  int nFree = g.attribute("free" , "0" ).toInt();
1332  int nTotal = g.attribute("total", "0" ).toInt();
1333  int nUsed = g.attribute("used" , "0" ).toInt();
1334  QString nDir = g.attribute("dir" , "" );
1335  QString id = g.attribute("id" , "" );
1336 
1337  nDir.replace(QRegExp(","), ", ");
1338 
1339 
1340  if (id != "total")
1341  {
1342 
1343  os << " <li>MythTV Drive #" << id << ":"
1344  << "\r\n"
1345  << " <ul>\r\n";
1346 
1347  if (nDir.contains(','))
1348  os << " <li>Directories: ";
1349  else
1350  os << " <li>Directory: ";
1351 
1352  os << nDir << "</li>\r\n";
1353 
1354  os << " <li>Total Space: ";
1355  sRep = QString("%L1").arg(nTotal) + " MB";
1356  os << sRep << "</li>\r\n";
1357 
1358  os << " <li>Space Used: ";
1359  sRep = QString("%L1").arg(nUsed) + " MB";
1360  os << sRep << "</li>\r\n";
1361 
1362  os << " <li>Space Free: ";
1363  sRep = QString("%L1").arg(nFree) + " MB";
1364  os << sRep << "</li>\r\n";
1365 
1366  os << " </ul>\r\n"
1367  << " </li>\r\n";
1368  }
1369 
1370  }
1371 
1372  node = node.nextSibling();
1373  }
1374 
1375  os << " </ul>\r\n";
1376 
1377  // Guide Info ---------------------
1378 
1379  node = info.namedItem( "Guide" );
1380 
1381  if (!node.isNull())
1382  {
1383  QDomElement e = node.toElement();
1384 
1385  if (!e.isNull())
1386  {
1387  int nDays = e.attribute( "guideDays", "0" ).toInt();
1388  QString sStart = e.attribute( "start" , "" );
1389  QString sEnd = e.attribute( "end" , "" );
1390  QString sStatus = e.attribute( "status" , "" );
1391  QDateTime next = MythDate::fromString( e.attribute( "next" , "" ));
1392  QString sNext = next.isNull() ? "" :
1394  QString sMsg = "";
1395 
1396  QDateTime thru = MythDate::fromString( e.attribute( "guideThru", "" ));
1397 
1398  QDomText text = e.firstChild().toText();
1399 
1400  QString mfdblrs =
1401  gCoreContext->GetSetting("mythfilldatabaseLastRunStart");
1402  QDateTime lastrunstart = MythDate::fromString(mfdblrs);
1403 
1404  if (!text.isNull())
1405  sMsg = text.nodeValue();
1406 
1407  os << " Last mythfilldatabase run started on " << sStart
1408  << " and ";
1409 
1410  if (sEnd < sStart)
1411  os << "is ";
1412  else
1413  os << "ended on " << sEnd << ". ";
1414 
1415  os << sStatus << "<br />\r\n";
1416 
1417  if (!next.isNull() && next >= lastrunstart)
1418  {
1419  os << " Suggested next mythfilldatabase run: "
1420  << sNext << ".<br />\r\n";
1421  }
1422 
1423  if (!thru.isNull())
1424  {
1425  os << " There's guide data until "
1427 
1428  if (nDays > 0)
1429  os << " " << QObject::tr("(%n day(s))", "", nDays);
1430 
1431  os << ".";
1432 
1433  if (nDays <= 3)
1434  os << " <strong>WARNING</strong>: is mythfilldatabase running?";
1435  }
1436  else
1437  os << " There's <strong>no guide data</strong> available! "
1438  << "Have you run mythfilldatabase?";
1439  }
1440  }
1441  os << "\r\n </div>\r\n";
1442 
1443  return( 1 );
1444 }
1445 
1446 int HttpStatus::PrintMiscellaneousInfo( QTextStream &os, const QDomElement& info )
1447 {
1448  if (info.isNull())
1449  return( 0 );
1450 
1451  // Miscellaneous information
1452 
1453  QDomNodeList nodes = info.elementsByTagName("Information");
1454  uint count = nodes.count();
1455  if (count > 0)
1456  {
1457  QString display;
1458  QString linebreak;
1459  //QString name, value;
1460  os << "<div class=\"content\">\r\n"
1461  << " <h2 class=\"status\">Miscellaneous</h2>\r\n";
1462  for (unsigned int i = 0; i < count; i++)
1463  {
1464  QDomNode node = nodes.item(i);
1465  if (node.isNull())
1466  continue;
1467 
1468  QDomElement e = node.toElement();
1469  if (e.isNull())
1470  continue;
1471 
1472  display = e.attribute("display", "");
1473  //name = e.attribute("name", "");
1474  //value = e.attribute("value", "");
1475 
1476  if (display.isEmpty())
1477  continue;
1478 
1479  // Only include HTML line break if display value doesn't already
1480  // contain breaks.
1481  if (display.contains("<p>", Qt::CaseInsensitive) ||
1482  display.contains("<br", Qt::CaseInsensitive))
1483  {
1484  // matches <BR> or <br /
1485  linebreak = "\r\n";
1486  }
1487  else
1488  linebreak = "<br />\r\n";
1489 
1490  os << " " << display << linebreak;
1491  }
1492  os << "</div>\r\n";
1493  }
1494 
1495  return( 1 );
1496 }
1497 
1498 void HttpStatus::FillProgramInfo(QDomDocument *pDoc,
1499  QDomNode &node,
1500  ProgramInfo *pInfo,
1501  bool bIncChannel /* = true */,
1502  bool bDetails /* = true */)
1503 {
1504  if ((pDoc == nullptr) || (pInfo == nullptr))
1505  return;
1506 
1507  // Build Program Element
1508 
1509  QDomElement program = pDoc->createElement( "Program" );
1510  node.appendChild( program );
1511 
1512  program.setAttribute( "startTime" ,
1514  program.setAttribute( "endTime" , pInfo->GetScheduledEndTime(MythDate::ISODate));
1515  program.setAttribute( "title" , pInfo->GetTitle() );
1516  program.setAttribute( "subTitle" , pInfo->GetSubtitle());
1517  program.setAttribute( "category" , pInfo->GetCategory());
1518  program.setAttribute( "catType" , pInfo->GetCategoryTypeString());
1519  program.setAttribute( "repeat" , static_cast<int>(pInfo->IsRepeat()));
1520 
1521  if (bDetails)
1522  {
1523 
1524  program.setAttribute( "seriesId" , pInfo->GetSeriesID() );
1525  program.setAttribute( "programId" , pInfo->GetProgramID() );
1526  program.setAttribute( "stars" , pInfo->GetStars() );
1527  program.setAttribute( "fileSize" ,
1528  QString::number( pInfo->GetFilesize() ));
1529  program.setAttribute( "lastModified",
1531  program.setAttribute( "programFlags", pInfo->GetProgramFlags() );
1532  program.setAttribute( "hostname" , pInfo->GetHostname() );
1533 
1534  if (pInfo->GetOriginalAirDate().isValid())
1535  program.setAttribute(
1536  "airdate", pInfo->GetOriginalAirDate().toString());
1537 
1538  QDomText textNode = pDoc->createTextNode( pInfo->GetDescription() );
1539  program.appendChild( textNode );
1540 
1541  }
1542 
1543  if ( bIncChannel )
1544  {
1545  // Build Channel Child Element
1546 
1547  QDomElement channel = pDoc->createElement( "Channel" );
1548  program.appendChild( channel );
1549 
1550  FillChannelInfo( channel, pInfo, bDetails );
1551  }
1552 
1553  // Build Recording Child Element
1554 
1555  if ( pInfo->GetRecordingStatus() != RecStatus::Unknown )
1556  {
1557  QDomElement recording = pDoc->createElement( "Recording" );
1558  program.appendChild( recording );
1559 
1560  recording.setAttribute( "recStatus" ,
1561  pInfo->GetRecordingStatus() );
1562  recording.setAttribute( "recPriority" ,
1563  pInfo->GetRecordingPriority() );
1564  recording.setAttribute( "recStartTs" ,
1566  recording.setAttribute( "recEndTs" ,
1568 
1569  if (bDetails)
1570  {
1571  recording.setAttribute( "recordId" ,
1572  pInfo->GetRecordingRuleID() );
1573  recording.setAttribute( "recGroup" ,
1574  pInfo->GetRecordingGroup() );
1575  recording.setAttribute( "playGroup" ,
1576  pInfo->GetPlaybackGroup() );
1577  recording.setAttribute( "recType" ,
1578  pInfo->GetRecordingRuleType() );
1579  recording.setAttribute( "dupInType" ,
1580  pInfo->GetDuplicateCheckSource() );
1581  recording.setAttribute( "dupMethod" ,
1582  pInfo->GetDuplicateCheckMethod() );
1583  recording.setAttribute( "encoderId" ,
1584  pInfo->GetInputID() );
1585  const RecordingInfo ri(*pInfo);
1586  recording.setAttribute( "recProfile" ,
1588  //recording.setAttribute( "preRollSeconds", m_nPreRollSeconds );
1589  }
1590  }
1591 }
1592 
1594 //
1596 
1597 void HttpStatus::FillChannelInfo( QDomElement &channel,
1598  ProgramInfo *pInfo,
1599  bool bDetails /* = true */ )
1600 {
1601  if (pInfo)
1602  {
1603 /*
1604  QString sHostName = gCoreContext->GetHostName();
1605  QString sPort = gCoreContext->GetSettingOnHost( "BackendStatusPort",
1606  sHostName);
1607  QString sIconURL = QString( "http://%1:%2/getChannelIcon?ChanId=%3" )
1608  .arg( sHostName )
1609  .arg( sPort )
1610  .arg( pInfo->chanid );
1611 */
1612 
1613  channel.setAttribute( "chanId" , pInfo->GetChanID() );
1614  channel.setAttribute( "chanNum" , pInfo->GetChanNum());
1615  channel.setAttribute( "callSign" , pInfo->GetChannelSchedulingID());
1616  //channel.setAttribute( "iconURL" , sIconURL );
1617  channel.setAttribute( "channelName", pInfo->GetChannelName());
1618 
1619  if (bDetails)
1620  {
1621  channel.setAttribute( "chanFilters",
1622  pInfo->GetChannelPlaybackFilters() );
1623  channel.setAttribute( "sourceId" , pInfo->GetSourceID() );
1624  channel.setAttribute( "inputId" , pInfo->GetInputID() );
1625  channel.setAttribute( "commFree" ,
1626  (pInfo->IsCommercialFree()) ? 1 : 0 );
1627  }
1628  }
1629 }
1630 
1631 
1632 
1633 
1634 // vim:set shiftwidth=4 tabstop=4 expandtab:
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
float GetStars(void) const
Definition: programinfo.h:436
void Run(time_t timeout=0)
Runs a command inside the /bin/sh shell. Returns immediately.
QStringList GetBasePaths() override
Definition: httpstatus.cpp:80
static void FillProgramInfo(QDomDocument *pDoc, QDomNode &node, ProgramInfo *pInfo, bool bIncChannel=true, bool bDetails=true)
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
uint GetInputID(void) const
Definition: programinfo.h:457
QString m_sBaseUrl
Definition: httprequest.h:125
Watching LiveTV is the state for when we are watching a recording and the user has control over the c...
Definition: tv.h:63
static int PrintMachineInfo(QTextStream &os, const QDomElement &info)
RecordingDupMethodType GetDuplicateCheckMethod(void) const
What should be compared to determine if two programs are the same?
Definition: programinfo.h:453
static QString setting_to_localtime(const char *setting)
Definition: httpstatus.cpp:166
QString GetChannelName(void) const
This is the channel name in the local market, i.e.
Definition: programinfo.h:380
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
bool ProcessRequest(HTTPRequest *pRequest) override
Definition: httpstatus.cpp:89
virtual uint64_t GetFilesize(void) const
RecList::const_iterator RecConstIter
Definition: mythscheduler.h:16
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
uint GetSourceID(void) const
Definition: programinfo.h:456
static QString JobText(int jobType)
Definition: jobqueue.cpp:1115
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
QString m_sMethod
Definition: httprequest.h:127
QStringMap m_mapRespHeaders
Definition: httprequest.h:151
QString GetTitle(void) const
Definition: programinfo.h:355
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:34
long m_nResponseStatus
Definition: httprequest.h:150
bool IsCommercialFree(void) const
Definition: programinfo.h:472
Watching Recording is the state for when we are watching an in progress recording,...
Definition: tv.h:80
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
AutoExpire * m_pExpirer
Definition: httpstatus.h:48
int GetRecordingPriority(void) const
Definition: programinfo.h:434
Do Today/Yesterday/Tomorrow transform.
Definition: mythdate.h:23
QString GetChanNum(void) const
This is the channel "number", in the form 1, 1_2, 1-2, 1#1, etc.
Definition: programinfo.h:370
void FillStatusXML(QDomDocument *pDoc)
Definition: httpstatus.cpp:173
TVState
TVState is an enumeration of the states used by TV and TVRec.
Definition: tv.h:50
QString m_sResourceUrl
Definition: httprequest.h:126
QString GetChannelPlaybackFilters(void) const
Definition: programinfo.h:381
static int GetJobsInQueue(QMap< int, JobQueueEntry > &jobs, int findJobs=JOB_LIST_NOT_DONE)
Definition: jobqueue.cpp:1276
void GetStatusXML(HTTPRequest *pRequest)
Definition: httpstatus.cpp:129
static int PrintMiscellaneousInfo(QTextStream &os, const QDomElement &info)
static int PrintFrontends(QTextStream &os, const QDomElement &frontends)
Definition: httpstatus.cpp:982
QDate GetOriginalAirDate(void) const
Definition: programinfo.h:422
QString GetMasterServerIP(void)
Returns the Master Backend IP address If the address is an IPv6 address, the scope Id is removed.
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:384
QVariant value(int i) const
Definition: mythdbcon.h:198
static void FillChannelInfo(QDomElement &channel, ProgramInfo *pInfo, bool bDetails=true)
Default local time.
Definition: mythdate.h:20
Holds information on recordings and videos.
Definition: programinfo.h:67
Recording Only is a TVRec only state for when we are recording a program, but there is no one current...
Definition: tv.h:84
static int PrintEncoderStatus(QTextStream &os, const QDomElement &encoders)
Definition: httpstatus.cpp:705
static QReadWriteLock s_inputsLock
Definition: tv_rec.h:426
QString GetChannelSchedulingID(void) const
This is the unique programming identifier of a channel.
Definition: programinfo.h:377
uint32_t GetProgramFlags(void) const
Definition: programinfo.h:466
Default UTC, database format.
Definition: mythdate.h:24
QString GetDescription(void) const
Definition: programinfo.h:359
HttpStatus(QMap< int, EncoderLink * > *tvList, Scheduler *sched, AutoExpire *expirer, bool bIsMaster)
Definition: httpstatus.cpp:48
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QString GetSetting(const QString &key, const QString &defaultval="")
QByteArray & ReadAll()
Default local time.
Definition: mythdate.h:16
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
QString GetSubtitle(void) const
Definition: programinfo.h:357
static QString StatusText(int status)
Definition: jobqueue.cpp:1134
bool IsRepeat(void) const
Definition: programinfo.h:480
#define MYTH_PROTO_VERSION
Increment this whenever the MythTV network protocol changes.
Definition: mythversion.h:48
QString GetRecordingGroup(void) const
Definition: programinfo.h:413
QString GetMasterHostName(void)
SleepStatus
SleepStatus is an enumeration of the awake/sleep status of a slave.
Definition: tv.h:97
void BackendQueryDiskSpace(QStringList &strlist, bool consolidated, bool allHosts)
string hostname
Definition: caa.py:17
Add year to string if not included.
Definition: mythdate.h:22
unsigned int uint
Definition: compat.h:140
QString GetCategoryTypeString(void) const
Returns catType as a string.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:391
int m_nPreRollSeconds
Definition: httpstatus.h:51
#define getloadavg(x, y)
Definition: compat.h:322
QMap< int, EncoderLink * > * m_pEncoders
Definition: httpstatus.h:47
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:101
QString GetPlaybackGroup(void) const
Definition: programinfo.h:414
static int PrintBackends(QTextStream &os, const QDomElement &backends)
A slave is considered asleep when it is not awake and not undefined.
Definition: tv.h:104
uint Wait(time_t timeout=0)
void GetStatusHTML(HTTPRequest *pRequest)
Definition: httpstatus.cpp:153
QMap< QString, DeviceLocation * > EntryMap
Key == Unique Service Name (USN)
Definition: ssdpcache.h:28
QString GetProgramRecordingProfile(void) const
Returns recording profile name that will be, or was used, for this program, creating "record" field i...
QString GetSeriesID(void) const
Definition: programinfo.h:429
static QString GetDeviceLabel(const QString &inputtype, const QString &videodevice)
run process through shell
Definition: mythsystem.h:41
int GetNumSetting(const QString &key, int defaultval=0)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
MainServer * m_pMainServer
Definition: httpstatus.h:49
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:441
PictureAttribute next(PictureAttributeSupported Supported, PictureAttribute Attribute)
bool GetBoolSetting(const QString &key, bool defaultval=false)
static void PrintStatus(QTextStream &os, QDomDocument *pDoc)
Definition: httpstatus.cpp:622
Default local time.
Definition: mythdate.h:17
static int PrintJobQueue(QTextStream &os, const QDomElement &jobs)
RecordingType GetRecordingRuleType(void) const
Definition: programinfo.h:445
Used to expire recordings to make space for new recordings.
Definition: autoexpire.h:61
std::deque< RecordingInfo * > RecList
Definition: mythscheduler.h:12
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:366
AutoExpire * expirer
uint GetRecordingRuleID(void) const
Definition: programinfo.h:443
A slave's sleep status is undefined when it has never connected to the master backend or is not able ...
Definition: tv.h:117
QString GetProgramID(void) const
Definition: programinfo.h:430
QBuffer m_response
Definition: httprequest.h:155
#define MYTH_BINARY_VERSION
Update this whenever the plug-in ABI changes.
Definition: mythversion.h:16
HttpResponseType m_eResponseType
Definition: httprequest.h:147
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:398
void GetEntryMap(EntryMap &map)
Returns a copy of the EntryMap.
Definition: ssdpcache.cpp:84
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end.
Definition: programinfo.h:406
HttpStatusMethod
Definition: httpstatus.h:21
int GetMasterServerStatusPort(void)
Returns the Master Backend status port If no master server status port has been defined in the databa...
Scheduler * sched
QDateTime GetLastModifiedTime(void) const
Definition: programinfo.h:423
static HttpStatusMethod GetMethod(const QString &sURI)
Definition: httpstatus.cpp:66
QString GetHostname(void) const
Definition: programinfo.h:415
Default UTC.
Definition: mythdate.h:14
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
static SSDPCacheEntries * Find(const QString &sURI)
Definition: ssdp.h:126
Scheduler * m_pSched
Definition: httpstatus.h:46
QString GetCategory(void) const
Definition: programinfo.h:363
static int PrintScheduled(QTextStream &os, const QDomElement &scheduled)
Definition: httpstatus.cpp:849
RecordingDupInType GetDuplicateCheckSource(void) const
Where should we check for duplicates?
Definition: programinfo.h:449
bool IsMasterBackend(void)
is this the actual MBE process
JobQueue * jobqueue
static QList< QHostAddress > g_IPAddrList
Definition: upnp.h:113
QString GetHostName(void)
bool GetAllPending(RecList &retList, int recRuleId=0) const
Definition: scheduler.cpp:1725
QMap< int, EncoderLink * > tvList
#define output
bool m_bIsMaster
Definition: httpstatus.h:50
allow access to stdout
Definition: mythsystem.h:39
Default local time.
Definition: mythdate.h:19