MythTV  0.26-pre
upnpcdstv.cpp
Go to the documentation of this file.
00001 
00002 // Program Name: upnpcdstv.cpp
00003 //
00004 // Purpose - uPnp Content Directory Extension for Recorded TV
00005 //
00006 // Created By  : David Blain                    Created On : Jan. 24, 2005
00007 // Modified By :                                Modified On:
00008 //
00010 
00011 // POSIX headers
00012 #include <limits.h>
00013 #include <stdint.h>
00014 
00015 // MythTV headers
00016 #include "upnpcdstv.h"
00017 #include "httprequest.h"
00018 #include "storagegroup.h"
00019 #include "mythmiscutil.h"
00020 #include "mythcorecontext.h"
00021 
00022 
00023 /*
00024    Recordings                              RecTv
00025     - All Programs                         RecTv/All
00026       + <recording 1>                      RecTv/All/item?ChanId=1004&StartTime=2006-04-06T20:00:00
00027       + <recording 2>
00028       + <recording 3>
00029     - By Title                             RecTv/title
00030       - <title 1>                          RecTv/title/key=Stargate SG-1
00031         + <recording 1>                    RecTv/title/key=Stargate SG-1/item?ChanId=1004&StartTime=2006-04-06T20:00:00
00032         + <recording 2>
00033     - By Genre
00034     - By Date
00035     - By Channel
00036     - By Group
00037 */
00038 
00039 
00040 UPnpCDSRootInfo UPnpCDSTv::g_RootNodes[] =
00041 {
00042     {   "All Recordings",
00043         "*",
00044         "SELECT 0 as key, "
00045           "CONCAT( title, ': ', subtitle) as name, "
00046           "1 as children "
00047             "FROM recorded "
00048             "%1 "
00049             "ORDER BY starttime DESC",
00050         "", "starttime DESC" },
00051 
00052     {   "By Title",
00053         "title",
00054         "SELECT title as id, "
00055           "title as name, "
00056           "count( title ) as children "
00057             "FROM recorded "
00058             "%1 "
00059             "GROUP BY title "
00060             "ORDER BY title",
00061         "WHERE title=:KEY", "title" },
00062 
00063     {   "By Genre",
00064         "category",
00065         "SELECT category as id, "
00066           "category as name, "
00067           "count( category ) as children "
00068             "FROM recorded "
00069             "%1 "
00070             "GROUP BY category "
00071             "ORDER BY category",
00072         "WHERE category=:KEY", "category" },
00073 
00074     {   "By Date",
00075         "DATE_FORMAT(starttime, '%Y-%m-%d')",
00076         "SELECT  DATE_FORMAT(starttime, '%Y-%m-%d') as id, "
00077           "DATE_FORMAT(starttime, '%Y-%m-%d %W') as name, "
00078           "count( DATE_FORMAT(starttime, '%Y-%m-%d %W') ) as children "
00079             "FROM recorded "
00080             "%1 "
00081             "GROUP BY name "
00082             "ORDER BY starttime DESC",
00083         "WHERE DATE_FORMAT(starttime, '%Y-%m-%d') =:KEY", "starttime DESC" },
00084 
00085     {   "By Channel",
00086         "chanid",
00087         "SELECT channel.chanid as id, "
00088           "CONCAT(channel.channum, ' ', channel.callsign) as name, "
00089           "count( channum ) as children "
00090             "FROM channel "
00091                 "INNER JOIN recorded ON channel.chanid = recorded.chanid "
00092             "%1 "
00093             "GROUP BY name "
00094             "ORDER BY channel.chanid",
00095         "WHERE channel.chanid=:KEY", ""},
00096 
00097     {   "By Group",
00098         "recgroup",
00099         "SELECT recgroup as id, "
00100           "recgroup as name, count( recgroup ) as children "
00101             "FROM recorded "
00102             "%1 "
00103             "GROUP BY recgroup "
00104             "ORDER BY recgroup",
00105         "WHERE recgroup=:KEY", "recgroup" }
00106 };
00107 
00108 int UPnpCDSTv::g_nRootCount = sizeof( g_RootNodes ) / sizeof( UPnpCDSRootInfo );
00109 
00111 //
00113 
00114 UPnpCDSRootInfo *UPnpCDSTv::GetRootInfo( int nIdx )
00115 {
00116     if ((nIdx >=0 ) && ( nIdx < g_nRootCount ))
00117         return &(g_RootNodes[ nIdx ]);
00118 
00119     return NULL;
00120 }
00121 
00123 //
00125 
00126 int UPnpCDSTv::GetRootCount()
00127 {
00128     return g_nRootCount;
00129 }
00130 
00132 //
00134 
00135 QString UPnpCDSTv::GetTableName( QString /* sColumn */)
00136 {
00137     return "recorded";
00138 }
00139 
00141 //
00143 
00144 QString UPnpCDSTv::GetItemListSQL( QString /* sColumn */ )
00145 {
00146     return "SELECT chanid, starttime, endtime, title, " \
00147                   "subtitle, description, category, "   \
00148                   "hostname, recgroup, filesize, "      \
00149                   "basename, progstart, progend, "      \
00150                   "storagegroup "                       \
00151            "FROM recorded ";
00152 }
00153 
00155 //
00157 
00158 void UPnpCDSTv::BuildItemQuery( MSqlQuery &query, const QStringMap &mapParams )
00159 {
00160     int     nChanId    = mapParams[ "ChanId"    ].toInt();
00161     QString sStartTime = mapParams[ "StartTime" ];
00162 
00163     QString sSQL = QString("%1 WHERE chanid=:CHANID AND starttime=:STARTTIME ")
00164                       .arg( GetItemListSQL() );
00165 
00166     query.prepare( sSQL );
00167 
00168     query.bindValue(":CHANID"   , (int)nChanId    );
00169     query.bindValue(":STARTTIME", sStartTime );
00170 }
00171 
00173 //
00175 
00176 bool UPnpCDSTv::IsBrowseRequestForUs( UPnpCDSRequest *pRequest )
00177 {
00178     // ----------------------------------------------------------------------
00179     // See if we need to modify the request for compatibility
00180     // ----------------------------------------------------------------------
00181 
00182     // ----------------------------------------------------------------------
00183     // Xbox360 compatibility code.
00184     // ----------------------------------------------------------------------
00185 
00186     if (pRequest->m_eClient == CDS_ClientXBox && 
00187         pRequest->m_sContainerID == "15" &&
00188         gCoreContext->GetSetting("UPnP/WMPSource") != "1") 
00189     {
00190         pRequest->m_sObjectId = "Videos/0";
00191 
00192         LOG(VB_UPNP, LOG_INFO,
00193             "UPnpCDSTv::IsBrowseRequestForUs - Yes ContainerID == 15");
00194         return true;
00195     }
00196 
00197     // ----------------------------------------------------------------------
00198     // WMP11 compatibility code
00199     // ----------------------------------------------------------------------
00200     if (pRequest->m_eClient == CDS_ClientWMP && 
00201         pRequest->m_nClientVersion < 12.0 && 
00202         pRequest->m_sContainerID == "13" &&
00203         gCoreContext->GetSetting("UPnP/WMPSource") != "1")
00204     {
00205         pRequest->m_sObjectId = "RecTv/0";
00206 
00207         LOG(VB_UPNP, LOG_INFO,
00208             "UPnpCDSTv::IsBrowseRequestForUs - Yes, ObjectId == 13");
00209         return true;
00210     }
00211 
00212     LOG(VB_UPNP, LOG_INFO,
00213         "UPnpCDSTv::IsBrowseRequestForUs - Not sure... Calling base class.");
00214 
00215     return UPnpCDSExtension::IsBrowseRequestForUs( pRequest );
00216 }
00217 
00219 //
00221 
00222 bool UPnpCDSTv::IsSearchRequestForUs( UPnpCDSRequest *pRequest )
00223 {
00224     // ----------------------------------------------------------------------
00225     // See if we need to modify the request for compatibility
00226     // ----------------------------------------------------------------------
00227 
00228     // ----------------------------------------------------------------------
00229     // XBox 360 compatibility code
00230     // ----------------------------------------------------------------------
00231 
00232     if (pRequest->m_eClient == CDS_ClientXBox && 
00233         pRequest->m_sContainerID == "15" &&
00234         gCoreContext->GetSetting("UPnP/WMPSource") !=  "1") 
00235     {
00236         pRequest->m_sObjectId = "Videos/0";
00237 
00238         LOG(VB_UPNP, LOG_INFO, "UPnpCDSTv::IsSearchRequestForUs... Yes.");
00239 
00240         return true;
00241     }
00242 
00243 
00244     if ((pRequest->m_sObjectId.isEmpty()) && 
00245         (!pRequest->m_sContainerID.isEmpty()))
00246         pRequest->m_sObjectId = pRequest->m_sContainerID;
00247 
00248     // ----------------------------------------------------------------------
00249 
00250     bool bOurs = UPnpCDSExtension::IsSearchRequestForUs( pRequest );
00251 
00252     // ----------------------------------------------------------------------
00253     // WMP11 compatibility code
00254     //
00255     // In this mode browsing for "Videos" is forced to either RecordedTV (us)
00256     // or Videos (handled by upnpcdsvideo)
00257     //
00258     // ----------------------------------------------------------------------
00259 
00260     if ( bOurs && pRequest->m_eClient == CDS_ClientWMP && 
00261          pRequest->m_nClientVersion < 12.0)
00262     {
00263         // GetBoolSetting()?
00264         if ( gCoreContext->GetSetting("UPnP/WMPSource") != "1")
00265         {
00266             pRequest->m_sObjectId = "RecTv/0";
00267             // -=>TODO: Not sure why this was added
00268             pRequest->m_sParentId = '8';        
00269         }
00270         else
00271             bOurs = false;
00272     }
00273 
00274     return bOurs;
00275 }
00276 
00278 //
00280 
00281 void UPnpCDSTv::AddItem( const UPnpCDSRequest    *pRequest, 
00282                          const QString           &sObjectId,
00283                          UPnpCDSExtensionResults *pResults,
00284                          bool                     bAddRef,
00285                          MSqlQuery               &query )
00286 {
00287     int            nChanid      = query.value( 0).toInt();
00288     QDateTime      dtStartTime  = query.value( 1).toDateTime();
00289     QDateTime      dtEndTime    = query.value( 2).toDateTime();
00290     QString        sTitle       = query.value( 3).toString();
00291     QString        sSubtitle    = query.value( 4).toString();
00292     QString        sDescription = query.value( 5).toString();
00293     QString        sCategory    = query.value( 6).toString();
00294     QString        sHostName    = query.value( 7).toString();
00295     QString        sRecGroup    = query.value( 8).toString();
00296     uint64_t       nFileSize    = query.value( 9).toULongLong();
00297     QString        sBaseName    = query.value(10).toString();
00298 
00299     QDateTime      dtProgStart  = query.value(11).toDateTime();
00300     QDateTime      dtProgEnd    = query.value(12).toDateTime();
00301     QString        sStorageGrp  = query.value(13).toString();
00302 
00303     // ----------------------------------------------------------------------
00304     // Cache Host ip Address & Port
00305     // ----------------------------------------------------------------------
00306 
00307     if (!m_mapBackendIp.contains( sHostName ))
00308         m_mapBackendIp[ sHostName ] = gCoreContext->GetSettingOnHost( "BackendServerIp", sHostName);
00309 
00310     if (!m_mapBackendPort.contains( sHostName ))
00311         m_mapBackendPort[ sHostName ] = gCoreContext->GetSettingOnHost("BackendStatusPort", sHostName);
00312 
00313     // ----------------------------------------------------------------------
00314     // Build Support Strings
00315     // ----------------------------------------------------------------------
00316 
00317     QString sName      = sTitle + ": " + (sSubtitle.isEmpty() ? sDescription.left(128) : sSubtitle);
00318 
00319     QString sURIBase   = QString( "http://%1:%2/Content/" )
00320                             .arg( m_mapBackendIp  [ sHostName ] )
00321                             .arg( m_mapBackendPort[ sHostName ] );
00322 
00323     QString sURIParams = QString( "?ChanId=%1&amp;StartTime=%2" )
00324                             .arg( nChanid )
00325                             .arg( dtStartTime.toUTC().toString(Qt::ISODate));
00326 
00327     QString sId        = QString( "RecTv/0/item%1")
00328                             .arg( sURIParams );
00329 
00330     CDSObject *pItem   = CDSObject::CreateVideoItem( sId,
00331                                                      sName,
00332                                                      sObjectId );
00333     pItem->m_bRestricted  = false;
00334     pItem->m_bSearchable  = true;
00335     pItem->m_sWriteStatus = "WRITABLE";
00336 
00337     if ( bAddRef )
00338     {
00339         QString sRefId = QString( "%1/0/item%2")
00340                             .arg( m_sExtensionId )
00341                             .arg( sURIParams     );
00342 
00343         pItem->SetPropValue( "refID", sRefId );
00344     }
00345 
00346     pItem->SetPropValue( "genre"          , sCategory    );
00347     pItem->SetPropValue( "longDescription", sDescription );
00348     pItem->SetPropValue( "description"    , sSubtitle    );
00349 
00350     //pItem->SetPropValue( "producer"       , );
00351     //pItem->SetPropValue( "rating"         , );
00352     //pItem->SetPropValue( "actor"          , );
00353     //pItem->SetPropValue( "director"       , );
00354     //pItem->SetPropValue( "publisher"      , );
00355     //pItem->SetPropValue( "language"       , );
00356     //pItem->SetPropValue( "relation"       , );
00357     //pItem->SetPropValue( "region"         , );
00358 
00359     // ----------------------------------------------------------------------
00360     // Needed for Microsoft Media Player Compatibility
00361     // (Won't display correct Title without them)
00362     // ----------------------------------------------------------------------
00363 
00364     pItem->SetPropValue( "creator"       , "[Unknown Author]" );
00365     pItem->SetPropValue( "artist"        , "[Unknown Author]" );
00366     pItem->SetPropValue( "album"         , "[Unknown Series]" );
00367     pItem->SetPropValue( "actor"         , "[Unknown Author]" );
00368     pItem->SetPropValue( "date"          , dtStartTime.toString(Qt::ISODate));
00369 
00370     pResults->Add( pItem );
00371 
00372     // ----------------------------------------------------------------------
00373     // Add Video Resource Element based on File contents/extension (HTTP)
00374     // ----------------------------------------------------------------------
00375 
00376     StorageGroup sg(sStorageGrp, sHostName);
00377     QString sFilePath = sg.FindFile(sBaseName);
00378     QString sMimeType;
00379 
00380     if ( QFile::exists(sFilePath) )
00381         sMimeType = HTTPRequest::TestMimeType( sFilePath );
00382     else
00383         sMimeType = HTTPRequest::TestMimeType( sBaseName );
00384 
00385 
00386     // If we are dealing with Window Media Player 12 (i.e. Windows 7)
00387     // then fake the Mime type to place the recorded TV in the
00388     // recorded TV section.
00389     if (pRequest->m_eClient == CDS_ClientWMP &&
00390         pRequest->m_nClientVersion >= 12.0)
00391     {
00392         sMimeType = "video/x-ms-dvr";
00393     }
00394 
00395     // If we are dealing with a Sony Blu-ray player then we fake the
00396     // MIME type to force the video to appear
00397     if ( pRequest->m_eClient == CDS_ClientSonyDB )
00398     {
00399         sMimeType = "video/avi";
00400     }
00401 
00402 
00403     // DLNA string below is temp fix for ps3 seeking.
00404     QString sProtocol = QString( "http-get:*:%1:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000" ).arg( sMimeType  );
00405     QString sURI      = QString( "%1GetRecording%2").arg( sURIBase   )
00406                                                     .arg( sURIParams );
00407 
00408     // Sony BDPS370 requires a DLNA Profile Name
00409     // FIXME: detection to determine the correct DLNA Profile Name
00410     if (sMimeType == "video/mpeg")
00411     {
00412         sProtocol += ";DLNA.ORG_PN=MPEG_TS_SD_NA_ISO";
00413     }
00414 
00415     Resource *pRes = pItem->AddResource( sProtocol, sURI );
00416 
00417     uint uiStart = dtProgStart.toTime_t();
00418     uint uiEnd   = dtProgEnd.toTime_t();
00419     uint uiDur   = uiEnd - uiStart;
00420     
00421     MSqlQuery query2(MSqlQuery::InitCon());
00422     query2.prepare( "SELECT data FROM recordedmarkup WHERE chanid=:CHANID AND "
00423                     "starttime=:STARTTIME AND type = 33" );
00424     query2.bindValue(":CHANID", (int)nChanid);
00425     query2.bindValue(":STARTTIME", dtProgStart.toString("yyyy/MM/dd hh:mm:ss"));
00426     if (query2.exec() && query2.next())
00427         uiDur = query2.value(0).toUInt() / 1000;
00428 
00429     QString sDur;
00430 
00431     sDur.sprintf("%02d:%02d:%02d",
00432                   (uiDur / 3600) % 24,
00433                   (uiDur / 60) % 60,
00434                    uiDur % 60);
00435 
00436     LOG(VB_UPNP, LOG_DEBUG, "Duration: " + sDur );
00437 
00438     pRes->AddAttribute( "duration"  , sDur      );
00439     pRes->AddAttribute( "size"      , QString::number( nFileSize) );
00440 
00441 /*
00442     // ----------------------------------------------------------------------
00443     // Add Video Resource Element based on File extension (mythtv)
00444     // ----------------------------------------------------------------------
00445 
00446     sProtocol = QString( "myth:*:%1:*"     ).arg( sMimeType  );
00447     sURI      = QString( "myth://%1/%2" )
00448                    .arg( m_mapBackendIp  [ sHostName ] )
00449                    .arg( sBaseName );
00450 
00451     pRes = pItem->AddResource( sProtocol, sURI );
00452 
00453     pRes->AddAttribute( "duration"  , sDur      );
00454     pRes->AddAttribute( "size"      , QString::number( nFileSize) );
00455 */
00456 
00457     // ----------------------------------------------------------------------
00458     // Add Preview URI as albumArt
00459     // ----------------------------------------------------------------------
00460 
00461     sURI = QString( "%1GetPreviewImage%2%3").arg( sURIBase   )
00462                                             .arg( sURIParams )
00463                                             .arg( "&amp;Width=160" ); 
00464 
00465     pItem->SetPropValue( "albumArtURI", sURI );
00466     Property *pProp = pItem->GetProperty("albumArtURI");
00467     if (pProp)
00468     {
00469         pProp->AddAttribute("dlna:profileID", "PNG_TN");
00470         pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0");
00471     
00472     }
00473 
00474 }
00475 
00476 // vim:ts=4:sw=4:ai:et:si:sts=4
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends