bool CHTTPRequestHandler::handleImageRequest(std::string p_sObjectId, CHTTPMessage* pRequest, CHTTPMessage* pResponse) { /*cout << "image request" << endl; cout << pRequest->getVarAsInt("width") << "x" << pRequest->getVarAsInt("height") << endl;*/ std::stringstream sSql; //OBJECT_TYPE nObjectType; std::string sExt; std::string sPath; std::string sMimeType; SQLQuery qry; string sDevice = pRequest->virtualFolderLayout(); if(pRequest->GetVarExists("vfolder")) { sDevice = pRequest->getGetVar("vfolder"); if(sDevice == "none") sDevice = ""; } object_id_t objectId = HexToInt(p_sObjectId); DbObject* tmp = DbObject::createFromObjectId(objectId, &qry, sDevice); if(tmp == NULL) { CSharedLog::Log(L_EXT, __FILE__, __LINE__, "unknown object id: %s", p_sObjectId.c_str()); return false; } DbObject obj = *tmp; delete tmp; /* bool result = false; // real image if(obj->type() >= ITEM_IMAGE_ITEM && obj->type() < ITEM_IMAGE_ITEM_MAX) { result = handleRealImageRequest(obj, pRequest, pResponse); } // audio file embedded image else if(obj->type() >= ITEM_AUDIO_ITEM && obj->type() < ITEM_AUDIO_ITEM_MAX) { result = handleAudioEmbeddedImageRequest(obj, pRequest, pResponse); } // video file thumbnail image else if(obj->type() >= ITEM_VIDEO_ITEM && obj->type() < ITEM_VIDEO_ITEM_MAX) { result = handleVideoImageRequest(obj, pRequest, pResponse); } else { CSharedLog::Log(L_EXT, __FILE__, __LINE__, "unsupported image request on object type %d", obj->type()); } delete obj; return result; */ string sql = qry.build(SQL_GET_OBJECT_DETAILS, objectId, sDevice); qry.select(sql); if(qry.eof()) { CSharedLog::Log(L_EXT, __FILE__, __LINE__, "unknown object id: %s", p_sObjectId.c_str()); return false; } sPath = qry.result()->asString("PATH") + qry.result()->asString("FILE_NAME"); sExt = ExtractFileExt(sPath); sMimeType = pRequest->DeviceSettings()->MimeType(sExt); bool audioFile = false; bool videoFile = false; bool hasCached = false; //OBJECT_TYPE type = (OBJECT_TYPE)qry.result()->asInt("TYPE"); if(obj.type() >= ITEM_IMAGE_ITEM && obj.type() < ITEM_IMAGE_ITEM_MAX) { //cout << "request image file " << sPath << endl; } else if(obj.type() >= ITEM_AUDIO_ITEM && obj.type() < ITEM_AUDIO_ITEM_MAX) { //cout << "request image from audio file " << sPath << endl; audioFile = true; } else if(obj.type() >= ITEM_VIDEO_ITEM && obj.type() < ITEM_VIDEO_ITEM_MAX) { //cout << "request image from video file " << sPath << endl; videoFile = true; if(qry.result()->asUInt("ALBUM_ART_ID") > 0) { sPath = PathFinder::findThumbnailsDir() + qry.result()->asString("ALBUM_ART_ID") + ".jpg"; hasCached = fuppes::File::exists(sPath); sExt = "jpg"; sMimeType = pRequest->DeviceSettings()->MimeType(sExt); } } else { CSharedLog::Log(L_EXT, __FILE__, __LINE__, "unsupported image request on object type %d", obj.type()); return false; } if(!fuppes::File::exists(sPath)) { CSharedLog::Log(L_EXT, __FILE__, __LINE__, "file: %s not found", sPath.c_str()); return false; } //cout << "image request: " << sPath << endl; int width = pRequest->getVarAsInt("width"); int height = pRequest->getVarAsInt("height"); /*int less = pRequest->getVarAsInt("less"); int greater = pRequest->getVarAsInt("greater");*/ // transcode | scale request via GET // and/or embedded image from audio file if((width > 0 || height > 0 || audioFile || videoFile) && !hasCached) { CSharedLog::Log(L_EXT, __FILE__, __LINE__, "GET transcode %s - %dx%d", sPath.c_str(), width, height); size_t inSize = 0; size_t outSize = 0; unsigned char* inBuffer = (unsigned char*)malloc(1); unsigned char* outBuffer = (unsigned char*)malloc(1); char tmpMime[100]; //memset(tmpMime, 0, 1); // embedded image from audio or video file bool transcode = true; if(audioFile || videoFile) { string plugin; if(audioFile) { plugin = "taglib"; } else if(videoFile) { plugin = "ffmpegthumbnailer"; transcode = false; } CMetadataPlugin* metadata = CPluginMgr::metadataPlugin(plugin); if(!metadata) { CSharedLog::Log(L_EXT, __FILE__, __LINE__, "metadata plugin %s not found", plugin.c_str()); free(inBuffer); free(outBuffer); //free(tmpMime); return false; } metadata->openFile(sPath); inSize = 0; if(!metadata->readImage(&tmpMime[0], &inBuffer, &inSize)) { metadata->closeFile(); CSharedLog::Log(L_EXT, __FILE__, __LINE__, "metadata plugin %s failed to read embedded image", "taglib"); free(inBuffer); free(outBuffer); //free(tmpMime); return false; } metadata->closeFile(); // get the mime type and the extension of the extracted file sMimeType = tmpMime; sExt = pRequest->DeviceSettings()->extensionByMimeType(sMimeType); delete metadata; } // embedded image // an actual image file or a cached image is requested else { if(hasCached) { transcode = false; } std::fstream fsImg; fsImg.open(sPath.c_str(), ios::binary|ios::in); if(fsImg.fail() == 1) { CSharedLog::Log(L_EXT, __FILE__, __LINE__, "failed to load image file %s", sPath.c_str()); free(inBuffer); free(outBuffer); //free(tmpMime); return false; } fsImg.seekg(0, ios::end); inSize = streamoff(fsImg.tellg()); fsImg.seekg(0, ios::beg); inBuffer = (unsigned char*)realloc(inBuffer, inSize); fsImg.read((char*)inBuffer, inSize); fsImg.close(); } // image file if(transcode) { CTranscoderBase* transcoder = CPluginMgr::transcoderPlugin("magickWand"); if(transcoder == NULL) { CSharedLog::Log(L_EXT, __FILE__, __LINE__, "image magick transcoder not available"); free(inBuffer); free(outBuffer); //free(tmpMime); return false; } CFileSettings* settings = new CFileSettings(pRequest->DeviceSettings()->FileSettings(sExt)); // TODO fixme. Robert: What are we supposed to be fixing? if(!settings->pImageSettings) { settings->pImageSettings = new CImageSettings(); } settings->pImageSettings->nHeight = height; settings->pImageSettings->nWidth = width; settings->pImageSettings->bGreater = true; settings->pImageSettings->bLess = true; transcoder->TranscodeMem(settings, (const unsigned char**)&inBuffer, inSize, &outBuffer, &outSize); delete settings; delete transcoder; pResponse->SetBinContent((char*)outBuffer, outSize); // todo: get the actual image dimensions //width = width; //height = height: sMimeType = pRequest->DeviceSettings()->MimeType(sExt); } else { // transcode pResponse->SetBinContent((char*)inBuffer, inSize); width = obj.details()->width(); height = obj.details()->height(); } /* pResponse->SetMessageType(HTTP_MESSAGE_TYPE_200_OK); #warning todo: set correct mime type // the current mimeType variable only holds a dummy mime type set above pResponse->SetContentType(sMimeType); */ free(inBuffer); free(outBuffer); //free(tmpMime); //return true; } // embedded audio or width|height via GET // a real image file else { width = obj.details()->width(); height = obj.details()->height(); if(pRequest->DeviceSettings()->DoTranscode(sExt, qry.result()->asString("AUDIO_CODEC"), qry.result()->asString("VIDEO_CODEC"))) { CSharedLog::Log(L_EXT, __FILE__, __LINE__, "transcode %s", sPath.c_str()); sMimeType = pRequest->DeviceSettings()->MimeType(sExt, qry.result()->asString("AUDIO_CODEC"), qry.result()->asString("VIDEO_CODEC")); if(pRequest->GetMessageType() == HTTP_MESSAGE_TYPE_GET) { DbObject object(qry.result()); if(!pResponse->TranscodeContentFromFile(sPath, &object)) { return false; } } else if(pRequest->GetMessageType() == HTTP_MESSAGE_TYPE_HEAD) { // mark the head response as chunked so // the correct header will be build pResponse->SetIsBinary(true); if(pRequest->DeviceSettings()->TranscodingHTTPResponse(sExt) == RESPONSE_CHUNKED) { pResponse->SetTransferEncoding(HTTP_TRANSFER_ENCODING_CHUNKED); } else if(pRequest->DeviceSettings()->TranscodingHTTPResponse(sExt) == RESPONSE_STREAM) { pResponse->SetTransferEncoding(HTTP_TRANSFER_ENCODING_NONE); } } } else { sMimeType = pRequest->DeviceSettings()->MimeType(sExt, qry.result()->asString("AUDIO_CODEC"), qry.result()->asString("VIDEO_CODEC")); pResponse->LoadContentFromFile(sPath); } } // a real image file // dlna if(//(pRequest->dlnaGetContentFeatures() == true) && (pRequest->DeviceSettings()->dlnaVersion() != CMediaServerSettings::dlna_none)) { std::string mimeType; std::string profile; DLNA::getImageProfile(sExt, width, height, profile, mimeType); sMimeType = mimeType; std::string dlnaFeatures = CContentDirectory::buildDlnaInfo(false, profile); std::string dlnaMode = "Interactive"; pResponse->dlnaContentFeatures(dlnaFeatures); pResponse->dlnaTransferMode(dlnaMode); } // we always set the response type to "200 OK" // if the message should be a "206 partial content" // CHTTPServer will change the type pResponse->SetMessageType(HTTP_MESSAGE_TYPE_200_OK); pResponse->SetContentType(sMimeType); return true; }
bool CUPnPSearch::prepareSQL() { if(m_query.length() > 0 && m_queryCount.length() > 0) { return true; } string sOpenBr; string sProp; string sOp; string sVal; string sCloseBr; string sLogOp; string sPrevLog; bool bNumericProp = false; bool bLikeOp = false; bool bBuildOK = false; bool bVirtualSearch = false; bool bFirst = true; stringstream sSql; SQLQuery qry; string sDevice = virtualFolderLayout(); bVirtualSearch = !sDevice.empty(); stringstream tmp; unsigned int nContainerId = GetObjectIDAsUInt(); //GetContainerIdAsUInt(); if(nContainerId > 0) { if(m_sParentIds.length() == 0) { stringstream sIds; sIds << nContainerId; BuildParentIdList(&qry, sIds.str(), sDevice, &m_sParentIds); m_sParentIds = m_sParentIds + sIds.str(); //cout << "PARENT ID LIST: " << m_sParentIds << endl; fflush(stdout); } } m_query = qry.build(SQL_SEARCH_PART_SELECT_FIELDS, 0, sDevice); //"select * "; m_queryCount = qry.build(SQL_SEARCH_PART_SELECT_COUNT, 0, sDevice); //"select count(*) as COUNT "; /*sSql << "from " << " MAP_OBJECTS m, OBJECTS o " << " left join OBJECT_DETAILS d on (d.ID = o.DETAIL_ID) " << "where " << " o.DEVICE " << sDevice << " and " << " m.DEVICE " << sDevice << " and " << " o.OBJECT_ID = m.OBJECT_ID "; */ sSql << qry.build(SQL_SEARCH_PART_FROM, 0, sDevice); if(m_sParentIds.length() > 0) { sSql << " and " << " PARENT_ID in (" << m_sParentIds << ") "; } m_sSearchCriteria = StringReplace(m_sSearchCriteria, """, "\""); m_sSearchCriteria = StringReplace(m_sSearchCriteria, "@", ":"); RegEx rxSearch("(\\(*) *([\\w+:*\\w*]+) ([=|!=|<|<=|>|>=|contains|doesNotContain|derivedfrom|exists]+) (\".*?[^\\\\]\"|true|false) *(\\)*) *([and|or]*)"); if(rxSearch.Search(m_sSearchCriteria.c_str())) { do { //cout << rxSearch.Match(1) << " X " << rxSearch.Match(2) << " X " << rxSearch.Match(3) << " X " << rxSearch.Match(4) << " X " << rxSearch.Match(5) << " X " << rxSearch.Match(6) << endl; sOpenBr = rxSearch.Match(1); sProp = rxSearch.Match(2); sOp = rxSearch.Match(3); sVal = rxSearch.Match(4); sCloseBr = rxSearch.Match(5); sLogOp = rxSearch.Match(6); if(sOp.compare("exists") == 0) { bBuildOK = false; if(sProp.compare(":refID") == 0) { bBuildOK = true; sProp = "REF_ID"; if(sVal.compare("true") == 0) sOp = ">"; else if (sVal.compare("false") == 0) sOp = "="; sVal = "0"; } } else { bBuildOK = true; // replace property if(sProp.compare("upnp:class") == 0) { sProp = "TYPE"; bNumericProp = true; } else if(sProp.compare("dc:title") == 0) { sProp = "TITLE"; bNumericProp = false; } else if(sProp.compare("upnp:artist") == 0) { sProp = "AV_ARTIST"; bNumericProp = false; } else if(sProp.compare("upnp:genre") == 0) { sProp = "AV_GENRE"; bNumericProp = false; } else if(sProp.compare("upnp:album") == 0) { sProp = "AV_ALBUM"; bNumericProp = false; } else if(sProp.compare("res:protocolInfo") == 0) { sPrevLog = sLogOp; continue; } else { bBuildOK = false; } // replace operator bLikeOp = false; if(sOp.compare("contains") == 0) { if(bNumericProp) sOp = "in"; else sOp = "like"; bLikeOp = true; } else if(sOp.compare("derivedfrom") == 0) { sOp = "in"; } else if(sOp.compare("=") == 0) { } else { bBuildOK = false; } // trim value //cout << "Val: " << sVal << " => "; sVal = sVal.substr(1, sVal.length() - 2); //cout << sVal << endl; // replace value if(sProp.compare("TYPE") == 0) { sOp = "in"; tmp.str(""); if(sVal.compare("object.item") == 0) { sOp = ">="; tmp << ITEM; } else if(sVal.compare("object.item.imageItem") == 0) { sOp = "between"; tmp << ITEM_IMAGE_ITEM << " and " << (ITEM_IMAGE_ITEM_MAX - 1); //sVal = "(110, 111)"; } else if(sVal.compare("object.item.audioItem") == 0) { sOp = "between"; tmp << ITEM_AUDIO_ITEM << " and " << (ITEM_AUDIO_ITEM_MAX - 1); //sVal = "(120, 121, 122)"; } else if(sVal.compare("object.item.videoItem") == 0){ sOp = "between"; tmp << ITEM_VIDEO_ITEM << " and " << (ITEM_VIDEO_ITEM_MAX - 1); //sVal = "(130, 131, 132, 133)"; } else if(sVal.compare("object.container.person.musicArtist") == 0) { sOp = "="; tmp << CONTAINER_PERSON_MUSIC_ARTIST; //sVal = "(11)"; } else if(sVal.compare("object.container.album.musicAlbum") == 0) { sOp = "="; tmp << CONTAINER_ALBUM_MUSIC_ALBUM; //sVal = "(31)"; } else if(sVal.compare("object.container.genre.musicGenre") == 0) { sOp = "="; tmp << CONTAINER_GENRE_MUSIC_GENRE; //sVal = "(41)"; } else if(sVal.compare("object.container.genre.movieGenre") == 0) { sOp = "="; tmp << CONTAINER_GENRE_MOVIE_GENRE; } else if (sVal.compare("object.container.playlistContainer") == 0) { sOp = "="; tmp << CONTAINER_PLAYLIST_CONTAINER; //sVal = "201)"; } else bBuildOK = false; if(bBuildOK) sVal = tmp.str(); tmp.str(""); } else if (!bNumericProp) { if(bLikeOp) sVal = "'%" + sVal + "%'"; else sVal = "'" + sVal + "'"; } } // != exists if(bBuildOK) { if(bFirst) { sSql << " and "; bFirst = false; } sSql << sPrevLog << " " << sOpenBr << sProp << " " << sOp << " " << sVal << sCloseBr << " "; sPrevLog = sLogOp; } else { cout << "error parsing search request!" << endl << "please file a bugreport containing the following lines: " << endl << endl << "=== CUT ===" << endl << m_sSearchCriteria << endl << "=== CUT ===" << endl; } } while (rxSearch.SearchAgain()); } m_query += sSql.str(); m_queryCount += sSql.str(); sSql.str(""); // order by sSql << " order by " << m_sortCriteriaSQL << " "; // limit if((m_nRequestedCount > 0) || (m_nStartingIndex > 0)) { sSql << " limit " << m_nStartingIndex << ", "; if(m_nRequestedCount == 0) sSql << "-1"; else sSql << m_nRequestedCount; } // order by and limit are not needed // in a count request m_query += sSql.str(); //cout << m_query << endl; return true; }
bool CHTTPRequestHandler::handleAVItemRequest(std::string p_sObjectId, CHTTPMessage* pRequest, CHTTPMessage* pResponse, bool audio, std::string requestExt) { std::stringstream sSql; //OBJECT_TYPE nObjectType; std::string sExt; std::string sPath; std::string sMimeType; string targetExt; SQLQuery qry; bool bResult = true; bool transcode = false; unsigned int objectId = HexToInt(p_sObjectId); string sDevice = pRequest->virtualFolderLayout(); string sql = qry.build(SQL_GET_OBJECT_DETAILS, objectId, sDevice); qry.select(sql); if(qry.eof()) { CSharedLog::Log(L_EXT, __FILE__, __LINE__, "unknown object id: %s", p_sObjectId.c_str()); return false; } // TODO Object Types are still on the todo list //nObjectType = (OBJECT_TYPE)atoi(pDb->GetResult()->asString("TYPE").c_str()); //cout << "OBJECT_TYPE: " << nObjectType << endl; sPath = qry.result()->asString("PATH") + qry.result()->asString("FILE_NAME"); sExt = ExtractFileExt(sPath); if(!fuppes::File::exists(sPath)) { CSharedLog::Log(L_EXT, __FILE__, __LINE__, "file: %s not found", sPath.c_str()); return false; } // check for subtitles if(!audio && requestExt.compare("srt") == 0) { sPath = TruncateFileExt(sPath) + "." + requestExt; cout << "SUB REQUEST: " << sPath << "*" << endl; if(!fuppes::File::exists(sPath)) return false; pResponse->LoadContentFromFile(sPath); pResponse->SetMessageType(HTTP_MESSAGE_TYPE_200_OK); pResponse->SetContentType("application/x-subrip"); return true; } transcode = pRequest->DeviceSettings()->DoTranscode(sExt, qry.result()->asString("AUDIO_CODEC"), qry.result()->asString("VIDEO_CODEC")); sMimeType = pRequest->DeviceSettings()->MimeType(sExt, qry.result()->asString("AUDIO_CODEC"), qry.result()->asString("VIDEO_CODEC")); targetExt = pRequest->DeviceSettings()->Extension(sExt, qry.result()->asString("AUDIO_CODEC"), qry.result()->asString("VIDEO_CODEC")); if(!transcode) { pResponse->LoadContentFromFile(sPath); } else { CSharedLog::Log(L_EXT, __FILE__, __LINE__, "transcode %s", sPath.c_str()); if(pRequest->GetMessageType() == HTTP_MESSAGE_TYPE_GET) { DbObject object(qry.result()); bResult = pResponse->TranscodeContentFromFile(sPath, &object); } else if(pRequest->GetMessageType() == HTTP_MESSAGE_TYPE_HEAD) { // mark the head response as chunked so // the correct header will be build pResponse->SetIsBinary(true); bResult = true; if(pRequest->DeviceSettings()->TranscodingHTTPResponse(sExt) == RESPONSE_CHUNKED) { pResponse->SetTransferEncoding(HTTP_TRANSFER_ENCODING_CHUNKED); } else if(pRequest->DeviceSettings()->TranscodingHTTPResponse(sExt) == RESPONSE_STREAM) { pResponse->SetTransferEncoding(HTTP_TRANSFER_ENCODING_NONE); } } } // dlna if(//(CPluginMgr::dlnaPlugin() != NULL) && //(pRequest->dlnaGetContentFeatures() == true) && (pRequest->DeviceSettings()->dlnaVersion() != CMediaServerSettings::dlna_none)) { bool hasProfile = false; std::string profile; if(audio) { int channels = 0; int bitrate = 0; if(!transcode) { channels = qry.result()->asInt("A_CHANNELS"); bitrate = qry.result()->asInt("A_BITRATE"); } if(audio) { hasProfile = DLNA::getAudioProfile(targetExt, channels, bitrate, profile, sMimeType); } else { //hasProfile = CPluginMgr::dlnaPlugin()->getVideoProfile(targetExt, channels, bitrate, &profile, &sMimeType); } } else { //CPluginMgr::dlnaPlugin()->getVideoProfile(); } //if(hasProfile) { std::string dlnaFeatures = CContentDirectory::buildDlnaInfo(transcode, profile); std::string dlnaMode = (transcode ? "Streaming" : "Interactive"); pResponse->dlnaContentFeatures(dlnaFeatures); pResponse->dlnaTransferMode(dlnaMode); //} } // end dlna // we always set the response type to "200 OK" // if the message should be a "206 partial content" // CHTTPServer will change the type pResponse->SetMessageType(HTTP_MESSAGE_TYPE_200_OK); pResponse->SetContentType(sMimeType); bResult = true; return bResult; }