mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-22 06:05:38 +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
|
#define basic sources and headers
|
||||||
set(ES_HEADERS
|
set(ES_HEADERS
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/AsyncHandle.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/AudioManager.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/AudioManager.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/EmulationStation.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/EmulationStation.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/FileData.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;
|
return mStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string HttpReq::getContent()
|
std::string HttpReq::getContent() const
|
||||||
{
|
{
|
||||||
if(mStatus != REQ_SUCCESS)
|
assert(mStatus == REQ_SUCCESS);
|
||||||
{
|
|
||||||
LOG(LogError) << "Called getContent() on an incomplete HttpReq!";
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return mContent.str();
|
return mContent.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ public:
|
||||||
|
|
||||||
std::string getErrorMsg();
|
std::string getErrorMsg();
|
||||||
|
|
||||||
std::string getContent();
|
std::string getContent() const; // mStatus must be REQ_SUCCESS
|
||||||
|
|
||||||
static std::string urlEncode(const std::string &s);
|
static std::string urlEncode(const std::string &s);
|
||||||
static bool isUrl(const std::string& s);
|
static bool isUrl(const std::string& s);
|
||||||
|
|
|
@ -116,6 +116,8 @@ void ComponentList::onCursorChanged(const CursorState& state)
|
||||||
mCameraOffset = 0;
|
mCameraOffset = 0;
|
||||||
else if(mCameraOffset + mSize.y() > totalHeight)
|
else if(mCameraOffset + mSize.y() > totalHeight)
|
||||||
mCameraOffset = totalHeight - mSize.y();
|
mCameraOffset = totalHeight - mSize.y();
|
||||||
|
}else{
|
||||||
|
mCameraOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateHelpPrompts();
|
updateHelpPrompts();
|
||||||
|
|
|
@ -259,7 +259,7 @@ private:
|
||||||
ss << getSelectedObjects().size() << " SELECTED";
|
ss << getSelectedObjects().size() << " SELECTED";
|
||||||
mText.setText(ss.str());
|
mText.setText(ss.str());
|
||||||
mText.setSize(0, mText.getSize().y());
|
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...
|
if(mParent) // hack since theres no "on child size changed" callback atm...
|
||||||
mParent->onSizeChanged();
|
mParent->onSizeChanged();
|
||||||
}else{
|
}else{
|
||||||
|
@ -270,7 +270,7 @@ private:
|
||||||
{
|
{
|
||||||
mText.setText(strToUpper(it->name));
|
mText.setText(strToUpper(it->name));
|
||||||
mText.setSize(0, mText.getSize().y());
|
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...
|
if(mParent) // hack since theres no "on child size changed" callback atm...
|
||||||
mParent->onSizeChanged();
|
mParent->onSizeChanged();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -15,6 +15,8 @@ ScraperSearchComponent::ScraperSearchComponent(Window* window, SearchType type)
|
||||||
{
|
{
|
||||||
addChild(&mGrid);
|
addChild(&mGrid);
|
||||||
|
|
||||||
|
mBlockAccept = false;
|
||||||
|
|
||||||
using namespace Eigen;
|
using namespace Eigen;
|
||||||
|
|
||||||
// left spacer (empty component, needed for borders)
|
// left spacer (empty component, needed for borders)
|
||||||
|
@ -87,6 +89,8 @@ void ScraperSearchComponent::search(const ScraperSearchParams& params)
|
||||||
{
|
{
|
||||||
mResultList->clear();
|
mResultList->clear();
|
||||||
mScraperResults.clear();
|
mScraperResults.clear();
|
||||||
|
mThumbnailReq.reset();
|
||||||
|
mMDResolveHandle.reset();
|
||||||
updateInfoPane();
|
updateInfoPane();
|
||||||
|
|
||||||
mLastSearch = params;
|
mLastSearch = params;
|
||||||
|
@ -120,6 +124,7 @@ void ScraperSearchComponent::onSearchDone(const std::vector<ScraperSearchResult>
|
||||||
mGrid.resetCursor();
|
mGrid.resetCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mBlockAccept = false;
|
||||||
updateInfoPane();
|
updateInfoPane();
|
||||||
|
|
||||||
if(mSearchType == ALWAYS_ACCEPT_FIRST_RESULT)
|
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(config->isMappedTo("a", input) && input.value != 0)
|
||||||
{
|
{
|
||||||
|
if(mBlockAccept)
|
||||||
|
return true;
|
||||||
|
|
||||||
//if you're on a result
|
//if you're on a result
|
||||||
if(getSelectedIndex() != -1)
|
if(getSelectedIndex() != -1)
|
||||||
{
|
{
|
||||||
|
@ -195,24 +203,28 @@ bool ScraperSearchComponent::input(InputConfig* config, Input input)
|
||||||
return ret;
|
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)
|
void ScraperSearchComponent::returnResult(ScraperSearchResult result)
|
||||||
{
|
{
|
||||||
|
mBlockAccept = true;
|
||||||
|
|
||||||
// resolve metadata image before returning
|
// resolve metadata image before returning
|
||||||
if(!result.imageUrl.empty())
|
if(!result.imageUrl.empty())
|
||||||
{
|
{
|
||||||
downloadImageAsync(mWindow, result.imageUrl, getSaveAsPath(mLastSearch, "image", result.imageUrl),
|
mMDResolveHandle = resolveMetaDataAssets(result, mLastSearch);
|
||||||
[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
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,19 +238,30 @@ void ScraperSearchComponent::update(int deltaTime)
|
||||||
updateThumbnail();
|
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());
|
onSearchDone(mSearchHandle->getResults());
|
||||||
}else if(mSearchHandle->status() == SEARCH_ERROR)
|
}else if(mSearchHandle->status() == ASYNC_ERROR)
|
||||||
{
|
{
|
||||||
onSearchError(mSearchHandle->getStatusString());
|
onSearchError(mSearchHandle->getStatusString());
|
||||||
}
|
}
|
||||||
|
|
||||||
mSearchHandle.reset();
|
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);
|
GuiComponent::update(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ public:
|
||||||
|
|
||||||
bool input(InputConfig* config, Input input) override;
|
bool input(InputConfig* config, Input input) override;
|
||||||
void update(int deltaTime) override;
|
void update(int deltaTime) override;
|
||||||
|
void render(const Eigen::Affine3f& parentTrans) override;
|
||||||
std::vector<HelpPrompt> getHelpPrompts() override;
|
std::vector<HelpPrompt> getHelpPrompts() override;
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
void onFocusGained() override;
|
void onFocusGained() override;
|
||||||
|
@ -65,8 +66,10 @@ private:
|
||||||
std::function<void(const ScraperSearchResult&)> mAcceptCallback;
|
std::function<void(const ScraperSearchResult&)> mAcceptCallback;
|
||||||
std::function<void()> mSkipCallback;
|
std::function<void()> mSkipCallback;
|
||||||
std::function<void()> mCancelCallback;
|
std::function<void()> mCancelCallback;
|
||||||
|
bool mBlockAccept;
|
||||||
|
|
||||||
std::unique_ptr<ScraperSearchHandle> mSearchHandle;
|
std::unique_ptr<ScraperSearchHandle> mSearchHandle;
|
||||||
|
std::unique_ptr<MDResolveHandle> mMDResolveHandle;
|
||||||
std::vector<ScraperSearchResult> mScraperResults;
|
std::vector<ScraperSearchResult> mScraperResults;
|
||||||
std::unique_ptr<HttpReq> mThumbnailReq;
|
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) :
|
GamesDBHandle::GamesDBHandle(const ScraperSearchParams& params, const std::string& url) :
|
||||||
mReq(std::unique_ptr<HttpReq>(new HttpReq(url)))
|
mReq(std::unique_ptr<HttpReq>(new HttpReq(url)))
|
||||||
{
|
{
|
||||||
setStatus(SEARCH_IN_PROGRESS);
|
setStatus(ASYNC_IN_PROGRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GamesDBHandle::update()
|
void GamesDBHandle::update()
|
||||||
{
|
{
|
||||||
|
if(mStatus == ASYNC_DONE)
|
||||||
|
return;
|
||||||
|
|
||||||
if(mReq->status() == HttpReq::REQ_IN_PROGRESS)
|
if(mReq->status() == HttpReq::REQ_IN_PROGRESS)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -156,7 +159,7 @@ void GamesDBHandle::update()
|
||||||
game = game.next_sibling("Game");
|
game = game.next_sibling("Game");
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus(SEARCH_DONE);
|
setStatus(ASYNC_DONE);
|
||||||
setResults(results);
|
setResults(results);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,48 +9,116 @@
|
||||||
#include "GamesDBScraper.h"
|
#include "GamesDBScraper.h"
|
||||||
#include "TheArchiveScraper.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)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<MDResolveHandle>(new MDResolveHandle(result, search));
|
||||||
|
}
|
||||||
|
|
||||||
|
MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, const ScraperSearchParams& search) : mResult(result)
|
||||||
|
{
|
||||||
|
if(!result.imageUrl.empty())
|
||||||
{
|
{
|
||||||
case SEARCH_IN_PROGRESS:
|
std::string imgPath = getSaveAsPath(search, "image", result.imageUrl);
|
||||||
return "search in progress";
|
mFuncs.push_back(ResolvePair(downloadImageAsync(result.imageUrl, imgPath), [this, imgPath]
|
||||||
case SEARCH_ERROR:
|
{
|
||||||
return mError;
|
mResult.mdl.set("image", imgPath);
|
||||||
case SEARCH_DONE:
|
mResult.imageUrl = "";
|
||||||
return "search done";
|
}));
|
||||||
default:
|
|
||||||
return "something impossible has occured";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
if(it->first->status() == ASYNC_ERROR)
|
||||||
return "";
|
{
|
||||||
|
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(mFuncs.empty())
|
||||||
if(stream.fail())
|
setStatus(ASYNC_DONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
LOG(LogError) << "Failed to open \"" << saveAs << "\" for writing downloaded file.";
|
std::stringstream ss;
|
||||||
return "";
|
ss << "Network error: " << mReq->getErrorMsg();
|
||||||
|
setError(ss.str());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string content = r->getContent();
|
// 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.write(content.data(), content.length());
|
||||||
stream.close();
|
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
|
//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)
|
if(maxWidth == 0 && maxHeight == 0)
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
FREE_IMAGE_FORMAT format = FIF_UNKNOWN;
|
FREE_IMAGE_FORMAT format = FIF_UNKNOWN;
|
||||||
FIBITMAP* image = NULL;
|
FIBITMAP* image = NULL;
|
||||||
|
@ -62,7 +130,7 @@ void resizeImage(const std::string& path, int maxWidth, int maxHeight)
|
||||||
if(format == FIF_UNKNOWN)
|
if(format == FIF_UNKNOWN)
|
||||||
{
|
{
|
||||||
LOG(LogError) << "Error - could not detect filetype for image \"" << path << "\"!";
|
LOG(LogError) << "Error - could not detect filetype for image \"" << path << "\"!";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//make sure we can read this filetype first, then load it
|
//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());
|
image = FreeImage_Load(format, path.c_str());
|
||||||
}else{
|
}else{
|
||||||
LOG(LogError) << "Error - file format reading not supported for image \"" << path << "\"!";
|
LOG(LogError) << "Error - file format reading not supported for image \"" << path << "\"!";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
float width = (float)FreeImage_GetWidth(image);
|
float width = (float)FreeImage_GetWidth(image);
|
||||||
|
@ -91,43 +159,16 @@ void resizeImage(const std::string& path, int maxWidth, int maxHeight)
|
||||||
if(imageRescaled == NULL)
|
if(imageRescaled == NULL)
|
||||||
{
|
{
|
||||||
LOG(LogError) << "Could not resize image! (not enough memory? invalid bitdepth?)";
|
LOG(LogError) << "Could not resize image! (not enough memory? invalid bitdepth?)";
|
||||||
return;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if(!FreeImage_Save(format, imageRescaled, path.c_str()))
|
|
||||||
{
|
|
||||||
LOG(LogError) << "Failed to save resized image!";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool saved = FreeImage_Save(format, imageRescaled, path.c_str());
|
||||||
FreeImage_Unload(imageRescaled);
|
FreeImage_Unload(imageRescaled);
|
||||||
}
|
|
||||||
|
|
||||||
void downloadImageAsync(Window* window, const std::string& url, const std::string& saveAs, std::function<void(std::string)> returnFunc)
|
if(!saved)
|
||||||
{
|
LOG(LogError) << "Failed to save resized image!";
|
||||||
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);
|
|
||||||
|
|
||||||
window->pushGui(req);
|
return saved;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getSaveAsPath(const ScraperSearchParams& params, const std::string& suffix, const std::string& url)
|
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;
|
path += name + ext;
|
||||||
return path;
|
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 "../MetaData.h"
|
||||||
#include "../SystemData.h"
|
#include "../SystemData.h"
|
||||||
#include "../HttpReq.h"
|
#include "../HttpReq.h"
|
||||||
|
#include "../AsyncHandle.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
class Window;
|
|
||||||
|
|
||||||
struct ScraperSearchParams
|
struct ScraperSearchParams
|
||||||
{
|
{
|
||||||
SystemData* system;
|
SystemData* system;
|
||||||
|
@ -25,34 +24,16 @@ struct ScraperSearchResult
|
||||||
std::string thumbnailUrl;
|
std::string thumbnailUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ScraperSearchStatus
|
class ScraperSearchHandle : public AsyncHandle
|
||||||
{
|
|
||||||
SEARCH_IN_PROGRESS,
|
|
||||||
SEARCH_ERROR,
|
|
||||||
SEARCH_DONE
|
|
||||||
};
|
|
||||||
|
|
||||||
class ScraperSearchHandle
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void update() = 0;
|
virtual void update() = 0;
|
||||||
|
inline const std::vector<ScraperSearchResult>& getResults() const { assert(mStatus != ASYNC_IN_PROGRESS); return mResults; }
|
||||||
// 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; }
|
|
||||||
|
|
||||||
protected:
|
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; }
|
inline void setResults(const std::vector<ScraperSearchResult>& results) { mResults = results; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mError;
|
|
||||||
ScraperSearchStatus mStatus;
|
|
||||||
std::vector<ScraperSearchResult> mResults;
|
std::vector<ScraperSearchResult> mResults;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -67,23 +48,48 @@ public:
|
||||||
|
|
||||||
std::shared_ptr<Scraper> createScraperByName(const std::string& name);
|
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]".
|
//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.
|
//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);
|
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").
|
//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.
|
// Resolves all metadata assets that need to be downloaded.
|
||||||
//Returns empty string if an error occured.
|
std::unique_ptr<MDResolveHandle> resolveMetaDataAssets(const ScraperSearchResult& result, const ScraperSearchParams& search);
|
||||||
//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);
|
|
||||||
|
|
||||||
//You can pass 0 for maxWidth or maxHeight to automatically keep the aspect ratio.
|
//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.
|
//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) :
|
TheArchiveHandle::TheArchiveHandle(const ScraperSearchParams& params, const std::string& url) :
|
||||||
mReq(std::unique_ptr<HttpReq>(new HttpReq(url)))
|
mReq(std::unique_ptr<HttpReq>(new HttpReq(url)))
|
||||||
{
|
{
|
||||||
setStatus(SEARCH_IN_PROGRESS);
|
setStatus(ASYNC_IN_PROGRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TheArchiveHandle::update()
|
void TheArchiveHandle::update()
|
||||||
{
|
{
|
||||||
if(status() == SEARCH_DONE)
|
if(mStatus == ASYNC_DONE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(mReq->status() == HttpReq::REQ_IN_PROGRESS)
|
if(mReq->status() == HttpReq::REQ_IN_PROGRESS)
|
||||||
|
@ -91,6 +91,6 @@ void TheArchiveHandle::update()
|
||||||
game = game.next_sibling("game");
|
game = game.next_sibling("game");
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus(SEARCH_DONE);
|
setStatus(ASYNC_DONE);
|
||||||
setResults(results);
|
setResults(results);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue