ES-DE/es-app/src/guis/GuiScraperMenu.cpp

1423 lines
65 KiB
C++

// SPDX-License-Identifier: MIT
//
// ES-DE
// GuiScraperMenu.cpp
//
// Game media scraper, including settings as well as the scraping start button.
// Submenu to the GuiMenu main menu.
// Will call GuiScraperMulti to perform the actual scraping.
//
#include "guis/GuiScraperMenu.h"
#include "FileData.h"
#include "FileSorts.h"
#include "SystemData.h"
#include "components/OptionListComponent.h"
#include "components/SwitchComponent.h"
#include "guis/GuiMsgBox.h"
#include "guis/GuiOfflineGenerator.h"
#include "guis/GuiScraperMulti.h"
GuiScraperMenu::GuiScraperMenu(std::string title)
: mRenderer {Renderer::getInstance()}
, mMenu {title}
{
// Scraper service.
mScraper =
std::make_shared<OptionListComponent<std::string>>(getHelpStyle(), "SCRAPE FROM", false);
std::vector<std::string> scrapers = getScraperList();
// Select either the first entry or the one read from the settings,
// just in case the scraper from settings has vanished.
for (auto it = scrapers.cbegin(); it != scrapers.cend(); ++it)
mScraper->add(*it, *it, *it == Settings::getInstance()->getString("Scraper"));
// If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the scraper to "screenscraper" in this case.
if (mScraper->getSelectedObjects().size() == 0)
mScraper->selectEntry(0);
mMenu.addWithLabel("SCRAPE FROM", mScraper);
// Search filters, getSearches() will generate a queue of games to scrape
// based on the outcome of the checks below.
mFilters = std::make_shared<OptionListComponent<GameFilterFunc>>(getHelpStyle(),
"SCRAPE THESE GAMES", false);
mFilters->add(
"ALL GAMES",
[](SystemData*, FileData*) -> bool {
// All games.
return true;
},
false);
mFilters->add(
"FAVORITE GAMES",
[](SystemData*, FileData* g) -> bool {
// Favorite games.
return g->getFavorite();
},
false);
mFilters->add(
"NO METADATA",
[](SystemData*, FileData* g) -> bool {
// No metadata.
return g->metadata.get("desc").empty();
},
false);
mFilters->add(
"NO GAME IMAGE",
[](SystemData*, FileData* g) -> bool {
// No game image.
return g->getImagePath().empty();
},
false);
mFilters->add(
"NO GAME VIDEO",
[](SystemData*, FileData* g) -> bool {
// No game video.
return g->getVideoPath().empty();
},
false);
mFilters->add(
"FOLDERS ONLY",
[](SystemData*, FileData* g) -> bool {
// Folders only.
return g->getType() == FOLDER;
},
false);
mFilters->selectEntry(Settings::getInstance()->getInt("ScraperFilter"));
mMenu.addWithLabel("SCRAPE THESE GAMES", mFilters);
mMenu.addSaveFunc([this] {
if (mScraper->getSelected() != Settings::getInstance()->getString("Scraper")) {
Settings::getInstance()->setString("Scraper", mScraper->getSelected());
mMenu.setNeedsSaving();
}
// The filter setting is only retained during the program session i.e. it's not saved
// to es_settings.xml.
if (mFilters->getSelectedId() !=
static_cast<unsigned int>(Settings::getInstance()->getInt("ScraperFilter")))
Settings::getInstance()->setInt("ScraperFilter", mFilters->getSelectedId());
});
// Add systems (all systems with an existing platform ID are listed).
mSystems = std::make_shared<OptionListComponent<SystemData*>>(getHelpStyle(),
"SCRAPE THESE SYSTEMS", true);
for (unsigned int i {0}; i < SystemData::sSystemVector.size(); ++i) {
if (!SystemData::sSystemVector[i]->hasPlatformId(PlatformIds::PLATFORM_IGNORE)) {
mSystems->add(SystemData::sSystemVector[i]->getFullName(), SystemData::sSystemVector[i],
!SystemData::sSystemVector[i]->getPlatformIds().empty());
SystemData::sSystemVector[i]->getScrapeFlag() ? mSystems->selectEntry(i) :
mSystems->unselectEntry(i);
}
}
mMenu.addWithLabel("SCRAPE THESE SYSTEMS", mSystems);
addEntry("ACCOUNT SETTINGS", mMenuColorPrimary, true, [this] {
// Open the account options menu.
openAccountOptions();
});
addEntry("CONTENT SETTINGS", mMenuColorPrimary, true, [this] {
// If the scraper service has been changed before entering this menu, then save the
// settings so that the specific options supported by the respective scrapers
// can be enabled or disabled.
if (mScraper->getSelected() != Settings::getInstance()->getString("Scraper"))
mMenu.save();
openContentOptions();
});
addEntry("MIXIMAGE SETTINGS", mMenuColorPrimary, true, [this] {
// Open the miximage options menu.
openMiximageOptions();
});
addEntry("OTHER SETTINGS", mMenuColorPrimary, true, [this] {
// If the scraper service has been changed before entering this menu, then save the
// settings so that the specific options supported by the respective scrapers
// can be enabled or disabled.
if (mScraper->getSelected() != Settings::getInstance()->getString("Scraper"))
mMenu.save();
openOtherOptions();
});
addChild(&mMenu);
mMenu.addButton("START", "start scraper", std::bind(&GuiScraperMenu::pressedStart, this));
mMenu.addButton("BACK", "back", [&] { delete this; });
setSize(mMenu.getSize());
setPosition((mRenderer->getScreenWidth() - mSize.x) / 2.0f,
mRenderer->getScreenHeight() * 0.13f);
// Make sure that the hash searching max file size is within the allowed range.
if (Settings::getInstance()->getInt("ScraperSearchFileHashMaxSize") < 32)
Settings::getInstance()->setInt("ScraperSearchFileHashMaxSize", 32);
else if (Settings::getInstance()->getInt("ScraperSearchFileHashMaxSize") > 800)
Settings::getInstance()->setInt("ScraperSearchFileHashMaxSize", 800);
}
GuiScraperMenu::~GuiScraperMenu()
{
// Save the scrape flags to the system settings so that they are
// remembered throughout the program session.
std::vector<SystemData*> sys {mSystems->getSelectedObjects()};
for (auto it = SystemData::sSystemVector.cbegin(); // Line break.
it != SystemData::sSystemVector.cend(); ++it) {
(*it)->setScrapeFlag(false);
for (auto it_sys = sys.cbegin(); it_sys != sys.cend(); ++it_sys) {
if ((*it)->getFullName() == (*it_sys)->getFullName())
(*it)->setScrapeFlag(true);
}
}
}
void GuiScraperMenu::openAccountOptions()
{
auto s = new GuiSettings("ACCOUNT SETTINGS");
// ScreenScraper username.
auto scraperUsernameScreenScraper = std::make_shared<TextComponent>(
"", Font::get(FONT_SIZE_MEDIUM), mMenuColorPrimary, ALIGN_RIGHT);
s->addEditableTextComponent("SCREENSCRAPER USERNAME", scraperUsernameScreenScraper,
Settings::getInstance()->getString("ScraperUsernameScreenScraper"));
s->addSaveFunc([scraperUsernameScreenScraper, s] {
if (scraperUsernameScreenScraper->getValue() !=
Settings::getInstance()->getString("ScraperUsernameScreenScraper")) {
Settings::getInstance()->setString("ScraperUsernameScreenScraper",
scraperUsernameScreenScraper->getValue());
s->setNeedsSaving();
}
});
// ScreenScraper password.
auto scraperPasswordScreenScraper = std::make_shared<TextComponent>(
"", Font::get(FONT_SIZE_MEDIUM), mMenuColorPrimary, ALIGN_RIGHT);
std::string passwordMasked;
if (Settings::getInstance()->getString("ScraperPasswordScreenScraper") != "") {
passwordMasked = "********";
scraperPasswordScreenScraper->setHiddenValue(
Settings::getInstance()->getString("ScraperPasswordScreenScraper"));
}
s->addEditableTextComponent("SCREENSCRAPER PASSWORD", scraperPasswordScreenScraper,
passwordMasked, "", true);
s->addSaveFunc([scraperPasswordScreenScraper, s] {
if (scraperPasswordScreenScraper->getHiddenValue() !=
Settings::getInstance()->getString("ScraperPasswordScreenScraper")) {
Settings::getInstance()->setString("ScraperPasswordScreenScraper",
scraperPasswordScreenScraper->getHiddenValue());
s->setNeedsSaving();
}
});
// Whether to use the ScreenScraper account when scraping.
auto scraperUseAccountScreenScraper = std::make_shared<SwitchComponent>();
scraperUseAccountScreenScraper->setState(
Settings::getInstance()->getBool("ScraperUseAccountScreenScraper"));
s->addWithLabel("USE THIS ACCOUNT FOR SCREENSCRAPER", scraperUseAccountScreenScraper);
s->addSaveFunc([scraperUseAccountScreenScraper, s] {
if (scraperUseAccountScreenScraper->getState() !=
Settings::getInstance()->getBool("ScraperUseAccountScreenScraper")) {
Settings::getInstance()->setBool("ScraperUseAccountScreenScraper",
scraperUseAccountScreenScraper->getState());
s->setNeedsSaving();
}
});
mWindow->pushGui(s);
}
void GuiScraperMenu::openContentOptions()
{
auto s = new GuiSettings("CONTENT SETTINGS");
// Scrape game names.
auto scrapeGameNames = std::make_shared<SwitchComponent>();
scrapeGameNames->setState(Settings::getInstance()->getBool("ScrapeGameNames"));
s->addWithLabel("GAME NAMES", scrapeGameNames);
s->addSaveFunc([scrapeGameNames, s] {
if (scrapeGameNames->getState() != Settings::getInstance()->getBool("ScrapeGameNames")) {
Settings::getInstance()->setBool("ScrapeGameNames", scrapeGameNames->getState());
s->setNeedsSaving();
}
});
// Scrape ratings.
auto scrapeRatings = std::make_shared<SwitchComponent>();
scrapeRatings->setState(Settings::getInstance()->getBool("ScrapeRatings"));
s->addWithLabel("RATINGS", scrapeRatings);
s->addSaveFunc([scrapeRatings, s] {
if (scrapeRatings->getState() != Settings::getInstance()->getBool("ScrapeRatings")) {
Settings::getInstance()->setBool("ScrapeRatings", scrapeRatings->getState());
s->setNeedsSaving();
}
});
// Ratings are not supported by TheGamesDB, so gray out the option if this scraper is selected.
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
scrapeRatings->setEnabled(false);
scrapeRatings->setOpacity(DISABLED_OPACITY);
scrapeRatings->getParent()
->getChild(scrapeRatings->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
// ScreenScraper controller scraping is currently broken, it's unclear if they will fix it.
// // Scrape controllers (arcade systems only).
// auto scrapeControllers = std::make_shared<SwitchComponent>();
// scrapeControllers->setState(Settings::getInstance()->getBool("ScrapeControllers"));
// s->addWithLabel("CONTROLLERS (ARCADE SYSTEMS ONLY)", scrapeControllers);
// s->addSaveFunc([scrapeControllers, s] {
// if (scrapeControllers->getState() !=
// Settings::getInstance()->getBool("ScrapeControllers")) {
// Settings::getInstance()->setBool("ScrapeControllers", scrapeControllers->getState());
// s->setNeedsSaving();
// }
// });
// // Controllers are not supported by TheGamesDB, so gray out the option if this scraper is
// // selected.
// if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
// scrapeControllers->setEnabled(false);
// scrapeControllers->setOpacity(DISABLED_OPACITY);
// scrapeControllers->getParent()
// ->getChild(scrapeControllers->getChildIndex() - 1)
// ->setOpacity(DISABLED_OPACITY);
// }
// Scrape other metadata.
auto scrapeMetadata = std::make_shared<SwitchComponent>();
scrapeMetadata->setState(Settings::getInstance()->getBool("ScrapeMetadata"));
s->addWithLabel("OTHER METADATA", scrapeMetadata);
s->addSaveFunc([scrapeMetadata, s] {
if (scrapeMetadata->getState() != Settings::getInstance()->getBool("ScrapeMetadata")) {
Settings::getInstance()->setBool("ScrapeMetadata", scrapeMetadata->getState());
s->setNeedsSaving();
}
});
// Scrape videos.
auto scrapeVideos = std::make_shared<SwitchComponent>();
scrapeVideos->setState(Settings::getInstance()->getBool("ScrapeVideos"));
s->addWithLabel("VIDEOS", scrapeVideos);
s->addSaveFunc([scrapeVideos, s] {
if (scrapeVideos->getState() != Settings::getInstance()->getBool("ScrapeVideos")) {
Settings::getInstance()->setBool("ScrapeVideos", scrapeVideos->getState());
s->setNeedsSaving();
}
});
// Videos are not supported by TheGamesDB, so gray out the option if this scraper is selected.
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
scrapeVideos->setEnabled(false);
scrapeVideos->setOpacity(DISABLED_OPACITY);
scrapeVideos->getParent()
->getChild(scrapeVideos->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
// Scrape screenshots images.
auto scrapeScreenshots = std::make_shared<SwitchComponent>();
scrapeScreenshots->setState(Settings::getInstance()->getBool("ScrapeScreenshots"));
s->addWithLabel("SCREENSHOT IMAGES", scrapeScreenshots);
s->addSaveFunc([scrapeScreenshots, s] {
if (scrapeScreenshots->getState() !=
Settings::getInstance()->getBool("ScrapeScreenshots")) {
Settings::getInstance()->setBool("ScrapeScreenshots", scrapeScreenshots->getState());
s->setNeedsSaving();
}
});
// Scrape title screen images.
auto scrapeTitleScreens = std::make_shared<SwitchComponent>();
scrapeTitleScreens->setState(Settings::getInstance()->getBool("ScrapeTitleScreens"));
s->addWithLabel("TITLE SCREEN IMAGES", scrapeTitleScreens);
s->addSaveFunc([scrapeTitleScreens, s] {
if (scrapeTitleScreens->getState() !=
Settings::getInstance()->getBool("ScrapeTitleScreens")) {
Settings::getInstance()->setBool("ScrapeTitleScreens", scrapeTitleScreens->getState());
s->setNeedsSaving();
}
});
// Scrape box cover images.
auto scrapeCovers = std::make_shared<SwitchComponent>();
scrapeCovers->setState(Settings::getInstance()->getBool("ScrapeCovers"));
s->addWithLabel("BOX COVER IMAGES", scrapeCovers);
s->addSaveFunc([scrapeCovers, s] {
if (scrapeCovers->getState() != Settings::getInstance()->getBool("ScrapeCovers")) {
Settings::getInstance()->setBool("ScrapeCovers", scrapeCovers->getState());
s->setNeedsSaving();
}
});
// Scrape box back cover images.
auto scrapeBackCovers = std::make_shared<SwitchComponent>();
scrapeBackCovers->setState(Settings::getInstance()->getBool("ScrapeBackCovers"));
s->addWithLabel("BOX BACK COVER IMAGES", scrapeBackCovers);
s->addSaveFunc([scrapeBackCovers, s] {
if (scrapeBackCovers->getState() != Settings::getInstance()->getBool("ScrapeBackCovers")) {
Settings::getInstance()->setBool("ScrapeBackCovers", scrapeBackCovers->getState());
s->setNeedsSaving();
}
});
// Scrape marquee images.
auto scrapeMarquees = std::make_shared<SwitchComponent>();
scrapeMarquees->setState(Settings::getInstance()->getBool("ScrapeMarquees"));
s->addWithLabel("MARQUEE (WHEEL) IMAGES", scrapeMarquees);
s->addSaveFunc([scrapeMarquees, s] {
if (scrapeMarquees->getState() != Settings::getInstance()->getBool("ScrapeMarquees")) {
Settings::getInstance()->setBool("ScrapeMarquees", scrapeMarquees->getState());
s->setNeedsSaving();
}
});
// Scrape 3D box images.
auto scrape3dBoxes = std::make_shared<SwitchComponent>();
scrape3dBoxes->setState(Settings::getInstance()->getBool("Scrape3DBoxes"));
s->addWithLabel("3D BOX IMAGES", scrape3dBoxes);
s->addSaveFunc([scrape3dBoxes, s] {
if (scrape3dBoxes->getState() != Settings::getInstance()->getBool("Scrape3DBoxes")) {
Settings::getInstance()->setBool("Scrape3DBoxes", scrape3dBoxes->getState());
s->setNeedsSaving();
}
});
// 3D box images are not supported by TheGamesDB, so gray out the option if this scraper
// is selected.
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
scrape3dBoxes->setEnabled(false);
scrape3dBoxes->setOpacity(DISABLED_OPACITY);
scrape3dBoxes->getParent()
->getChild(scrape3dBoxes->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
// Scrape physical media images.
auto scrapePhysicalMedia = std::make_shared<SwitchComponent>();
scrapePhysicalMedia->setState(Settings::getInstance()->getBool("ScrapePhysicalMedia"));
s->addWithLabel("PHYSICAL MEDIA IMAGES", scrapePhysicalMedia);
s->addSaveFunc([scrapePhysicalMedia, s] {
if (scrapePhysicalMedia->getState() !=
Settings::getInstance()->getBool("ScrapePhysicalMedia")) {
Settings::getInstance()->setBool("ScrapePhysicalMedia",
scrapePhysicalMedia->getState());
s->setNeedsSaving();
}
});
// Physical media images are not supported by TheGamesDB, so gray out the option if this
// scraper is selected.
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
scrapePhysicalMedia->setEnabled(false);
scrapePhysicalMedia->setOpacity(DISABLED_OPACITY);
scrapePhysicalMedia->getParent()
->getChild(scrapePhysicalMedia->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
// Scrape fan art images.
auto scrapeFanArt = std::make_shared<SwitchComponent>();
scrapeFanArt->setState(Settings::getInstance()->getBool("ScrapeFanArt"));
s->addWithLabel("FAN ART IMAGES", scrapeFanArt);
s->addSaveFunc([scrapeFanArt, s] {
if (scrapeFanArt->getState() != Settings::getInstance()->getBool("ScrapeFanArt")) {
Settings::getInstance()->setBool("ScrapeFanArt", scrapeFanArt->getState());
s->setNeedsSaving();
}
});
// Scrape game manuals.
auto scrapeManuals = std::make_shared<SwitchComponent>();
scrapeManuals->setState(Settings::getInstance()->getBool("ScrapeManuals"));
s->addWithLabel("GAME MANUALS", scrapeManuals);
s->addSaveFunc([scrapeManuals, s] {
if (scrapeManuals->getState() != Settings::getInstance()->getBool("ScrapeManuals")) {
Settings::getInstance()->setBool("ScrapeManuals", scrapeManuals->getState());
s->setNeedsSaving();
}
});
// Game manuals are not supported by TheGamesDB, so gray out the option if this scraper
// is selected.
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
scrapeManuals->setEnabled(false);
scrapeManuals->setOpacity(DISABLED_OPACITY);
scrapeManuals->getParent()
->getChild(scrapeManuals->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
mWindow->pushGui(s);
}
void GuiScraperMenu::openMiximageOptions()
{
auto s = new GuiSettings("MIXIMAGE SETTINGS");
// Miximage resolution.
auto miximageResolution = std::make_shared<OptionListComponent<std::string>>(
getHelpStyle(), "MIXIMAGE RESOLUTION", false);
std::string selectedResolution {Settings::getInstance()->getString("MiximageResolution")};
miximageResolution->add("1280x960", "1280x960", selectedResolution == "1280x960");
miximageResolution->add("1920x1440", "1920x1440", selectedResolution == "1920x1440");
miximageResolution->add("640x480", "640x480", selectedResolution == "640x480");
// If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the resolution to "1280x960" in this case.
if (miximageResolution->getSelectedObjects().size() == 0)
miximageResolution->selectEntry(0);
s->addWithLabel("MIXIMAGE RESOLUTION", miximageResolution);
s->addSaveFunc([miximageResolution, s] {
if (miximageResolution->getSelected() !=
Settings::getInstance()->getString("MiximageResolution")) {
Settings::getInstance()->setString("MiximageResolution",
miximageResolution->getSelected());
s->setNeedsSaving();
}
});
// Horizontally oriented screenshots fit.
auto miximageHorizontalFit = std::make_shared<OptionListComponent<std::string>>(
getHelpStyle(), "HORIZONTAL SCREENSHOT FIT", false);
const std::string selectedHorizontalFit {
Settings::getInstance()->getString("MiximageScreenshotHorizontalFit")};
miximageHorizontalFit->add("contain", "contain", selectedHorizontalFit == "contain");
miximageHorizontalFit->add("crop", "crop", selectedHorizontalFit == "crop");
miximageHorizontalFit->add("stretch", "stretch", selectedHorizontalFit == "stretch");
// If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the horizontal screenshot fit to "crop" in this case.
if (miximageHorizontalFit->getSelectedObjects().size() == 0)
miximageHorizontalFit->selectEntry(1);
s->addWithLabel("HORIZONTAL SCREENSHOT FIT", miximageHorizontalFit);
s->addSaveFunc([miximageHorizontalFit, s] {
if (miximageHorizontalFit->getSelected() !=
Settings::getInstance()->getString("MiximageScreenshotHorizontalFit")) {
Settings::getInstance()->setString("MiximageScreenshotHorizontalFit",
miximageHorizontalFit->getSelected());
s->setNeedsSaving();
}
});
// Vertically oriented screenshots fit.
auto miximageVerticalFit = std::make_shared<OptionListComponent<std::string>>(
getHelpStyle(), "VERTICAL SCREENSHOT FIT", false);
const std::string selectedVerticalFit {
Settings::getInstance()->getString("MiximageScreenshotVerticalFit")};
miximageVerticalFit->add("contain", "contain", selectedVerticalFit == "contain");
miximageVerticalFit->add("crop", "crop", selectedVerticalFit == "crop");
miximageVerticalFit->add("stretch", "stretch", selectedVerticalFit == "stretch");
// If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the vertical screenshot fit to "contain" in this case.
if (miximageVerticalFit->getSelectedObjects().size() == 0)
miximageVerticalFit->selectEntry(0);
s->addWithLabel("VERTICAL SCREENSHOT FIT", miximageVerticalFit);
s->addSaveFunc([miximageVerticalFit, s] {
if (miximageVerticalFit->getSelected() !=
Settings::getInstance()->getString("MiximageScreenshotVerticalFit")) {
Settings::getInstance()->setString("MiximageScreenshotVerticalFit",
miximageVerticalFit->getSelected());
s->setNeedsSaving();
}
});
// Screenshots aspect ratio threshold.
auto miximageAspectThreshold = std::make_shared<OptionListComponent<std::string>>(
getHelpStyle(), "ASPECT RATIO THRESHOLD", false);
const std::string selectedAspectThreshold {
Settings::getInstance()->getString("MiximageScreenshotAspectThreshold")};
miximageAspectThreshold->add("high", "high", selectedAspectThreshold == "high");
miximageAspectThreshold->add("low", "low", selectedAspectThreshold == "low");
// If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the screenshot aspect threshold to "high" in this case.
if (miximageAspectThreshold->getSelectedObjects().size() == 0)
miximageAspectThreshold->selectEntry(0);
s->addWithLabel("SCREENSHOT ASPECT RATIO THRESHOLD", miximageAspectThreshold);
s->addSaveFunc([miximageAspectThreshold, s] {
if (miximageAspectThreshold->getSelected() !=
Settings::getInstance()->getString("MiximageScreenshotAspectThreshold")) {
Settings::getInstance()->setString("MiximageScreenshotAspectThreshold",
miximageAspectThreshold->getSelected());
s->setNeedsSaving();
}
});
// Blank areas fill color.
auto miximageBlankAreasColor = std::make_shared<OptionListComponent<std::string>>(
getHelpStyle(), "BLANK AREAS FILL COLOR", false);
const std::string selectedBlankAreasColor {
Settings::getInstance()->getString("MiximageScreenshotBlankAreasColor")};
miximageBlankAreasColor->add("black", "black", selectedBlankAreasColor == "black");
miximageBlankAreasColor->add("frame", "frame", selectedBlankAreasColor == "frame");
// If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the blank area fill color to "black" in this case.
if (miximageBlankAreasColor->getSelectedObjects().size() == 0)
miximageBlankAreasColor->selectEntry(0);
s->addWithLabel("BLANK AREAS FILL COLOR", miximageBlankAreasColor);
s->addSaveFunc([miximageBlankAreasColor, s] {
if (miximageBlankAreasColor->getSelected() !=
Settings::getInstance()->getString("MiximageScreenshotBlankAreasColor")) {
Settings::getInstance()->setString("MiximageScreenshotBlankAreasColor",
miximageBlankAreasColor->getSelected());
s->setNeedsSaving();
}
});
// Screenshot scaling method.
auto miximageScaling = std::make_shared<OptionListComponent<std::string>>(
getHelpStyle(), "SCREENSHOT SCALING", false);
std::string selectedScaling {Settings::getInstance()->getString("MiximageScreenshotScaling")};
miximageScaling->add("sharp", "sharp", selectedScaling == "sharp");
miximageScaling->add("smooth", "smooth", selectedScaling == "smooth");
// If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the scaling method to "sharp" in this case.
if (miximageScaling->getSelectedObjects().size() == 0)
miximageScaling->selectEntry(0);
s->addWithLabel("SCREENSHOT SCALING METHOD", miximageScaling);
s->addSaveFunc([miximageScaling, s] {
if (miximageScaling->getSelected() !=
Settings::getInstance()->getString("MiximageScreenshotScaling")) {
Settings::getInstance()->setString("MiximageScreenshotScaling",
miximageScaling->getSelected());
s->setNeedsSaving();
}
});
// Box/cover size.
auto miximageBoxSize =
std::make_shared<OptionListComponent<std::string>>(getHelpStyle(), "BOX SIZE", false);
std::string selectedBoxSize {Settings::getInstance()->getString("MiximageBoxSize")};
miximageBoxSize->add("small", "small", selectedBoxSize == "small");
miximageBoxSize->add("medium", "medium", selectedBoxSize == "medium");
miximageBoxSize->add("large", "large", selectedBoxSize == "large");
// If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the box size to "medium" in this case.
if (miximageBoxSize->getSelectedObjects().size() == 0)
miximageBoxSize->selectEntry(0);
s->addWithLabel("BOX SIZE", miximageBoxSize);
s->addSaveFunc([miximageBoxSize, s] {
if (miximageBoxSize->getSelected() !=
Settings::getInstance()->getString("MiximageBoxSize")) {
Settings::getInstance()->setString("MiximageBoxSize", miximageBoxSize->getSelected());
s->setNeedsSaving();
}
});
// Physical media size.
auto miximagePhysicalMediaSize = std::make_shared<OptionListComponent<std::string>>(
getHelpStyle(), "PHYSICAL MEDIA SIZE", false);
std::string selectedPhysicalMediaSize {
Settings::getInstance()->getString("MiximagePhysicalMediaSize")};
miximagePhysicalMediaSize->add("small", "small", selectedPhysicalMediaSize == "small");
miximagePhysicalMediaSize->add("medium", "medium", selectedPhysicalMediaSize == "medium");
miximagePhysicalMediaSize->add("large", "large", selectedPhysicalMediaSize == "large");
// If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the physical media size to "medium" in this case.
if (miximagePhysicalMediaSize->getSelectedObjects().size() == 0)
miximagePhysicalMediaSize->selectEntry(0);
s->addWithLabel("PHYSICAL MEDIA SIZE", miximagePhysicalMediaSize);
s->addSaveFunc([miximagePhysicalMediaSize, s] {
if (miximagePhysicalMediaSize->getSelected() !=
Settings::getInstance()->getString("MiximagePhysicalMediaSize")) {
Settings::getInstance()->setString("MiximagePhysicalMediaSize",
miximagePhysicalMediaSize->getSelected());
s->setNeedsSaving();
}
});
// Whether to generate miximages when scraping.
auto miximageGenerate = std::make_shared<SwitchComponent>();
miximageGenerate->setState(Settings::getInstance()->getBool("MiximageGenerate"));
s->addWithLabel("GENERATE MIXIMAGES WHEN SCRAPING", miximageGenerate);
s->addSaveFunc([miximageGenerate, s] {
if (miximageGenerate->getState() != Settings::getInstance()->getBool("MiximageGenerate")) {
Settings::getInstance()->setBool("MiximageGenerate", miximageGenerate->getState());
s->setNeedsSaving();
}
});
// Whether to overwrite miximages (both for the scraper and offline generator).
auto miximageOverwrite = std::make_shared<SwitchComponent>();
miximageOverwrite->setState(Settings::getInstance()->getBool("MiximageOverwrite"));
s->addWithLabel("OVERWRITE MIXIMAGES (SCRAPER/OFFLINE GENERATOR)", miximageOverwrite);
s->addSaveFunc([miximageOverwrite, s] {
if (miximageOverwrite->getState() !=
Settings::getInstance()->getBool("MiximageOverwrite")) {
Settings::getInstance()->setBool("MiximageOverwrite", miximageOverwrite->getState());
s->setNeedsSaving();
}
});
// Whether to remove letterboxes from the screenshots.
auto miximageRemoveLetterboxes = std::make_shared<SwitchComponent>();
miximageRemoveLetterboxes->setState(
Settings::getInstance()->getBool("MiximageRemoveLetterboxes"));
s->addWithLabel("REMOVE LETTERBOXES FROM SCREENSHOTS", miximageRemoveLetterboxes);
s->addSaveFunc([miximageRemoveLetterboxes, s] {
if (miximageRemoveLetterboxes->getState() !=
Settings::getInstance()->getBool("MiximageRemoveLetterboxes")) {
Settings::getInstance()->setBool("MiximageRemoveLetterboxes",
miximageRemoveLetterboxes->getState());
s->setNeedsSaving();
}
});
// Whether to remove pillarboxes from the screenshots.
auto miximageRemovePillarboxes = std::make_shared<SwitchComponent>();
miximageRemovePillarboxes->setState(
Settings::getInstance()->getBool("MiximageRemovePillarboxes"));
s->addWithLabel("REMOVE PILLARBOXES FROM SCREENSHOTS", miximageRemovePillarboxes);
s->addSaveFunc([miximageRemovePillarboxes, s] {
if (miximageRemovePillarboxes->getState() !=
Settings::getInstance()->getBool("MiximageRemovePillarboxes")) {
Settings::getInstance()->setBool("MiximageRemovePillarboxes",
miximageRemovePillarboxes->getState());
s->setNeedsSaving();
}
});
// Whether to rotate horizontally oriented boxes.
auto miximageRotateBoxes = std::make_shared<SwitchComponent>();
miximageRotateBoxes->setState(
Settings::getInstance()->getBool("MiximageRotateHorizontalBoxes"));
s->addWithLabel("ROTATE HORIZONTALLY ORIENTED BOXES", miximageRotateBoxes);
s->addSaveFunc([miximageRotateBoxes, s] {
if (miximageRotateBoxes->getState() !=
Settings::getInstance()->getBool("MiximageRotateHorizontalBoxes")) {
Settings::getInstance()->setBool("MiximageRotateHorizontalBoxes",
miximageRotateBoxes->getState());
s->setNeedsSaving();
}
});
// Whether to include marquee images.
auto miximageIncludeMarquee = std::make_shared<SwitchComponent>();
miximageIncludeMarquee->setState(Settings::getInstance()->getBool("MiximageIncludeMarquee"));
s->addWithLabel("INCLUDE MARQUEE IMAGE", miximageIncludeMarquee);
s->addSaveFunc([miximageIncludeMarquee, s] {
if (miximageIncludeMarquee->getState() !=
Settings::getInstance()->getBool("MiximageIncludeMarquee")) {
Settings::getInstance()->setBool("MiximageIncludeMarquee",
miximageIncludeMarquee->getState());
s->setNeedsSaving();
}
});
// Whether to include box images.
auto miximageIncludeBox = std::make_shared<SwitchComponent>();
miximageIncludeBox->setState(Settings::getInstance()->getBool("MiximageIncludeBox"));
s->addWithLabel("INCLUDE BOX IMAGE", miximageIncludeBox);
s->addSaveFunc([miximageIncludeBox, s] {
if (miximageIncludeBox->getState() !=
Settings::getInstance()->getBool("MiximageIncludeBox")) {
Settings::getInstance()->setBool("MiximageIncludeBox", miximageIncludeBox->getState());
s->setNeedsSaving();
}
});
// Whether to use cover image if there is no 3D box image.
auto miximageCoverFallback = std::make_shared<SwitchComponent>();
miximageCoverFallback->setState(Settings::getInstance()->getBool("MiximageCoverFallback"));
s->addWithLabel("USE COVER IMAGE IF 3D BOX IS MISSING", miximageCoverFallback);
s->addSaveFunc([miximageCoverFallback, s] {
if (miximageCoverFallback->getState() !=
Settings::getInstance()->getBool("MiximageCoverFallback")) {
Settings::getInstance()->setBool("MiximageCoverFallback",
miximageCoverFallback->getState());
s->setNeedsSaving();
}
});
// Whether to include physical media images.
auto miximageIncludePhysicalMedia = std::make_shared<SwitchComponent>();
miximageIncludePhysicalMedia->setState(
Settings::getInstance()->getBool("MiximageIncludePhysicalMedia"));
s->addWithLabel("INCLUDE PHYSICAL MEDIA IMAGE", miximageIncludePhysicalMedia);
s->addSaveFunc([miximageIncludePhysicalMedia, s] {
if (miximageIncludePhysicalMedia->getState() !=
Settings::getInstance()->getBool("MiximageIncludePhysicalMedia")) {
Settings::getInstance()->setBool("MiximageIncludePhysicalMedia",
miximageIncludePhysicalMedia->getState());
s->setNeedsSaving();
}
});
// Miximage offline generator.
ComponentListRow offlineGeneratorRow;
offlineGeneratorRow.elements.clear();
offlineGeneratorRow.addElement(std::make_shared<TextComponent>("OFFLINE GENERATOR",
Font::get(FONT_SIZE_MEDIUM),
mMenuColorPrimary),
true);
offlineGeneratorRow.addElement(mMenu.makeArrow(), false);
offlineGeneratorRow.makeAcceptInputHandler(
std::bind(&GuiScraperMenu::openOfflineGenerator, this, s));
s->addRow(offlineGeneratorRow);
mWindow->pushGui(s);
}
void GuiScraperMenu::openOfflineGenerator(GuiSettings* settings)
{
if (mSystems->getSelectedObjects().empty()) {
mWindow->pushGui(new GuiMsgBox(getHelpStyle(),
"THE OFFLINE GENERATOR USES THE SAME SYSTEM\n"
"SELECTIONS AS THE SCRAPER, SO PLEASE SELECT\n"
"AT LEAST ONE SYSTEM TO GENERATE IMAGES FOR"));
return;
}
// Always save the settings before starting the generator, in case any of the
// miximage settings were modified.
settings->save();
// Also unset the save flag so that a double saving does not take place when closing
// the miximage options menu later on.
settings->setNeedsSaving(false);
// Build the queue of games to process.
std::queue<FileData*> gameQueue;
std::vector<SystemData*> systems {mSystems->getSelectedObjects()};
for (auto sys = systems.cbegin(); sys != systems.cend(); ++sys) {
std::vector<FileData*> games {(*sys)->getRootFolder()->getChildrenRecursive()};
// Sort the games by "name, ascending".
std::stable_sort(games.begin(), games.end(), FileSorts::SortTypes.at(0).comparisonFunction);
for (FileData* game : games)
gameQueue.push(game);
}
mWindow->pushGui(new GuiOfflineGenerator(gameQueue));
}
void GuiScraperMenu::openOtherOptions()
{
auto s = new GuiSettings("OTHER SETTINGS");
// Scraper region.
auto scraperRegion =
std::make_shared<OptionListComponent<std::string>>(getHelpStyle(), "REGION", false);
std::string selectedScraperRegion {Settings::getInstance()->getString("ScraperRegion")};
// clang-format off
scraperRegion->add("Europe", "eu", selectedScraperRegion == "eu");
scraperRegion->add("Japan", "jp", selectedScraperRegion == "jp");
scraperRegion->add("USA", "us", selectedScraperRegion == "us");
scraperRegion->add("World", "wor", selectedScraperRegion == "wor");
// clang-format on
// If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the region to "Europe" in this case.
if (scraperRegion->getSelectedObjects().size() == 0)
scraperRegion->selectEntry(0);
s->addWithLabel("REGION", scraperRegion);
s->addSaveFunc([scraperRegion, s] {
if (scraperRegion->getSelected() != Settings::getInstance()->getString("ScraperRegion")) {
Settings::getInstance()->setString("ScraperRegion", scraperRegion->getSelected());
s->setNeedsSaving();
}
});
// Regions are not supported by TheGamesDB, so gray out the option if this scraper is selected.
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
scraperRegion->setEnabled(false);
scraperRegion->setOpacity(DISABLED_OPACITY);
scraperRegion->getParent()
->getChild(scraperRegion->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
// Scraper language.
auto scraperLanguage = std::make_shared<OptionListComponent<std::string>>(
getHelpStyle(), "PREFERRED LANGUAGE", false);
std::string selectedScraperLanguage {Settings::getInstance()->getString("ScraperLanguage")};
// clang-format off
scraperLanguage->add("English", "en", selectedScraperLanguage == "en");
scraperLanguage->add("Español", "es", selectedScraperLanguage == "es");
scraperLanguage->add("Português", "pt", selectedScraperLanguage == "pt");
scraperLanguage->add("Français", "fr", selectedScraperLanguage == "fr");
scraperLanguage->add("Deutsch", "de", selectedScraperLanguage == "de");
scraperLanguage->add("Italiano", "it", selectedScraperLanguage == "it");
scraperLanguage->add("Nederlands", "nl", selectedScraperLanguage == "nl");
scraperLanguage->add("日本語", "ja", selectedScraperLanguage == "ja");
scraperLanguage->add("简体中文", "zh", selectedScraperLanguage == "zh");
scraperLanguage->add("한국어", "ko", selectedScraperLanguage == "ko");
scraperLanguage->add("Русский", "ru", selectedScraperLanguage == "ru");
scraperLanguage->add("Dansk", "da", selectedScraperLanguage == "da");
scraperLanguage->add("Suomi", "fi", selectedScraperLanguage == "fi");
scraperLanguage->add("Svenska", "sv", selectedScraperLanguage == "sv");
scraperLanguage->add("Magyar", "hu", selectedScraperLanguage == "hu");
scraperLanguage->add("Norsk", "no", selectedScraperLanguage == "no");
scraperLanguage->add("Polski", "pl", selectedScraperLanguage == "pl");
scraperLanguage->add("Čeština", "cz", selectedScraperLanguage == "cz");
scraperLanguage->add("Slovenčina", "sk", selectedScraperLanguage == "sk");
scraperLanguage->add("Türkçe", "tr", selectedScraperLanguage == "tr");
// clang-format on
// If there are no objects returned, then there must be a manually modified entry in the
// configuration file. Simply set the language to "English" in this case.
if (scraperLanguage->getSelectedObjects().size() == 0)
scraperLanguage->selectEntry(0);
s->addWithLabel("PREFERRED LANGUAGE", scraperLanguage);
s->addSaveFunc([scraperLanguage, s] {
if (scraperLanguage->getSelected() !=
Settings::getInstance()->getString("ScraperLanguage")) {
Settings::getInstance()->setString("ScraperLanguage", scraperLanguage->getSelected());
s->setNeedsSaving();
}
});
// Languages are not supported by TheGamesDB, so gray out the option if this scraper is
// selected.
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
scraperLanguage->setEnabled(false);
scraperLanguage->setOpacity(DISABLED_OPACITY);
scraperLanguage->getParent()
->getChild(scraperLanguage->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
// Automatic retries on error.
mScraperRetryOnErrorCount = std::make_shared<SliderComponent>(0.0f, 10.0f, 1.0f);
mScraperRetryOnErrorCount->setValue(
static_cast<float>(Settings::getInstance()->getInt("ScraperRetryOnErrorCount")));
s->addWithLabel("AUTOMATIC RETRIES ON ERROR", mScraperRetryOnErrorCount);
s->addSaveFunc([this, s] {
if (mScraperRetryOnErrorCount->getValue() !=
static_cast<float>(Settings::getInstance()->getInt("ScraperRetryOnErrorCount"))) {
Settings::getInstance()->setInt(
"ScraperRetryOnErrorCount",
static_cast<int>(mScraperRetryOnErrorCount->getValue()));
s->setNeedsSaving();
}
});
// Retry attempt timer.
auto scraperRetryOnErrorTimer = std::make_shared<SliderComponent>(1.0f, 30.0f, 1.0f, "s");
scraperRetryOnErrorTimer->setValue(
static_cast<float>(Settings::getInstance()->getInt("ScraperRetryOnErrorTimer")));
s->addWithLabel("RETRY ATTEMPT TIMER", scraperRetryOnErrorTimer);
s->addSaveFunc([scraperRetryOnErrorTimer, s] {
if (scraperRetryOnErrorTimer->getValue() !=
static_cast<float>(Settings::getInstance()->getInt("ScraperRetryOnErrorTimer"))) {
Settings::getInstance()->setInt("ScraperRetryOnErrorTimer",
static_cast<int>(scraperRetryOnErrorTimer->getValue()));
s->setNeedsSaving();
}
});
if (mScraperRetryOnErrorCount->getValue() == 0.0f) {
scraperRetryOnErrorTimer->setEnabled(false);
scraperRetryOnErrorTimer->setOpacity(DISABLED_OPACITY);
scraperRetryOnErrorTimer->getParent()
->getChild(scraperRetryOnErrorTimer->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
// Maximum file size for non-interactive mode file hash searching.
auto scraperSearchFileHashMaxSize =
std::make_shared<SliderComponent>(32.0f, 800.0f, 32.0f, "MiB");
scraperSearchFileHashMaxSize->setValue(
static_cast<float>(Settings::getInstance()->getInt("ScraperSearchFileHashMaxSize")));
s->addWithLabel("HASH SEARCHES MAX FILE SIZE", scraperSearchFileHashMaxSize);
s->addSaveFunc([scraperSearchFileHashMaxSize, s] {
if (scraperSearchFileHashMaxSize->getValue() !=
static_cast<float>(Settings::getInstance()->getInt("ScraperSearchFileHashMaxSize"))) {
Settings::getInstance()->setInt(
"ScraperSearchFileHashMaxSize",
static_cast<int>(scraperSearchFileHashMaxSize->getValue()));
s->setNeedsSaving();
}
});
// File hash searching is not supported by TheGamesDB, so gray out the option if this scraper
// is selected. Also gray it out for ScreenScraper if file hash searching has been disabled.
if (Settings::getInstance()->getString("Scraper") == "thegamesdb" ||
!Settings::getInstance()->getBool("ScraperSearchFileHash")) {
scraperSearchFileHashMaxSize->setEnabled(false);
scraperSearchFileHashMaxSize->setOpacity(DISABLED_OPACITY);
scraperSearchFileHashMaxSize->getParent()
->getChild(scraperSearchFileHashMaxSize->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
// Overwrite files and data.
auto scraperOverwriteData = std::make_shared<SwitchComponent>();
scraperOverwriteData->setState(Settings::getInstance()->getBool("ScraperOverwriteData"));
s->addWithLabel("OVERWRITE FILES AND DATA", scraperOverwriteData);
s->addSaveFunc([scraperOverwriteData, s] {
if (scraperOverwriteData->getState() !=
Settings::getInstance()->getBool("ScraperOverwriteData")) {
Settings::getInstance()->setBool("ScraperOverwriteData",
scraperOverwriteData->getState());
s->setNeedsSaving();
}
});
// Halt scraping on invalid media files.
auto scraperHaltOnInvalidMedia = std::make_shared<SwitchComponent>();
scraperHaltOnInvalidMedia->setState(
Settings::getInstance()->getBool("ScraperHaltOnInvalidMedia"));
s->addWithLabel("HALT ON INVALID MEDIA FILES", scraperHaltOnInvalidMedia);
s->addSaveFunc([scraperHaltOnInvalidMedia, s] {
if (scraperHaltOnInvalidMedia->getState() !=
Settings::getInstance()->getBool("ScraperHaltOnInvalidMedia")) {
Settings::getInstance()->setBool("ScraperHaltOnInvalidMedia",
scraperHaltOnInvalidMedia->getState());
s->setNeedsSaving();
}
});
// Search using file hashes for non-interactive mode.
auto scraperSearchFileHash = std::make_shared<SwitchComponent>();
scraperSearchFileHash->setState(Settings::getInstance()->getBool("ScraperSearchFileHash"));
s->addWithLabel("SEARCH USING FILE HASHES (NON-INTERACTIVE MODE)", scraperSearchFileHash);
s->addSaveFunc([scraperSearchFileHash, s] {
if (scraperSearchFileHash->getState() !=
Settings::getInstance()->getBool("ScraperSearchFileHash")) {
Settings::getInstance()->setBool("ScraperSearchFileHash",
scraperSearchFileHash->getState());
s->setNeedsSaving();
}
});
// File hash searching is not supported by TheGamesDB, so gray out the option if this scraper
// is selected.
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
scraperSearchFileHash->setEnabled(false);
scraperSearchFileHash->setOpacity(DISABLED_OPACITY);
scraperSearchFileHash->getParent()
->getChild(scraperSearchFileHash->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
// Search using metadata names.
auto scraperSearchMetadataName = std::make_shared<SwitchComponent>();
scraperSearchMetadataName->setState(
Settings::getInstance()->getBool("ScraperSearchMetadataName"));
s->addWithLabel("SEARCH USING METADATA NAMES", scraperSearchMetadataName);
s->addSaveFunc([scraperSearchMetadataName, s] {
if (scraperSearchMetadataName->getState() !=
Settings::getInstance()->getBool("ScraperSearchMetadataName")) {
Settings::getInstance()->setBool("ScraperSearchMetadataName",
scraperSearchMetadataName->getState());
s->setNeedsSaving();
}
});
// Include actual folders when scraping.
auto scraperIncludeFolders = std::make_shared<SwitchComponent>();
scraperIncludeFolders->setState(Settings::getInstance()->getBool("ScraperIncludeFolders"));
s->addWithLabel("SCRAPE ACTUAL FOLDERS", scraperIncludeFolders);
s->addSaveFunc([scraperIncludeFolders, s] {
if (scraperIncludeFolders->getState() !=
Settings::getInstance()->getBool("ScraperIncludeFolders")) {
Settings::getInstance()->setBool("ScraperIncludeFolders",
scraperIncludeFolders->getState());
s->setNeedsSaving();
}
});
// Interactive scraping.
auto scraperInteractive = std::make_shared<SwitchComponent>();
scraperInteractive->setState(Settings::getInstance()->getBool("ScraperInteractive"));
s->addWithLabel("INTERACTIVE MODE", scraperInteractive);
s->addSaveFunc([scraperInteractive, s] {
if (scraperInteractive->getState() !=
Settings::getInstance()->getBool("ScraperInteractive")) {
Settings::getInstance()->setBool("ScraperInteractive", scraperInteractive->getState());
s->setNeedsSaving();
}
});
// Semi-automatic scraping.
auto scraperSemiautomatic = std::make_shared<SwitchComponent>();
scraperSemiautomatic->setState(Settings::getInstance()->getBool("ScraperSemiautomatic"));
s->addWithLabel("AUTO-ACCEPT SINGLE GAME MATCHES", scraperSemiautomatic);
s->addSaveFunc([scraperSemiautomatic, s] {
if (scraperSemiautomatic->getState() !=
Settings::getInstance()->getBool("ScraperSemiautomatic")) {
Settings::getInstance()->setBool("ScraperSemiautomatic",
scraperSemiautomatic->getState());
s->setNeedsSaving();
}
});
// If interactive mode is set to off, then gray out this option.
if (!Settings::getInstance()->getBool("ScraperInteractive")) {
scraperSemiautomatic->setEnabled(false);
scraperSemiautomatic->setOpacity(DISABLED_OPACITY);
scraperSemiautomatic->getParent()
->getChild(scraperSemiautomatic->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
// Respect the per-file multi-scraper exclusion flag.
auto scraperRespectExclusions = std::make_shared<SwitchComponent>();
scraperRespectExclusions->setState(
Settings::getInstance()->getBool("ScraperRespectExclusions"));
s->addWithLabel("RESPECT PER-FILE SCRAPER EXCLUSIONS", scraperRespectExclusions);
s->addSaveFunc([scraperRespectExclusions, s] {
if (scraperRespectExclusions->getState() !=
Settings::getInstance()->getBool("ScraperRespectExclusions")) {
Settings::getInstance()->setBool("ScraperRespectExclusions",
scraperRespectExclusions->getState());
s->setNeedsSaving();
}
});
// Exclude files recursively for excluded folders.
auto scraperExcludeRecursively = std::make_shared<SwitchComponent>();
scraperExcludeRecursively->setState(
Settings::getInstance()->getBool("ScraperExcludeRecursively"));
s->addWithLabel("EXCLUDE FOLDERS RECURSIVELY", scraperExcludeRecursively);
s->addSaveFunc([scraperExcludeRecursively, s] {
if (scraperExcludeRecursively->getState() !=
Settings::getInstance()->getBool("ScraperExcludeRecursively")) {
Settings::getInstance()->setBool("ScraperExcludeRecursively",
scraperExcludeRecursively->getState());
s->setNeedsSaving();
}
});
// If respecting excluded files is set to off, then gray out this option.
if (!Settings::getInstance()->getBool("ScraperRespectExclusions")) {
scraperExcludeRecursively->setEnabled(false);
scraperExcludeRecursively->setOpacity(DISABLED_OPACITY);
scraperExcludeRecursively->getParent()
->getChild(scraperExcludeRecursively->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
// Convert underscores to spaces when searching.
auto scraperConvertUnderscores = std::make_shared<SwitchComponent>();
scraperConvertUnderscores->setState(
Settings::getInstance()->getBool("ScraperConvertUnderscores"));
s->addWithLabel("CONVERT UNDERSCORES TO SPACES WHEN SEARCHING", scraperConvertUnderscores);
s->addSaveFunc([scraperConvertUnderscores, s] {
if (scraperConvertUnderscores->getState() !=
Settings::getInstance()->getBool("ScraperConvertUnderscores")) {
Settings::getInstance()->setBool("ScraperConvertUnderscores",
scraperConvertUnderscores->getState());
s->setNeedsSaving();
}
});
// Whether to remove dots from game names when searching using the automatic scraper.
auto scraperAutomaticRemoveDots = std::make_shared<SwitchComponent>();
scraperAutomaticRemoveDots->setState(
Settings::getInstance()->getBool("ScraperAutomaticRemoveDots"));
s->addWithLabel("REMOVE DOTS FROM SEARCHES WHEN AUTO-SCRAPING", scraperAutomaticRemoveDots);
s->addSaveFunc([scraperAutomaticRemoveDots, s] {
if (scraperAutomaticRemoveDots->getState() !=
Settings::getInstance()->getBool("ScraperAutomaticRemoveDots")) {
Settings::getInstance()->setBool("ScraperAutomaticRemoveDots",
scraperAutomaticRemoveDots->getState());
s->setNeedsSaving();
}
});
// This is not needed for TheGamesDB, so gray out the option if this scraper is selected.
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
scraperAutomaticRemoveDots->setEnabled(false);
scraperAutomaticRemoveDots->setOpacity(DISABLED_OPACITY);
scraperAutomaticRemoveDots->getParent()
->getChild(scraperAutomaticRemoveDots->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
// Whether to fallback to additional regions.
auto scraperRegionFallback = std::make_shared<SwitchComponent>(mWindow);
scraperRegionFallback->setState(Settings::getInstance()->getBool("ScraperRegionFallback"));
s->addWithLabel("ENABLE FALLBACK TO ADDITIONAL REGIONS", scraperRegionFallback);
s->addSaveFunc([scraperRegionFallback, s] {
if (scraperRegionFallback->getState() !=
Settings::getInstance()->getBool("ScraperRegionFallback")) {
Settings::getInstance()->setBool("ScraperRegionFallback",
scraperRegionFallback->getState());
s->setNeedsSaving();
}
});
// Regions are not supported by TheGamesDB, so gray out the option if this scraper is selected.
if (Settings::getInstance()->getString("Scraper") == "thegamesdb") {
scraperRegionFallback->setEnabled(false);
scraperRegionFallback->setOpacity(DISABLED_OPACITY);
scraperRegionFallback->getParent()
->getChild(scraperRegionFallback->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
// Slider callback.
auto scraperRetryCountFunc = [this, scraperRetryOnErrorTimer]() {
if (mScraperRetryOnErrorCount->getValue() == 0.0f) {
scraperRetryOnErrorTimer->setEnabled(false);
scraperRetryOnErrorTimer->setOpacity(DISABLED_OPACITY);
scraperRetryOnErrorTimer->getParent()
->getChild(scraperRetryOnErrorTimer->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
else {
scraperRetryOnErrorTimer->setEnabled(true);
scraperRetryOnErrorTimer->setOpacity(1.0f);
scraperRetryOnErrorTimer->getParent()
->getChild(scraperRetryOnErrorTimer->getChildIndex() - 1)
->setOpacity(1.0f);
}
};
// Switch callbacks.
auto hashSearchToggleFunc = [scraperSearchFileHashMaxSize]() {
if (scraperSearchFileHashMaxSize->getEnabled()) {
scraperSearchFileHashMaxSize->setEnabled(false);
scraperSearchFileHashMaxSize->setOpacity(DISABLED_OPACITY);
scraperSearchFileHashMaxSize->getParent()
->getChild(scraperSearchFileHashMaxSize->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
else {
scraperSearchFileHashMaxSize->setEnabled(true);
scraperSearchFileHashMaxSize->setOpacity(1.0f);
scraperSearchFileHashMaxSize->getParent()
->getChild(scraperSearchFileHashMaxSize->getChildIndex() - 1)
->setOpacity(1.0f);
}
};
auto interactiveToggleFunc = [scraperSemiautomatic]() {
if (scraperSemiautomatic->getEnabled()) {
scraperSemiautomatic->setEnabled(false);
scraperSemiautomatic->setOpacity(DISABLED_OPACITY);
scraperSemiautomatic->getParent()
->getChild(scraperSemiautomatic->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
else {
scraperSemiautomatic->setEnabled(true);
scraperSemiautomatic->setOpacity(1.0f);
scraperSemiautomatic->getParent()
->getChild(scraperSemiautomatic->getChildIndex() - 1)
->setOpacity(1.0f);
}
};
auto excludeRecursivelyToggleFunc = [scraperExcludeRecursively]() {
if (scraperExcludeRecursively->getEnabled()) {
scraperExcludeRecursively->setEnabled(false);
scraperExcludeRecursively->setOpacity(DISABLED_OPACITY);
scraperExcludeRecursively->getParent()
->getChild(scraperExcludeRecursively->getChildIndex() - 1)
->setOpacity(DISABLED_OPACITY);
}
else {
scraperExcludeRecursively->setEnabled(true);
scraperExcludeRecursively->setOpacity(1.0f);
scraperExcludeRecursively->getParent()
->getChild(scraperExcludeRecursively->getChildIndex() - 1)
->setOpacity(1.0f);
}
};
mScraperRetryOnErrorCount->setCallback(scraperRetryCountFunc);
scraperSearchFileHash->setCallback(hashSearchToggleFunc);
scraperInteractive->setCallback(interactiveToggleFunc);
scraperRespectExclusions->setCallback(excludeRecursivelyToggleFunc);
mWindow->pushGui(s);
}
void GuiScraperMenu::pressedStart()
{
// If the scraper service has been changed, then save the settings as otherwise the
// wrong scraper would be used.
if (mScraper->getSelected() != Settings::getInstance()->getString("Scraper"))
mMenu.save();
std::vector<SystemData*> sys {mSystems->getSelectedObjects()};
for (auto it = sys.cbegin(); it != sys.cend(); ++it) {
if ((*it)->getPlatformIds().empty()) {
std::string warningString;
if (sys.size() == 1) {
warningString = "THE SELECTED SYSTEM DOES NOT HAVE A PLATFORM SET, RESULTS MAY BE "
"INACCURATE";
}
else {
warningString = "AT LEAST ONE OF YOUR SELECTED SYSTEMS DOES NOT HAVE A PLATFORM "
"SET, RESULTS MAY BE INACCURATE";
}
mWindow->pushGui(
new GuiMsgBox(getHelpStyle(), Utils::String::toUpper(warningString), "PROCEED",
std::bind(&GuiScraperMenu::start, this), "CANCEL", nullptr, "",
nullptr, nullptr, false, true,
(mRenderer->getIsVerticalOrientation() ?
0.80f :
0.50f * (1.778f / mRenderer->getScreenAspectRatio()))));
return;
}
}
start();
}
void GuiScraperMenu::start()
{
if (mSystems->getSelectedObjects().empty()) {
mWindow->pushGui(
new GuiMsgBox(getHelpStyle(), "PLEASE SELECT AT LEAST ONE SYSTEM TO SCRAPE"));
return;
}
bool contentToScrape {false};
std::string scraperService {Settings::getInstance()->getString("Scraper")};
// Check if there is actually any type of content selected for scraping.
do {
if (Settings::getInstance()->getBool("ScrapeGameNames")) {
contentToScrape = true;
break;
}
if (scraperService == "screenscraper" &&
Settings::getInstance()->getBool("ScrapeRatings")) {
contentToScrape = true;
break;
}
// ScreenScraper controller scraping is currently broken, it's unclear if they will fix it.
// if (scraperService == "screenscraper" &&
// Settings::getInstance()->getBool("ScrapeControllers")) {
// contentToScrape = true;
// break;
// }
if (Settings::getInstance()->getBool("ScrapeMetadata")) {
contentToScrape = true;
break;
}
if (scraperService == "screenscraper" && Settings::getInstance()->getBool("ScrapeVideos")) {
contentToScrape = true;
break;
}
if (Settings::getInstance()->getBool("ScrapeScreenshots")) {
contentToScrape = true;
break;
}
if (Settings::getInstance()->getBool("ScrapeTitleScreens")) {
contentToScrape = true;
break;
}
if (Settings::getInstance()->getBool("ScrapeCovers")) {
contentToScrape = true;
break;
}
if (Settings::getInstance()->getBool("ScrapeBackCovers")) {
contentToScrape = true;
break;
}
if (Settings::getInstance()->getBool("ScrapeFanArt")) {
contentToScrape = true;
break;
}
if (scraperService == "screenscraper" &&
Settings::getInstance()->getBool("ScrapeManuals")) {
contentToScrape = true;
break;
}
if (Settings::getInstance()->getBool("ScrapeMarquees")) {
contentToScrape = true;
break;
}
if (scraperService == "screenscraper" &&
Settings::getInstance()->getBool("Scrape3DBoxes")) {
contentToScrape = true;
break;
}
if (scraperService == "screenscraper" &&
Settings::getInstance()->getBool("ScrapePhysicalMedia")) {
contentToScrape = true;
break;
}
} while (0);
if (!contentToScrape) {
mWindow->pushGui(
new GuiMsgBox(getHelpStyle(), "PLEASE SELECT AT LEAST ONE CONTENT TYPE TO SCRAPE"));
return;
}
auto searches = getSearches(mSystems->getSelectedObjects(), mFilters->getSelected());
if (searches.first.empty()) {
mWindow->pushGui(
new GuiMsgBox(getHelpStyle(), "ALL GAMES WERE FILTERED, NOTHING TO SCRAPE"));
}
else {
GuiScraperMulti* gsm {
new GuiScraperMulti(searches, Settings::getInstance()->getBool("ScraperInteractive"))};
mWindow->pushGui(gsm);
mMenu.setCursorToList();
mMenu.setCursorToFirstListEntry();
}
}
std::pair<std::queue<ScraperSearchParams>, std::map<SystemData*, int>> GuiScraperMenu::getSearches(
std::vector<SystemData*> systems, GameFilterFunc selector)
{
std::pair<std::queue<ScraperSearchParams>, std::map<SystemData*, int>> queue;
for (auto sys = systems.cbegin(); sys != systems.cend(); ++sys) {
std::vector<FileData*> games {(*sys)->getRootFolder()->getScrapeFilesRecursive(
Settings::getInstance()->getBool("ScraperIncludeFolders"),
Settings::getInstance()->getBool("ScraperExcludeRecursively"),
Settings::getInstance()->getBool("ScraperRespectExclusions"))};
for (auto game = games.cbegin(); game != games.cend(); ++game) {
if (selector((*sys), (*game))) {
ScraperSearchParams search;
search.game = *game;
search.system = *sys;
++queue.second[*sys];
queue.first.push(search);
}
}
}
return queue;
}
void GuiScraperMenu::addEntry(const std::string& name,
unsigned int color,
bool add_arrow,
const std::function<void()>& func)
{
std::shared_ptr<Font> font {Font::get(FONT_SIZE_MEDIUM)};
// Populate the list.
ComponentListRow row;
row.addElement(std::make_shared<TextComponent>(name, font, color), true);
if (add_arrow) {
std::shared_ptr<ImageComponent> bracket {mMenu.makeArrow()};
row.addElement(bracket, false);
}
row.makeAcceptInputHandler(func);
mMenu.addRow(row);
}
bool GuiScraperMenu::input(InputConfig* config, Input input)
{
if (GuiComponent::input(config, input))
return true;
if (config->isMappedTo("y", input) && input.value != 0)
pressedStart();
if (config->isMappedTo("b", input) && input.value != 0) {
delete this;
return true;
}
return false;
}
std::vector<HelpPrompt> GuiScraperMenu::getHelpPrompts()
{
std::vector<HelpPrompt> prompts {mMenu.getHelpPrompts()};
prompts.push_back(HelpPrompt("b", "back"));
prompts.push_back(HelpPrompt("y", "start scraper"));
return prompts;
}