mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-25 23:55:38 +00:00
Added semi-automatic scraping mode, fixed some scraping bugs and improved folder metadata editing.
This commit is contained in:
parent
b01bccc8d6
commit
8492160a80
|
@ -38,10 +38,7 @@ const std::vector<MetaDataDecl> gameMDD(gameDecls, gameDecls +
|
|||
|
||||
MetaDataDecl folderDecls[] = {
|
||||
{"name", MD_STRING, "", false, "name", "enter game name", true},
|
||||
{"sortname", MD_STRING, "", false, "sortname", "enter game sort name", false},
|
||||
{"desc", MD_MULTILINE_STRING, "", false, "description", "enter description", true},
|
||||
{"rating", MD_RATING, "0", false, "rating", "enter rating", true},
|
||||
{"releasedate", MD_DATE, "not-a-date-time", false, "release date", "enter release date", true},
|
||||
{"developer", MD_STRING, "unknown", false, "developer", "enter game developer", true},
|
||||
{"publisher", MD_STRING, "unknown", false, "publisher", "enter game publisher", true},
|
||||
{"genre", MD_STRING, "unknown", false, "genre", "enter game genre", true},
|
||||
|
|
|
@ -72,6 +72,8 @@ public:
|
|||
|
||||
inline MetaDataListType getType() const { return mType; }
|
||||
inline const std::vector<MetaDataDecl>& getMDD() const { return getMDDByType(getType()); }
|
||||
inline const std::vector<MetaDataDecl>& getMDD(MetaDataListType type) const
|
||||
{ return getMDDByType(type); }
|
||||
|
||||
private:
|
||||
MetaDataListType mType;
|
||||
|
|
|
@ -126,14 +126,27 @@ GuiGamelistOptions::GuiGamelistOptions(
|
|||
mMenu.addRow(row);
|
||||
}
|
||||
|
||||
if (UIModeController::getInstance()->isUIModeFull() && !fromPlaceholder &&
|
||||
!(mSystem->isCollection() && file->getType() == FOLDER)) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow,
|
||||
"EDIT THIS GAME'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(makeArrow(mWindow), false);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this));
|
||||
mMenu.addRow(row);
|
||||
if (file->getType() == FOLDER) {
|
||||
if (UIModeController::getInstance()->isUIModeFull() && !fromPlaceholder &&
|
||||
!(mSystem->isCollection() && file->getType() == FOLDER)) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow,
|
||||
"EDIT THIS FOLDER'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(makeArrow(mWindow), false);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this));
|
||||
mMenu.addRow(row);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (UIModeController::getInstance()->isUIModeFull() && !fromPlaceholder &&
|
||||
!(mSystem->isCollection() && file->getType() == FOLDER)) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow,
|
||||
"EDIT THIS GAME'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(makeArrow(mWindow), false);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this));
|
||||
mMenu.addRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
// Buttons. Logic to apply or cancel settings are handled by the destructor.
|
||||
|
@ -234,10 +247,20 @@ void GuiGamelistOptions::openMetaDataEd()
|
|||
};
|
||||
}
|
||||
|
||||
mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata, file->metadata.getMDD(), p,
|
||||
Utils::FileSystem::getFileName(file->getPath()), std::bind(
|
||||
&IGameListView::onFileChanged, ViewController::get()->getGameListView(
|
||||
file->getSystem()).get(), file, FILE_METADATA_CHANGED), deleteBtnFunc));
|
||||
if (file->getType() == FOLDER) {
|
||||
mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata,
|
||||
file->metadata.getMDD(FOLDER_METADATA), p,
|
||||
Utils::FileSystem::getFileName(file->getPath()), std::bind(
|
||||
&IGameListView::onFileChanged, ViewController::get()->getGameListView(
|
||||
file->getSystem()).get(), file, FILE_METADATA_CHANGED), deleteBtnFunc));
|
||||
}
|
||||
else {
|
||||
mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata,
|
||||
file->metadata.getMDD(GAME_METADATA), p,
|
||||
Utils::FileSystem::getFileName(file->getPath()), std::bind(
|
||||
&IGameListView::onFileChanged, ViewController::get()->getGameListView(
|
||||
file->getSystem()).get(), file, FILE_METADATA_CHANGED), deleteBtnFunc));
|
||||
}
|
||||
}
|
||||
|
||||
void GuiGamelistOptions::jumpToLetter()
|
||||
|
|
|
@ -163,7 +163,7 @@ GuiMetaDataEd::GuiMetaDataEd(
|
|||
defaultLaunchString, ed, updateVal, multiLine] {
|
||||
mWindow->pushGui(new GuiComplexTextEditPopup(mWindow, getHelpStyle(),
|
||||
title, staticTextString, defaultLaunchString, ed->getValue(),
|
||||
updateVal, multiLine, "SAVE"));
|
||||
updateVal, multiLine, "APPLY"));
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ GuiMetaDataEd::GuiMetaDataEd(
|
|||
auto updateVal = [ed](const std::string& newVal) { ed->setValue(newVal); };
|
||||
row.makeAcceptInputHandler([this, title, ed, updateVal, multiLine] {
|
||||
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), title,
|
||||
ed->getValue(), updateVal, multiLine, "SAVE"));
|
||||
ed->getValue(), updateVal, multiLine, "APPLY"));
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
@ -204,9 +204,11 @@ GuiMetaDataEd::GuiMetaDataEd(
|
|||
|
||||
std::vector< std::shared_ptr<ButtonComponent> > buttons;
|
||||
|
||||
if (!scraperParams.system->hasPlatformId(PlatformIds::PLATFORM_IGNORE))
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "SCRAPE", "scrape",
|
||||
std::bind(&GuiMetaDataEd::fetch, this)));
|
||||
if (mScraperParams.game->getType() != FOLDER) {
|
||||
if (!scraperParams.system->hasPlatformId(PlatformIds::PLATFORM_IGNORE))
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "SCRAPE", "scrape",
|
||||
std::bind(&GuiMetaDataEd::fetch, this)));
|
||||
}
|
||||
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "SAVE", "save metadata",
|
||||
[&] { save(); delete this; }));
|
||||
|
@ -290,6 +292,15 @@ void GuiMetaDataEd::fetchDone(const ScraperSearchResult& result)
|
|||
MetaDataList* metadata = nullptr;
|
||||
metadata = new MetaDataList(*mMetaData);
|
||||
|
||||
// Check if any values were manually changed before starting the scraping.
|
||||
// If so, it's these values we should compare against when scraping, not
|
||||
// the values previously saved for the game.
|
||||
for (unsigned int i = 0; i < mEditors.size(); i++) {
|
||||
const std::string& key = mMetaDataDecl.at(i).key;
|
||||
if (metadata->get(key) != mEditors[i]->getValue())
|
||||
metadata->set(key, mEditors[i]->getValue());
|
||||
}
|
||||
|
||||
mMetadataUpdated = GuiScraperSearch::saveMetadata(result, *metadata);
|
||||
|
||||
// Update the list with the scraped metadata values.
|
||||
|
|
|
@ -202,13 +202,21 @@ void GuiScraperMenu::openOtherSettings()
|
|||
s->addSaveFunc([scrape_overwrite] { Settings::getInstance()->setBool("ScraperOverwriteData",
|
||||
scrape_overwrite->getState()); });
|
||||
|
||||
// Automatic scraping.
|
||||
// Interactive scraping.
|
||||
auto scraper_interactive = std::make_shared<SwitchComponent>(mWindow);
|
||||
scraper_interactive->setState(Settings::getInstance()->getBool("ScraperInteractive"));
|
||||
s->addWithLabel("INTERACTIVE MODE", scraper_interactive);
|
||||
s->addSaveFunc([scraper_interactive] { Settings::getInstance()->setBool("ScraperInteractive",
|
||||
scraper_interactive->getState()); });
|
||||
|
||||
// Semi-automatic scraping.
|
||||
auto scraper_semiautomatic = std::make_shared<SwitchComponent>(mWindow);
|
||||
scraper_semiautomatic->setState(Settings::getInstance()->getBool("ScraperSemiautomatic"));
|
||||
s->addWithLabel("AUTO-APPROVE SINGLE GAME MATCHES", scraper_semiautomatic);
|
||||
s->addSaveFunc([scraper_semiautomatic] {
|
||||
Settings::getInstance()->setBool("ScraperSemiautomatic",
|
||||
scraper_semiautomatic->getState()); });
|
||||
|
||||
mWindow->pushGui(s);
|
||||
}
|
||||
|
||||
|
|
|
@ -55,9 +55,15 @@ GuiScraperMulti::GuiScraperMulti(
|
|||
Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER);
|
||||
mGrid.setEntry(mSubtitle, Vector2i(0, 2), false, true);
|
||||
|
||||
mSearchComp = std::make_shared<GuiScraperSearch>(mWindow,
|
||||
approveResults ? GuiScraperSearch::ALWAYS_ACCEPT_MATCHING_CRC
|
||||
: GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT);
|
||||
if (approveResults && !Settings::getInstance()->getBool("ScraperSemiautomatic"))
|
||||
mSearchComp = std::make_shared<GuiScraperSearch>(mWindow,
|
||||
GuiScraperSearch::NEVER_AUTO_ACCEPT);
|
||||
else if (approveResults && Settings::getInstance()->getBool("ScraperSemiautomatic"))
|
||||
mSearchComp = std::make_shared<GuiScraperSearch>(mWindow,
|
||||
GuiScraperSearch::ACCEPT_SINGLE_MATCHES);
|
||||
else if (!approveResults)
|
||||
mSearchComp = std::make_shared<GuiScraperSearch>(mWindow,
|
||||
GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT);
|
||||
mSearchComp->setAcceptCallback(std::bind(&GuiScraperMulti::acceptResult,
|
||||
this, std::placeholders::_1));
|
||||
mSearchComp->setSkipCallback(std::bind(&GuiScraperMulti::skip, this));
|
||||
|
|
|
@ -299,22 +299,31 @@ void GuiScraperSearch::onSearchDone(const std::vector<ScraperSearchResult>& resu
|
|||
}
|
||||
else {
|
||||
mFoundGame = true;
|
||||
ComponentListRow row;
|
||||
for (size_t i = 0; i < results.size(); i++) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow,
|
||||
Utils::String::toUpper(results.at(i).mdl.get("name")), font, color), true);
|
||||
row.makeAcceptInputHandler([this, i] { returnResult(mScraperResults.at(i)); });
|
||||
mResultList->addRow(row);
|
||||
if (!(mSearchType == ACCEPT_SINGLE_MATCHES && results.size() == 1)) {
|
||||
ComponentListRow row;
|
||||
|
||||
for (size_t i = 0; i < results.size(); i++) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow,
|
||||
Utils::String::toUpper(results.at(i).mdl.get("name")), font, color), true);
|
||||
row.makeAcceptInputHandler([this, i] { returnResult(mScraperResults.at(i)); });
|
||||
mResultList->addRow(row);
|
||||
}
|
||||
mGrid.resetCursor();
|
||||
}
|
||||
mGrid.resetCursor();
|
||||
}
|
||||
|
||||
mBlockAccept = false;
|
||||
updateInfoPane();
|
||||
|
||||
// If there is no scraping result or if there is no game media to download
|
||||
// as a thumbnail, then proceed directly.
|
||||
// If there is no thumbnail to download and we're in semi-automatic mode, proceed to return
|
||||
// the results or we'll get stuck forever waiting for a thumbnail to be downloaded.
|
||||
if (mSearchType == ACCEPT_SINGLE_MATCHES && results.size() == 1 &&
|
||||
mScraperResults.front().ThumbnailImageUrl == "")
|
||||
returnResult(mScraperResults.front());
|
||||
|
||||
// For automatic mode, if there's no thumbnail to download or no matching games found,
|
||||
// proceed directly or we'll get stuck forever.
|
||||
if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) {
|
||||
if (mScraperResults.size() == 0 || (mScraperResults.size() > 0 &&
|
||||
mScraperResults.front().ThumbnailImageUrl == "")) {
|
||||
|
@ -324,7 +333,6 @@ void GuiScraperSearch::onSearchDone(const std::vector<ScraperSearchResult>& resu
|
|||
returnResult(mScraperResults.front());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GuiScraperSearch::onSearchError(const std::string& error)
|
||||
|
@ -562,14 +570,11 @@ void GuiScraperSearch::updateThumbnail()
|
|||
|
||||
mThumbnailReq.reset();
|
||||
|
||||
// When the thumbnail has been downloaded and we are in non-interactive
|
||||
// mode, we proceed to automatically download the rest of the media files.
|
||||
// The reason to always complete the thumbnail download first is that it looks
|
||||
// a lot more consistent in the GUI. And since the thumbnail is being cached
|
||||
// anyway, this hardly takes any more time. Maybe rather the opposite as the
|
||||
// image used for the thumbnail (cover or screenshot) would have had to be
|
||||
// requested from the server again.
|
||||
if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT &&
|
||||
// When the thumbnail has been downloaded and we are in automatic mode, or if
|
||||
// we are in semi-automatic mode with a single matching game result, we proceed
|
||||
// to immediately download the rest of the media files.
|
||||
if ((mSearchType == ALWAYS_ACCEPT_FIRST_RESULT ||
|
||||
(mSearchType == ACCEPT_SINGLE_MATCHES && mScraperResults.size() == 1)) &&
|
||||
mScraperResults.front().thumbnailDownloadStatus == COMPLETED) {
|
||||
if (mScraperResults.size() == 0)
|
||||
mSkipCallback();
|
||||
|
@ -645,6 +650,10 @@ bool GuiScraperSearch::saveMetadata(
|
|||
if (result.mdl.get(key) == metadata.get(key))
|
||||
continue;
|
||||
|
||||
// Make sure to set releasedate to the proper default value.
|
||||
if (key == "releasedate" && metadata.get(key) == "19700101T010000")
|
||||
metadata.set(key, mMetaDataDecl.at(i).defaultValue);
|
||||
|
||||
// Overwrite all the other values if the flag to overwrite data has been set.
|
||||
if (Settings::getInstance()->getBool("ScraperOverwriteData")) {
|
||||
metadata.set(key, result.mdl.get(key));
|
||||
|
|
|
@ -32,7 +32,7 @@ class GuiScraperSearch : public GuiComponent
|
|||
public:
|
||||
enum SearchType {
|
||||
ALWAYS_ACCEPT_FIRST_RESULT,
|
||||
ALWAYS_ACCEPT_MATCHING_CRC,
|
||||
ACCEPT_SINGLE_MATCHES,
|
||||
NEVER_AUTO_ACCEPT
|
||||
};
|
||||
|
||||
|
|
|
@ -398,13 +398,6 @@ bool ViewController::input(InputConfig* config, Input input)
|
|||
// to play when we've closed the menu.
|
||||
if (mSystemListView->isAnimationPlaying(0))
|
||||
mSystemListView->finishAnimation(0);
|
||||
// for (unsigned int i = 0; i > mGameListViews.size(); i++)
|
||||
// mGameListViews[i].get()->finishAnimation(0);
|
||||
|
||||
for (auto it = mGameListViews.begin(); it != mGameListViews.end(); it++) {
|
||||
std::string teststring = it->second->getCursor()->getName();
|
||||
std::string teststring2;
|
||||
}
|
||||
|
||||
mWindow->pushGui(new GuiMenu(mWindow));
|
||||
return true;
|
||||
|
|
|
@ -327,8 +327,8 @@ void InputManager::loadDefaultKBConfig()
|
|||
cfg->mapInput("b", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_BACKSPACE, 1, true));
|
||||
cfg->mapInput("x", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_DELETE, 1, true));
|
||||
cfg->mapInput("y", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_INSERT, 1, true));
|
||||
cfg->mapInput("start", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_F1, 1, true));
|
||||
cfg->mapInput("select", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_F2, 1, true));
|
||||
cfg->mapInput("start", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_ESCAPE, 1, true));
|
||||
cfg->mapInput("select", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_F1, 1, true));
|
||||
|
||||
cfg->mapInput("leftshoulder", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_PAGEUP, 1, true));
|
||||
cfg->mapInput("rightshoulder", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_PAGEDOWN, 1, true));
|
||||
|
|
|
@ -137,6 +137,7 @@ void Settings::setDefaults()
|
|||
// mBoolMap["ScraperGenerateMiximages"] = false;
|
||||
// mBoolMap["ScraperGenerateThumbnails"] = false;
|
||||
mBoolMap["ScraperInteractive"] = true;
|
||||
mBoolMap["ScraperSemiautomatic"] = true;
|
||||
mBoolMap["ScraperOverwriteData"] = false;
|
||||
mBoolMap["ScrapeMetadata"] = true;
|
||||
mBoolMap["ScrapeGameNames"] = true;
|
||||
|
|
Loading…
Reference in a new issue