mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-02-16 20:15:38 +00:00
Multi-game scraper seems to be functional!
This commit is contained in:
parent
d419bb368a
commit
69852af751
|
@ -184,6 +184,7 @@ set(ES_HEADERS
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMenu.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMenu.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiSettingsMenu.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiSettingsMenu.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiScraperStart.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiScraperStart.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiScraperLog.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/Scraper.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/Scraper.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/GamesDBScraper.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/GamesDBScraper.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/TheArchiveScraper.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/TheArchiveScraper.h
|
||||||
|
@ -241,6 +242,7 @@ set(ES_SOURCES
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMenu.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMenu.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiSettingsMenu.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiSettingsMenu.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiScraperStart.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiScraperStart.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiScraperLog.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/Scraper.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/Scraper.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/GamesDBScraper.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/GamesDBScraper.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/TheArchiveScraper.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/scrapers/TheArchiveScraper.cpp
|
||||||
|
|
|
@ -255,7 +255,11 @@ int run_scraper_cmdline()
|
||||||
if(url.length() != urlShort.length()) urlShort += "...";
|
if(url.length() != urlShort.length()) urlShort += "...";
|
||||||
|
|
||||||
out << " " << game->metadata()->get("name") << " [from: " << urlShort << "]...\n";
|
out << " " << game->metadata()->get("name") << " [from: " << urlShort << "]...\n";
|
||||||
game->metadata()->set(key, downloadImage(url, getSaveAsPath((*sysIt)->getName(), game->getCleanName(), url)));
|
|
||||||
|
ScraperSearchParams p;
|
||||||
|
p.game = game;
|
||||||
|
p.system = *sysIt;
|
||||||
|
game->metadata()->set(key, downloadImage(url, getSaveAsPath(p, key, url)));
|
||||||
if(game->metadata()->get(key).empty())
|
if(game->metadata()->get(key).empty())
|
||||||
{
|
{
|
||||||
out << " FAILED! Skipping.\n";
|
out << " FAILED! Skipping.\n";
|
||||||
|
|
|
@ -33,6 +33,7 @@ void Settings::setDefaults()
|
||||||
mBoolMap["DEBUG"] = false;
|
mBoolMap["DEBUG"] = false;
|
||||||
mBoolMap["WINDOWED"] = false;
|
mBoolMap["WINDOWED"] = false;
|
||||||
mBoolMap["DISABLESOUNDS"] = false;
|
mBoolMap["DISABLESOUNDS"] = false;
|
||||||
|
mBoolMap["DisableGamelistWrites"] = false;
|
||||||
|
|
||||||
mIntMap["DIMTIME"] = 30*1000;
|
mIntMap["DIMTIME"] = 30*1000;
|
||||||
mIntMap["ScraperResizeWidth"] = 450;
|
mIntMap["ScraperResizeWidth"] = 450;
|
||||||
|
@ -40,6 +41,7 @@ void Settings::setDefaults()
|
||||||
|
|
||||||
mIntMap["GameListSortIndex"] = 0;
|
mIntMap["GameListSortIndex"] = 0;
|
||||||
|
|
||||||
|
|
||||||
mScraper = std::shared_ptr<Scraper>(new GamesDBScraper());
|
mScraper = std::shared_ptr<Scraper>(new GamesDBScraper());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "pugiXML/pugixml.hpp"
|
#include "pugiXML/pugixml.hpp"
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
#include "Settings.h"
|
||||||
|
|
||||||
//this is obviously an incredibly inefficient way to go about searching
|
//this is obviously an incredibly inefficient way to go about searching
|
||||||
//but I don't think it'll matter too much with the size of most collections
|
//but I don't think it'll matter too much with the size of most collections
|
||||||
|
@ -193,6 +194,9 @@ void updateGamelist(SystemData* system)
|
||||||
//We have the complete information for every game though, so we can simply remove a game
|
//We have the complete information for every game though, so we can simply remove a game
|
||||||
//we already have in the system from the XML, and then add it back from its GameData information...
|
//we already have in the system from the XML, and then add it back from its GameData information...
|
||||||
|
|
||||||
|
if(Settings::getInstance()->getBool("DisableGamelistWrites"))
|
||||||
|
return;
|
||||||
|
|
||||||
std::string xmlpath = system->getGamelistPath();
|
std::string xmlpath = system->getGamelistPath();
|
||||||
if(xmlpath.empty())
|
if(xmlpath.empty())
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -126,6 +126,8 @@ void GuiMetaDataEd::fetch()
|
||||||
|
|
||||||
void GuiMetaDataEd::fetchDone(MetaDataList result)
|
void GuiMetaDataEd::fetchDone(MetaDataList result)
|
||||||
{
|
{
|
||||||
|
//TODO - replace this with resolveMetaDataAssetsAsync!
|
||||||
|
|
||||||
//this is a little tricky:
|
//this is a little tricky:
|
||||||
//go through the list of returned results, if anything is an image and the path looks like a URL:
|
//go through the list of returned results, if anything is an image and the path looks like a URL:
|
||||||
// (1) start an async download + resize (will create an AsyncReq that blocks further user input)
|
// (1) start an async download + resize (will create an AsyncReq that blocks further user input)
|
||||||
|
@ -139,7 +141,7 @@ void GuiMetaDataEd::fetchDone(MetaDataList result)
|
||||||
//val is /probably/ a URL
|
//val is /probably/ a URL
|
||||||
if(it->type == MD_IMAGE_PATH && HttpReq::isUrl(val))
|
if(it->type == MD_IMAGE_PATH && HttpReq::isUrl(val))
|
||||||
{
|
{
|
||||||
downloadImageAsync(mWindow, val, getSaveAsPath(mScraperParams.system->getName(), mScraperParams.game->getCleanName() + "-" + key, val),
|
downloadImageAsync(mWindow, val, getSaveAsPath(mScraperParams, key, val),
|
||||||
[this, result, key] (std::string filePath) mutable -> void {
|
[this, result, key] (std::string filePath) mutable -> void {
|
||||||
//skip it
|
//skip it
|
||||||
if(filePath.empty())
|
if(filePath.empty())
|
||||||
|
|
158
src/components/GuiScraperLog.cpp
Normal file
158
src/components/GuiScraperLog.cpp
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
#include "GuiScraperLog.h"
|
||||||
|
#include "../Settings.h"
|
||||||
|
#include "GuiGameScraper.h"
|
||||||
|
#include "../Renderer.h"
|
||||||
|
#include "../Log.h"
|
||||||
|
|
||||||
|
GuiScraperLog::GuiScraperLog(Window* window, const std::queue<ScraperSearchParams>& searches, bool manualMode) : GuiComponent(window),
|
||||||
|
mManualMode(manualMode),
|
||||||
|
mBox(window, ":/frame.png"),
|
||||||
|
mSearches(searches),
|
||||||
|
mStatus(window),
|
||||||
|
mTextLines(40)
|
||||||
|
{
|
||||||
|
addChild(&mBox);
|
||||||
|
addChild(&mStatus);
|
||||||
|
|
||||||
|
setSize(Renderer::getScreenWidth() * 0.8f, (float)Renderer::getScreenHeight());
|
||||||
|
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, 0);
|
||||||
|
|
||||||
|
mStatus.setColor(0x000000FF);
|
||||||
|
mStatus.setSize(mSize.x(), (float)mStatus.getFont()->getHeight());
|
||||||
|
mStatus.setCentered(true);
|
||||||
|
updateStatus();
|
||||||
|
|
||||||
|
mBox.setEdgeColor(0x111111FF);
|
||||||
|
mBox.setCenterColor(0xDFDFDFFF);
|
||||||
|
mBox.fitTo(mSize, Eigen::Vector3f::Zero(), Eigen::Vector2f(8, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiScraperLog::start()
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiScraperLog::next()
|
||||||
|
{
|
||||||
|
if(mSearches.empty())
|
||||||
|
{
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScraperSearchParams search = mSearches.front();
|
||||||
|
mSearches.pop();
|
||||||
|
|
||||||
|
writeLine(search.game->getCleanName(), 0x0000FFFF);
|
||||||
|
|
||||||
|
if(mManualMode)
|
||||||
|
{
|
||||||
|
GuiGameScraper* ggs = new GuiGameScraper(mWindow, search,
|
||||||
|
[this, search] (MetaDataList result) { resultFetched(search, result); },
|
||||||
|
[this, search] { resultEmpty(search); });
|
||||||
|
|
||||||
|
mWindow->pushGui(ggs);
|
||||||
|
ggs->search();
|
||||||
|
}else{
|
||||||
|
std::shared_ptr<Scraper> scraper = Settings::getInstance()->getScraper();
|
||||||
|
scraper->getResultsAsync(search, mWindow, [this, search] (std::vector<MetaDataList> mdls) {
|
||||||
|
if(mdls.empty())
|
||||||
|
resultEmpty(search);
|
||||||
|
else
|
||||||
|
resultFetched(search, mdls[0]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiScraperLog::resultFetched(ScraperSearchParams params, MetaDataList mdl)
|
||||||
|
{
|
||||||
|
writeLine(" -> \"" + mdl.get("name") + "\"", 0x0000FFFF);
|
||||||
|
writeLine(" Downloading images...", 0x0000FFFF);
|
||||||
|
resolveMetaDataAssetsAsync(mWindow, params, mdl, [this, params] (MetaDataList meta) { resultResolved(params, meta); });
|
||||||
|
|
||||||
|
//-> resultResolved
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiScraperLog::resultResolved(ScraperSearchParams params, MetaDataList mdl)
|
||||||
|
{
|
||||||
|
writeLine(" Success!", 0x00FF00FF);
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiScraperLog::resultEmpty(ScraperSearchParams search)
|
||||||
|
{
|
||||||
|
if(mManualMode)
|
||||||
|
writeLine(" SKIPPING", 0xFF0000FF);
|
||||||
|
else
|
||||||
|
writeLine(" NO RESULTS, skipping", 0xFF0000FF);
|
||||||
|
|
||||||
|
LOG(LogInfo) << "Scraper skipping [" << search.game->getCleanName() << "]";
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiScraperLog::done()
|
||||||
|
{
|
||||||
|
writeLine("====================", 0x000000FF);
|
||||||
|
writeLine("DONE!!", 0x00FF00FF);
|
||||||
|
writeLine("====================", 0x000000FF);
|
||||||
|
|
||||||
|
//done with everything!
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiScraperLog::writeLine(const std::string& line, unsigned int color)
|
||||||
|
{
|
||||||
|
std::shared_ptr<TextComponent> cmp(new TextComponent(mWindow));
|
||||||
|
cmp->setText(line);
|
||||||
|
cmp->setColor(color);
|
||||||
|
cmp->setSize(mSize.x(), (float)cmp->getFont()->getHeight());
|
||||||
|
|
||||||
|
mTextLines.push_back(cmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiScraperLog::render(const Eigen::Affine3f& parentTrans)
|
||||||
|
{
|
||||||
|
renderChildren(parentTrans * getTransform());
|
||||||
|
|
||||||
|
Eigen::Affine3f trans = parentTrans * getTransform();
|
||||||
|
|
||||||
|
//draw messages
|
||||||
|
float fontHeight = (float)Font::get(FONT_SIZE_MEDIUM)->getHeight();
|
||||||
|
trans = trans.translate(Eigen::Vector3f(0, mSize.y() - fontHeight, 0));
|
||||||
|
|
||||||
|
for(auto it = mTextLines.rbegin(); it != mTextLines.rend(); it++)
|
||||||
|
{
|
||||||
|
(*it)->render(trans);
|
||||||
|
trans = trans.translate(Eigen::Vector3f(0, -fontHeight, 0));
|
||||||
|
|
||||||
|
if(trans.translation().y() < fontHeight)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GuiScraperLog::input(InputConfig* config, Input input)
|
||||||
|
{
|
||||||
|
if(input.value != 0)
|
||||||
|
{
|
||||||
|
//we're done
|
||||||
|
if(mSearches.empty())
|
||||||
|
{
|
||||||
|
delete this;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GuiComponent::input(config, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiScraperLog::updateStatus()
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
ss << mSearches.size() << " games remaining";
|
||||||
|
|
||||||
|
mStatus.setText(ss.str());
|
||||||
|
}
|
39
src/components/GuiScraperLog.h
Normal file
39
src/components/GuiScraperLog.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../GuiComponent.h"
|
||||||
|
#include "NinePatchComponent.h"
|
||||||
|
#include <queue>
|
||||||
|
#include "../scrapers/Scraper.h"
|
||||||
|
#include <boost/circular_buffer.hpp>
|
||||||
|
#include "TextComponent.h"
|
||||||
|
|
||||||
|
class GuiScraperLog : public GuiComponent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GuiScraperLog(Window* window, const std::queue<ScraperSearchParams>& params, bool manualMode);
|
||||||
|
|
||||||
|
void start();
|
||||||
|
|
||||||
|
void render(const Eigen::Affine3f& parentTrans) override;
|
||||||
|
bool input(InputConfig* config, Input input) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateStatus();
|
||||||
|
void writeLine(const std::string& line, unsigned int color);
|
||||||
|
|
||||||
|
void resultFetched(ScraperSearchParams params, MetaDataList mdl);
|
||||||
|
void resultResolved(ScraperSearchParams params, MetaDataList mdl);
|
||||||
|
void resultEmpty(ScraperSearchParams params);
|
||||||
|
|
||||||
|
void next();
|
||||||
|
void done();
|
||||||
|
|
||||||
|
bool mManualMode;
|
||||||
|
|
||||||
|
NinePatchComponent mBox;
|
||||||
|
|
||||||
|
std::queue<ScraperSearchParams> mSearches;
|
||||||
|
|
||||||
|
TextComponent mStatus;
|
||||||
|
boost::circular_buffer< std::shared_ptr<TextComponent> > mTextLines;
|
||||||
|
};
|
|
@ -1,5 +1,5 @@
|
||||||
#include "GuiScraperStart.h"
|
#include "GuiScraperStart.h"
|
||||||
#include "GuiMsgBoxOk.h"
|
#include "GuiScraperLog.h"
|
||||||
|
|
||||||
GuiScraperStart::GuiScraperStart(Window* window) : GuiComponent(window),
|
GuiScraperStart::GuiScraperStart(Window* window) : GuiComponent(window),
|
||||||
mBox(window, ":/frame.png"),
|
mBox(window, ":/frame.png"),
|
||||||
|
@ -57,7 +57,10 @@ void GuiScraperStart::start()
|
||||||
{
|
{
|
||||||
std::queue<ScraperSearchParams> searches = getSearches(mSystemsOpt.getSelectedObjects(), mFiltersOpt.getSelectedObjects()[0]);
|
std::queue<ScraperSearchParams> searches = getSearches(mSystemsOpt.getSelectedObjects(), mFiltersOpt.getSelectedObjects()[0]);
|
||||||
|
|
||||||
mWindow->pushGui(new GuiMsgBoxOk(mWindow, "this isn't implemented yet"));
|
GuiScraperLog* gsl = new GuiScraperLog(mWindow, searches, mManualSwitch.getState());
|
||||||
|
mWindow->pushGui(gsl);
|
||||||
|
gsl->start();
|
||||||
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::queue<ScraperSearchParams> GuiScraperStart::getSearches(std::vector<SystemData*> systems, GameFilterFunc selector)
|
std::queue<ScraperSearchParams> GuiScraperStart::getSearches(std::vector<SystemData*> systems, GameFilterFunc selector)
|
||||||
|
|
|
@ -140,8 +140,11 @@ std::string downloadImage(const std::string& url, const std::string& saveAs)
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getSaveAsPath(const std::string& subdirectory, const std::string& name, const std::string& url)
|
std::string getSaveAsPath(const ScraperSearchParams& params, const std::string& suffix, const std::string& url)
|
||||||
{
|
{
|
||||||
|
const std::string subdirectory = params.system->getName();
|
||||||
|
const std::string name = params.game->getCleanName() + "-" + suffix;
|
||||||
|
|
||||||
std::string path = getHomePath() + "/.emulationstation/downloaded_images/";
|
std::string path = getHomePath() + "/.emulationstation/downloaded_images/";
|
||||||
|
|
||||||
if(!boost::filesystem::exists(path))
|
if(!boost::filesystem::exists(path))
|
||||||
|
@ -171,3 +174,31 @@ std::shared_ptr<Scraper> createScraperByName(const std::string& name)
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resolveMetaDataAssetsAsync(Window* window, const ScraperSearchParams& params, MetaDataList mdl, std::function<void(MetaDataList)> returnFunc)
|
||||||
|
{
|
||||||
|
std::vector<MetaDataDecl> mdd = params.system->getGameMDD();
|
||||||
|
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 [" << params.game->getCleanName() << "]! Skipping.";
|
||||||
|
}
|
||||||
|
|
||||||
|
mdl.set(key, savedAs);
|
||||||
|
resolveMetaDataAssetsAsync(window, params, mdl, returnFunc);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
returnFunc(mdl);
|
||||||
|
}
|
||||||
|
|
|
@ -32,9 +32,9 @@ private:
|
||||||
|
|
||||||
std::shared_ptr<Scraper> createScraperByName(const std::string& name);
|
std::shared_ptr<Scraper> createScraperByName(const std::string& name);
|
||||||
|
|
||||||
//About the same as "~/.emulationstation/downloaded_images/[subdirectory]/[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 std::string& subdirectory, const std::string& name, 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 the path to the downloaded file (saveAs) on completion.
|
||||||
//Returns empty string if an error occured.
|
//Returns empty string if an error occured.
|
||||||
|
@ -47,6 +47,8 @@ std::string downloadImage(const std::string& url, const std::string& saveAs);
|
||||||
//Same as downloadImage, just async.
|
//Same as downloadImage, just async.
|
||||||
void downloadImageAsync(Window* window, const std::string& url, const std::string& saveAs, std::function<void(std::string)> returnFunc);
|
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);
|
void resizeImage(const std::string& path, int maxWidth, int maxHeight);
|
||||||
|
|
Loading…
Reference in a new issue