|
MythTV
0.26-pre
|
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&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( "&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
1.7.6.1