void UPnpCDSTv::AddItem( const UPnpCDSRequest *pRequest, const QString &sObjectId, UPnpCDSExtensionResults *pResults, bool bAddRef, MSqlQuery &query ) { int nChanid = query.value( 0).toInt(); QDateTime dtStartTime = query.value( 1).toDateTime(); QDateTime dtEndTime = query.value( 2).toDateTime(); QString sTitle = query.value( 3).toString(); QString sSubtitle = query.value( 4).toString(); QString sDescription = query.value( 5).toString(); QString sCategory = query.value( 6).toString(); QString sHostName = query.value( 7).toString(); QString sRecGroup = query.value( 8).toString(); uint64_t nFileSize = query.value( 9).toULongLong(); QString sBaseName = query.value(10).toString(); QDateTime dtProgStart = query.value(11).toDateTime(); QDateTime dtProgEnd = query.value(12).toDateTime(); QString sStorageGrp = query.value(13).toString(); // ---------------------------------------------------------------------- // Cache Host ip Address & Port // ---------------------------------------------------------------------- if (!m_mapBackendIp.contains( sHostName )) m_mapBackendIp[ sHostName ] = gCoreContext->GetSettingOnHost( "BackendServerIp", sHostName); if (!m_mapBackendPort.contains( sHostName )) m_mapBackendPort[ sHostName ] = gCoreContext->GetSettingOnHost("BackendStatusPort", sHostName); // ---------------------------------------------------------------------- // Build Support Strings // ---------------------------------------------------------------------- QString sName = sTitle + ": " + (sSubtitle.isEmpty() ? sDescription : sSubtitle); QString sURIBase = QString( "http://%1:%2/Myth/" ) .arg( m_mapBackendIp [ sHostName ] ) .arg( m_mapBackendPort[ sHostName ] ); QString sURIParams = QString( "?ChanId=%1&StartTime=%2" ) .arg( nChanid ) .arg( dtStartTime.toString(Qt::ISODate)); QString sId = QString( "RecTv/0/item%1") .arg( sURIParams ); CDSObject *pItem = CDSObject::CreateVideoItem( sId, sName, sObjectId ); pItem->m_bRestricted = false; pItem->m_bSearchable = true; pItem->m_sWriteStatus = "WRITABLE"; if ( bAddRef ) { QString sRefId = QString( "%1/0/item%2") .arg( m_sExtensionId ) .arg( sURIParams ); pItem->SetPropValue( "refID", sRefId ); } pItem->SetPropValue( "genre" , sCategory ); pItem->SetPropValue( "longDescription", sDescription ); pItem->SetPropValue( "description" , sSubtitle ); //pItem->SetPropValue( "producer" , ); //pItem->SetPropValue( "rating" , ); //pItem->SetPropValue( "actor" , ); //pItem->SetPropValue( "director" , ); //pItem->SetPropValue( "publisher" , ); //pItem->SetPropValue( "language" , ); //pItem->SetPropValue( "relation" , ); //pItem->SetPropValue( "region" , ); // ---------------------------------------------------------------------- // Needed for Microsoft Media Player Compatibility // (Won't display correct Title without them) // ---------------------------------------------------------------------- pItem->SetPropValue( "creator" , "[Unknown Author]" ); pItem->SetPropValue( "artist" , "[Unknown Author]" ); pItem->SetPropValue( "album" , "[Unknown Series]" ); pItem->SetPropValue( "actor" , "[Unknown Author]" ); pItem->SetPropValue( "date" , dtStartTime.toString(Qt::ISODate)); pResults->Add( pItem ); // ---------------------------------------------------------------------- // Add Video Resource Element based on File contents/extension (HTTP) // ---------------------------------------------------------------------- StorageGroup sg(sStorageGrp, sHostName); QString sFilePath = sg.FindRecordingFile(sBaseName); QString sMimeType; if ( QFile::exists(sFilePath) ) sMimeType = HTTPRequest::TestMimeType( sFilePath ); else sMimeType = HTTPRequest::TestMimeType( sBaseName ); // If we are dealing with Window Media Player 12 (i.e. Windows 7) // then fake the Mime type to place the recorded TV in the // recorded TV section. if (pRequest->m_eClient == CDS_ClientWMP && pRequest->m_nClientVersion >= 12.0) { sMimeType = "video/x-ms-dvr"; } // DLNA string below is temp fix for ps3 seeking. QString sProtocol = QString( "http-get:*:%1:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000" ).arg( sMimeType ); QString sURI = QString( "%1GetRecording%2").arg( sURIBase ) .arg( sURIParams ); Resource *pRes = pItem->AddResource( sProtocol, sURI ); uint uiStart = dtProgStart.toTime_t(); uint uiEnd = dtProgEnd.toTime_t(); uint uiDur = uiEnd - uiStart; QString sDur; sDur.sprintf("%02d:%02d:%02d", (uiDur / 3600) % 24, (uiDur / 60) % 60, uiDur % 60); pRes->AddAttribute( "duration" , sDur ); pRes->AddAttribute( "size" , QString::number( nFileSize) ); /* // ---------------------------------------------------------------------- // Add Video Resource Element based on File extension (mythtv) // ---------------------------------------------------------------------- sProtocol = QString( "myth:*:%1:*" ).arg( sMimeType ); sURI = QString( "myth://%1/%2" ) .arg( m_mapBackendIp [ sHostName ] ) .arg( sBaseName ); pRes = pItem->AddResource( sProtocol, sURI ); pRes->AddAttribute( "duration" , sDur ); pRes->AddAttribute( "size" , QString::number( nFileSize) ); */ // ---------------------------------------------------------------------- // Add Preview URI as albumArt // ---------------------------------------------------------------------- sURI = QString( "%1GetPreviewImage%2%3").arg( sURIBase ) .arg( sURIParams ) .arg( "&Width=160" ); pItem->SetPropValue( "albumArtURI", sURI ); Property *pProp = pItem->GetProperty("albumArtURI"); if (pProp) { pProp->AddAttribute("dlna:profileID", "PNG_TN"); pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0"); } }
bool UPnpCDSVideo::LoadVideos(const UPnpCDSRequest* pRequest, UPnpCDSExtensionResults* pResults, IDTokenMap tokens) { QString sRequestId = pRequest->m_sObjectId; uint16_t nCount = pRequest->m_nRequestedCount; uint16_t nOffset = pRequest->m_nStartingIndex; // We must use a dedicated connection to get an acccurate value from // FOUND_ROWS() MSqlQuery query(MSqlQuery::InitCon(MSqlQuery::kDedicatedConnection)); QString sql = "SELECT SQL_CALC_FOUND_ROWS " "v.intid, title, subtitle, filename, director, plot, " "rating, year, userrating, length, " "season, episode, coverfile, insertdate, host, " "g.genre, studio, collectionref, contenttype " "FROM videometadata v " "LEFT JOIN videogenre g ON g.intid=v.category " "%1 " // "ORDER BY title, season, episode " "LIMIT :OFFSET,:COUNT "; QStringList clauses; QString whereString = BuildWhereClause(clauses, tokens); query.prepare(sql.arg(whereString)); BindValues(query, tokens); query.bindValue(":OFFSET", nOffset); query.bindValue(":COUNT", nCount); if (!query.exec()) return false; while (query.next()) { int nVidID = query.value( 0).toInt(); QString sTitle = query.value( 1).toString(); QString sSubtitle = query.value( 2).toString(); QString sFilePath = query.value( 3).toString(); QString sDirector = query.value( 4).toString(); QString sPlot = query.value( 5).toString(); // QString sRating = query.value( 6).toString(); int nYear = query.value( 7).toInt(); // int nUserRating = query.value( 8).toInt(); uint32_t nLength = query.value( 9).toUInt(); // Convert from minutes to milliseconds nLength = (nLength * 60 *1000); int nSeason = query.value(10).toInt(); int nEpisode = query.value(11).toInt(); QString sCoverArt = query.value(12).toString(); QDateTime dtInsertDate = MythDate::as_utc(query.value(13).toDateTime()); QString sHostName = query.value(14).toString(); QString sGenre = query.value(15).toString(); // QString sStudio = query.value(16).toString(); // QString sCollectionRef = query.value(17).toString(); QString sContentType = query.value(18).toString(); // ---------------------------------------------------------------------- // Cache Host ip Address & Port // ---------------------------------------------------------------------- // If the host-name is empty then we assume it is our local host // otherwise, we look up the host's IP address and port. When the // client then trys to play the video it will be directed to the // host which actually has the content. if (!m_mapBackendIp.contains( sHostName )) { if (sHostName.isEmpty()) { m_mapBackendIp[sHostName] = gCoreContext->GetBackendServerIP4(); } else { m_mapBackendIp[sHostName] = gCoreContext->GetBackendServerIP4(sHostName); } } if (!m_mapBackendPort.contains( sHostName )) { if (sHostName.isEmpty()) { m_mapBackendPort[sHostName] = gCoreContext->GetBackendStatusPort(); } else { m_mapBackendPort[sHostName] = gCoreContext->GetBackendStatusPort(sHostName); } } // ---------------------------------------------------------------------- // Build Support Strings // ---------------------------------------------------------------------- QString sName = sTitle; if( !sSubtitle.isEmpty() ) { sName += " - " + sSubtitle; } QUrl URIBase; URIBase.setScheme("http"); URIBase.setHost(m_mapBackendIp[sHostName]); URIBase.setPort(m_mapBackendPort[sHostName]); CDSObject *pItem; if (sContentType == "MOVIE") { pItem = CDSObject::CreateMovie( CreateIDString(sRequestId, "Video", nVidID), sTitle, pRequest->m_sParentId ); } else { pItem = CDSObject::CreateVideoItem( CreateIDString(sRequestId, "Video", nVidID), sName, pRequest->m_sParentId ); } if (!sSubtitle.isEmpty()) pItem->SetPropValue( "description", sSubtitle ); else pItem->SetPropValue( "description", sPlot.left(128).append(" ...")); pItem->SetPropValue( "longDescription", sPlot ); pItem->SetPropValue( "director" , sDirector ); if (nEpisode > 0 || nSeason > 0) // There has got to be a better way { pItem->SetPropValue( "seriesTitle" , sTitle ); pItem->SetPropValue( "programTitle" , sSubtitle ); pItem->SetPropValue( "episodeNumber" , QString::number(nEpisode)); //pItem->SetPropValue( "episodeCount" , nEpisodeCount); } pItem->SetPropValue( "genre" , sGenre ); if (nYear > 1830 && nYear < 9999) pItem->SetPropValue( "date", QDate(nYear,1,1).toString(Qt::ISODate)); else pItem->SetPropValue( "date", UPnPDateTime::DateTimeFormat(dtInsertDate) ); // HACK: Windows Media Centre Compat (Not a UPnP or DLNA requirement, should only be done for WMC) // pItem->SetPropValue( "genre" , "[Unknown Genre]" ); // pItem->SetPropValue( "actor" , "[Unknown Author]" ); // pItem->SetPropValue( "creator" , "[Unknown Creator]" ); // pItem->SetPropValue( "album" , "[Unknown Album]" ); //// //pItem->SetPropValue( "producer" , ); //pItem->SetPropValue( "rating" , ); //pItem->SetPropValue( "actor" , ); //pItem->SetPropValue( "publisher" , ); //pItem->SetPropValue( "language" , ); //pItem->SetPropValue( "relation" , ); //pItem->SetPropValue( "region" , ); // Only add the reference ID for items which are not in the // 'All Videos' container QString sRefIDBase = QString("%1/Video").arg(m_sExtensionId); if ( pRequest->m_sParentId != sRefIDBase ) { QString sRefId = QString( "%1=%2") .arg( sRefIDBase ) .arg( nVidID ); pItem->SetPropValue( "refID", sRefId ); } // FIXME - If the slave or storage hosting this video is offline we // won't find it. We probably shouldn't list it, but better // still would be storing the filesize in the database so we // don't waste time re-checking it constantly QString sFullFileName = sFilePath; if (!QFile::exists( sFullFileName )) { StorageGroup sgroup("Videos"); sFullFileName = sgroup.FindFile( sFullFileName ); } QFileInfo fInfo( sFullFileName ); // ---------------------------------------------------------------------- // Add Video Resource Element based on File extension (HTTP) // ---------------------------------------------------------------------- QString sMimeType = HTTPRequest::GetMimeType( QFileInfo(sFilePath).suffix() ); // HACK: If we are dealing with a Sony Blu-ray player then we fake the // MIME type to force the video to appear // if ( pRequest->m_eClient == CDS_ClientSonyDB ) // { // sMimeType = "video/avi"; // } QUrl resURI = URIBase; QUrlQuery resQuery; resURI.setPath("/Content/GetVideo"); resQuery.addQueryItem("Id", QString::number(nVidID)); resURI.setQuery(resQuery); // DLNA requires a mimetype of video/mp2p for TS files, it's not the // correct mimetype, but then DLNA doesn't seem to care about such // things if (sMimeType == "video/mp2t" || sMimeType == "video/mp2p") sMimeType = "video/mpeg"; QString sProtocol = DLNA::ProtocolInfoString(UPNPProtocol::kHTTP, sMimeType); Resource *pRes = pItem->AddResource( sProtocol, resURI.toEncoded() ); pRes->AddAttribute( "size" , QString("%1").arg(fInfo.size()) ); pRes->AddAttribute( "duration", UPnPDateTime::resDurationFormat(nLength) ); // ---------------------------------------------------------------------- // Add Artwork // ---------------------------------------------------------------------- if (!sCoverArt.isEmpty() && (sCoverArt != "No Cover")) { PopulateArtworkURIS(pItem, nVidID, URIBase); } pResults->Add( pItem ); pItem->DecrRef(); } // Just in case FOUND_ROWS() should fail, ensure m_nTotalMatches contains // at least the size of this result set if (query.size() >= 0) pResults->m_nTotalMatches = query.size(); // Fetch the total number of matches ignoring any LIMITs query.prepare("SELECT FOUND_ROWS()"); if (query.exec() && query.next()) pResults->m_nTotalMatches = query.value(0).toUInt(); return true; }
void UPnpCDSTv::AddItem( const UPnpCDSRequest *pRequest, const QString &sObjectId, UPnpCDSExtensionResults *pResults, bool bAddRef, MSqlQuery &query ) { int nChanid = query.value( 0).toInt(); QDateTime dtStartTime = MythDate::as_utc(query.value(1).toDateTime()); QDateTime dtEndTime = MythDate::as_utc(query.value(2).toDateTime()); QString sTitle = query.value( 3).toString(); QString sSubtitle = query.value( 4).toString(); QString sDescription = query.value( 5).toString(); QString sCategory = query.value( 6).toString(); QString sHostName = query.value( 7).toString(); QString sRecGroup = query.value( 8).toString(); uint64_t nFileSize = query.value( 9).toULongLong(); QString sBaseName = query.value(10).toString(); QDateTime dtProgStart = MythDate::as_utc(query.value(11).toDateTime()); QDateTime dtProgEnd = MythDate::as_utc(query.value(12).toDateTime()); QString sStorageGrp = query.value(13).toString(); QString sInetRef = query.value(14).toString(); // ---------------------------------------------------------------------- // Cache Host ip Address & Port // ---------------------------------------------------------------------- if (!m_mapBackendIp.contains( sHostName )) m_mapBackendIp[ sHostName ] = gCoreContext->GetBackendServerIP4(sHostName); if (!m_mapBackendPort.contains( sHostName )) m_mapBackendPort[ sHostName ] = gCoreContext->GetBackendStatusPort(sHostName); // ---------------------------------------------------------------------- // Build Support Strings // ---------------------------------------------------------------------- QString sName = sTitle + ": " + (sSubtitle.isEmpty() ? sDescription.left(128) : sSubtitle); QString sURIBase = QString( "http://%1:%2/Content/" ) .arg( m_mapBackendIp [ sHostName ] ) .arg( m_mapBackendPort[ sHostName ] ); QString sURIParams = QString( "?ChanId=%1&StartTime=%2" ) .arg( nChanid ) .arg( dtStartTime.toString(Qt::ISODate)); QString sId = QString( "RecTv/0/item%1") .arg( sURIParams ); CDSObject *pItem = CDSObject::CreateVideoItem( sId, sName, sObjectId ); pItem->m_bRestricted = false; pItem->m_bSearchable = true; pItem->m_sWriteStatus = "WRITABLE"; if ( bAddRef ) { QString sRefId = QString( "%1/0/item%2") .arg( m_sExtensionId ) .arg( sURIParams ); pItem->SetPropValue( "refID", sRefId ); } pItem->SetPropValue( "genre" , sCategory ); pItem->SetPropValue( "longDescription", sDescription ); pItem->SetPropValue( "description" , sSubtitle ); //pItem->SetPropValue( "producer" , ); //pItem->SetPropValue( "rating" , ); //pItem->SetPropValue( "actor" , ); //pItem->SetPropValue( "director" , ); //pItem->SetPropValue( "publisher" , ); //pItem->SetPropValue( "language" , ); //pItem->SetPropValue( "relation" , ); //pItem->SetPropValue( "region" , ); // ---------------------------------------------------------------------- // Needed for Microsoft Media Player Compatibility // (Won't display correct Title without them) // ---------------------------------------------------------------------- pItem->SetPropValue( "creator" , "[Unknown Author]" ); pItem->SetPropValue( "artist" , "[Unknown Author]" ); pItem->SetPropValue( "album" , "[Unknown Series]" ); pItem->SetPropValue( "actor" , "[Unknown Author]" ); pItem->SetPropValue( "date" , dtStartTime.toString(Qt::ISODate)); pResults->Add( pItem ); // ---------------------------------------------------------------------- // Add Video Resource Element based on File contents/extension (HTTP) // ---------------------------------------------------------------------- StorageGroup sg(sStorageGrp, sHostName); QString sFilePath = sg.FindFile(sBaseName); QString sMimeType; if ( QFile::exists(sFilePath) ) sMimeType = HTTPRequest::TestMimeType( sFilePath ); else sMimeType = HTTPRequest::TestMimeType( sBaseName ); // If we are dealing with Window Media Player 12 (i.e. Windows 7) // then fake the Mime type to place the recorded TV in the // recorded TV section. if (pRequest->m_eClient == CDS_ClientWMP && pRequest->m_nClientVersion >= 12.0) { sMimeType = "video/x-ms-dvr"; } // If we are dealing with a Sony Blu-ray player then we fake the // MIME type to force the video to appear if ( pRequest->m_eClient == CDS_ClientSonyDB ) { sMimeType = "video/avi"; } // DLNA string below is temp fix for ps3 seeking. QString sProtocol = QString( "http-get:*:%1:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000" ).arg( sMimeType ); QString sURI = QString( "%1GetRecording%2").arg( sURIBase ) .arg( sURIParams ); // Sony BDPS370 requires a DLNA Profile Name // FIXME: detection to determine the correct DLNA Profile Name if (sMimeType == "video/mpeg") { sProtocol += ";DLNA.ORG_PN=MPEG_TS_SD_NA_ISO"; } Resource *pRes = pItem->AddResource( sProtocol, sURI ); uint uiStart = dtProgStart.toTime_t(); uint uiEnd = dtProgEnd.toTime_t(); uint uiDur = uiEnd - uiStart; MSqlQuery query2(MSqlQuery::InitCon()); query2.prepare( "SELECT data FROM recordedmarkup WHERE chanid=:CHANID AND " "starttime=:STARTTIME AND type = 33" ); query2.bindValue(":CHANID", (int)nChanid); query2.bindValue(":STARTTIME", dtStartTime); if (query2.exec() && query2.next()) uiDur = query2.value(0).toUInt() / 1000; QString sDur; sDur.sprintf("%02d:%02d:%02d", (uiDur / 3600) % 24, (uiDur / 60) % 60, uiDur % 60); LOG(VB_UPNP, LOG_DEBUG, "Duration: " + sDur ); pRes->AddAttribute( "duration" , sDur ); pRes->AddAttribute( "size" , QString::number( nFileSize) ); /* // ---------------------------------------------------------------------- // Add Video Resource Element based on File extension (mythtv) // ---------------------------------------------------------------------- sProtocol = QString( "myth:*:%1:*" ).arg( sMimeType ); sURI = QString( "myth://%1/%2" ) .arg( m_mapBackendIp [ sHostName ] ) .arg( sBaseName ); pRes = pItem->AddResource( sProtocol, sURI ); pRes->AddAttribute( "duration" , sDur ); pRes->AddAttribute( "size" , QString::number( nFileSize) ); */ // ---------------------------------------------------------------------- // Add Preview URI as <res> // MUST be _TN and 160px // ---------------------------------------------------------------------- sURI = QString( "%1GetPreviewImage%2%3").arg( sURIBase ) .arg( sURIParams ) .arg( "&Width=160" ); // TODO: Must be JPG for minimal compliance sProtocol = QString( "http-get:*:image/png:DLNA.ORG_PN=PNG_TN"); pItem->AddResource( sProtocol, sURI ); // ---------------------------------------------------------------------- // Add Artwork URI as albumArt // ---------------------------------------------------------------------- sURI = QString( "%1GetRecordingArtwork?Type=coverart&Inetref=%3") .arg( sURIBase ) .arg( sInetRef ); QList<Property*> propList = pItem->GetProperties("albumArtURI"); if (propList.size() >= 4) { // Prefer JPEG over PNG here, although PNG is allowed JPEG probably // has wider device support and crucially the filesizes are smaller // which speeds up loading times over the network // We MUST include the thumbnail size, but since some clients may use the // first image they see and the thumbnail is tiny, instead return the // medium first. The large could be very large, which is no good if the // client is pulling images for an entire list at once! // Medium Property *pProp = propList.at(0); if (pProp) { // Must be no more than 1024x768 pProp->m_sValue = sURI; pProp->m_sValue.append("&Width=1024&Height=768"); pProp->AddAttribute("dlna:profileID", "JPG_MED"); pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0"); } // Thumbnail pProp = propList.at(1); if (pProp) { // At least one albumArtURI must be a ThumbNail (TN) no larger // than 160x160, and it must also be a jpeg pProp->m_sValue = sURI; pProp->m_sValue.append("&Width=160&Height=160"); pProp->AddAttribute("dlna:profileID", "JPG_TN"); pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0"); } // Medium pProp = propList.at(2); if (pProp) { // Must be no more than 1024x768 pProp->m_sValue = sURI; pProp->m_sValue.append("&Width=1024&Height=768"); pProp->AddAttribute("dlna:profileID", "JPG_MED"); pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0"); } // Large pProp = propList.at(3); if (pProp) { // Must be no more than 4096x4096 - for our purposes, just return // a fullsize image pProp->m_sValue = sURI; pProp->AddAttribute("dlna:profileID", "JPG_LRG"); pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0"); } } }
void UPnpCDSMusic::AddItem( const UPnpCDSRequest *pRequest, const QString &sObjectId, UPnpCDSExtensionResults *pResults, bool bAddRef, MSqlQuery &query ) { QString sName; int nId = query.value( 0).toInt(); QString sArtist = query.value( 1).toString(); QString sAlbum = query.value( 2).toString(); QString sTitle = query.value( 3).toString(); QString sGenre = query.value( 4).toString(); int nYear = query.value( 5).toInt(); int nTrackNum = query.value( 6).toInt(); QString sDescription = query.value( 7).toString(); QString sFileName = query.value( 8).toString(); uint nLength = query.value( 9).toInt(); uint64_t nFileSize = (quint64)query.value(10).toULongLong(); #if 0 if ((nNodeIdx == 0) || (nNodeIdx == 1)) { sName = QString( "%1-%2:%3" ) .arg( sArtist) .arg( sAlbum ) .arg( sTitle ); } else #endif sName = sTitle; // ---------------------------------------------------------------------- // Cache Host ip Address & Port // ---------------------------------------------------------------------- #if 0 if (!m_mapBackendIp.contains( sHostName )) m_mapBackendIp[ sHostName ] = gCoreContext->GetSettingOnHost( "BackendServerIp", sHostName); if (!m_mapBackendPort.contains( sHostName )) m_mapBackendPort[ sHostName ] = gCoreContext->GetSettingOnHost("BackendStatusPort", sHostName); #endif QString sServerIp = gCoreContext->GetSetting( "BackendServerIp" ); QString sPort = gCoreContext->GetSetting( "BackendStatusPort" ); // ---------------------------------------------------------------------- // Build Support Strings // ---------------------------------------------------------------------- QString sURIBase = QString( "http://%1:%2/Content/" ) .arg( sServerIp ) .arg( sPort ); QString sURIParams = QString( "?Id=%1" ) .arg( nId ); QString sId = QString( "Music/1/item%1") .arg( sURIParams ); CDSObject *pItem = CDSObject::CreateMusicTrack( sId, sName, sObjectId ); pItem->m_bRestricted = true; pItem->m_bSearchable = true; pItem->m_sWriteStatus = "NOT_WRITABLE"; if ( bAddRef ) { QString sRefId = QString( "%1/0/item%2") .arg( m_sExtensionId ) .arg( sURIParams ); pItem->SetPropValue( "refID", sRefId ); } pItem->SetPropValue( "genre" , sGenre ); pItem->SetPropValue( "description" , sTitle ); pItem->SetPropValue( "longDescription" , sDescription); pItem->SetPropValue( "artist" , sArtist ); pItem->SetPropValue( "album" , sAlbum ); pItem->SetPropValue( "originalTrackNumber" , QString::number(nTrackNum)); if (nYear > 0 && nYear < 9999) pItem->SetPropValue( "date", QDate(nYear,1,1).toString(Qt::ISODate)); #if 0 pObject->AddProperty( new Property( "publisher" , "dc" )); pObject->AddProperty( new Property( "language" , "dc" )); pObject->AddProperty( new Property( "relation" , "dc" )); pObject->AddProperty( new Property( "rights" , "dc" )); pObject->AddProperty( new Property( "playlist" , "upnp" )); pObject->AddProperty( new Property( "storageMedium" , "upnp" )); pObject->AddProperty( new Property( "contributor" , "dc" )); pObject->AddProperty( new Property( "date" , "dc" )); #endif QString sArtURI = QString( "%1GetAlbumArt?Id=%2").arg( sURIBase ) .arg( nId ); QList<Property*> propList = pItem->GetProperties("albumArtURI"); if (propList.size() >= 4) { // Prefer JPEG over PNG here, although PNG is allowed JPEG probably // has wider device support and crucially the filesizes are smaller // which speeds up loading times over the network // We MUST include the thumbnail size, but since some clients may use the // first image they see and the thumbnail is tiny, instead return the // medium first. The large could be very large, which is no good if the // client is pulling images for an entire list at once! // Medium Property *pProp = propList.at(0); if (pProp) { // Must be no more than 1024x768 pProp->m_sValue = sArtURI; pProp->m_sValue.append("&Width=1024&Height=768"); pProp->AddAttribute("dlna:profileID", "JPG_MED"); pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0"); } // Thumbnail pProp = propList.at(1); if (pProp) { // At least one albumArtURI must be a ThumbNail (TN) no larger // than 160x160, and it must also be a jpeg pProp->m_sValue = sArtURI; pProp->m_sValue.append("&Width=160&Height=160"); pProp->AddAttribute("dlna:profileID", "JPG_TN"); pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0"); } // Small pProp = propList.at(2); if (pProp) { // Must be no more than 640x480 pProp->m_sValue = sArtURI; pProp->m_sValue.append("&Width=640&Height=480"); pProp->AddAttribute("dlna:profileID", "JPG_SM"); pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0"); } // Large pProp = propList.at(3); if (pProp) { // Must be no more than 4096x4096 - for our purposes, just return // a fullsize image pProp->m_sValue = sArtURI; pProp->AddAttribute("dlna:profileID", "JPG_LRG"); pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0"); } } pResults->Add( pItem ); // ---------------------------------------------------------------------- // Add Music Resource Element based on File extension (HTTP) // ---------------------------------------------------------------------- QFileInfo fInfo( sFileName ); QString sMimeType = HTTPRequest::GetMimeType( fInfo.suffix() ); QString sProtocol = QString( "http-get:*:%1:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000" ).arg( sMimeType ); QString sURI = QString( "%1GetMusic%2").arg( sURIBase ) .arg( sURIParams ); Resource *pRes = pItem->AddResource( sProtocol, sURI ); nLength /= 1000; QString sDur; sDur.sprintf("%02d:%02d:%02d", (nLength / 3600) % 24, (nLength / 60) % 60, nLength % 60); pRes->AddAttribute( "duration" , sDur ); if (nFileSize > 0) pRes->AddAttribute( "size" , QString::number( nFileSize) ); }
void UPnpCDSMusic::AddItem( const UPnpCDSRequest *pRequest, const QString &sObjectId, UPnpCDSExtensionResults *pResults, bool bAddRef, MSqlQuery &query ) { QString sName; int nId = query.value( 0).toInt(); QString sArtist = query.value( 1).toString(); QString sAlbum = query.value( 2).toString(); QString sTitle = query.value( 3).toString(); QString sGenre = query.value( 4).toString(); // int nYear = query.value( 5).toInt(); int nTrackNum = query.value( 6).toInt(); QString sDescription = query.value( 7).toString(); QString sFileName = query.value( 8).toString(); uint nLength = query.value( 9).toInt(); /* if ((nNodeIdx == 0) || (nNodeIdx == 1)) { sName = QString( "%1-%2:%3" ) .arg( sArtist) .arg( sAlbum ) .arg( sTitle ); } else */ sName = sTitle; //cout << nId << " " << sName << endl; // ---------------------------------------------------------------------- // Cache Host ip Address & Port // ---------------------------------------------------------------------- // if (!m_mapBackendIp.contains( sHostName )) // m_mapBackendIp[ sHostName ] = gCoreContext->GetSettingOnHost( "BackendServerIp", sHostName); // // if (!m_mapBackendPort.contains( sHostName )) // m_mapBackendPort[ sHostName ] = gCoreContext->GetSettingOnHost("BackendStatusPort", sHostName); QString sServerIp = gCoreContext->GetSetting( "BackendServerIp" ); QString sPort = gCoreContext->GetSetting( "BackendStatusPort" ); // ---------------------------------------------------------------------- // Build Support Strings // ---------------------------------------------------------------------- QString sURIBase = QString( "http://%1:%2/Myth/" ) .arg( sServerIp ) .arg( sPort ); QString sURIParams = QString( "?Id=%1" ) .arg( nId ); QString sId = QString( "Music/1/item%1") .arg( sURIParams ); CDSObject *pItem = CDSObject::CreateMusicTrack( sId, sName, sObjectId ); pItem->m_bRestricted = true; pItem->m_bSearchable = true; pItem->m_sWriteStatus = "NOT_WRITABLE"; if ( bAddRef ) { QString sRefId = QString( "%1/0/item%2") .arg( m_sExtensionId ) .arg( sURIParams ); pItem->SetPropValue( "refID", sRefId ); } pItem->SetPropValue( "genre" , sGenre ); pItem->SetPropValue( "description" , sTitle ); pItem->SetPropValue( "longDescription" , sDescription); pItem->SetPropValue( "artist" , sArtist ); pItem->SetPropValue( "album" , sAlbum ); pItem->SetPropValue( "originalTrackNumber" , QString::number( nTrackNum )); /* pObject->AddProperty( new Property( "publisher" , "dc" )); pObject->AddProperty( new Property( "language" , "dc" )); pObject->AddProperty( new Property( "relation" , "dc" )); pObject->AddProperty( new Property( "rights" , "dc" )); pObject->AddProperty( new Property( "playlist" , "upnp" )); pObject->AddProperty( new Property( "storageMedium" , "upnp" )); pObject->AddProperty( new Property( "contributor" , "dc" )); pObject->AddProperty( new Property( "date" , "dc" )); */ pResults->Add( pItem ); // ---------------------------------------------------------------------- // Add Music Resource Element based on File extension (HTTP) // ---------------------------------------------------------------------- QFileInfo fInfo( sFileName ); QString sMimeType = HTTPRequest::GetMimeType( fInfo.suffix() ); QString sProtocol = QString( "http-get:*:%1:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000" ).arg( sMimeType ); QString sURI = QString( "%1GetMusic%2").arg( sURIBase ) .arg( sURIParams ); Resource *pRes = pItem->AddResource( sProtocol, sURI ); nLength /= 1000; QString sDur; sDur.sprintf("%02d:%02d:%02d", (nLength / 3600) % 24, (nLength / 60) % 60, nLength % 60); pRes->AddAttribute( "duration" , sDur ); }