00001
00002
00003
00004
00005
00006
00007
00008
00010
00011 #include "httpstatus.h"
00012 #include "backendutil.h"
00013 #include "mythxml.h"
00014
00015 #include "libmyth/mythcontext.h"
00016 #include "libmyth/util.h"
00017 #include "libmyth/mythdbcon.h"
00018 #include "libmyth/compat.h"
00019
00020 #include <qtextstream.h>
00021 #include <qdir.h>
00022 #include <qfile.h>
00023 #include <qregexp.h>
00024 #include <qbuffer.h>
00025 #include <qlocale.h>
00026 #include <math.h>
00027 #include <stdio.h>
00028 #include <stdlib.h>
00029 #include <unistd.h>
00030
00031 #include "../../config.h"
00032
00034
00036
00037 HttpStatus::HttpStatus( QMap<int, EncoderLink *> *tvList, Scheduler *sched, AutoExpire *expirer, bool bIsMaster )
00038 : HttpServerExtension( "HttpStatus" )
00039 {
00040 m_pEncoders = tvList;
00041 m_pSched = sched;
00042 m_pExpirer = expirer;
00043 m_bIsMaster = bIsMaster;
00044
00045 m_nPreRollSeconds = gContext->GetNumSetting("RecordPreRoll", 0);
00046 }
00047
00049
00051
00052 HttpStatus::~HttpStatus()
00053 {
00054 }
00055
00057
00059
00060 HttpStatusMethod HttpStatus::GetMethod( const QString &sURI )
00061 {
00062 if (sURI == "" ) return( HSM_GetStatusHTML );
00063 if (sURI == "GetStatusHTML" ) return( HSM_GetStatusHTML );
00064 if (sURI == "GetStatus" ) return( HSM_GetStatusXML );
00065 if (sURI == "xml" ) return( HSM_GetStatusXML );
00066
00067 return( HSM_Unknown );
00068 }
00069
00071
00073
00074 bool HttpStatus::ProcessRequest( HttpWorkerThread * , HTTPRequest *pRequest )
00075 {
00076 try
00077 {
00078 if (pRequest)
00079 {
00080 if (pRequest->m_sBaseUrl != "/")
00081 return( false );
00082
00083 switch( GetMethod( pRequest->m_sMethod ))
00084 {
00085 case HSM_GetStatusXML : GetStatusXML ( pRequest ); return true;
00086 case HSM_GetStatusHTML : GetStatusHTML ( pRequest ); return true;
00087
00088 default:
00089 {
00090 pRequest->m_eResponseType = ResponseTypeHTML;
00091 pRequest->m_nResponseStatus = 200;
00092
00093 break;
00094 }
00095 }
00096 }
00097 }
00098 catch( ... )
00099 {
00100 cerr << "HttpStatus::ProcessRequest() - Unexpected Exception" << endl;
00101 }
00102
00103 return( false );
00104 }
00105
00107
00109
00110 void HttpStatus::GetStatusXML( HTTPRequest *pRequest )
00111 {
00112 QDomDocument doc( "Status" );
00113
00114 FillStatusXML( &doc );
00115
00116 pRequest->m_eResponseType = ResponseTypeXML;
00117 pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000";
00118 pRequest->m_response << doc.toString();
00119 }
00120
00122
00124
00125 void HttpStatus::GetStatusHTML( HTTPRequest *pRequest )
00126 {
00127 pRequest->m_eResponseType = ResponseTypeHTML;
00128 pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000";
00129
00130 QDomDocument doc( "Status" );
00131
00132 FillStatusXML( &doc );
00133
00134 PrintStatus( pRequest->m_response, &doc );
00135 }
00136
00137 void HttpStatus::FillStatusXML( QDomDocument *pDoc )
00138 {
00139 QString dateFormat = gContext->GetSetting("DateFormat", "M/d/yyyy");
00140
00141 if (dateFormat.find(QRegExp("yyyy")) < 0)
00142 dateFormat += " yyyy";
00143
00144 QString shortdateformat = gContext->GetSetting("ShortDateFormat", "M/d");
00145 QString timeformat = gContext->GetSetting("TimeFormat", "h:mm AP");
00146 QDateTime qdtNow = QDateTime::currentDateTime();
00147
00148
00149
00150 QDomElement root = pDoc->createElement("Status");
00151 pDoc->appendChild(root);
00152
00153 root.setAttribute("date" , qdtNow.toString(dateFormat));
00154 root.setAttribute("time" , qdtNow.toString(timeformat) );
00155 root.setAttribute("ISODate" , qdtNow.toString(Qt::ISODate) );
00156 root.setAttribute("version" , MYTH_BINARY_VERSION );
00157 root.setAttribute("protoVer", MYTH_PROTO_VERSION );
00158
00159
00160
00161 QDomElement encoders = pDoc->createElement("Encoders");
00162 root.appendChild(encoders);
00163
00164 int numencoders = 0;
00165 bool isLocal = true;
00166
00167 QMap<int, EncoderLink *>::Iterator iter = m_pEncoders->begin();
00168
00169 for (; iter != m_pEncoders->end(); ++iter)
00170 {
00171 EncoderLink *elink = iter.data();
00172
00173 if (elink != NULL)
00174 {
00175 isLocal = elink->IsLocal();
00176
00177 QDomElement encoder = pDoc->createElement("Encoder");
00178 encoders.appendChild(encoder);
00179
00180 encoder.setAttribute("id" , elink->GetCardID() );
00181 encoder.setAttribute("local" , isLocal );
00182 encoder.setAttribute("connected" , elink->IsConnected() );
00183 encoder.setAttribute("state" , elink->GetState() );
00184
00185
00186 if (isLocal)
00187 encoder.setAttribute("hostname", gContext->GetHostName());
00188 else
00189 encoder.setAttribute("hostname", elink->GetHostName());
00190
00191 if (elink->IsConnected())
00192 numencoders++;
00193
00194 switch (elink->GetState())
00195 {
00196 case kState_WatchingLiveTV:
00197 case kState_RecordingOnly:
00198 case kState_WatchingRecording:
00199 {
00200 ProgramInfo *pInfo = elink->GetRecording();
00201
00202 if (pInfo)
00203 {
00204 MythXML::FillProgramInfo(pDoc, encoder, pInfo);
00205 delete pInfo;
00206 }
00207
00208 break;
00209 }
00210
00211 default:
00212 break;
00213 }
00214 }
00215 }
00216
00217 encoders.setAttribute("count", numencoders);
00218
00219
00220
00221 QDomElement scheduled = pDoc->createElement("Scheduled");
00222 root.appendChild(scheduled);
00223
00224 RecList recordingList;
00225
00226 if (m_pSched)
00227 m_pSched->getAllPending(&recordingList);
00228
00229 unsigned int iNum = 10;
00230 unsigned int iNumRecordings = 0;
00231
00232 RecConstIter itProg = recordingList.begin();
00233 for (; (itProg != recordingList.end()) && iNumRecordings < iNum; itProg++)
00234 {
00235 if (((*itProg)->recstatus <= rsWillRecord) &&
00236 ((*itProg)->recstartts >= QDateTime::currentDateTime()))
00237 {
00238 iNumRecordings++;
00239 MythXML::FillProgramInfo(pDoc, scheduled, *itProg);
00240 }
00241 }
00242
00243 while (recordingList.size() > 0)
00244 {
00245 ProgramInfo *pginfo = recordingList.back();
00246 delete pginfo;
00247 recordingList.pop_back();
00248 }
00249
00250 scheduled.setAttribute("count", iNumRecordings);
00251
00252
00253
00254 QDomElement jobqueue = pDoc->createElement("JobQueue");
00255 root.appendChild(jobqueue);
00256
00257 QMap<int, JobQueueEntry> jobs;
00258 QMap<int, JobQueueEntry>::Iterator it;
00259
00260 JobQueue::GetJobsInQueue(jobs,
00261 JOB_LIST_NOT_DONE | JOB_LIST_ERROR |
00262 JOB_LIST_RECENT);
00263
00264 if (jobs.size())
00265 {
00266 for (it = jobs.begin(); it != jobs.end(); ++it)
00267 {
00268 ProgramInfo *pInfo;
00269
00270 pInfo = ProgramInfo::GetProgramFromRecorded(it.data().chanid,
00271 it.data().starttime);
00272
00273 if (!pInfo)
00274 continue;
00275
00276 QDomElement job = pDoc->createElement("Job");
00277 jobqueue.appendChild(job);
00278
00279 job.setAttribute("id" , it.data().id );
00280 job.setAttribute("chanId" , it.data().chanid );
00281 job.setAttribute("startTime" , it.data().starttime.toString(Qt::ISODate));
00282 job.setAttribute("startTs" , it.data().startts );
00283 job.setAttribute("insertTime", it.data().inserttime.toString(Qt::ISODate));
00284 job.setAttribute("type" , it.data().type );
00285 job.setAttribute("cmds" , it.data().cmds );
00286 job.setAttribute("flags" , it.data().flags );
00287 job.setAttribute("status" , it.data().status );
00288 job.setAttribute("statusTime", it.data().statustime.toString(Qt::ISODate));
00289 job.setAttribute("schedTime" , it.data().schedruntime.toString(Qt::ISODate));
00290 job.setAttribute("args" , it.data().args );
00291
00292 if (it.data().hostname == "")
00293 job.setAttribute("hostname", QObject::tr("master"));
00294 else
00295 job.setAttribute("hostname",it.data().hostname);
00296
00297 QDomText textNode = pDoc->createTextNode(it.data().comment);
00298 job.appendChild(textNode);
00299
00300 MythXML::FillProgramInfo(pDoc, job, pInfo);
00301
00302 delete pInfo;
00303 }
00304 }
00305
00306 jobqueue.setAttribute( "count", jobs.size() );
00307
00308
00309
00310 QDomElement mInfo = pDoc->createElement("MachineInfo");
00311 QDomElement storage = pDoc->createElement("Storage" );
00312 QDomElement load = pDoc->createElement("Load" );
00313 QDomElement guide = pDoc->createElement("Guide" );
00314
00315 root.appendChild (mInfo );
00316 mInfo.appendChild(storage);
00317 mInfo.appendChild(load );
00318 mInfo.appendChild(guide );
00319
00320
00321
00322 QStringList strlist;
00323 QString dirs = "";
00324 QString hostname;
00325 QString directory;
00326 QString isLocalstr;
00327 QString fsID;
00328 QString ids;
00329 long long iTotal = -1, iUsed = -1, iAvail = -1;
00330
00331 BackendQueryDiskSpace(strlist, m_pEncoders, true, m_bIsMaster);
00332
00333 QStringList::const_iterator sit = strlist.begin();
00334 while (sit != strlist.end())
00335 {
00336 hostname = *(sit++);
00337 directory = *(sit++);
00338 isLocalstr = *(sit++);
00339 fsID = *(sit++);
00340 sit++;
00341 iTotal = decodeLongLong(strlist, sit);
00342 iUsed = decodeLongLong(strlist, sit);
00343 iAvail = iTotal - iUsed;
00344
00345 if (fsID == "-2")
00346 fsID = "total";
00347
00348 if (ids == "")
00349 ids = fsID;
00350 else
00351 ids = ids + "," + fsID;
00352
00353 storage.setAttribute("drive_" + fsID + "_total", (int)(iTotal>>10));
00354 storage.setAttribute("drive_" + fsID + "_used" , (int)(iUsed>>10));
00355 storage.setAttribute("drive_" + fsID + "_free" , (int)(iAvail>>10));
00356 storage.setAttribute("drive_" + fsID + "_dirs" , directory);
00357 }
00358
00359 storage.setAttribute("fsids", ids);
00360
00361
00362
00363 double rgdAverages[3];
00364
00365 if (getloadavg(rgdAverages, 3) != -1)
00366 {
00367 load.setAttribute("avg1", rgdAverages[0]);
00368 load.setAttribute("avg2", rgdAverages[1]);
00369 load.setAttribute("avg3", rgdAverages[2]);
00370 }
00371
00372
00373
00374 QDateTime GuideDataThrough;
00375
00376 MSqlQuery query(MSqlQuery::InitCon());
00377 query.prepare("SELECT MAX(endtime) FROM program WHERE manualid = 0;");
00378
00379 if (query.exec() && query.isActive() && query.size())
00380 {
00381 query.next();
00382
00383 if (query.isValid())
00384 GuideDataThrough = QDateTime::fromString(query.value(0).toString(),
00385 Qt::ISODate);
00386 }
00387
00388 guide.setAttribute("start", gContext->GetSetting("mythfilldatabaseLastRunStart"));
00389 guide.setAttribute("end", gContext->GetSetting("mythfilldatabaseLastRunEnd"));
00390 guide.setAttribute("status", gContext->GetSetting("mythfilldatabaseLastRunStatus"));
00391 if (gContext->GetNumSetting("MythFillGrabberSuggestsTime", 0))
00392 {
00393 guide.setAttribute("next",
00394 gContext->GetSetting("MythFillSuggestedRunTime"));
00395 }
00396
00397 if (!GuideDataThrough.isNull())
00398 {
00399 guide.setAttribute("guideThru", QDateTime(GuideDataThrough).toString(Qt::ISODate));
00400 guide.setAttribute("guideDays", qdtNow.daysTo(GuideDataThrough));
00401 }
00402
00403 QDomText dataDirectMessage = pDoc->createTextNode(gContext->GetSetting("DataDirectMessage"));
00404 guide.appendChild(dataDirectMessage);
00405
00406
00407
00408
00409 QString info_script = gContext->GetSetting("MiscStatusScript");
00410 if ((!info_script.isEmpty()) && (info_script != "none"))
00411 {
00412 QDomElement misc = pDoc->createElement("Miscellaneous");
00413 root.appendChild(misc);
00414
00415 FILE *fp = popen(info_script.ascii(), "r");
00416
00417 if (fp)
00418 {
00419 char buffer[256];
00420 QString input = "";
00421
00422 while (fgets(buffer, sizeof(buffer), fp))
00423 {
00424 input.append(QString::fromLocal8Bit(buffer));
00425 }
00426
00427 if (pclose(fp))
00428 {
00429 VERBOSE(VB_IMPORTANT, QString("Error running miscellaneous "
00430 "status information script: %1").arg(info_script));
00431 }
00432 else
00433 {
00434 QStringList output = QStringList::split("\n", input, false);
00435
00436 QStringList::iterator iter = output.begin();
00437 for (; iter != output.end(); iter++)
00438 {
00439 QDomElement info = pDoc->createElement("Information");
00440
00441 QStringList list = QStringList::split("[]:[]", *iter, true);
00442 unsigned int size = list.size();
00443 unsigned int hasAttributes = 0;
00444
00445
00446 if ((size > 0) && (!list[0].isEmpty()))
00447 {
00448 info.setAttribute("display", list[0]);
00449 hasAttributes++;
00450 }
00451 if ((size > 1) && (!list[1].isEmpty()))
00452 {
00453 info.setAttribute("name", list[1]);
00454 hasAttributes++;
00455 }
00456 if ((size > 2) && (!list[2].isEmpty()))
00457 {
00458 info.setAttribute("value", list[2]);
00459 hasAttributes++;
00460 }
00461
00462 if (hasAttributes > 0)
00463 misc.appendChild(info);
00464 }
00465 }
00466 }
00467 else
00468 {
00469 VERBOSE(VB_IMPORTANT, QString("Failed to run miscellaneous status "
00470 "information script: %1").arg(info_script));
00471 }
00472 }
00473
00474 }
00475
00477
00479
00480 void HttpStatus::PrintStatus( QTextStream &os, QDomDocument *pDoc )
00481 {
00482
00483 QString shortdateformat = gContext->GetSetting("ShortDateFormat", "M/d");
00484 QString timeformat = gContext->GetSetting("TimeFormat", "h:mm AP");
00485
00486 os.setEncoding(QTextStream::UnicodeUTF8);
00487
00488 QDateTime qdtNow = QDateTime::currentDateTime();
00489
00490 QDomElement docElem = pDoc->documentElement();
00491
00492 os << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
00493 << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n"
00494 << "<html xmlns=\"http://www.w3.org/1999/xhtml\""
00495 << " xml:lang=\"en\" lang=\"en\">\r\n"
00496 << "<head>\r\n"
00497 << " <meta http-equiv=\"Content-Type\""
00498 << "content=\"text/html; charset=UTF-8\" />\r\n"
00499 << " <style type=\"text/css\" title=\"Default\" media=\"all\">\r\n"
00500 << " <!--\r\n"
00501 << " body {\r\n"
00502 << " background-color:#fff;\r\n"
00503 << " font:11px verdana, arial, helvetica, sans-serif;\r\n"
00504 << " margin:20px;\r\n"
00505 << " }\r\n"
00506 << " h1 {\r\n"
00507 << " font-size:28px;\r\n"
00508 << " font-weight:900;\r\n"
00509 << " color:#ccc;\r\n"
00510 << " letter-spacing:0.5em;\r\n"
00511 << " margin-bottom:30px;\r\n"
00512 << " width:650px;\r\n"
00513 << " text-align:center;\r\n"
00514 << " }\r\n"
00515 << " h2 {\r\n"
00516 << " font-size:18px;\r\n"
00517 << " font-weight:800;\r\n"
00518 << " color:#360;\r\n"
00519 << " border:none;\r\n"
00520 << " letter-spacing:0.3em;\r\n"
00521 << " padding:0px;\r\n"
00522 << " margin-bottom:10px;\r\n"
00523 << " margin-top:0px;\r\n"
00524 << " }\r\n"
00525 << " hr {\r\n"
00526 << " display:none;\r\n"
00527 << " }\r\n"
00528 << " div.content {\r\n"
00529 << " width:650px;\r\n"
00530 << " border-top:1px solid #000;\r\n"
00531 << " border-right:1px solid #000;\r\n"
00532 << " border-bottom:1px solid #000;\r\n"
00533 << " border-left:10px solid #000;\r\n"
00534 << " padding:10px;\r\n"
00535 << " margin-bottom:30px;\r\n"
00536 << " -moz-border-radius:8px 0px 0px 8px;\r\n"
00537 << " }\r\n"
00538 << " div.schedule a {\r\n"
00539 << " display:block;\r\n"
00540 << " color:#000;\r\n"
00541 << " text-decoration:none;\r\n"
00542 << " padding:.2em .8em;\r\n"
00543 << " border:thin solid #fff;\r\n"
00544 << " width:350px;\r\n"
00545 << " }\r\n"
00546 << " div.schedule a span {\r\n"
00547 << " display:none;\r\n"
00548 << " }\r\n"
00549 << " div.schedule a:hover {\r\n"
00550 << " background-color:#F4F4F4;\r\n"
00551 << " border-top:thin solid #000;\r\n"
00552 << " border-bottom:thin solid #000;\r\n"
00553 << " border-left:thin solid #000;\r\n"
00554 << " cursor:default;\r\n"
00555 << " }\r\n"
00556 << " div.schedule a:hover span {\r\n"
00557 << " display:block;\r\n"
00558 << " position:absolute;\r\n"
00559 << " background-color:#F4F4F4;\r\n"
00560 << " color:#000;\r\n"
00561 << " left:400px;\r\n"
00562 << " margin-top:-20px;\r\n"
00563 << " width:280px;\r\n"
00564 << " padding:5px;\r\n"
00565 << " border:thin dashed #000;\r\n"
00566 << " }\r\n"
00567 << " div.loadstatus {\r\n"
00568 << " width:325px;\r\n"
00569 << " height:7em;\r\n"
00570 << " }\r\n"
00571 << " .jobfinished { color: #0000ff; }\r\n"
00572 << " .jobaborted { color: #7f0000; }\r\n"
00573 << " .joberrored { color: #ff0000; }\r\n"
00574 << " .jobrunning { color: #005f00; }\r\n"
00575 << " .jobqueued { }\r\n"
00576 << " -->\r\n"
00577 << " </style>\r\n"
00578 << " <title>MythTV Status - "
00579 << docElem.attribute( "date", qdtNow.toString(shortdateformat) )
00580 << " "
00581 << docElem.attribute( "time", qdtNow.toString(timeformat) ) << " - "
00582 << docElem.attribute( "version", MYTH_BINARY_VERSION ) << "</title>\r\n"
00583 << "</head>\r\n"
00584 << "<body>\r\n\r\n"
00585 << " <h1>MythTV Status</h1>\r\n";
00586
00587 int nNumEncoders = 0;
00588
00589
00590
00591 QDomNode node = docElem.namedItem( "Encoders" );
00592
00593 if (!node.isNull())
00594 nNumEncoders = PrintEncoderStatus( os, node.toElement() );
00595
00596
00597
00598 node = docElem.namedItem( "Scheduled" );
00599
00600 if (!node.isNull())
00601 PrintScheduled( os, node.toElement());
00602
00603
00604
00605 node = docElem.namedItem( "JobQueue" );
00606
00607 if (!node.isNull())
00608 PrintJobQueue( os, node.toElement());
00609
00610
00611
00612
00613 node = docElem.namedItem( "MachineInfo" );
00614
00615 if (!node.isNull())
00616 PrintMachineInfo( os, node.toElement());
00617
00618
00619
00620 node = docElem.namedItem( "Miscellaneous" );
00621
00622 if (!node.isNull())
00623 PrintMiscellaneousInfo( os, node.toElement());
00624
00625 os << "\r\n</body>\r\n</html>\r\n";
00626
00627 }
00628
00630
00632
00633 int HttpStatus::PrintEncoderStatus( QTextStream &os, QDomElement encoders )
00634 {
00635 QString timeformat = gContext->GetSetting("TimeFormat", "h:mm AP");
00636 int nNumEncoders = 0;
00637
00638 if (encoders.isNull())
00639 return 0;
00640
00641 os << " <div class=\"content\">\r\n"
00642 << " <h2>Encoder status</h2>\r\n";
00643
00644 QDomNode node = encoders.firstChild();
00645
00646 while (!node.isNull())
00647 {
00648 QDomElement e = node.toElement();
00649
00650 if (!e.isNull())
00651 {
00652 if (e.tagName() == "Encoder")
00653 {
00654 QString sIsLocal = (e.attribute( "local" , "remote" )== "1")
00655 ? "local" : "remote";
00656 QString sCardId = e.attribute( "id" , "0" );
00657 QString sHostName = e.attribute( "hostname" , "Unknown");
00658 bool bConnected= e.attribute( "connected", "0" ).toInt();
00659
00660 bool bIsLowOnFreeSpace=e.attribute( "lowOnFreeSpace", "0").toInt();
00661
00662 os << " Encoder " << sCardId << " is " << sIsLocal
00663 << " on " << sHostName;
00664
00665 if ((sIsLocal == "remote") && !bConnected)
00666 {
00667 os << " (currently not connected).<br />";
00668
00669 node = node.nextSibling();
00670 continue;
00671 }
00672
00673 nNumEncoders++;
00674
00675 TVState encState = (TVState) e.attribute( "state", "0").toInt();
00676
00677 switch( encState )
00678 {
00679 case kState_WatchingLiveTV:
00680 os << " and is watching Live TV";
00681 break;
00682
00683 case kState_RecordingOnly:
00684 case kState_WatchingRecording:
00685 os << " and is recording";
00686 break;
00687
00688 default:
00689 os << " and is not recording.";
00690 break;
00691 }
00692
00693
00694
00695 QDomNode tmpNode = e.namedItem( "Program" );
00696
00697 if (!tmpNode.isNull())
00698 {
00699 QDomElement program = tmpNode.toElement();
00700
00701 if (!program.isNull())
00702 {
00703 os << ": '" << program.attribute( "title", "Unknown" ) << "'";
00704
00705
00706
00707 tmpNode = program.namedItem( "Channel" );
00708
00709 if (!tmpNode.isNull())
00710 {
00711 QDomElement channel = tmpNode.toElement();
00712
00713 if (!channel.isNull())
00714 os << " on "
00715 << channel.attribute( "callSign", "unknown" );
00716 }
00717
00718
00719
00720 tmpNode = program.namedItem( "Recording" );
00721
00722 if (!tmpNode.isNull())
00723 {
00724 QDomElement recording = tmpNode.toElement();
00725
00726 if (!recording.isNull())
00727 {
00728 QDateTime endTs = QDateTime::fromString(
00729 recording.attribute( "recEndTs", "" ),
00730 Qt::ISODate );
00731
00732 os << ". This recording will end "
00733 << "at " << endTs.toString(timeformat);
00734 }
00735 }
00736 }
00737
00738 os << ".";
00739 }
00740
00741 if (bIsLowOnFreeSpace)
00742 {
00743 os << " <strong>WARNING</strong>:"
00744 << " This backend is low on free disk space!";
00745 }
00746
00747 os << "<br />\r\n";
00748 }
00749 }
00750
00751 node = node.nextSibling();
00752 }
00753
00754 os << " </div>\r\n\r\n";
00755
00756 return( nNumEncoders );
00757 }
00758
00760
00762
00763 int HttpStatus::PrintScheduled( QTextStream &os, QDomElement scheduled )
00764 {
00765 QDateTime qdtNow = QDateTime::currentDateTime();
00766 QString shortdateformat = gContext->GetSetting("ShortDateFormat", "M/d");
00767 QString timeformat = gContext->GetSetting("TimeFormat", "h:mm AP");
00768
00769 if (scheduled.isNull())
00770 return( 0 );
00771
00772 int nNumRecordings= scheduled.attribute( "count", "0" ).toInt();
00773
00774 os << " <div class=\"content\">\r\n"
00775 << " <h2>Schedule</h2>\r\n";
00776
00777 if (nNumRecordings == 0)
00778 {
00779 os << " There are no shows scheduled for recording.\r\n"
00780 << " </div>\r\n";
00781 return( 0 );
00782 }
00783
00784 os << " The next " << nNumRecordings << " show" << (nNumRecordings == 1 ? "" : "s" )
00785 << " that " << (nNumRecordings == 1 ? "is" : "are")
00786 << " scheduled for recording:\r\n";
00787
00788 os << " <div class=\"schedule\">\r\n";
00789
00790
00791
00792 QDomNode node = scheduled.firstChild();
00793
00794 while (!node.isNull())
00795 {
00796 QDomElement e = node.toElement();
00797
00798 if (!e.isNull())
00799 {
00800 QDomNode recNode = e.namedItem( "Recording" );
00801 QDomNode chanNode = e.namedItem( "Channel" );
00802
00803 if ((e.tagName() == "Program") && !recNode.isNull() && !chanNode.isNull())
00804 {
00805 QDomElement r = recNode.toElement();
00806 QDomElement c = chanNode.toElement();
00807
00808 QString sTitle = e.attribute( "title" , "" );
00809 QString sSubTitle = e.attribute( "subTitle", "" );
00810 QDateTime startTs = QDateTime::fromString( e.attribute( "startTime" ,"" ), Qt::ISODate );
00811 QDateTime endTs = QDateTime::fromString( e.attribute( "endTime" ,"" ), Qt::ISODate );
00812 QDateTime recStartTs = QDateTime::fromString( r.attribute( "recStartTs","" ), Qt::ISODate );
00813
00814 int nPreRollSecs = r.attribute( "preRollSeconds", "0" ).toInt();
00815 int nEncoderId = r.attribute( "encoderId" , "0" ).toInt();
00816 QString sProfile = r.attribute( "recProfile" , "" );
00817 QString sChanName = c.attribute( "channelName" , "" );
00818 QString sDesc = "";
00819
00820 QDomText text = e.firstChild().toText();
00821 if (!text.isNull())
00822 sDesc = text.nodeValue();
00823
00824
00825
00826 int nTotalSecs = qdtNow.secsTo( recStartTs ) - nPreRollSecs;
00827
00828
00829
00830 nTotalSecs -= 60;
00831
00832 int nTotalDays = nTotalSecs / 86400;
00833 int nTotalHours = (nTotalSecs / 3600)
00834 - (nTotalDays * 24);
00835 int nTotalMins = (nTotalSecs / 60) % 60;
00836
00837 QString sTimeToStart = "in";
00838
00839 if ( nTotalDays > 1 )
00840 sTimeToStart += QString(" %1 days,").arg( nTotalDays );
00841 else if ( nTotalDays == 1 )
00842 sTimeToStart += (" 1 day,");
00843
00844 if ( nTotalHours != 1)
00845 sTimeToStart += QString(" %1 hours and").arg( nTotalHours );
00846 else if (nTotalHours == 1)
00847 sTimeToStart += " 1 hour and";
00848
00849 if ( nTotalMins != 1)
00850 sTimeToStart += QString(" %1 minutes").arg( nTotalMins );
00851 else
00852 sTimeToStart += " 1 minute";
00853
00854 if ( nTotalHours == 0 && nTotalMins == 0)
00855 sTimeToStart = "within one minute";
00856
00857 if ( nTotalSecs < 0)
00858 sTimeToStart = "soon";
00859
00860
00861
00862 os << " <a href=\"#\">";
00863 if (shortdateformat.find("ddd") == -1) {
00864
00865 os << recStartTs.addSecs(-nPreRollSecs).toString("ddd")
00866 << " ";
00867 }
00868 os << recStartTs.addSecs(-nPreRollSecs).toString(shortdateformat) << " "
00869 << recStartTs.addSecs(-nPreRollSecs).toString(timeformat) << " - ";
00870
00871 if (nEncoderId > 0)
00872 os << "Encoder " << nEncoderId << " - ";
00873
00874 os << sChanName << " - " << sTitle << "<br />"
00875 << "<span><strong>" << sTitle << "</strong> ("
00876 << startTs.toString(timeformat) << "-"
00877 << endTs.toString(timeformat) << ")<br />";
00878
00879 if ( !sSubTitle.isNull() && !sSubTitle.isEmpty())
00880 os << "<em>" << sSubTitle << "</em><br /><br />";
00881
00882 os << sDesc << "<br /><br />"
00883 << "This recording will start " << sTimeToStart
00884 << " using encoder " << nEncoderId << " with the '"
00885 << sProfile << "' profile.</span></a><hr />\r\n";
00886 }
00887 }
00888
00889 node = node.nextSibling();
00890 }
00891 os << " </div>\r\n";
00892 os << " </div>\r\n\r\n";
00893
00894 return( nNumRecordings );
00895 }
00896
00898
00900
00901 int HttpStatus::PrintJobQueue( QTextStream &os, QDomElement jobs )
00902 {
00903 QString shortdateformat = gContext->GetSetting("ShortDateFormat", "M/d");
00904 QString timeformat = gContext->GetSetting("TimeFormat", "h:mm AP");
00905
00906 if (jobs.isNull())
00907 return( 0 );
00908
00909 int nNumJobs= jobs.attribute( "count", "0" ).toInt();
00910
00911 os << " <div class=\"content\">\r\n"
00912 << " <h2>Job Queue</h2>\r\n";
00913
00914 if (nNumJobs != 0)
00915 {
00916 QString statusColor;
00917 QString jobColor;
00918 QString timeDateFormat;
00919
00920 timeDateFormat = gContext->GetSetting("DateFormat", "ddd MMMM d") +
00921 " " + gContext->GetSetting("TimeFormat", "h:mm AP");
00922
00923 os << " Jobs currently in Queue or recently ended:\r\n<br />"
00924 << " <div class=\"schedule\">\r\n";
00925
00926
00927 QDomNode node = jobs.firstChild();
00928
00929 while (!node.isNull())
00930 {
00931 QDomElement e = node.toElement();
00932
00933 if (!e.isNull())
00934 {
00935 QDomNode progNode = e.namedItem( "Program" );
00936
00937 if ((e.tagName() == "Job") && !progNode.isNull() )
00938 {
00939 QDomElement p = progNode.toElement();
00940
00941 QDomNode recNode = p.namedItem( "Recording" );
00942 QDomNode chanNode = p.namedItem( "Channel" );
00943
00944 QDomElement r = recNode.toElement();
00945 QDomElement c = chanNode.toElement();
00946
00947 int nType = e.attribute( "type" , "0" ).toInt();
00948 int nStatus = e.attribute( "status", "0" ).toInt();
00949
00950 switch( nStatus )
00951 {
00952 case JOB_ABORTED:
00953 statusColor = " class=\"jobaborted\"";
00954 jobColor = "";
00955 break;
00956
00957 case JOB_ERRORED:
00958 statusColor = " class=\"joberrored\"";
00959 jobColor = " class=\"joberrored\"";
00960 break;
00961
00962 case JOB_FINISHED:
00963 statusColor = " class=\"jobfinished\"";
00964 jobColor = " class=\"jobfinished\"";
00965 break;
00966
00967 case JOB_RUNNING:
00968 statusColor = " class=\"jobrunning\"";
00969 jobColor = " class=\"jobrunning\"";
00970 break;
00971
00972 default:
00973 statusColor = " class=\"jobqueued\"";
00974 jobColor = " class=\"jobqueued\"";
00975 break;
00976 }
00977
00978 QString sTitle = p.attribute( "title" , "" );
00979 QString sSubTitle = p.attribute( "subTitle", "" );
00980 QDateTime startTs = QDateTime::fromString( p.attribute( "startTime" ,"" ), Qt::ISODate );
00981 QDateTime endTs = QDateTime::fromString( p.attribute( "endTime" ,"" ), Qt::ISODate );
00982 QDateTime recStartTs = QDateTime::fromString( r.attribute( "recStartTs","" ), Qt::ISODate );
00983 QDateTime statusTime = QDateTime::fromString( e.attribute( "statusTime","" ), Qt::ISODate );
00984 QDateTime schedRunTime = QDateTime::fromString( e.attribute( "schedTime","" ), Qt::ISODate );
00985 QString sHostname = e.attribute( "hostname", "master" );
00986 QString sComment = "";
00987
00988 QDomText text = e.firstChild().toText();
00989 if (!text.isNull())
00990 sComment = text.nodeValue();
00991
00992 os << "<a href=\"#\">"
00993 << recStartTs.toString("ddd") << " "
00994 << recStartTs.toString(shortdateformat) << " "
00995 << recStartTs.toString(timeformat) << " - "
00996 << sTitle << " - <font" << jobColor << ">"
00997 << JobQueue::JobText( nType ) << "</font><br />"
00998 << "<span><strong>" << sTitle << "</strong> ("
00999 << startTs.toString(timeformat) << "-"
01000 << endTs.toString(timeformat) << ")<br />";
01001
01002 if ( !sSubTitle.isNull() && !sSubTitle.isEmpty())
01003 os << "<em>" << sSubTitle << "</em><br /><br />";
01004
01005 os << "Job: " << JobQueue::JobText( nType ) << "<br />";
01006
01007 if (schedRunTime > QDateTime::currentDateTime())
01008 os << "Scheduled Run Time: "
01009 << schedRunTime.toString(timeDateFormat)
01010 << "<br />";
01011
01012 os << "Status: <font" << statusColor << ">"
01013 << JobQueue::StatusText( nStatus )
01014 << "</font><br />"
01015 << "Status Time: "
01016 << statusTime.toString(timeDateFormat)
01017 << "<br />";
01018
01019 if ( nStatus != JOB_QUEUED)
01020 os << "Host: " << sHostname << "<br />";
01021
01022 if (!sComment.isNull() && !sComment.isEmpty())
01023 os << "<br />Comments:<br />" << sComment << "<br />";
01024
01025 os << "</span></a><hr />\r\n";
01026 }
01027 }
01028
01029 node = node.nextSibling();
01030 }
01031 os << " </div>\r\n";
01032 }
01033 else
01034 os << " Job Queue is currently empty.\r\n\r\n";
01035
01036 os << " </div>\r\n\r\n ";
01037
01038 return( nNumJobs );
01039
01040 }
01041
01043
01045
01046 int HttpStatus::PrintMachineInfo( QTextStream &os, QDomElement info )
01047 {
01048 QString shortdateformat = gContext->GetSetting("ShortDateFormat", "M/d");
01049 QString timeformat = gContext->GetSetting("TimeFormat", "h:mm AP");
01050 QString sRep;
01051
01052 if (info.isNull())
01053 return( 0 );
01054
01055 os << "<div class=\"content\">\r\n"
01056 << " <h2>Machine information</h2>\r\n";
01057
01058
01059
01060 QDomNode node = info.namedItem( "Load" );
01061
01062 if (!node.isNull())
01063 {
01064 QDomElement e = node.toElement();
01065
01066 if (!e.isNull())
01067 {
01068 double dAvg1 = e.attribute( "avg1" , "0" ).toDouble();
01069 double dAvg2 = e.attribute( "avg2" , "0" ).toDouble();
01070 double dAvg3 = e.attribute( "avg3" , "0" ).toDouble();
01071
01072 os << " <div class=\"loadstatus\">\r\n"
01073 << " This machine's load average:"
01074 << "\r\n <ul>\r\n <li>"
01075 << "1 Minute: " << dAvg1 << "</li>\r\n"
01076 << " <li>5 Minutes: " << dAvg2 << "</li>\r\n"
01077 << " <li>15 Minutes: " << dAvg3
01078 << "</li>\r\n </ul>\r\n"
01079 << " </div>\r\n";
01080 }
01081 }
01082
01083
01084
01085 node = info.namedItem( "Storage" );
01086
01087 if (!node.isNull())
01088 {
01089 QDomElement e = node.toElement();
01090
01091 if (!e.isNull())
01092 {
01093 QString ids = e.attribute("fsids", "");
01094 QStringList tokens = QStringList::split(",", ids);
01095
01096 os << " Disk Usage:<br />\r\n";
01097 os << " <ul>\r\n";
01098
01099 for (unsigned int i = 0; i < tokens.size(); i++)
01100 {
01101
01102 if ((tokens.size() == 2) && (i == 0) &&
01103 (tokens[i] != "total") &&
01104 (tokens[i+1] == "total"))
01105 i++;
01106
01107 int nFree =
01108 e.attribute("drive_" + tokens[i] + "_free" , "0" ).toInt();
01109 int nTotal =
01110 e.attribute("drive_" + tokens[i] + "_total", "0" ).toInt();
01111 int nUsed =
01112 e.attribute("drive_" + tokens[i] + "_used" , "0" ).toInt();
01113 QString nDirs =
01114 e.attribute("drive_" + tokens[i] + "_dirs" , "" );
01115
01116 nDirs.replace(QRegExp(","), ", ");
01117
01118 if (tokens[i] == "total")
01119 {
01120 os << " <li>Total Disk Space:\r\n"
01121 << " <ul>\r\n";
01122 }
01123 else
01124 {
01125 os << " <li>MythTV Drive #" << tokens[i] << ":"
01126 << "\r\n<br />\r\n";
01127
01128 if (nDirs.contains(","))
01129 os << " <ul><li>Directories: ";
01130 else
01131 os << " <ul><li>Directory: ";
01132
01133 os << nDirs << "</li>\r\n";
01134 }
01135
01136 QLocale c(QLocale::C);
01137 os << " <li>Total Space: ";
01138 sRep = c.toString(nTotal) + " MB ";
01139 os << sRep << "</li>\r\n";
01140
01141 os << " <li>Space Used: ";
01142 sRep = c.toString(nUsed) + " MB ";
01143 os << sRep << "</li>\r\n";
01144
01145 os << " <li>Space Free: ";
01146 sRep = c.toString(nFree) + " MB ";
01147 os << sRep << "</li>\r\n";
01148
01149 os << " </ul>\r\n"
01150 << " </li>\r\n";
01151 }
01152 os << " </ul>\r\n";
01153 }
01154 }
01155
01156
01157
01158 node = info.namedItem( "Guide" );
01159
01160 if (!node.isNull())
01161 {
01162 QDomElement e = node.toElement();
01163
01164 if (!e.isNull())
01165 {
01166 QString datetimefmt = "yyyy-MM-dd hh:mm";
01167 int nDays = e.attribute( "guideDays", "0" ).toInt();
01168 QString sStart = e.attribute( "start" , "" );
01169 QString sEnd = e.attribute( "end" , "" );
01170 QString sStatus = e.attribute( "status" , "" );
01171 QDateTime next = QDateTime::fromString( e.attribute( "next" , "" ), Qt::ISODate);
01172 QString sNext = next.isNull() ? "" : next.toString(datetimefmt);
01173 QString sMsg = "";
01174
01175 QDateTime thru = QDateTime::fromString( e.attribute( "guideThru", "" ), Qt::ISODate);
01176
01177 QDomText text = e.firstChild().toText();
01178
01179 if (!text.isNull())
01180 sMsg = text.nodeValue();
01181
01182 os << " Last mythfilldatabase run started on " << sStart
01183 << " and ";
01184
01185 if (sEnd < sStart)
01186 os << "is ";
01187 else
01188 os << "ended on " << sEnd << ". ";
01189
01190 os << sStatus << "<br />\r\n";
01191
01192 if (!next.isNull() && sNext >= sStart)
01193 {
01194 os << " Suggested next mythfilldatabase run: "
01195 << sNext << ".<br />\r\n";
01196 }
01197
01198 if (!thru.isNull())
01199 {
01200 os << " There's guide data until "
01201 << QDateTime( thru ).toString(datetimefmt);
01202
01203 if (nDays > 0)
01204 os << " (" << nDays << " day" << (nDays == 1 ? "" : "s" ) << ")";
01205
01206 os << ".";
01207
01208 if (nDays <= 3)
01209 os << " <strong>WARNING</strong>: is mythfilldatabase running?";
01210 }
01211 else
01212 os << " There's <strong>no guide data</strong> available! "
01213 << "Have you run mythfilldatabase?";
01214
01215 if (!sMsg.isNull() && !sMsg.isEmpty())
01216 os << "<br />\r\n DataDirect Status: " << sMsg;
01217 }
01218 }
01219 os << "\r\n </div>\r\n";
01220
01221 return( 1 );
01222 }
01223
01224 int HttpStatus::PrintMiscellaneousInfo( QTextStream &os, QDomElement info )
01225 {
01226 if (info.isNull())
01227 return( 0 );
01228
01229
01230
01231 QDomNodeList nodes = info.elementsByTagName("Information");
01232 uint count = nodes.count();
01233 if (count > 0)
01234 {
01235 QString display, linebreak;
01236
01237 os << "<div class=\"content\">\r\n"
01238 << " <h2>Miscellaneous</h2>\r\n";
01239 for (unsigned int i = 0; i < count; i++)
01240 {
01241 QDomNode node = nodes.item(i);
01242 if (node.isNull())
01243 continue;
01244
01245 QDomElement e = node.toElement();
01246 if (e.isNull())
01247 continue;
01248
01249 display = e.attribute("display", "");
01250
01251
01252
01253 if (display.isEmpty())
01254 continue;
01255
01256
01257
01258 if ((display.contains("<p>", false) > 0) ||
01259 (display.contains("<br", false) > 0))
01260 linebreak = "\r\n";
01261 else
01262 linebreak = "<br />\r\n";
01263
01264 os << " " << display << linebreak;
01265 }
01266 os << "</div>\r\n";
01267 }
01268
01269 return( 1 );
01270 }
01271
01272