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 ); }
UPnpCDSExtensionResults *UPnpCDSExtension::ProcessKey( UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, QStringList &idPath ) { pResults->m_nTotalMatches = 0; pResults->m_nUpdateID = 1; // ---------------------------------------------------------------------- // // ---------------------------------------------------------------------- QString sKey = idPath.last().section( '=', 1, 1 ); sKey = QUrl::fromPercentEncoding(sKey.toUtf8()); if (sKey.length() > 0) { int nNodeIdx = idPath[ idPath.count() - 2 ].toInt(); switch( pRequest->m_eBrowseFlag ) { case CDS_BrowseMetadata: { UPnpCDSRootInfo *pInfo = GetRootInfo( nNodeIdx ); if (pInfo == NULL) return pResults; pRequest->m_sParentId = RemoveToken( "/", pRequest->m_sObjectId, 1 ); // -------------------------------------------------------------- // Since Key is not always the title, we need to lookup title. // -------------------------------------------------------------- MSqlQuery query(MSqlQuery::InitCon()); if (query.isConnected()) { QString sSQL = QString( pInfo->sql ) .arg( pInfo->where ); // -=>TODO: There is a problem when called for an Item, instead of a container // sKey = '<KeyName>/item?ChanId' which is incorrect. query.prepare ( sSQL ); query.bindValue( ":KEY", sKey ); if (query.exec() && query.next()) { // ---------------------------------------------- // Return Container Object Only // ---------------------------------------------- pResults->m_nTotalMatches = 1; pResults->m_nUpdateID = 1; CDSObject *pItem = CreateContainer( pRequest->m_sObjectId, query.value(1).toString(), pRequest->m_sParentId ); pItem->SetChildCount( GetDistinctCount( pInfo )); pResults->Add( pItem ); } } break; } case CDS_BrowseDirectChildren: { CreateItems( pRequest, pResults, nNodeIdx, sKey, true ); break; } case CDS_BrowseUnknown: default: break; } } return pResults; }
UPnpCDSExtensionResults *UPnpCDSExtension::ProcessRoot( UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, QStringList &/*idPath*/ ) { pResults->m_nTotalMatches = 0; pResults->m_nUpdateID = 1; short nRootCount = GetRootCount(); switch( pRequest->m_eBrowseFlag ) { case CDS_BrowseMetadata: { // -------------------------------------------------------------- // Return Root Object Only // -------------------------------------------------------------- pResults->m_nTotalMatches = 1; pResults->m_nUpdateID = 1; CDSObject *pRoot = CreateContainer( m_sExtensionId, m_sName, "0"); pRoot->SetChildCount( nRootCount ); pResults->Add( pRoot ); break; } case CDS_BrowseDirectChildren: { VERBOSE(VB_UPNP, "CDS_BrowseDirectChildren"); pResults->m_nUpdateID = 1; pResults->m_nTotalMatches = nRootCount ; if ( pRequest->m_nRequestedCount == 0) pRequest->m_nRequestedCount = nRootCount ; short nStart = Max( pRequest->m_nStartingIndex, short( 0 )); short nEnd = Min( nRootCount, short( nStart + pRequest->m_nRequestedCount)); if (nStart < nRootCount) { for (short nIdx = nStart; nIdx < nEnd; nIdx++) { UPnpCDSRootInfo *pInfo = GetRootInfo( nIdx ); if (pInfo != NULL) { QString sId = QString( "%1/%2" ).arg( pRequest->m_sObjectId ) .arg( nIdx ); CDSObject *pItem = CreateContainer( sId, QObject::tr( pInfo->title ), m_sExtensionId ); pItem->SetChildCount( GetDistinctCount( pInfo ) ); pResults->Add( pItem ); } } } } case CDS_BrowseUnknown: default: break; } return pResults; }
UPnpCDSExtensionResults *UPnpCDSExtension::ProcessContainer( UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, int nNodeIdx, QStringList &/*idPath*/ ) { pResults->m_nUpdateID = 1; pResults->m_nTotalMatches = 0; UPnpCDSRootInfo *pInfo = GetRootInfo( nNodeIdx ); if (pInfo == NULL) return pResults; switch( pRequest->m_eBrowseFlag ) { case CDS_BrowseMetadata: { // -------------------------------------------------------------- // Return Container Object Only // -------------------------------------------------------------- pResults->m_nTotalMatches = 1; pResults->m_nUpdateID = 1; CDSObject *pItem = CreateContainer( pRequest->m_sObjectId, QObject::tr( pInfo->title ), m_sExtensionId ); pItem->SetChildCount( GetDistinctCount( pInfo )); pResults->Add( pItem ); break; } case CDS_BrowseDirectChildren: { pResults->m_nTotalMatches = GetDistinctCount( pInfo ); pResults->m_nUpdateID = 1; if (pRequest->m_nRequestedCount == 0) pRequest->m_nRequestedCount = SHRT_MAX; MSqlQuery query(MSqlQuery::InitCon()); if (query.isConnected()) { // Remove where clause placeholder. QString sSQL = pInfo->sql; sSQL.remove( "%1" ); sSQL += QString( " LIMIT %2, %3" ) .arg( pRequest->m_nStartingIndex ) .arg( pRequest->m_nRequestedCount ); query.prepare( sSQL ); if (query.exec()) { while(query.next()) { QString sKey = query.value(0).toString(); QString sTitle = query.value(1).toString(); long nCount = query.value(2).toInt(); if (sTitle.length() == 0) sTitle = "(undefined)"; QString sId = QString( "%1/key=%2" ) .arg( pRequest->m_sParentId ) .arg( sKey ); CDSObject *pRoot = CreateContainer( sId, sTitle, pRequest->m_sParentId ); pRoot->SetChildCount( nCount ); pResults->Add( pRoot ); } } } break; } case CDS_BrowseUnknown: break; } return pResults; }
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 CDSObject::toXml( QTextStream &os ) //, FilterMap &filter ) { QString sEndTag = ""; bool bFilter = false; // -=>TODO: Need to add Filter Support // if (filter.find( "*" ) != filter.end()) // bFilter = false; switch( m_eType ) { case OT_Container: { os << "<container id=\"" << m_sId << "\" parentID=\"" << m_sParentId << "\" childCount=\"" << GetChildCount() << "\" restricted=\"" << GetBool( m_bRestricted ) << "\" searchable=\"" << GetBool( m_bSearchable ) << "\" >"; sEndTag = "</container>"; break; } case OT_Item: { os << "<item id=\"" << m_sId << "\" parentID=\"" << m_sParentId << "\" restricted=\"" << GetBool( m_bRestricted ) << "\" >"; sEndTag = "</item>"; break; } default: break; } os << "<dc:title>" << m_sTitle << "</dc:title>"; os << "<upnp:class>" << m_sClass << "</upnp:class>"; // ---------------------------------------------------------------------- // Output all Properties // ---------------------------------------------------------------------- for( PropertiesIterator it( m_properties ); it.current(); ++it ) { Property *pProp = it.current(); if (pProp->m_bRequired || (pProp->m_sValue.length() > 0)) { QString sName; if (pProp->m_sNameSpace.length() > 0) sName = pProp->m_sNameSpace + ":" + pProp->m_sName; else sName = pProp->m_sName; if (pProp->m_bRequired || !bFilter ) //|| ( filter.find( sName ) != filter.end()) { os << "<" << sName << ">"; os << pProp->m_sValue; os << "</" << sName << ">"; } } } // ---------------------------------------------------------------------- // Output any Res Elements // ---------------------------------------------------------------------- for ( Resource *pRes = m_resources.first(); pRes != NULL; pRes = m_resources.next() ) { os << "<res protocolInfo=\"" << pRes->m_sProtocolInfo << "\" "; for (NameValue *pNV = pRes->m_lstAttributes.first(); pNV != NULL; pNV = pRes->m_lstAttributes.next()) { os << pNV->sName << "=\"" << pNV->sValue << "\" "; } os << ">" << pRes->m_sURI; os << "</res>\r\n"; } // ---------------------------------------------------------------------- // Output any children // ---------------------------------------------------------------------- for ( CDSObject *pObject = m_children.first(); pObject != NULL; pObject = m_children.next() ) { pObject->toXml( os ); //, filter ); } // ---------------------------------------------------------------------- // Close Element Tag // ---------------------------------------------------------------------- os << sEndTag; }