mirror of
				https://github.com/RetroDECK/ES-DE.git
				synced 2025-04-10 19:15:13 +00:00 
			
		
		
		
	Replaced AsyncReqComponent with some handles.
UI is no longer completely blocked during asynchronous operations.
This commit is contained in:
		
							parent
							
								
									dbde900629
								
							
						
					
					
						commit
						1e8b040f73
					
				|  | @ -135,6 +135,7 @@ endif() | |||
| #------------------------------------------------------------------------------- | ||||
| #define basic sources and headers | ||||
| set(ES_HEADERS | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/src/AsyncHandle.h | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/src/AudioManager.h | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/src/EmulationStation.h | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/src/FileData.h | ||||
|  |  | |||
							
								
								
									
										43
									
								
								src/AsyncHandle.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/AsyncHandle.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | |||
| #pragma once | ||||
| 
 | ||||
| enum AsyncHandleStatus | ||||
| { | ||||
| 	ASYNC_IN_PROGRESS, | ||||
| 	ASYNC_ERROR, | ||||
| 	ASYNC_DONE | ||||
| }; | ||||
| 
 | ||||
| // Handle for some asynchronous operation.
 | ||||
| class AsyncHandle | ||||
| { | ||||
| public: | ||||
| 	AsyncHandle() : mStatus(ASYNC_IN_PROGRESS) {}; | ||||
| 
 | ||||
| 	virtual void update() = 0; | ||||
| 
 | ||||
| 	// Update and return the latest status.
 | ||||
| 	inline AsyncHandleStatus status() { update(); return mStatus; } | ||||
| 
 | ||||
| 	// User-friendly string of our current status.  Will return error message if status() == SEARCH_ERROR.
 | ||||
| 	inline std::string getStatusString() | ||||
| 	{ | ||||
| 		switch(mStatus) | ||||
| 		{ | ||||
| 		case ASYNC_IN_PROGRESS: | ||||
| 			return "in progress"; | ||||
| 		case ASYNC_ERROR: | ||||
| 			return mError; | ||||
| 		case ASYNC_DONE: | ||||
| 			return "done"; | ||||
| 		default: | ||||
| 			return "something impossible has occured; row, row, fight the power"; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| protected: | ||||
| 	inline void setStatus(AsyncHandleStatus status) { mStatus = status; } | ||||
| 	inline void setError(const std::string& error) { setStatus(ASYNC_ERROR); mError = error; } | ||||
| 
 | ||||
| 	std::string mError; | ||||
| 	AsyncHandleStatus mStatus; | ||||
| }; | ||||
|  | @ -144,14 +144,9 @@ HttpReq::Status HttpReq::status() | |||
| 	return mStatus; | ||||
| } | ||||
| 
 | ||||
| std::string HttpReq::getContent() | ||||
| std::string HttpReq::getContent() const | ||||
| { | ||||
| 	if(mStatus != REQ_SUCCESS) | ||||
| 	{ | ||||
| 		LOG(LogError) << "Called getContent() on an incomplete HttpReq!"; | ||||
| 		return ""; | ||||
| 	} | ||||
| 
 | ||||
| 	assert(mStatus == REQ_SUCCESS); | ||||
| 	return mContent.str(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -42,7 +42,7 @@ public: | |||
| 
 | ||||
| 	std::string getErrorMsg(); | ||||
| 
 | ||||
| 	std::string getContent(); | ||||
| 	std::string getContent() const; // mStatus must be REQ_SUCCESS
 | ||||
| 
 | ||||
| 	static std::string urlEncode(const std::string &s); | ||||
| 	static bool isUrl(const std::string& s); | ||||
|  |  | |||
|  | @ -116,6 +116,8 @@ void ComponentList::onCursorChanged(const CursorState& state) | |||
| 			mCameraOffset = 0; | ||||
| 		else if(mCameraOffset + mSize.y() > totalHeight) | ||||
| 			mCameraOffset = totalHeight - mSize.y(); | ||||
| 	}else{ | ||||
| 		mCameraOffset = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	updateHelpPrompts(); | ||||
|  |  | |||
|  | @ -259,7 +259,7 @@ private: | |||
| 			ss << getSelectedObjects().size() << " SELECTED"; | ||||
| 			mText.setText(ss.str()); | ||||
| 			mText.setSize(0, mText.getSize().y()); | ||||
| 			setSize(mText.getSize().x() + mRightArrow.getSize().x() + 16, mText.getSize().y()); | ||||
| 			setSize(mText.getSize().x() + mRightArrow.getSize().x() + 24, mText.getSize().y()); | ||||
| 			if(mParent) // hack since theres no "on child size changed" callback atm...
 | ||||
| 				mParent->onSizeChanged(); | ||||
| 		}else{ | ||||
|  | @ -270,7 +270,7 @@ private: | |||
| 				{ | ||||
| 					mText.setText(strToUpper(it->name)); | ||||
| 					mText.setSize(0, mText.getSize().y()); | ||||
| 					setSize(mText.getSize().x() + mLeftArrow.getSize().x() + mRightArrow.getSize().x() + 16, mText.getSize().y()); | ||||
| 					setSize(mText.getSize().x() + mLeftArrow.getSize().x() + mRightArrow.getSize().x() + 24, mText.getSize().y()); | ||||
| 					if(mParent) // hack since theres no "on child size changed" callback atm...
 | ||||
| 						mParent->onSizeChanged(); | ||||
| 					break; | ||||
|  |  | |||
|  | @ -15,6 +15,8 @@ ScraperSearchComponent::ScraperSearchComponent(Window* window, SearchType type) | |||
| { | ||||
| 	addChild(&mGrid); | ||||
| 
 | ||||
| 	mBlockAccept = false; | ||||
| 
 | ||||
| 	using namespace Eigen; | ||||
| 
 | ||||
| 	// left spacer (empty component, needed for borders)
 | ||||
|  | @ -87,6 +89,8 @@ void ScraperSearchComponent::search(const ScraperSearchParams& params) | |||
| { | ||||
| 	mResultList->clear(); | ||||
| 	mScraperResults.clear(); | ||||
| 	mThumbnailReq.reset(); | ||||
| 	mMDResolveHandle.reset(); | ||||
| 	updateInfoPane(); | ||||
| 
 | ||||
| 	mLastSearch = params; | ||||
|  | @ -120,6 +124,7 @@ void ScraperSearchComponent::onSearchDone(const std::vector<ScraperSearchResult> | |||
| 		mGrid.resetCursor(); | ||||
| 	} | ||||
| 
 | ||||
| 	mBlockAccept = false; | ||||
| 	updateInfoPane(); | ||||
| 
 | ||||
| 	if(mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) | ||||
|  | @ -177,6 +182,9 @@ bool ScraperSearchComponent::input(InputConfig* config, Input input) | |||
| { | ||||
| 	if(config->isMappedTo("a", input) && input.value != 0) | ||||
| 	{ | ||||
| 		if(mBlockAccept) | ||||
| 			return true; | ||||
| 
 | ||||
| 		//if you're on a result
 | ||||
| 		if(getSelectedIndex() != -1) | ||||
| 		{ | ||||
|  | @ -195,24 +203,28 @@ bool ScraperSearchComponent::input(InputConfig* config, Input input) | |||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| void ScraperSearchComponent::render(const Eigen::Affine3f& parentTrans) | ||||
| { | ||||
| 	Eigen::Affine3f trans = parentTrans * getTransform(); | ||||
| 
 | ||||
| 	if(mBlockAccept) | ||||
| 	{ | ||||
| 		Renderer::setMatrix(trans); | ||||
| 		Renderer::drawRect((int)mResultList->getPosition().x(), (int)mResultList->getPosition().y(), | ||||
| 			(int)mResultList->getSize().x(), (int)mResultList->getSize().y(), 0x00000011); | ||||
| 	} | ||||
| 
 | ||||
| 	renderChildren(trans); | ||||
| } | ||||
| 
 | ||||
| void ScraperSearchComponent::returnResult(ScraperSearchResult result) | ||||
| { | ||||
| 	mBlockAccept = true; | ||||
| 
 | ||||
| 	// resolve metadata image before returning
 | ||||
| 	if(!result.imageUrl.empty()) | ||||
| 	{ | ||||
| 		downloadImageAsync(mWindow, result.imageUrl, getSaveAsPath(mLastSearch, "image", result.imageUrl),  | ||||
| 			[this, result] (std::string filePath) mutable -> void | ||||
| 			{ | ||||
| 					if(filePath.empty()) | ||||
| 					{ | ||||
| 						onSearchError("Error downloading boxart."); | ||||
| 						return; | ||||
| 					} | ||||
| 					 | ||||
| 					result.mdl.set("image", filePath); | ||||
| 					result.imageUrl = ""; | ||||
| 					this->returnResult(result); // re-enter this function
 | ||||
| 			}); | ||||
| 		mMDResolveHandle = resolveMetaDataAssets(result, mLastSearch); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -226,19 +238,30 @@ void ScraperSearchComponent::update(int deltaTime) | |||
| 		updateThumbnail(); | ||||
| 	} | ||||
| 
 | ||||
| 	if(mSearchHandle && mSearchHandle->status() != SEARCH_IN_PROGRESS) | ||||
| 	if(mSearchHandle && mSearchHandle->status() != ASYNC_IN_PROGRESS) | ||||
| 	{ | ||||
| 		if(mSearchHandle->status() == SEARCH_DONE) | ||||
| 		if(mSearchHandle->status() == ASYNC_DONE) | ||||
| 		{ | ||||
| 			onSearchDone(mSearchHandle->getResults()); | ||||
| 		}else if(mSearchHandle->status() == SEARCH_ERROR) | ||||
| 		}else if(mSearchHandle->status() == ASYNC_ERROR) | ||||
| 		{ | ||||
| 			onSearchError(mSearchHandle->getStatusString()); | ||||
| 		} | ||||
| 
 | ||||
| 		mSearchHandle.reset(); | ||||
| 	} | ||||
| 
 | ||||
| 	if(mMDResolveHandle && mMDResolveHandle->status() != ASYNC_IN_PROGRESS) | ||||
| 	{ | ||||
| 		if(mMDResolveHandle->status() == ASYNC_DONE) | ||||
| 		{ | ||||
| 			returnResult(mMDResolveHandle->getResult()); | ||||
| 		}else if(mMDResolveHandle->status() == ASYNC_ERROR) | ||||
| 		{ | ||||
| 			onSearchError(mMDResolveHandle->getStatusString()); | ||||
| 		} | ||||
| 		mMDResolveHandle.reset(); | ||||
| 	} | ||||
| 
 | ||||
| 	GuiComponent::update(deltaTime); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -34,6 +34,7 @@ public: | |||
| 
 | ||||
| 	bool input(InputConfig* config, Input input) override; | ||||
| 	void update(int deltaTime) override; | ||||
| 	void render(const Eigen::Affine3f& parentTrans) override; | ||||
| 	std::vector<HelpPrompt> getHelpPrompts() override; | ||||
| 	void onSizeChanged() override;	 | ||||
| 	void onFocusGained() override; | ||||
|  | @ -65,8 +66,10 @@ private: | |||
| 	std::function<void(const ScraperSearchResult&)> mAcceptCallback; | ||||
| 	std::function<void()> mSkipCallback; | ||||
| 	std::function<void()> mCancelCallback; | ||||
| 	bool mBlockAccept; | ||||
| 
 | ||||
| 	std::unique_ptr<ScraperSearchHandle> mSearchHandle; | ||||
| 	std::unique_ptr<MDResolveHandle> mMDResolveHandle; | ||||
| 	std::vector<ScraperSearchResult> mScraperResults; | ||||
| 	std::unique_ptr<HttpReq> mThumbnailReq; | ||||
| }; | ||||
|  |  | |||
|  | @ -79,11 +79,14 @@ std::unique_ptr<ScraperSearchHandle> GamesDBScraper::getResultsAsync(const Scrap | |||
| GamesDBHandle::GamesDBHandle(const ScraperSearchParams& params, const std::string& url) :  | ||||
| 	mReq(std::unique_ptr<HttpReq>(new HttpReq(url))) | ||||
| { | ||||
| 	setStatus(SEARCH_IN_PROGRESS); | ||||
| 	setStatus(ASYNC_IN_PROGRESS); | ||||
| } | ||||
| 
 | ||||
| void GamesDBHandle::update() | ||||
| { | ||||
| 	if(mStatus == ASYNC_DONE) | ||||
| 		return; | ||||
| 
 | ||||
| 	if(mReq->status() == HttpReq::REQ_IN_PROGRESS) | ||||
| 		return; | ||||
| 
 | ||||
|  | @ -156,7 +159,7 @@ void GamesDBHandle::update() | |||
| 		game = game.next_sibling("Game"); | ||||
| 	} | ||||
| 
 | ||||
| 	setStatus(SEARCH_DONE); | ||||
| 	setStatus(ASYNC_DONE); | ||||
| 	setResults(results); | ||||
| 	return; | ||||
| } | ||||
|  |  | |||
|  | @ -9,48 +9,116 @@ | |||
| #include "GamesDBScraper.h" | ||||
| #include "TheArchiveScraper.h" | ||||
| 
 | ||||
| std::string ScraperSearchHandle::getStatusString() | ||||
| std::shared_ptr<Scraper> createScraperByName(const std::string& name) | ||||
| { | ||||
| 	switch(mStatus) | ||||
| 	if(name == "TheGamesDB") | ||||
| 		return std::shared_ptr<Scraper>(new GamesDBScraper()); | ||||
| 	else if(name == "TheArchive") | ||||
| 		return std::shared_ptr<Scraper>(new TheArchiveScraper()); | ||||
| 
 | ||||
| 	return nullptr; | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<MDResolveHandle> resolveMetaDataAssets(const ScraperSearchResult& result, const ScraperSearchParams& search) | ||||
| { | ||||
| 	case SEARCH_IN_PROGRESS: | ||||
| 		return "search in progress"; | ||||
| 	case SEARCH_ERROR: | ||||
| 		return mError; | ||||
| 	case SEARCH_DONE: | ||||
| 		return "search done"; | ||||
| 	default: | ||||
| 		return "something impossible has occured"; | ||||
| 	return std::unique_ptr<MDResolveHandle>(new MDResolveHandle(result, search)); | ||||
| } | ||||
| 
 | ||||
| MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, const ScraperSearchParams& search) : mResult(result) | ||||
| { | ||||
| 	if(!result.imageUrl.empty()) | ||||
| 	{ | ||||
| 		std::string imgPath = getSaveAsPath(search, "image", result.imageUrl); | ||||
| 		mFuncs.push_back(ResolvePair(downloadImageAsync(result.imageUrl, imgPath), [this, imgPath] | ||||
| 		{ | ||||
| 			mResult.mdl.set("image", imgPath); | ||||
| 			mResult.imageUrl = ""; | ||||
| 		})); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| std::string processFileDownload(std::shared_ptr<HttpReq> r, std::string saveAs) | ||||
| void MDResolveHandle::update() | ||||
| { | ||||
| 	if(r->status() != HttpReq::REQ_SUCCESS) | ||||
| 	if(mStatus == ASYNC_DONE || mStatus == ASYNC_ERROR) | ||||
| 		return; | ||||
| 	 | ||||
| 	auto it = mFuncs.begin(); | ||||
| 	while(it != mFuncs.end()) | ||||
| 	{ | ||||
| 		LOG(LogError) << "Failed to download file - HttpReq error: " << r->getErrorMsg(); | ||||
| 		return ""; | ||||
| 		if(it->first->status() == ASYNC_ERROR) | ||||
| 		{ | ||||
| 			setError(it->first->getStatusString()); | ||||
| 			return; | ||||
| 		}else if(it->first->status() == ASYNC_DONE) | ||||
| 		{ | ||||
| 			it->second(); | ||||
| 			it = mFuncs.erase(it); | ||||
| 			continue; | ||||
| 		} | ||||
| 		it++; | ||||
| 	} | ||||
| 
 | ||||
| 	std::ofstream stream(saveAs, std::ios_base::out | std::ios_base::binary); | ||||
| 	if(stream.fail()) | ||||
| 	{ | ||||
| 		LOG(LogError) << "Failed to open \"" << saveAs << "\" for writing downloaded file."; | ||||
| 		return ""; | ||||
| 	if(mFuncs.empty()) | ||||
| 		setStatus(ASYNC_DONE); | ||||
| } | ||||
| 
 | ||||
| 	std::string content = r->getContent(); | ||||
| std::unique_ptr<AsyncHandle> downloadImageAsync(const std::string& url, const std::string& saveAs) | ||||
| { | ||||
| 	return std::unique_ptr<ImageDownloadHandle>(new ImageDownloadHandle(url, saveAs,  | ||||
| 		Settings::getInstance()->getInt("ScraperResizeWidth"), Settings::getInstance()->getInt("ScraperResizeHeight"))); | ||||
| } | ||||
| 
 | ||||
| ImageDownloadHandle::ImageDownloadHandle(const std::string& url, const std::string& path, int maxWidth, int maxHeight) :  | ||||
| 	mSavePath(path), mMaxWidth(maxWidth), mMaxHeight(maxHeight), mReq(new HttpReq(url)) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void ImageDownloadHandle::update() | ||||
| { | ||||
| 	if(mReq->status() == HttpReq::REQ_IN_PROGRESS) | ||||
| 		return; | ||||
| 
 | ||||
| 	if(mReq->status() != HttpReq::REQ_SUCCESS) | ||||
| 	{ | ||||
| 		std::stringstream ss; | ||||
| 		ss << "Network error: " << mReq->getErrorMsg(); | ||||
| 		setError(ss.str()); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	// download is done, save it to disk
 | ||||
| 	std::ofstream stream(mSavePath, std::ios_base::out | std::ios_base::binary); | ||||
| 	if(stream.bad()) | ||||
| 	{ | ||||
| 		setError("Failed to open image path to write. Permission error? Disk full?"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	const std::string& content = mReq->getContent(); | ||||
| 	stream.write(content.data(), content.length()); | ||||
| 	stream.close(); | ||||
| 	if(stream.bad()) | ||||
| 	{ | ||||
| 		setError("Failed to save image. Disk full?"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	return saveAs; | ||||
| 	// resize it
 | ||||
| 	if(!resizeImage(mSavePath, mMaxWidth, mMaxHeight)) | ||||
| 	{ | ||||
| 		setError("Error saving resized image. Out of memory? Disk full?"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	setStatus(ASYNC_DONE); | ||||
| } | ||||
| 
 | ||||
| //you can pass 0 for width or height to keep aspect ratio
 | ||||
| void resizeImage(const std::string& path, int maxWidth, int maxHeight) | ||||
| bool resizeImage(const std::string& path, int maxWidth, int maxHeight) | ||||
| { | ||||
| 	// nothing to do
 | ||||
| 	if(maxWidth == 0 && maxHeight == 0) | ||||
| 		return; | ||||
| 		return true; | ||||
| 
 | ||||
| 	FREE_IMAGE_FORMAT format = FIF_UNKNOWN; | ||||
| 	FIBITMAP* image = NULL; | ||||
|  | @ -62,7 +130,7 @@ void resizeImage(const std::string& path, int maxWidth, int maxHeight) | |||
| 	if(format == FIF_UNKNOWN) | ||||
| 	{ | ||||
| 		LOG(LogError) << "Error - could not detect filetype for image \"" << path << "\"!"; | ||||
| 		return; | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	//make sure we can read this filetype first, then load it
 | ||||
|  | @ -71,7 +139,7 @@ void resizeImage(const std::string& path, int maxWidth, int maxHeight) | |||
| 		image = FreeImage_Load(format, path.c_str()); | ||||
| 	}else{ | ||||
| 		LOG(LogError) << "Error - file format reading not supported for image \"" << path << "\"!"; | ||||
| 		return; | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	float width = (float)FreeImage_GetWidth(image); | ||||
|  | @ -91,43 +159,16 @@ void resizeImage(const std::string& path, int maxWidth, int maxHeight) | |||
| 	if(imageRescaled == NULL) | ||||
| 	{ | ||||
| 		LOG(LogError) << "Could not resize image! (not enough memory? invalid bitdepth?)"; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if(!FreeImage_Save(format, imageRescaled, path.c_str())) | ||||
| 	{ | ||||
| 		LOG(LogError) << "Failed to save resized image!"; | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	bool saved = FreeImage_Save(format, imageRescaled, path.c_str()); | ||||
| 	FreeImage_Unload(imageRescaled); | ||||
| } | ||||
| 
 | ||||
| void downloadImageAsync(Window* window, const std::string& url, const std::string& saveAs, std::function<void(std::string)> returnFunc) | ||||
| { | ||||
| 	std::shared_ptr<HttpReq> httpreq = std::make_shared<HttpReq>(url); | ||||
| 	AsyncReqComponent* req = new AsyncReqComponent(window, httpreq,  | ||||
| 		[returnFunc, saveAs] (std::shared_ptr<HttpReq> r) | ||||
| 	{ | ||||
| 		std::string file = processFileDownload(r, saveAs); | ||||
| 		if(!file.empty()) | ||||
| 			resizeImage(file, Settings::getInstance()->getInt("ScraperResizeWidth"), Settings::getInstance()->getInt("ScraperResizeHeight")); | ||||
| 		returnFunc(file); | ||||
| 	}, NULL); | ||||
| 	if(!saved) | ||||
| 		LOG(LogError) << "Failed to save resized image!"; | ||||
| 
 | ||||
| 	window->pushGui(req); | ||||
| } | ||||
| 
 | ||||
| std::string downloadImage(const std::string& url, const std::string& saveAs) | ||||
| { | ||||
| 	std::shared_ptr<HttpReq> httpreq = std::make_shared<HttpReq>(url); | ||||
| 	while(httpreq->status() == HttpReq::REQ_IN_PROGRESS); | ||||
| 
 | ||||
| 	std::string file = processFileDownload(httpreq, saveAs); | ||||
| 
 | ||||
| 	if(!file.empty()) | ||||
| 		resizeImage(file, Settings::getInstance()->getInt("ScraperResizeWidth"), Settings::getInstance()->getInt("ScraperResizeHeight")); | ||||
| 
 | ||||
| 	return file; | ||||
| 	return saved; | ||||
| } | ||||
| 
 | ||||
| std::string getSaveAsPath(const ScraperSearchParams& params, const std::string& suffix, const std::string& url) | ||||
|  | @ -153,42 +194,3 @@ std::string getSaveAsPath(const ScraperSearchParams& params, const std::string& | |||
| 	path += name + ext; | ||||
| 	return path; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| std::shared_ptr<Scraper> createScraperByName(const std::string& name) | ||||
| { | ||||
| 	if(name == "TheGamesDB") | ||||
| 		return std::shared_ptr<Scraper>(new GamesDBScraper()); | ||||
| 	else if(name == "TheArchive") | ||||
| 		return std::shared_ptr<Scraper>(new TheArchiveScraper()); | ||||
| 
 | ||||
| 	return nullptr; | ||||
| } | ||||
| 
 | ||||
| void resolveMetaDataAssetsAsync(Window* window, const ScraperSearchParams& params, MetaDataList mdl, std::function<void(MetaDataList)> returnFunc) | ||||
| { | ||||
| 	const std::vector<MetaDataDecl>& mdd = params.game->metadata.getMDD(); | ||||
| 	for(auto it = mdd.begin(); it != mdd.end(); it++) | ||||
| 	{ | ||||
| 		std::string key = it->key; | ||||
| 		std::string val = mdl.get(key); | ||||
| 		if(it->type == MD_IMAGE_PATH && HttpReq::isUrl(val)) | ||||
| 		{ | ||||
| 			downloadImageAsync(window, val, getSaveAsPath(params, key, val), [window, params, mdl, key, returnFunc] (std::string savedAs) mutable ->  | ||||
| 				void  | ||||
| 			{ | ||||
| 				if(savedAs.empty()) | ||||
| 				{ | ||||
| 					//error
 | ||||
| 					LOG(LogError) << "Could not resolve image for [" << getCleanFileName(params.game->getPath()) << "]! Skipping."; | ||||
| 				} | ||||
| 
 | ||||
| 				mdl.set(key, savedAs); | ||||
| 				resolveMetaDataAssetsAsync(window, params, mdl, returnFunc); | ||||
| 			}); | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	returnFunc(mdl); | ||||
| } | ||||
|  |  | |||
|  | @ -3,11 +3,10 @@ | |||
| #include "../MetaData.h" | ||||
| #include "../SystemData.h" | ||||
| #include "../HttpReq.h" | ||||
| #include "../AsyncHandle.h" | ||||
| #include <vector> | ||||
| #include <functional> | ||||
| 
 | ||||
| class Window; | ||||
| 
 | ||||
| struct ScraperSearchParams | ||||
| { | ||||
| 	SystemData* system; | ||||
|  | @ -25,34 +24,16 @@ struct ScraperSearchResult | |||
| 	std::string thumbnailUrl; | ||||
| }; | ||||
| 
 | ||||
| enum ScraperSearchStatus | ||||
| { | ||||
| 	SEARCH_IN_PROGRESS, | ||||
| 	SEARCH_ERROR, | ||||
| 	SEARCH_DONE | ||||
| }; | ||||
| 
 | ||||
| class ScraperSearchHandle | ||||
| class ScraperSearchHandle : public AsyncHandle | ||||
| { | ||||
| public: | ||||
| 	virtual void update() = 0; | ||||
| 
 | ||||
| 	// Update and return the latest status.
 | ||||
| 	inline ScraperSearchStatus status() { update(); return mStatus; } | ||||
| 
 | ||||
| 	// User-friendly string of our current status.  Will return error message if status() == SEARCH_ERROR.
 | ||||
| 	std::string getStatusString(); | ||||
| 
 | ||||
| 	inline const std::vector<ScraperSearchResult>& getResults() const { assert(mStatus != SEARCH_IN_PROGRESS); return mResults; } | ||||
| 	inline const std::vector<ScraperSearchResult>& getResults() const { assert(mStatus != ASYNC_IN_PROGRESS); return mResults; } | ||||
| 
 | ||||
| protected: | ||||
| 	inline void setError(const std::string& error) { setStatus(SEARCH_ERROR); mError = error; } | ||||
| 	inline void setStatus(ScraperSearchStatus status) { mStatus = status; } | ||||
| 	inline void setResults(const std::vector<ScraperSearchResult>& results) { mResults = results; } | ||||
| 
 | ||||
| private: | ||||
| 	std::string mError; | ||||
| 	ScraperSearchStatus mStatus; | ||||
| 	std::vector<ScraperSearchResult> mResults; | ||||
| }; | ||||
| 
 | ||||
|  | @ -67,23 +48,48 @@ public: | |||
| 
 | ||||
| std::shared_ptr<Scraper> createScraperByName(const std::string& name); | ||||
| 
 | ||||
| 
 | ||||
| // Meta data asset downloading stuff.
 | ||||
| class MDResolveHandle : public AsyncHandle | ||||
| { | ||||
| public: | ||||
| 	MDResolveHandle(const ScraperSearchResult& result, const ScraperSearchParams& search); | ||||
| 
 | ||||
| 	void update() override; | ||||
| 	inline const ScraperSearchResult& getResult() const { assert(mStatus == ASYNC_DONE); return mResult; } | ||||
| 
 | ||||
| private: | ||||
| 	ScraperSearchResult mResult; | ||||
| 
 | ||||
| 	typedef std::pair< std::unique_ptr<AsyncHandle>, std::function<void()> > ResolvePair; | ||||
| 	std::vector<ResolvePair> mFuncs; | ||||
| }; | ||||
| 
 | ||||
| class ImageDownloadHandle : public AsyncHandle | ||||
| { | ||||
| public: | ||||
| 	ImageDownloadHandle(const std::string& url, const std::string& path, int maxWidth, int maxHeight); | ||||
| 
 | ||||
| 	void update() override; | ||||
| 
 | ||||
| private: | ||||
| 	std::unique_ptr<HttpReq> mReq; | ||||
| 	std::string mSavePath; | ||||
| 	int mMaxWidth; | ||||
| 	int mMaxHeight; | ||||
| }; | ||||
| 
 | ||||
| //About the same as "~/.emulationstation/downloaded_images/[system_name]/[game_name].[url's extension]".
 | ||||
| //Will create the "downloaded_images" and "subdirectory" directories if they do not exist.
 | ||||
| std::string getSaveAsPath(const ScraperSearchParams& params, const std::string& suffix, const std::string& url); | ||||
| 
 | ||||
| //Returns the path to the downloaded file (saveAs) on completion.
 | ||||
| //Returns empty string if an error occured.
 | ||||
| //Will resize according to Settings::getInt("ScraperResizeWidth") and Settings::getInt("ScraperResizeHeight").
 | ||||
| std::string downloadImage(const std::string& url, const std::string& saveAs); | ||||
| std::unique_ptr<AsyncHandle> downloadImageAsync(const std::string& url, const std::string& saveAs); | ||||
| 
 | ||||
| //Returns (via returnFunc) the path to the downloaded file (saveAs) on completion.
 | ||||
| //Returns empty string if an error occured.
 | ||||
| //Will resize according to Settings::getInt("ScraperResizeWidth") and Settings::getInt("ScraperResizeHeight").
 | ||||
| //Same as downloadImage, just async.
 | ||||
| void downloadImageAsync(Window* window, const std::string& url, const std::string& saveAs, std::function<void(std::string)> returnFunc); | ||||
| 
 | ||||
| void resolveMetaDataAssetsAsync(Window* window, const ScraperSearchParams& params, MetaDataList mdl, std::function<void(MetaDataList)> returnFunc); | ||||
| // Resolves all metadata assets that need to be downloaded.
 | ||||
| std::unique_ptr<MDResolveHandle> resolveMetaDataAssets(const ScraperSearchResult& result, const ScraperSearchParams& search); | ||||
| 
 | ||||
| //You can pass 0 for maxWidth or maxHeight to automatically keep the aspect ratio.
 | ||||
| //Will overwrite the image at [path] with the new resized one.
 | ||||
| void resizeImage(const std::string& path, int maxWidth, int maxHeight); | ||||
| //Returns true if successful, false otherwise.
 | ||||
| bool resizeImage(const std::string& path, int maxWidth, int maxHeight); | ||||
|  |  | |||
|  | @ -25,12 +25,12 @@ std::unique_ptr<ScraperSearchHandle> TheArchiveScraper::getResultsAsync(const Sc | |||
| TheArchiveHandle::TheArchiveHandle(const ScraperSearchParams& params, const std::string& url) :  | ||||
| 	mReq(std::unique_ptr<HttpReq>(new HttpReq(url))) | ||||
| { | ||||
| 	setStatus(SEARCH_IN_PROGRESS); | ||||
| 	setStatus(ASYNC_IN_PROGRESS); | ||||
| } | ||||
| 
 | ||||
| void TheArchiveHandle::update() | ||||
| { | ||||
| 	if(status() == SEARCH_DONE) | ||||
| 	if(mStatus == ASYNC_DONE) | ||||
| 		return; | ||||
| 
 | ||||
| 	if(mReq->status() == HttpReq::REQ_IN_PROGRESS) | ||||
|  | @ -91,6 +91,6 @@ void TheArchiveHandle::update() | |||
| 		game = game.next_sibling("game"); | ||||
| 	} | ||||
| 
 | ||||
| 	setStatus(SEARCH_DONE); | ||||
| 	setStatus(ASYNC_DONE); | ||||
| 	setResults(results); | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Aloshi
						Aloshi