MythTV  0.26-pre
upnpcdsvideo.cpp
Go to the documentation of this file.
00001 // Program Name: upnpcdsvideo.cpp
00002 //                                                                            
00003 // Purpose - UPnP Content Directory Extension for MythVideo Videos
00004 //                                                                            
00006 
00007 // POSIX headers
00008 #include <limits.h>
00009 
00010 // Qt headers
00011 #include <QFileInfo>
00012 
00013 // MythTV headers
00014 #include "upnpcdsvideo.h"
00015 #include "httprequest.h"
00016 #include "mythmiscutil.h"
00017 #include "mythcorecontext.h"
00018 #include "storagegroup.h"
00019 
00020 #define LOC QString("UPnpCDSVideo: ")
00021 #define LOC_WARN QString("UPnpCDSVideo, Warning: ")
00022 #define LOC_ERR QString("UPnpCDSVideo, Error: ")
00023 
00024 UPnpCDSRootInfo UPnpCDSVideo::g_RootNodes[] = 
00025 {
00026     {   "All Videos", 
00027         "*",
00028         "SELECT 0 as key, "
00029           "title as name, "
00030           "1 as children "
00031             "FROM videometadata "
00032             "%1 "
00033             "ORDER BY title",
00034         "", "title" }
00035 
00036 };
00037 
00038 int UPnpCDSVideo::g_nRootCount = 1;
00039 
00040 //int UPnpCDSVideo::g_nRootCount;
00041 //= sizeof( g_RootNodes ) / sizeof( UPnpCDSRootInfo );
00042 
00044 //
00046 
00047 UPnpCDSRootInfo *UPnpCDSVideo::GetRootInfo( int nIdx )
00048 {
00049     if ((nIdx >=0 ) && ( nIdx < g_nRootCount ))
00050         return &(g_RootNodes[ nIdx ]); 
00051 
00052     return NULL;
00053 }
00054 
00056 //
00058 
00059 int UPnpCDSVideo::GetRootCount()
00060 {
00061     return g_nRootCount;
00062 }
00063 
00065 //
00067 
00068 QString UPnpCDSVideo::GetTableName( QString sColumn )
00069 {
00070     return "videometadata";
00071 }
00072 
00074 //
00076 
00077 QString UPnpCDSVideo::GetItemListSQL( QString sColumn )
00078 {
00079     return "SELECT intid, title, subtitle, filename, director, plot, "
00080             "rating, year, userrating, length, " 
00081             "season, episode, coverfile, insertdate, host FROM videometadata";
00082 
00083 }
00084 
00086 //
00088 
00089 void UPnpCDSVideo::BuildItemQuery( MSqlQuery &query, const QStringMap &mapParams )
00090 {
00091     int nVideoID = mapParams[ "Id" ].toInt();
00092 
00093     QString sSQL = QString( "%1 WHERE intid=:VIDEOID ORDER BY title DESC" )
00094                     .arg( GetItemListSQL( ) );
00095 
00096     query.prepare( sSQL );
00097 
00098     query.bindValue( ":VIDEOID", (int)nVideoID );
00099 }
00100 
00102 //
00104 
00105 bool UPnpCDSVideo::IsBrowseRequestForUs( UPnpCDSRequest *pRequest )
00106 {
00107     // ----------------------------------------------------------------------
00108     // See if we need to modify the request for compatibility
00109     // ----------------------------------------------------------------------
00110 
00111     // ----------------------------------------------------------------------
00112     // Xbox360 compatibility code.
00113     // ----------------------------------------------------------------------
00114 
00115     if (pRequest->m_eClient == CDS_ClientXBox && 
00116         pRequest->m_sContainerID == "15" &&
00117         gCoreContext->GetSetting("UPnP/WMPSource") == "1") 
00118     {
00119         pRequest->m_sObjectId = "Videos/0";
00120 
00121         LOG(VB_UPNP, LOG_INFO,
00122             "UPnpCDSVideo::IsBrowseRequestForUs - Yes ContainerID == 15");
00123         return true;
00124     }
00125 
00126     if ((pRequest->m_sObjectId.isEmpty()) && 
00127         (!pRequest->m_sContainerID.isEmpty()))
00128         pRequest->m_sObjectId = pRequest->m_sContainerID;
00129 
00130     // ----------------------------------------------------------------------
00131     // WMP11 compatibility code
00132     //
00133     // In this mode browsing for "Videos" is forced to either Videos (us)
00134     // or RecordedTV (handled by upnpcdstv)
00135     //
00136     // ----------------------------------------------------------------------
00137 
00138     if (pRequest->m_eClient == CDS_ClientWMP && 
00139         pRequest->m_sContainerID == "13" &&
00140         pRequest->m_nClientVersion < 12.0 &&
00141         gCoreContext->GetSetting("UPnP/WMPSource") == "1")
00142     {
00143         pRequest->m_sObjectId = "Videos/0";
00144 
00145         LOG(VB_UPNP, LOG_INFO,
00146             "UPnpCDSVideo::IsBrowseRequestForUs - Yes ContainerID == 13");
00147         return true;
00148     }
00149 
00150     LOG(VB_UPNP, LOG_INFO,
00151         "UPnpCDSVideo::IsBrowseRequestForUs - Not sure... Calling base class.");
00152 
00153     return UPnpCDSExtension::IsBrowseRequestForUs( pRequest );
00154 }
00155 
00157 //
00159 
00160 bool UPnpCDSVideo::IsSearchRequestForUs( UPnpCDSRequest *pRequest )
00161 {
00162     // ----------------------------------------------------------------------
00163     // See if we need to modify the request for compatibility
00164     // ----------------------------------------------------------------------
00165 
00166     // ----------------------------------------------------------------------
00167     // XBox 360 compatibility code
00168     // ----------------------------------------------------------------------
00169 
00170 
00171     if (pRequest->m_eClient == CDS_ClientXBox && 
00172         pRequest->m_sContainerID == "15" &&
00173         gCoreContext->GetSetting("UPnP/WMPSource") == "1") 
00174     {
00175         pRequest->m_sObjectId = "Videos/0";
00176 
00177         LOG(VB_UPNP, LOG_INFO, "UPnpCDSVideo::IsSearchRequestForUs... Yes.");
00178 
00179         return true;
00180     }
00181 
00182     if ((pRequest->m_sObjectId.isEmpty()) && 
00183         (!pRequest->m_sContainerID.isEmpty()))
00184         pRequest->m_sObjectId = pRequest->m_sContainerID;
00185 
00186     // ----------------------------------------------------------------------
00187 
00188     bool bOurs = UPnpCDSExtension::IsSearchRequestForUs( pRequest );
00189 
00190     // ----------------------------------------------------------------------
00191     // WMP11 compatibility code
00192     // ----------------------------------------------------------------------
00193 
00194     if ( bOurs && pRequest->m_eClient == CDS_ClientWMP && 
00195          pRequest->m_nClientVersion < 12.0 )
00196     {
00197         if ( gCoreContext->GetSetting("UPnP/WMPSource") == "1")
00198         {
00199             pRequest->m_sObjectId = "Videos/0";
00200             // -=>TODO: Not sure why this was added.
00201             pRequest->m_sParentId = "8"; 
00202         }
00203         else
00204             bOurs = false;
00205     }
00206 
00207     return bOurs;
00208 }
00209 
00211 //
00213 
00214 int UPnpCDSVideo::GetDistinctCount( UPnpCDSRootInfo *pInfo )
00215 {
00216     int nCount = 0;
00217 
00218     MSqlQuery query(MSqlQuery::InitCon());
00219 
00220     query.prepare("SELECT COUNT(*) FROM videometadata");
00221 
00222     if (query.exec() && query.next())
00223     {
00224         nCount = query.value(0).toInt();
00225     }
00226 
00227     return nCount;
00228 }
00229 
00230 
00232 //
00234 
00235 void UPnpCDSVideo::AddItem( const UPnpCDSRequest    *pRequest, 
00236                             const QString           &sObjectId,
00237                             UPnpCDSExtensionResults *pResults,
00238                             bool                     bAddRef,
00239                             MSqlQuery               &query )
00240 {
00241 
00242     int            nVidID       = query.value( 0).toInt();
00243     QString        sTitle       = query.value( 1).toString();
00244     QString        sSubtitle    = query.value( 2).toString();
00245     QString        sFilePath    = query.value( 3).toString();
00246     QString        sDirector    = query.value( 4).toString();
00247     QString        sPlot        = query.value( 5).toString();
00248     QString        sRating      = query.value( 6).toString();
00249     // int             nYear        = query.value( 7).toInt();
00250     // int             nUserRating  = query.value( 8).toInt();
00251     int            nLength      = query.value( 9).toInt();
00252     // int             nSeason      = query.value(10).toInt();
00253     // int             nEpisode     = query.value(11).toInt();
00254     QString        sCoverArt    = query.value(12).toString();
00255     QDateTime      dtInsertDate = query.value(13).toDateTime();
00256     QString        sHostName    = query.value(14).toString();
00257 
00258     // ----------------------------------------------------------------------
00259     // Cache Host ip Address & Port
00260     // ----------------------------------------------------------------------
00261 
00262     // If the host-name is empty then we assume it is our local host
00263     // otherwise, we look up the host's IP address and port.  When the
00264     // client then trys to play the video it will be directed to the
00265     // host which actually has the content.
00266     if (!m_mapBackendIp.contains( sHostName ))
00267     {
00268         if (sHostName.isEmpty())
00269         {
00270             m_mapBackendIp[sHostName] = 
00271                 gCoreContext->GetSetting( "BackendServerIP" );
00272         }
00273         else
00274         {
00275             m_mapBackendIp[sHostName] = 
00276                 gCoreContext->GetSettingOnHost( "BackendServerIp", sHostName);
00277         }
00278     }
00279 
00280     if (!m_mapBackendPort.contains( sHostName ))
00281     {
00282         if (sHostName.isEmpty())
00283         {
00284             m_mapBackendPort[sHostName] = 
00285                 gCoreContext->GetSetting( "BackendStatusPort" );
00286         }
00287         else
00288         {
00289             m_mapBackendPort[sHostName] = 
00290                 gCoreContext->GetSettingOnHost("BackendStatusPort", sHostName);
00291         }
00292     }
00293     
00294     
00295     // ----------------------------------------------------------------------
00296     // Build Support Strings
00297     // ----------------------------------------------------------------------
00298 
00299     QString sName      = sTitle;
00300     if( !sSubtitle.isEmpty() )
00301     {
00302         sName += " - " + sSubtitle;
00303     }
00304 
00305     QString sURIBase   = QString( "http://%1:%2/Content/" )
00306                             .arg( m_mapBackendIp  [sHostName] ) 
00307                             .arg( m_mapBackendPort[sHostName] );
00308 
00309     QString sURIParams = QString( "?Id=%1" ).arg( nVidID );
00310     QString sId        = QString( "Videos/0/item%1").arg( sURIParams );
00311 
00312     QString sParentID = "Videos/0";
00313 
00314     QString sAlbumArtURI= QString( "%1GetVideoArt%2")
00315                         .arg( sURIBase   )
00316                         .arg( sURIParams );
00317 
00318     CDSObject *pItem = CDSObject::CreateVideoItem( sId, sName, sParentID );
00319 
00320     pItem->m_bRestricted  = false;
00321     pItem->m_bSearchable  = true;
00322     pItem->m_sWriteStatus = "WRITABLE";
00323 
00324     pItem->SetPropValue( "longDescription", sPlot );
00325     // ?? pItem->SetPropValue( "description"    , sTitle );
00326     pItem->SetPropValue( "director"       , sDirector );
00327 
00328     pItem->SetPropValue( "genre"          , "[Unknown Genre]"     );
00329     pItem->SetPropValue( "actor"          , "[Unknown Author]"    );
00330     pItem->SetPropValue( "creator"        , "[Unknown Creator]"   );
00331     pItem->SetPropValue( "album"          , "[Unknown Album]"     );
00332 
00333     //pItem->SetPropValue( "producer"       , );
00334     //pItem->SetPropValue( "rating"         , );
00335     //pItem->SetPropValue( "actor"          , );
00336     //pItem->SetPropValue( "publisher"      , );
00337     //pItem->SetPropValue( "language"       , );
00338     //pItem->SetPropValue( "relation"       , );
00339     //pItem->SetPropValue( "region"         , );
00340 
00341     if ((sCoverArt != "") && (sCoverArt != "No Cover"))
00342         pItem->SetPropValue( "albumArtURI"    , sAlbumArtURI);
00343 
00344     if ( bAddRef )
00345     {
00346         QString sRefId = QString( "%1/0/item%2").arg( m_sExtensionId )
00347                                                 .arg( sURIParams     );
00348 
00349         pItem->SetPropValue( "refID", sRefId );
00350     }
00351 
00352     QString sFullFileName = sFilePath;
00353     if (!QFile::exists( sFullFileName ))
00354     {
00355         StorageGroup sgroup("Videos");
00356         sFullFileName = sgroup.FindFile( sFullFileName );
00357     }
00358     QFileInfo fInfo( sFullFileName );
00359 
00360     pItem->SetPropValue( "date", dtInsertDate.toString( "yyyy-MM-dd"));
00361     pResults->Add( pItem );
00362 
00363     // ----------------------------------------------------------------------
00364     // Add Video Resource Element based on File extension (HTTP)
00365     // ----------------------------------------------------------------------
00366 
00367     QString sMimeType = HTTPRequest::GetMimeType( fInfo.suffix() );
00368 
00369     // If we are dealing with a Sony Blu-ray player then we fake the
00370     // MIME type to force the video to appear
00371     if ( pRequest->m_eClient == CDS_ClientSonyDB )
00372     {   
00373         sMimeType = "video/avi";
00374     }
00375 
00376     QString sProtocol = QString( "http-get:*:%1:DLNA.ORG_OP=01;DLNA.ORG_CI=0;"
00377                                  "DLNA.ORG_FLAGS=0150000000000000000000000000"
00378                                  "0000" ).arg( sMimeType  );
00379     QString sURI      = QString( "%1GetVideo%2").arg( sURIBase   )
00380                                                 .arg( sURIParams );
00381 
00382     Resource *pRes = pItem->AddResource( sProtocol, sURI );
00383 
00384     pRes->AddAttribute( "size"      , QString("%1").arg(fInfo.size()) );
00385 
00386     QString sDur;
00387     sDur.sprintf("%02d:%02d:00", (nLength / 60), nLength % 60 );
00388 
00389     pRes->AddAttribute( "duration"  , sDur      );
00390 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends