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