00001
00002
00003
00004
00005
00006
00007
00008
00010
00011 #include "mythxml.h"
00012 #include "backendutil.h"
00013
00014 #include "libmyth/mythcontext.h"
00015 #include "libmyth/util.h"
00016 #include "libmyth/mythdbcon.h"
00017
00018 #include "previewgenerator.h"
00019 #include "backendutil.h"
00020
00021 #include <qtextstream.h>
00022 #include <qdir.h>
00023 #include <qfile.h>
00024 #include <qregexp.h>
00025 #include <qbuffer.h>
00026 #include <math.h>
00027
00028 #include "../../config.h"
00029
00031
00033
00034 MythXML::MythXML( UPnpDevice *pDevice ) : Eventing( "MythXML", "MYTHTV_Event" )
00035 {
00036 m_pEncoders = &tvList;
00037 m_pSched = sched;
00038 m_pExpirer = expirer;
00039
00040 m_nPreRollSeconds = gContext->GetNumSetting("RecordPreRoll", 0);
00041
00042
00043
00044
00045
00046 QString sUPnpDescPath = UPnp::g_pConfig->GetValue( "UPnP/DescXmlPath",
00047 m_sSharePath );
00048
00049 m_sServiceDescFileName = sUPnpDescPath + "MXML_scpd.xml";
00050 m_sControlUrl = "/Myth";
00051
00052
00053
00054 RegisterService( pDevice );
00055 }
00056
00058
00060
00061 MythXML::~MythXML()
00062 {
00063 }
00064
00066
00068
00069 MythXMLMethod MythXML::GetMethod( const QString &sURI )
00070 {
00071 if (sURI == "GetServDesc" ) return MXML_GetServiceDescription;
00072
00073 if (sURI == "GetProgramGuide" ) return MXML_GetProgramGuide;
00074 if (sURI == "GetProgramDetails" ) return MXML_GetProgramDetails;
00075
00076 if (sURI == "GetHosts" ) return MXML_GetHosts;
00077 if (sURI == "GetKeys" ) return MXML_GetKeys;
00078 if (sURI == "GetSetting" ) return MXML_GetSetting;
00079 if (sURI == "PutSetting" ) return MXML_PutSetting;
00080
00081 if (sURI == "GetChannelIcon" ) return MXML_GetChannelIcon;
00082 if (sURI == "GetAlbumArt" ) return MXML_GetAlbumArt;
00083 if (sURI == "GetRecorded" ) return MXML_GetRecorded;
00084 if (sURI == "GetExpiring" ) return MXML_GetExpiring;
00085 if (sURI == "GetPreviewImage" ) return MXML_GetPreviewImage;
00086 if (sURI == "GetRecording" ) return MXML_GetRecording;
00087 if (sURI == "GetVideo" ) return MXML_GetVideo;
00088 if (sURI == "GetMusic" ) return MXML_GetMusic;
00089 if (sURI == "GetConnectionInfo" ) return MXML_GetConnectionInfo;
00090 if (sURI == "GetVideoArt" ) return MXML_GetVideoArt;
00091
00092 return( MXML_Unknown );
00093 }
00094
00096
00098
00099 bool MythXML::ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest )
00100 {
00101 try
00102 {
00103 if (pRequest)
00104 {
00105 if (pRequest->m_sBaseUrl == "/Myth/GetVideo")
00106 {
00107 pRequest->m_sBaseUrl = m_sControlUrl;
00108 pRequest->m_sMethod = "GetVideo";
00109 }
00110 else if (pRequest->m_sBaseUrl == "/Myth/GetVideoArt")
00111 {
00112 pRequest->m_sBaseUrl = m_sControlUrl;
00113 pRequest->m_sMethod = "GetVideoArt";
00114 }
00115
00116 if (pRequest->m_sBaseUrl != m_sControlUrl)
00117 return( false );
00118
00119 VERBOSE(VB_UPNP, QString("MythXML::ProcessRequest: %1 : %2")
00120 .arg(pRequest->m_sMethod)
00121 .arg(pRequest->m_sRawRequest));
00122
00123 switch( GetMethod( pRequest->m_sMethod ))
00124 {
00125 case MXML_GetServiceDescription:
00126 pRequest->FormatFileResponse( m_sServiceDescFileName );
00127 return true;
00128
00129 case MXML_GetProgramGuide :
00130 GetProgramGuide( pRequest );
00131 return true;
00132 case MXML_GetProgramDetails :
00133 GetProgramDetails( pRequest );
00134 return true;
00135
00136 case MXML_GetHosts :
00137 GetHosts ( pRequest );
00138 return true;
00139 case MXML_GetKeys :
00140 GetKeys ( pRequest );
00141 return true;
00142 case MXML_GetSetting :
00143 GetSetting ( pRequest );
00144 return true;
00145 case MXML_PutSetting :
00146 PutSetting ( pRequest );
00147 return true;
00148
00149 case MXML_GetChannelIcon :
00150 GetChannelIcon ( pRequest );
00151 return true;
00152 case MXML_GetRecorded :
00153 GetRecorded ( pRequest );
00154 return true;
00155 case MXML_GetExpiring :
00156 GetExpiring ( pRequest );
00157 return true;
00158 case MXML_GetPreviewImage :
00159 GetPreviewImage( pRequest );
00160 return true;
00161
00162 case MXML_GetRecording :
00163 GetRecording ( pThread, pRequest );
00164 return true;
00165 case MXML_GetMusic :
00166 GetMusic ( pThread, pRequest );
00167 return true;
00168 case MXML_GetVideo :
00169 GetVideo ( pThread, pRequest );
00170 return true;
00171
00172 case MXML_GetConnectionInfo :
00173 GetConnectionInfo( pRequest );
00174 return true;
00175 case MXML_GetAlbumArt :
00176 GetAlbumArt ( pRequest );
00177 return true;
00178 case MXML_GetVideoArt :
00179 GetVideoArt ( pRequest );
00180 return true;
00181
00182
00183 default:
00184 {
00185 UPnp::FormatErrorResponse( pRequest, UPnPResult_InvalidAction );
00186
00187 return true;
00188 }
00189 }
00190 }
00191 }
00192 catch( ... )
00193 {
00194 VERBOSE( VB_IMPORTANT, "MythXML::ProcessRequest() - Unexpected "
00195 "Exception" );
00196 }
00197
00198 return( false );
00199 }
00200
00201
00202
00203
00204
00206
00208
00209 void MythXML::GetHosts( HTTPRequest *pRequest )
00210 {
00211 pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", "
00212 "max-age = 5000";
00213
00214 MSqlQuery query(MSqlQuery::InitCon());
00215
00216 if (query.isConnected())
00217 {
00218 query.prepare("SELECT DISTINCTROW hostname "
00219 "FROM settings WHERE (not isNull( hostname ));" );
00220 query.exec();
00221
00222 if (query.isActive() && query.size() > 0)
00223 {
00224 QString sHosts;
00225 QTextStream os( &sHosts, IO_WriteOnly );
00226
00227 while(query.next())
00228 {
00229 QString sHost = query.value(0).toString();
00230
00231 os << "<Host>"
00232 << HTTPRequest::Encode( sHost )
00233 << "</Host>";
00234 }
00235
00236 NameValueList list;
00237
00238 list.append( new NameValue( "Count", query.size() ));
00239 list.append( new NameValue( "Hosts", sHosts ));
00240
00241 pRequest->FormatActionResponse( &list );
00242
00243 }
00244 }
00245 else
00246 UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionFailed,
00247 "Database not open while trying to load list of hosts" );
00248 }
00249
00251
00253
00254 void MythXML::GetKeys( HTTPRequest *pRequest )
00255 {
00256 pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", "
00257 "max-age = 5000";
00258
00259 MSqlQuery query(MSqlQuery::InitCon());
00260
00261 if (query.isConnected())
00262 {
00263 query.prepare("SELECT DISTINCTROW value FROM settings;" );
00264 query.exec();
00265
00266 if (query.isActive() && query.size() > 0)
00267 {
00268 QString sKeys;
00269 QTextStream os( &sKeys, IO_WriteOnly );
00270
00271 while(query.next())
00272 {
00273 QString sKey = query.value(0).toString();
00274
00275 os << "<Key>"
00276 << HTTPRequest::Encode( sKey )
00277 << "</Key>";
00278 }
00279
00280 NameValueList list;
00281
00282 list.append( new NameValue( "Count", query.size() ));
00283 list.append( new NameValue( "Keys" , sKeys ));
00284
00285 pRequest->FormatActionResponse( &list );
00286 }
00287 }
00288 else
00289 UPnp::FormatErrorResponse( pRequest,
00290 UPnPResult_ActionFailed,
00291 QString("Database not open while trying to "
00292 "load setting: %1")
00293 .arg( pRequest->m_mapParams[ "Key" ] ));
00294 }
00295
00297
00299
00300 void MythXML::GetSetting( HTTPRequest *pRequest )
00301 {
00302 pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", "
00303 "max-age = 5000";
00304
00305 QString sKey = pRequest->m_mapParams[ "Key" ];
00306 QString sHostName = pRequest->m_mapParams[ "HostName" ];
00307 QString sValue;
00308
00309 MSqlQuery query(MSqlQuery::InitCon());
00310
00311 if (query.isConnected())
00312 {
00313 NameValueList list;
00314
00315
00316
00317 QString sXml;
00318 QTextStream os( &sXml, IO_WriteOnly );
00319
00320 if (sKey.length() > 0)
00321 {
00322 query.prepare("SELECT data, hostname from settings "
00323 "WHERE value = :KEY AND "
00324 "(hostname = :HOSTNAME OR hostname IS NULL) "
00325 "ORDER BY hostname DESC;" );
00326
00327 query.bindValue(":KEY" , sKey );
00328 query.bindValue(":HOSTNAME", sHostName );
00329 query.exec();
00330
00331 if (query.isActive() && query.size() > 0)
00332 {
00333 query.next();
00334
00335 if ( (sHostName.length() == 0) ||
00336 ((sHostName.length() > 0) &&
00337 (sHostName == query.value(1).toString())))
00338 {
00339 sValue = query.value(0).toString();
00340 sHostName = query.value(1).toString();
00341
00342 os << "<Value key='"
00343 << HTTPRequest::Encode( sKey )
00344 << "'>"
00345 << HTTPRequest::Encode( sValue )
00346 << "</Value>";
00347
00348 list.append( new NameValue( "Count" , 1 ));
00349 list.append( new NameValue( "HostName", sHostName ));
00350 list.append( new NameValue( "Values", sXml ));
00351
00352 pRequest->FormatActionResponse( &list );
00353
00354 return;
00355 }
00356 }
00357
00358 }
00359 else
00360 {
00361
00362 if (sHostName.length() == 0)
00363 {
00364 query.prepare("SELECT value, data FROM settings "
00365 "WHERE (hostname IS NULL)" );
00366 }
00367 else
00368 {
00369 query.prepare("SELECT value, data FROM settings "
00370 "WHERE (hostname = :HOSTNAME)" );
00371 query.bindValue(":HOSTNAME", sHostName );
00372 }
00373
00374 query.exec();
00375
00376 if (query.isActive() && query.size() > 0)
00377 {
00378
00379 while(query.next())
00380 {
00381 sKey = query.value(0).toString();
00382 sValue = query.value(1).toString();
00383
00384 os << "<Value key='"
00385 << HTTPRequest::Encode( sKey )
00386 << "'>"
00387 << HTTPRequest::Encode( sValue )
00388 << "</Value>";
00389 }
00390
00391
00392 list.append( new NameValue( "Count" , query.size()));
00393 list.append( new NameValue( "HostName", sHostName ));
00394 list.append( new NameValue( "Values" , sXml ));
00395
00396 pRequest->FormatActionResponse( &list );
00397
00398 return;
00399 }
00400
00401 }
00402
00403
00404
00405 os << "<Value key='"
00406 << HTTPRequest::Encode( sKey )
00407 << "'>"
00408 << HTTPRequest::Encode( pRequest->m_mapParams[ "Default" ] )
00409 << "</Value>";
00410
00411
00412 list.append( new NameValue( "Count" , 1 ));
00413 list.append( new NameValue( "HostName", sHostName));
00414 list.append( new NameValue( "Values", sXml ));
00415
00416 pRequest->FormatActionResponse( &list );
00417 }
00418 else
00419 UPnp::FormatErrorResponse( pRequest,
00420 UPnPResult_ActionFailed,
00421 QString("Database not open while trying to "
00422 "load setting: %1")
00423 .arg( pRequest->m_mapParams[ "Key" ] ));
00424 }
00425
00427
00429
00430 void MythXML::PutSetting( HTTPRequest *pRequest )
00431 {
00432 pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", "
00433 "max-age = 5000";
00434
00435 QString sHostName = pRequest->m_mapParams[ "HostName" ];
00436 QString sKey = pRequest->m_mapParams[ "Key" ];
00437 QString sValue = pRequest->m_mapParams[ "Value" ];
00438
00439 if (sKey.length() > 0)
00440 {
00441 NameValueList list;
00442
00443 if ( gContext->SaveSettingOnHost( sKey, sValue, sHostName ) )
00444 list.append( new NameValue( "Result", "True" ));
00445 else
00446 list.append( new NameValue( "Result", "False" ));
00447
00448 pRequest->FormatActionResponse( &list );
00449 }
00450 else
00451 UPnp::FormatErrorResponse( pRequest, UPnPResult_InvalidArgs,
00452 "Key Required" );
00453 }
00454
00456
00458
00459 void MythXML::GetProgramGuide( HTTPRequest *pRequest )
00460 {
00461 pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", "
00462 "max-age = 5000";
00463
00464 QString sStartTime = pRequest->m_mapParams[ "StartTime" ];
00465 QString sEndTime = pRequest->m_mapParams[ "EndTime" ];
00466 int iNumOfChannels = pRequest->m_mapParams[ "NumOfChannels"].toInt();
00467 int iStartChanId = pRequest->m_mapParams[ "StartChanId" ].toInt();
00468 bool bDetails = pRequest->m_mapParams[ "Details" ].toInt();
00469 int iEndChanId = iStartChanId;
00470
00471 QDateTime dtStart = QDateTime::fromString( sStartTime, Qt::ISODate );
00472 QDateTime dtEnd = QDateTime::fromString( sEndTime , Qt::ISODate );
00473
00474 if (!dtStart.isValid())
00475 {
00476 UPnp::FormatErrorResponse( pRequest, UPnPResult_ArgumentValueInvalid,
00477 "StartTime is invalid" );
00478 return;
00479 }
00480
00481 if (!dtEnd.isValid())
00482 {
00483 UPnp::FormatErrorResponse( pRequest, UPnPResult_ArgumentValueInvalid,
00484 "EndTime is invalid" );
00485 return;
00486 }
00487
00488 if (dtEnd < dtStart)
00489 {
00490 UPnp::FormatErrorResponse( pRequest, UPnPResult_ArgumentValueInvalid,
00491 "EndTime is before StartTime");
00492 return;
00493 }
00494
00495 if (iNumOfChannels == 0)
00496 iNumOfChannels = 1;
00497
00498 if (iNumOfChannels == -1)
00499 iNumOfChannels = SHRT_MAX;
00500
00501
00502
00503 MSqlQuery query(MSqlQuery::InitCon());
00504
00505 query.prepare( "SELECT chanid FROM channel WHERE (chanid >= :STARTCHANID )"
00506 " ORDER BY chanid LIMIT :NUMCHAN" );
00507
00508 query.bindValue(":STARTCHANID", iStartChanId );
00509 query.bindValue(":NUMCHAN" , iNumOfChannels );
00510
00511 if (!query.exec() || !query.isActive())
00512 MythContext::DBError("Select ChanId", query);
00513
00514 query.first(); iStartChanId = query.value(0).toInt();
00515 query.last(); iEndChanId = query.value(0).toInt();
00516
00517
00518
00519 MSqlBindings bindings;
00520 QString sSQL = "WHERE program.chanid >= :StartChanId "
00521 "AND program.chanid <= :EndChanId "
00522 "AND program.starttime >= :StartDate "
00523 "AND program.endtime <= :EndDate "
00524 "GROUP BY program.starttime, channel.channum, "
00525 "channel.callsign, program.title "
00526 "ORDER BY program.chanid ";
00527
00528 bindings[":StartChanId"] = iStartChanId;
00529 bindings[":EndChanId" ] = iEndChanId;
00530 bindings[":StartDate" ] = dtStart.toString( Qt::ISODate );
00531 bindings[":EndDate" ] = dtEnd.toString( Qt::ISODate );
00532
00533
00534
00535 RecList recList;
00536 ProgramList schedList;
00537
00538 if (m_pSched)
00539 m_pSched->getAllPending( &recList);
00540
00541
00542
00543
00544
00545
00546 for (RecIter itRecList = recList.begin();
00547 itRecList != recList.end(); itRecList++)
00548 {
00549 schedList.append( *itRecList );
00550 }
00551
00552
00553
00554 ProgramList progList;
00555
00556 progList.FromProgram( sSQL, bindings, schedList );
00557
00558
00559
00560 QDomDocument doc;
00561
00562 QDomElement channels = doc.createElement("Channels");
00563 doc.appendChild( channels );
00564
00565 int iChanCount = 0;
00566 QDomElement channel;
00567 QString sCurChanId = "";
00568 ProgramInfo *pInfo = progList.first();
00569
00570 while (pInfo != NULL)
00571 {
00572 if ( sCurChanId != pInfo->chanid )
00573 {
00574 iChanCount++;
00575
00576 sCurChanId = pInfo->chanid;
00577
00578
00579
00580 channel = doc.createElement( "Channel" );
00581 channels.appendChild( channel );
00582
00583 FillChannelInfo( channel, pInfo, bDetails );
00584 }
00585
00586 FillProgramInfo( &doc, channel, pInfo, false, bDetails );
00587
00588 pInfo = progList.next();
00589
00590 }
00591
00592
00593
00594 NameValueList list;
00595
00596 list.append( new NameValue( "StartTime" , sStartTime ));
00597 list.append( new NameValue( "EndTime" , sEndTime ));
00598 list.append( new NameValue( "StartChanId" , iStartChanId ));
00599 list.append( new NameValue( "EndChanId" , iEndChanId ));
00600 list.append( new NameValue( "NumOfChannels", iChanCount ));
00601 list.append( new NameValue( "Details" , bDetails ));
00602
00603 list.append( new NameValue( "Count" , (int)progList.count() ));
00604 list.append( new NameValue( "AsOf" , QDateTime::currentDateTime()
00605 .toString( Qt::ISODate )));
00606 list.append( new NameValue( "Version" , MYTH_BINARY_VERSION ));
00607 list.append( new NameValue( "ProtoVer" , MYTH_PROTO_VERSION ));
00608 list.append( new NameValue( "ProgramGuide" , doc.toString() ));
00609
00610 pRequest->FormatActionResponse( &list );
00611
00612 }
00613
00614
00616
00618
00619 void MythXML::GetProgramDetails( HTTPRequest *pRequest )
00620 {
00621 pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", "
00622 "max-age = 5000";
00623
00624 QString sStartTime = pRequest->m_mapParams[ "StartTime" ];
00625 QString sChanId = pRequest->m_mapParams[ "ChanId" ];
00626
00627 QDateTime dtStart = QDateTime::fromString( sStartTime, Qt::ISODate );
00628
00629 if (!dtStart.isValid())
00630 {
00631 UPnp::FormatErrorResponse( pRequest, UPnPResult_ArgumentValueInvalid,
00632 "StartTime is invalid" );
00633 return;
00634 }
00635
00636
00637
00638
00639
00640
00641
00642 MSqlBindings bindings;
00643 QString sSQL = "WHERE program.chanid = :ChanId "
00644 "AND program.starttime = :StartTime ";
00645
00646 bindings[":ChanId" ] = sChanId;
00647 bindings[":StartTime"] = dtStart.toString( Qt::ISODate );
00648
00649
00650
00651 RecList recList;
00652 ProgramList schedList;
00653
00654 if (m_pSched)
00655 m_pSched->getAllPending( &recList);
00656
00657
00658
00659
00660
00661
00662 for (RecIter itRecList = recList.begin();
00663 itRecList != recList.end(); itRecList++)
00664 {
00665 schedList.append( *itRecList );
00666 }
00667
00668
00669
00670 ProgramList progList;
00671
00672 progList.FromProgram( sSQL, bindings, schedList );
00673
00674 ProgramInfo *pInfo = progList.first();
00675
00676 if (pInfo==NULL)
00677 {
00678 UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionFailed,
00679 "Error Reading Program Info" );
00680 return;
00681 }
00682
00683
00684
00685 QDomDocument doc;
00686
00687
00688
00689
00690 FillProgramInfo( &doc, doc, pInfo, true );
00691
00692
00693
00694 NameValueList list;
00695
00696 list.append( new NameValue( "StartTime" , sStartTime ));
00697 list.append( new NameValue( "ChanId" , sChanId ));
00698
00699 list.append( new NameValue( "Count" , 1 ));
00700 list.append( new NameValue( "AsOf" , QDateTime::currentDateTime()
00701 .toString( Qt::ISODate )));
00702 list.append( new NameValue( "Version" , MYTH_BINARY_VERSION ));
00703 list.append( new NameValue( "ProtoVer" , MYTH_PROTO_VERSION ));
00704
00705 list.append( new NameValue( "ProgramDetails" , doc.toString() ));
00706
00707 pRequest->FormatActionResponse( &list );
00708 }
00709
00711
00713
00714 void MythXML::GetChannelIcon( HTTPRequest *pRequest )
00715 {
00716 bool bDefaultPixmap = false;
00717
00718 pRequest->m_eResponseType = ResponseTypeFile;
00719
00720 int iChanId = pRequest->m_mapParams[ "ChanId" ].toInt();
00721
00722
00723
00724 int nWidth = pRequest->m_mapParams[ "Width" ].toInt();
00725 int nHeight = pRequest->m_mapParams[ "Height" ].toInt();
00726
00727
00728
00729 MSqlQuery query(MSqlQuery::InitCon());
00730
00731 query.prepare( "SELECT icon FROM channel WHERE (chanid = :CHANID )" );
00732 query.bindValue(":CHANID", iChanId );
00733
00734 if (!query.exec() || !query.isActive())
00735 MythContext::DBError("Select ChanId", query);
00736
00737 if (query.size() > 0)
00738 {
00739 query.first();
00740
00741 pRequest->m_sFileName = query.value(0).toString();
00742 }
00743
00744 if ((nWidth == 0) && (nHeight == 0))
00745 {
00746 bDefaultPixmap = true;
00747 }
00748
00749 QString sFileName;
00750
00751 if (bDefaultPixmap)
00752 {
00753 return;
00754 }
00755 else
00756 sFileName = QString( "%1.%2x%3.png" )
00757 .arg( pRequest->m_sFileName )
00758 .arg( nWidth )
00759 .arg( nHeight );
00760
00761
00762
00763
00764
00765 if (QFile::exists( sFileName ))
00766 {
00767 pRequest->m_eResponseType = ResponseTypeFile;
00768 pRequest->m_nResponseStatus = 200;
00769 pRequest->m_sFileName = sFileName;
00770 return;
00771 }
00772
00773 float fAspect = 0.0;
00774
00775 QImage *pImage = new QImage(pRequest->m_sFileName);
00776
00777 if (!pImage)
00778 return;
00779
00780 if (fAspect <= 0)
00781 fAspect = (float)(pImage->width()) / pImage->height();
00782
00783 if (fAspect == 0)
00784 {
00785 delete pImage;
00786 return;
00787 }
00788
00789 if ( nWidth == 0 )
00790 nWidth = (int)rint(nHeight * fAspect);
00791
00792 if ( nHeight == 0 )
00793 nHeight = (int)rint(nWidth / fAspect);
00794
00795 QImage img = pImage->smoothScale( nWidth, nHeight);
00796
00797 img.save( sFileName.ascii(), "PNG" );
00798
00799 delete pImage;
00800
00801 pRequest->m_sFileName = sFileName;
00802 }
00803
00805
00807
00808 void MythXML::GetVideoArt( HTTPRequest *pRequest )
00809 {
00810 bool bDefaultPixmap = false;
00811
00812 pRequest->m_eResponseType = ResponseTypeFile;
00813
00814 QString sId = pRequest->m_mapParams[ "Id" ];
00815
00816 if (sId == 0)
00817 {
00818 QStringList idPath = QStringList::split( "/", pRequest->m_sRawRequest );
00819
00820 idPath = QStringList::split( " ", idPath[idPath.count() - 2] );
00821 idPath = QStringList::split( "?", idPath[0] );
00822
00823 sId = idPath[0];
00824
00825 if (sId.startsWith("Id"))
00826 sId = sId.right(sId.length() - 2);
00827 else
00828 return;
00829
00830 pRequest->m_mapParams[ "Id" ] = sId;
00831
00832 }
00833
00834 VERBOSE(VB_UPNP, QString("GetVideoArt ID = %1").arg(sId));
00835
00836
00837
00838 MSqlQuery query(MSqlQuery::InitCon());
00839
00840 query.prepare("SELECT coverart FROM upnpmedia WHERE intid = :ITEMID");
00841 query.bindValue(":ITEMID", sId);
00842
00843 if (!query.exec() || !query.isActive())
00844 MythContext::DBError("GetVideoArt ", query);
00845
00846 QString sFileName;
00847
00848 if (query.size() > 0)
00849 {
00850 query.first();
00851
00852 sFileName = query.value(0).toString();
00853 }
00854 else
00855 return;
00856
00857 if (bDefaultPixmap)
00858 {
00859 return;
00860 }
00861
00862
00863
00864
00865
00866 if (QFile::exists( sFileName ))
00867 {
00868 pRequest->m_eResponseType = ResponseTypeFile;
00869 pRequest->m_nResponseStatus = 200;
00870 pRequest->m_sFileName = sFileName;
00871 return;
00872 }
00873
00874 }
00875
00876 void MythXML::GetAlbumArt( HTTPRequest *pRequest )
00877 {
00878 bool bDefaultPixmap = false;
00879
00880 pRequest->m_eResponseType = ResponseTypeFile;
00881
00882 QString sId = pRequest->m_mapParams[ "Id" ];
00883
00884 if (sId == 0)
00885 {
00886 QStringList idPath = QStringList::split( "/", pRequest->m_sRawRequest );
00887
00888 idPath = QStringList::split( " ", idPath[idPath.count() - 2] );
00889 idPath = QStringList::split( "?", idPath[0] );
00890
00891 sId = idPath[0];
00892
00893 if (sId.startsWith("Id"))
00894 sId = sId.right(sId.length() - 2);
00895 else
00896 return;
00897
00898 pRequest->m_mapParams[ "Id" ] = sId;
00899
00900 }
00901
00902
00903
00904 int nWidth = pRequest->m_mapParams[ "Width" ].toInt();
00905 int nHeight = pRequest->m_mapParams[ "Height" ].toInt();
00906
00907
00908
00909 MSqlQuery query(MSqlQuery::InitCon());
00910 query.prepare("SELECT CONCAT_WS('/', music_directories.path, "
00911 "music_albumart.filename) FROM music_albumart "
00912 "LEFT JOIN music_directories ON "
00913 "music_directories.directory_id=music_albumart.directory_id "
00914 "WHERE music_albumart.albumart_id = :ARTID;");
00915 query.bindValue(":ARTID", sId );
00916
00917 if (!query.exec() || !query.isActive())
00918 MythContext::DBError("Select ArtId", query);
00919
00920 QString musicbasepath = gContext->GetSetting("MusicLocation", "");
00921
00922 if (query.size() > 0)
00923 {
00924 query.first();
00925
00926 pRequest->m_sFileName = QString( "%1/%2" )
00927 .arg( musicbasepath )
00928 .arg( QString::fromUtf8(query.value(0).toString()) );
00929 }
00930
00931 if ((nWidth == 0) && (nHeight == 0))
00932 {
00933 bDefaultPixmap = true;
00934 }
00935
00936 QString sFileName;
00937
00938 if (bDefaultPixmap)
00939 {
00940 return;
00941 }
00942 else
00943 sFileName = QString( "%1.%2x%3.png" )
00944 .arg( pRequest->m_sFileName )
00945 .arg( nWidth )
00946 .arg( nHeight );
00947
00948
00949
00950
00951
00952 if (QFile::exists( sFileName ))
00953 {
00954 pRequest->m_eResponseType = ResponseTypeFile;
00955 pRequest->m_nResponseStatus = 200;
00956 pRequest->m_sFileName = sFileName;
00957 return;
00958 }
00959
00960
00961
00962
00963
00964 float fAspect = 0.0;
00965
00966 QImage *pImage = new QImage(pRequest->m_sFileName);
00967
00968 if (!pImage)
00969 return;
00970
00971 if (fAspect <= 0)
00972 fAspect = (float)(pImage->width()) / pImage->height();
00973
00974 if ( nWidth == 0 )
00975 nWidth = (int)rint(nHeight * fAspect);
00976
00977 if ( nHeight == 0 )
00978 nHeight = (int)rint(nWidth / fAspect);
00979
00980 QImage img = pImage->smoothScale( nWidth, nHeight);
00981
00982 img.save( sFileName.ascii(), "PNG" );
00983
00984 delete pImage;
00985
00986 pRequest->m_sFileName = sFileName;
00987 }
00988
00990
00992
00993 void MythXML::GetRecorded( HTTPRequest *pRequest )
00994 {
00995 bool bDescending = pRequest->m_mapParams[ "Descending" ].toInt();
00996
00997
00998
00999 RecList recList;
01000 ProgramList schedList;
01001
01002 if (m_pSched)
01003 m_pSched->getAllPending( &recList);
01004
01005
01006
01007
01008
01009
01010 for (RecIter itRecList = recList.begin();
01011 itRecList != recList.end(); itRecList++)
01012 {
01013 schedList.append( *itRecList );
01014 }
01015
01016
01017
01018 ProgramList progList;
01019
01020 progList.FromRecorded( bDescending, &schedList );
01021
01022
01023
01024 QDomDocument doc;
01025
01026 QDomElement root = doc.createElement("Programs");
01027 doc.appendChild(root);
01028
01029 ProgramInfo *pInfo = progList.first();
01030
01031 while (pInfo != NULL)
01032 {
01033 FillProgramInfo( &doc, root, pInfo, true );
01034
01035 pInfo = progList.next();
01036 }
01037
01038
01039
01040 NameValueList list;
01041
01042 list.append( new NameValue( "Count" , (int)progList.count()));
01043 list.append( new NameValue( "AsOf" , QDateTime::currentDateTime()
01044 .toString( Qt::ISODate )));
01045 list.append( new NameValue( "Version" , MYTH_BINARY_VERSION ));
01046 list.append( new NameValue( "ProtoVer" , MYTH_PROTO_VERSION ));
01047
01048 list.append( new NameValue( "Recorded" , doc.toString() ));
01049
01050 pRequest->FormatActionResponse( &list );
01051
01052 }
01053
01055
01057
01058 void MythXML::GetExpiring( HTTPRequest *pRequest )
01059 {
01060 pginfolist_t infoList;
01061
01062 m_pExpirer->GetAllExpiring( infoList );
01063
01064
01065
01066 QDomDocument doc;
01067
01068 QDomElement root = doc.createElement("Programs");
01069 doc.appendChild(root);
01070
01071 pginfolist_t::iterator it = infoList.begin();
01072 for (; it !=infoList.end(); it++)
01073 {
01074 ProgramInfo *pInfo = (*it);
01075
01076 if (pInfo != NULL)
01077 {
01078 FillProgramInfo( &doc, root, pInfo, true );
01079 delete pInfo;
01080 }
01081 }
01082
01083
01084
01085 NameValueList list;
01086
01087 list.append( new NameValue( "Count" , (int)infoList.size()));
01088 list.append( new NameValue( "AsOf" , QDateTime::currentDateTime()
01089 .toString( Qt::ISODate )));
01090 list.append( new NameValue( "Version" , MYTH_BINARY_VERSION ));
01091 list.append( new NameValue( "ProtoVer" , MYTH_PROTO_VERSION ));
01092
01093 list.append( new NameValue( "Expiring" , doc.toString() ));
01094
01095 pRequest->FormatActionResponse( &list );
01096
01097 }
01098
01100
01102
01103 void MythXML::GetPreviewImage( HTTPRequest *pRequest )
01104 {
01105 bool bDefaultPixmap = false;
01106
01107 pRequest->m_eResponseType = ResponseTypeHTML;
01108 pRequest->m_nResponseStatus = 404;
01109
01110 QString sChanId = pRequest->m_mapParams[ "ChanId" ];
01111 QString sStartTime= pRequest->m_mapParams[ "StartTime" ];
01112
01113
01114
01115 int nWidth = pRequest->m_mapParams[ "Width" ].toInt();
01116 int nHeight = pRequest->m_mapParams[ "Height" ].toInt();
01117 int nSecsIn = pRequest->m_mapParams[ "SecsIn" ].toInt();
01118
01119 QDateTime dtStart = QDateTime::fromString( sStartTime, Qt::ISODate );
01120
01121 if (!dtStart.isValid())
01122 return;
01123
01124
01125
01126
01127
01128 ProgramInfo *pInfo = ProgramInfo::GetProgramFromRecorded( sChanId, dtStart );
01129
01130 if (pInfo==NULL)
01131 return;
01132
01133 if ( pInfo->hostname != gContext->GetHostName())
01134 {
01135
01136
01137 delete pInfo;
01138
01139 return;
01140 }
01141
01142 bDefaultPixmap = (nWidth == 0) && (nHeight == 0) && (nSecsIn >= 0);
01143
01144
01145
01146
01147
01148
01149
01150
01151 QString sFileName = GetPlaybackURL(pInfo);
01152 int defaultOffset = gContext->GetNumSetting("PreviewPixmapOffset", 64);
01153 int preRoll = gContext->GetNumSetting("RecordPreRoll", 0);
01154
01155 if (preRoll > 0)
01156 defaultOffset += preRoll;
01157
01158 if (nSecsIn <= 0 || nSecsIn == defaultOffset)
01159 {
01160 nSecsIn = defaultOffset;
01161 }
01162 else
01163 sFileName = QString("%1.%2").arg(sFileName).arg(nSecsIn);
01164
01165
01166
01167
01168 if (!QFile::exists( sFileName + ".png" ))
01169 {
01170
01171
01172
01173
01174 PreviewGenerator *previewgen = new PreviewGenerator(pInfo, true);
01175 previewgen->SetPreviewTimeAsSeconds(nSecsIn);
01176 previewgen->SetOutputFilename(sFileName + ".png");
01177 bool ok = previewgen->Run();
01178 if (!ok)
01179 {
01180 pRequest->m_eResponseType = ResponseTypeFile;
01181 pRequest->m_nResponseStatus = 404;
01182 previewgen->deleteLater();
01183 delete pInfo;
01184 return;
01185 }
01186 previewgen->deleteLater();
01187 }
01188
01189 delete pInfo;
01190
01191 pRequest->m_eResponseType = ResponseTypeFile;
01192 pRequest->m_nResponseStatus = 200;
01193
01194 float fAspect = 0.0;
01195
01196 QImage *pImage = new QImage(sFileName + ".png");
01197
01198 if (!pImage)
01199 return;
01200
01201 if (fAspect <= 0)
01202 fAspect = (float)(pImage->width()) / pImage->height();
01203
01204 if (fAspect == 0)
01205 {
01206 delete pImage;
01207 return;
01208 }
01209
01210 if ( nWidth == 0 )
01211 nWidth = (int)rint(nHeight * fAspect);
01212
01213 if ( nHeight == 0 )
01214 nHeight = (int)rint(nWidth / fAspect);
01215
01216 if (bDefaultPixmap)
01217 pRequest->m_sFileName = sFileName + ".png";
01218 else
01219 pRequest->m_sFileName = QString( "%1.%2x%3.png" )
01220 .arg( sFileName )
01221 .arg( nWidth )
01222 .arg( nHeight );
01223
01224
01225
01226
01227
01228
01229
01230 if (QFile::exists( pRequest->m_sFileName ))
01231 {
01232 delete pImage;
01233 return;
01234 }
01235
01236 QImage img = pImage->smoothScale( nWidth, nHeight);
01237
01238 img.save( pRequest->m_sFileName.ascii(), "PNG" );
01239
01240 delete pImage;
01241 }
01242
01244
01246
01247 void MythXML::GetRecording( HttpWorkerThread *pThread,
01248 HTTPRequest *pRequest )
01249 {
01250 bool bIndexFile = false;
01251
01252 pRequest->m_eResponseType = ResponseTypeHTML;
01253 pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", "
01254 "max-age = 5000";
01255 pRequest->m_nResponseStatus = 404;
01256
01257 QString sChanId = pRequest->m_mapParams[ "ChanId" ];
01258 QString sStartTime= pRequest->m_mapParams[ "StartTime" ];
01259
01260 if (sStartTime.length() == 0)
01261 {
01262 VERBOSE( VB_UPNP, "MythXML::GetRecording - StartTime missing.");
01263 return;
01264 }
01265
01266
01267
01268
01269
01270
01271
01272
01273 int nIdxPos = sStartTime.findRev( ".idx", -1, FALSE );
01274
01275 if (nIdxPos >=0 )
01276 {
01277 bIndexFile = true;
01278 sStartTime = sStartTime.left( nIdxPos );
01279 }
01280
01281
01282
01283
01284
01285 ThreadData *pData = (ThreadData *)pThread->GetWorkerData();
01286
01287 if (pData != NULL)
01288 {
01289 if ((pData->m_eType == ThreadData::DT_Recording) &&
01290 pData->IsSameRecording( sChanId, sStartTime ))
01291 {
01292 pRequest->m_sFileName = pData->m_sFileName;
01293
01294 }
01295 else
01296 pData = NULL;
01297 }
01298
01299
01300
01301
01302
01303 if (pData == NULL)
01304 {
01305
01306
01307
01308
01309 QDateTime dtStart = QDateTime::fromString( sStartTime, Qt::ISODate );
01310
01311 if (!dtStart.isValid())
01312 {
01313 VERBOSE( VB_UPNP, "MythXML::GetRecording - StartTime Invalid.");
01314 return;
01315 }
01316
01317
01318
01319
01320
01321 ProgramInfo *pInfo = ProgramInfo::GetProgramFromRecorded( sChanId, dtStart );
01322
01323 if (pInfo==NULL)
01324 {
01325 VERBOSE( VB_UPNP, QString( "MythXML::GetRecording - "
01326 "GetProgramFromRecorded( %1, %2 ) "
01327 "returned NULL" )
01328 .arg( sChanId )
01329 .arg( sStartTime ));
01330 return;
01331 }
01332
01333 if ( pInfo->hostname != gContext->GetHostName())
01334 {
01335
01336
01337 VERBOSE( VB_UPNP, QString( "MythXML::GetRecording - To access this "
01338 "recording, send request to %1." )
01339 .arg( pInfo->hostname ));
01340
01341 delete pInfo;
01342
01343 return;
01344 }
01345
01346 pRequest->m_sFileName = GetPlaybackURL(pInfo);
01347
01348 delete pInfo;
01349
01350
01351
01352
01353
01354 pData = new ThreadData( sChanId, sStartTime, pRequest->m_sFileName );
01355
01356 pThread->SetWorkerData( pData );
01357 }
01358
01359
01360
01361
01362
01363 if (bIndexFile)
01364 pRequest->m_sFileName += ".idx";
01365
01366
01367
01368
01369
01370 if (QFile::exists( pRequest->m_sFileName ))
01371 {
01372 pRequest->m_eResponseType = ResponseTypeFile;
01373 pRequest->m_nResponseStatus = 200;
01374 }
01375 }
01376
01378
01380
01381 void MythXML::GetMusic( HttpWorkerThread *pThread,
01382 HTTPRequest *pRequest )
01383 {
01384 pRequest->m_eResponseType = ResponseTypeHTML;
01385 pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", "
01386 "max-age = 5000";
01387 pRequest->m_nResponseStatus = 404;
01388
01389 QString sId = pRequest->m_mapParams[ "Id" ];
01390
01391 if (sId.length() == 0)
01392 return;
01393
01394 int nTrack = sId.toInt();
01395
01396
01397
01398
01399
01400 ThreadData *pData = (ThreadData *)pThread->GetWorkerData();
01401
01402 if (pData != NULL)
01403 {
01404 if ((pData->m_eType == ThreadData::DT_Music) &&
01405 (pData->m_nTrackNumber == nTrack))
01406 {
01407 pRequest->m_sFileName = pData->m_sFileName;
01408
01409 }
01410 else
01411 pData = NULL;
01412 }
01413
01414
01415
01416
01417
01418 if (pData == NULL)
01419 {
01420 QString sBasePath = gContext->GetSetting( "MusicLocation", "");
01421
01422
01423
01424
01425
01426 MSqlQuery query(MSqlQuery::InitCon());
01427
01428 if (query.isConnected())
01429 {
01430 query.prepare("SELECT CONCAT_WS('/', music_directories.path, "
01431 "music_songs.filename) AS filename FROM music_songs "
01432 "LEFT JOIN music_directories ON "
01433 "music_songs.directory_id=music_directories.directory_id "
01434 "WHERE music_songs.song_id = :KEY");
01435
01436 query.bindValue(":KEY", nTrack );
01437 query.exec();
01438
01439 if (query.isActive() && query.size() > 0)
01440 {
01441 query.first();
01442 pRequest->m_sFileName = QString( "%1/%2" )
01443 .arg( sBasePath )
01444 .arg( QString::fromUtf8(query.value(0).toString()) );
01445 }
01446 }
01447
01448
01449
01450
01451
01452 pData = new ThreadData( nTrack, pRequest->m_sFileName );
01453
01454 pThread->SetWorkerData( pData );
01455 }
01456
01457
01458
01459
01460
01461 if (QFile::exists( pRequest->m_sFileName ))
01462 {
01463 pRequest->m_eResponseType = ResponseTypeFile;
01464 pRequest->m_nResponseStatus = 200;
01465 }
01466 }
01467
01469
01471
01472 void MythXML::GetVideo( HttpWorkerThread *pThread,
01473 HTTPRequest *pRequest )
01474 {
01475 pRequest->m_eResponseType = ResponseTypeHTML;
01476 pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", "
01477 "max-age = 5000";
01478 pRequest->m_nResponseStatus = 404;
01479
01480 QString sId = pRequest->m_mapParams[ "Id" ];
01481
01482 if (sId.length() == 0)
01483 {
01484 QStringList idPath = QStringList::split( "/", pRequest->m_sRawRequest );
01485
01486 idPath = QStringList::split( " ", idPath[idPath.count() - 2] );
01487 idPath = QStringList::split( "?", idPath[0] );
01488
01489 sId = idPath[0];
01490
01491 if (sId.startsWith("Id"))
01492 sId = sId.right(sId.length() - 2);
01493 else
01494 return;
01495
01496
01497
01498 pRequest->m_mapParams[ "Id" ] = sId;
01499 }
01500
01501 bool wantCoverArt = (pRequest->m_mapParams[ "albumArt" ] == "true");
01502
01503 if (wantCoverArt)
01504 {
01505 GetVideoArt(pRequest);
01506 return;
01507 }
01508
01509
01510
01511
01512
01513 ThreadData *pData = (ThreadData *)pThread->GetWorkerData();
01514
01515 if (pData != NULL)
01516 {
01517 if ((pData->m_eType == ThreadData::DT_Video) && (pData->m_sVideoID == sId ))
01518 {
01519 pRequest->m_sFileName = pData->m_sFileName;
01520
01521 }
01522 else
01523 pData = NULL;
01524 }
01525
01526
01527
01528
01529
01530 if (pData == NULL)
01531 {
01532 QString sBasePath = "";
01533
01534
01535
01536
01537
01538 MSqlQuery query(MSqlQuery::InitCon());
01539
01540 if (query.isConnected())
01541 {
01542 query.prepare("SELECT filepath FROM upnpmedia WHERE intid = :KEY" );
01543 query.bindValue(":KEY", sId );
01544 query.exec();
01545
01546 if (query.isActive() && query.size() > 0)
01547 {
01548 query.first();
01549 pRequest->m_sFileName = QString( "%1/%2" )
01550 .arg( sBasePath )
01551 .arg( QString::fromUtf8(query.value(0).toString()) );
01552 }
01553 }
01554
01555
01556
01557
01558
01559 pData = new ThreadData( sId, pRequest->m_sFileName );
01560
01561 pThread->SetWorkerData( pData );
01562 }
01563
01564
01565
01566
01567
01568 if (QFile::exists( pRequest->m_sFileName ))
01569 {
01570 pRequest->m_eResponseType = ResponseTypeFile;
01571 pRequest->m_nResponseStatus = 200;
01572 }
01573 }
01574
01576
01578
01579 void MythXML::GetConnectionInfo( HTTPRequest *pRequest )
01580 {
01581 pRequest->m_mapRespHeaders[ "Cache-Control" ]
01582 = "no-cache=\"Ext\", max-age = 5000";
01583
01584 QString sPin = pRequest->m_mapParams[ "Pin" ];
01585 QString sSecurityPin = gContext->GetSetting( "SecurityPin", "");
01586
01587 if ( sSecurityPin.length() == 0 )
01588 {
01589 UPnp::FormatErrorResponse( pRequest,
01590 UPnPResult_HumanInterventionRequired,
01591 "No Security Pin assigned. Run mythtv-setup to set one." );
01592 return;
01593 }
01594
01595 if ((sSecurityPin != "0000" ) && ( sPin != sSecurityPin ))
01596 {
01597 UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionNotAuthorized );
01598 return;
01599 }
01600
01601 DatabaseParams params = gContext->GetDatabaseParams();
01602
01603
01604
01605 QString sServerIP = gContext->GetSetting( "BackendServerIP", "localhost" );
01606 QString sPeerIP = pRequest->GetPeerAddress();
01607
01608 if ((params.dbHostName == "localhost") &&
01609 (sServerIP != "localhost") &&
01610 (sServerIP != sPeerIP ))
01611 {
01612 params.dbHostName = sServerIP;
01613 }
01614
01615 QString sXml;
01616 QTextStream os( &sXml, IO_WriteOnly );
01617
01618 os << "<Database>";
01619 os << "<Host>" << params.dbHostName << "</Host>";
01620 os << "<Port>" << params.dbPort << "</Port>";
01621 os << "<UserName>" << params.dbUserName << "</UserName>";
01622 os << "<Password>" << params.dbPassword << "</Password>";
01623 os << "<Name>" << params.dbName << "</Name>";
01624 os << "<Type>" << params.dbType << "</Type>";
01625 os << "</Database>";
01626
01627 os << "<WOL>";
01628 os << "<Enabled>" << params.wolEnabled << "</Enabled>";
01629 os << "<Reconnect>" << params.wolReconnect << "</Reconnect>";
01630 os << "<Retry>" << params.wolRetry << "</Retry>";
01631 os << "<Command>" << params.wolCommand << "</Command>";
01632 os << "</WOL>";
01633
01634 NameValueList list;
01635
01636 list.append( new NameValue( "Info", sXml ));
01637
01638 pRequest->FormatActionResponse( &list );
01639
01640 return;
01641 }
01642
01645
01646
01647
01650
01651 void MythXML::FillProgramInfo(QDomDocument *pDoc,
01652 QDomNode &node,
01653 ProgramInfo *pInfo,
01654 bool bIncChannel ,
01655 bool bDetails )
01656 {
01657 if ((pDoc == NULL) || (pInfo == NULL))
01658 return;
01659
01660
01661
01662 QDomElement program = pDoc->createElement( "Program" );
01663 node.appendChild( program );
01664
01665 program.setAttribute( "startTime" , pInfo->startts.toString(Qt::ISODate));
01666 program.setAttribute( "endTime" , pInfo->endts.toString(Qt::ISODate));
01667 program.setAttribute( "title" , pInfo->title );
01668 program.setAttribute( "subTitle" , pInfo->subtitle );
01669 program.setAttribute( "category" , pInfo->category );
01670 program.setAttribute( "catType" , pInfo->catType );
01671 program.setAttribute( "repeat" , pInfo->repeat );
01672
01673 if (bDetails)
01674 {
01675
01676 program.setAttribute( "seriesId" , pInfo->seriesid );
01677 program.setAttribute( "programId" , pInfo->programid );
01678 program.setAttribute( "stars" , pInfo->stars );
01679 program.setAttribute( "fileSize" , longLongToString( pInfo->filesize ));
01680 program.setAttribute( "lastModified", pInfo->lastmodified.toString(Qt::ISODate) );
01681 program.setAttribute( "programFlags", pInfo->programflags );
01682 program.setAttribute( "hostname" , pInfo->hostname );
01683
01684 if (pInfo->hasAirDate)
01685 program.setAttribute( "airdate" , pInfo->originalAirDate
01686 .toString(Qt::ISODate) );
01687
01688 QDomText textNode = pDoc->createTextNode( pInfo->description );
01689 program.appendChild( textNode );
01690
01691 }
01692
01693 if ( bIncChannel )
01694 {
01695
01696
01697 QDomElement channel = pDoc->createElement( "Channel" );
01698 program.appendChild( channel );
01699
01700 FillChannelInfo( channel, pInfo, bDetails );
01701 }
01702
01703
01704
01705 if ( pInfo->recstatus != rsUnknown )
01706 {
01707 QDomElement recording = pDoc->createElement( "Recording" );
01708 program.appendChild( recording );
01709
01710 recording.setAttribute( "recStatus" , pInfo->recstatus );
01711 recording.setAttribute( "recPriority" , pInfo->recpriority );
01712 recording.setAttribute( "recStartTs" , pInfo->recstartts
01713 .toString(Qt::ISODate));
01714 recording.setAttribute( "recEndTs" , pInfo->recendts
01715 .toString(Qt::ISODate));
01716
01717 if (bDetails)
01718 {
01719 recording.setAttribute( "recordId" , pInfo->recordid );
01720 recording.setAttribute( "recGroup" , pInfo->recgroup );
01721 recording.setAttribute( "playGroup" , pInfo->playgroup );
01722 recording.setAttribute( "recType" , pInfo->rectype );
01723 recording.setAttribute( "dupInType" , pInfo->dupin );
01724 recording.setAttribute( "dupMethod" , pInfo->dupmethod );
01725 recording.setAttribute( "encoderId" , pInfo->cardid );
01726 recording.setAttribute( "recProfile" , pInfo->GetProgramRecordingProfile());
01727
01728 }
01729 }
01730 }
01731
01733
01735
01736 void MythXML::FillChannelInfo( QDomElement &channel,
01737 ProgramInfo *pInfo,
01738 bool bDetails )
01739 {
01740 if (pInfo)
01741 {
01742
01743
01744
01745
01746
01747
01748
01749
01750
01751 channel.setAttribute( "chanId" , pInfo->chanid );
01752 channel.setAttribute( "chanNum" , pInfo->chanstr );
01753 channel.setAttribute( "callSign" , pInfo->chansign );
01754
01755 channel.setAttribute( "channelName", pInfo->channame );
01756
01757 if (bDetails)
01758 {
01759 channel.setAttribute( "chanFilters", pInfo->chanOutputFilters );
01760 channel.setAttribute( "sourceId" , pInfo->sourceid );
01761 channel.setAttribute( "inputId" , pInfo->inputid );
01762 channel.setAttribute( "commFree" , pInfo->chancommfree);
01763 }
01764 }
01765 }
01766
01767