From d5fa6bc82c05eb8db769d2fd4d9d1b554246a253 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 28 Oct 2021 21:00:23 +0200 Subject: [PATCH] Added support for scraping title screens, box back covers and physical media images. --- es-app/src/FileData.cpp | 45 +++++++--- es-app/src/FileData.h | 5 +- es-app/src/MediaViewer.cpp | 20 ++++- es-app/src/MediaViewer.h | 3 +- es-app/src/guis/GuiMediaViewerOptions.cpp | 4 +- es-app/src/guis/GuiScraperMenu.cpp | 90 ++++++++++++++++--- es-app/src/guis/GuiScraperSearch.cpp | 3 + es-app/src/scrapers/GamesDBJSONScraper.cpp | 4 + es-app/src/scrapers/Scraper.cpp | 74 +++++++++++++++ es-app/src/scrapers/Scraper.h | 6 ++ es-app/src/scrapers/ScreenScraper.cpp | 11 ++- es-app/src/scrapers/ScreenScraper.h | 3 + .../src/views/gamelist/BasicGameListView.cpp | 21 +++++ .../src/views/gamelist/GridGameListView.cpp | 21 +++++ es-core/src/Settings.cpp | 3 + 15 files changed, 283 insertions(+), 30 deletions(-) diff --git a/es-app/src/FileData.cpp b/es-app/src/FileData.cpp index cc205a940..8174cdde2 100644 --- a/es-app/src/FileData.cpp +++ b/es-app/src/FileData.cpp @@ -208,7 +208,7 @@ const std::string FileData::getMediaDirectory() return mediaDirPath; } -const std::string FileData::getMediafilePath(std::string subdirectory, std::string mediatype) const +const std::string FileData::getMediafilePath(std::string subdirectory) const { const std::vector extList = {".png", ".jpg"}; std::string subFolders; @@ -234,53 +234,76 @@ const std::string FileData::getMediafilePath(std::string subdirectory, std::stri const std::string FileData::getImagePath() const { // Look for a mix image (a combination of screenshot, 2D/3D box and marquee). - std::string image = getMediafilePath("miximages", "miximage"); + std::string image = getMediafilePath("miximages"); if (image != "") return image; // If no mix image was found, try screenshot instead. - image = getMediafilePath("screenshots", "screenshot"); + image = getMediafilePath("screenshots"); + if (image != "") + return image; + + // If no screenshot image was found, try title screen instead. + image = getMediafilePath("titlescreens"); if (image != "") return image; // If no screenshot was found either, try cover. - return getMediafilePath("covers", "cover"); + return getMediafilePath("covers"); } const std::string FileData::get3DBoxPath() const { // Return path to the 3D box image. - return getMediafilePath("3dboxes", "3dbox"); + return getMediafilePath("3dboxes"); +} + +const std::string FileData::getBackCoverPath() const +{ + // Return path to the box back cover image. + return getMediafilePath("backcovers"); } const std::string FileData::getCoverPath() const { - // Return path to the cover image. - return getMediafilePath("covers", "cover"); + // Return path to the box cover image. + return getMediafilePath("covers"); } const std::string FileData::getMarqueePath() const { // Return path to the marquee image. - return getMediafilePath("marquees", "marquee"); + return getMediafilePath("marquees"); +} + +const std::string FileData::getPhysicalMediaPath() const +{ + // Return path to the physical media image. + return getMediafilePath("physicalmedia"); } const std::string FileData::getMiximagePath() const { // Return path to the miximage. - return getMediafilePath("miximages", "miximage"); + return getMediafilePath("miximages"); } const std::string FileData::getScreenshotPath() const { // Return path to the screenshot image. - return getMediafilePath("screenshots", "screenshot"); + return getMediafilePath("screenshots"); +} + +const std::string FileData::getTitleScreenPath() const +{ + // Return path to the title screen image. + return getMediafilePath("titlescreens"); } const std::string FileData::getThumbnailPath() const { // Return path to the thumbnail image. - return getMediafilePath("thumbnails", "thumbnail"); + return getMediafilePath("thumbnails"); } const std::string FileData::getVideoPath() const diff --git a/es-app/src/FileData.h b/es-app/src/FileData.h index 9621fd006..2411a4dd6 100644 --- a/es-app/src/FileData.h +++ b/es-app/src/FileData.h @@ -60,13 +60,16 @@ public: const bool getHasFoldersFlag() { return mHasFolders; } static const std::string getROMDirectory(); static const std::string getMediaDirectory(); - const std::string getMediafilePath(std::string subdirectory, std::string mediatype) const; + const std::string getMediafilePath(std::string subdirectory) const; const std::string getImagePath() const; const std::string get3DBoxPath() const; + const std::string getBackCoverPath() const; const std::string getCoverPath() const; const std::string getMarqueePath() const; + const std::string getPhysicalMediaPath() const; const std::string getMiximagePath() const; const std::string getScreenshotPath() const; + const std::string getTitleScreenPath() const; const std::string getThumbnailPath() const; const std::string getVideoPath() const; diff --git a/es-app/src/MediaViewer.cpp b/es-app/src/MediaViewer.cpp index 06f59e515..c24e9cf1c 100644 --- a/es-app/src/MediaViewer.cpp +++ b/es-app/src/MediaViewer.cpp @@ -41,7 +41,8 @@ bool MediaViewer::startMediaViewer(FileData* game) mHasVideo = false; mHasImages = false; mCurrentImageIndex = 0; - mScreenShotIndex = -1; + mScreenshotIndex = -1; + mTitleScreenIndex = -1; mGame = game; @@ -125,9 +126,12 @@ void MediaViewer::render(const glm::mat4& /*parentTrans*/) mImage->render(trans); #if defined(USE_OPENGL_21) - if (mCurrentImageIndex == mScreenShotIndex && + if (mCurrentImageIndex == mScreenshotIndex && Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES); + else if (mCurrentImageIndex == mTitleScreenIndex && + Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) + Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES); #endif // This is necessary so that the video loops if viewing an image when @@ -164,15 +168,23 @@ void MediaViewer::findMedia() if (!mHasVideo && (mediaFile = mGame->getScreenshotPath()) != "") { mImageFiles.push_back(mediaFile); - mScreenShotIndex = 0; + mScreenshotIndex = 0; } if ((mediaFile = mGame->getCoverPath()) != "") mImageFiles.push_back(mediaFile); + if ((mediaFile = mGame->getBackCoverPath()) != "") + mImageFiles.push_back(mediaFile); + + if ((mediaFile = mGame->getTitleScreenPath()) != "") { + mImageFiles.push_back(mediaFile); + mTitleScreenIndex = static_cast(mImageFiles.size() - 1); + } + if (mHasVideo && (mediaFile = mGame->getScreenshotPath()) != "") { mImageFiles.push_back(mediaFile); - mScreenShotIndex = static_cast(mImageFiles.size() - 1); + mScreenshotIndex = static_cast(mImageFiles.size() - 1); } if ((mediaFile = mGame->getMiximagePath()) != "") diff --git a/es-app/src/MediaViewer.h b/es-app/src/MediaViewer.h index d60481a27..e84c696e0 100644 --- a/es-app/src/MediaViewer.h +++ b/es-app/src/MediaViewer.h @@ -44,7 +44,8 @@ private: bool mDisplayingImage; int mCurrentImageIndex; - int mScreenShotIndex; + int mScreenshotIndex; + int mTitleScreenIndex; std::string mVideoFile; std::vector mImageFiles; diff --git a/es-app/src/guis/GuiMediaViewerOptions.cpp b/es-app/src/guis/GuiMediaViewerOptions.cpp index 38468beca..8732b7359 100644 --- a/es-app/src/guis/GuiMediaViewerOptions.cpp +++ b/es-app/src/guis/GuiMediaViewerOptions.cpp @@ -66,11 +66,11 @@ GuiMediaViewerOptions::GuiMediaViewerOptions(Window* window, const std::string& } }); - // Render scanlines for screenshots using a shader. + // Render scanlines for screenshots and title screens using a shader. auto screenshot_scanlines = std::make_shared(mWindow); screenshot_scanlines->setState( Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")); - addWithLabel("RENDER SCANLINES FOR SCREENSHOTS", screenshot_scanlines); + addWithLabel("RENDER SCANLINES FOR SCREENSHOTS AND TITLES", screenshot_scanlines); addSaveFunc([screenshot_scanlines, this] { if (screenshot_scanlines->getState() != Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) { diff --git a/es-app/src/guis/GuiScraperMenu.cpp b/es-app/src/guis/GuiScraperMenu.cpp index 8a9692332..a792bc7ac 100644 --- a/es-app/src/guis/GuiScraperMenu.cpp +++ b/es-app/src/guis/GuiScraperMenu.cpp @@ -221,7 +221,7 @@ void GuiScraperMenu::openContentOptions() // Scrape game names. auto scrape_game_names = std::make_shared(mWindow); scrape_game_names->setState(Settings::getInstance()->getBool("ScrapeGameNames")); - s->addWithLabel("SCRAPE GAME NAMES", scrape_game_names); + s->addWithLabel("GAME NAMES", scrape_game_names); s->addSaveFunc([scrape_game_names, s] { if (scrape_game_names->getState() != Settings::getInstance()->getBool("ScrapeGameNames")) { Settings::getInstance()->setBool("ScrapeGameNames", scrape_game_names->getState()); @@ -232,7 +232,7 @@ void GuiScraperMenu::openContentOptions() // Scrape ratings. auto scrape_ratings = std::make_shared(mWindow); scrape_ratings->setState(Settings::getInstance()->getBool("ScrapeRatings")); - s->addWithLabel("SCRAPE RATINGS", scrape_ratings); + s->addWithLabel("RATINGS", scrape_ratings); s->addSaveFunc([scrape_ratings, s] { if (scrape_ratings->getState() != Settings::getInstance()->getBool("ScrapeRatings")) { Settings::getInstance()->setBool("ScrapeRatings", scrape_ratings->getState()); @@ -252,7 +252,7 @@ void GuiScraperMenu::openContentOptions() // Scrape controllers (arcade systems only). auto scrapeControllers = std::make_shared(mWindow); scrapeControllers->setState(Settings::getInstance()->getBool("ScrapeControllers")); - s->addWithLabel("SCRAPE CONTROLLERS (ARCADE SYSTEMS ONLY)", scrapeControllers); + s->addWithLabel("CONTROLLERS (ARCADE SYSTEMS ONLY)", scrapeControllers); s->addSaveFunc([scrapeControllers, s] { if (scrapeControllers->getState() != Settings::getInstance()->getBool("ScrapeControllers")) { @@ -274,7 +274,7 @@ void GuiScraperMenu::openContentOptions() // Scrape other metadata. auto scrape_metadata = std::make_shared(mWindow); scrape_metadata->setState(Settings::getInstance()->getBool("ScrapeMetadata")); - s->addWithLabel("SCRAPE OTHER METADATA", scrape_metadata); + s->addWithLabel("OTHER METADATA", scrape_metadata); s->addSaveFunc([scrape_metadata, s] { if (scrape_metadata->getState() != Settings::getInstance()->getBool("ScrapeMetadata")) { Settings::getInstance()->setBool("ScrapeMetadata", scrape_metadata->getState()); @@ -285,7 +285,7 @@ void GuiScraperMenu::openContentOptions() // Scrape videos. auto scrape_videos = std::make_shared(mWindow); scrape_videos->setState(Settings::getInstance()->getBool("ScrapeVideos")); - s->addWithLabel("SCRAPE VIDEOS", scrape_videos); + s->addWithLabel("VIDEOS", scrape_videos); s->addSaveFunc([scrape_videos, s] { if (scrape_videos->getState() != Settings::getInstance()->getBool("ScrapeVideos")) { Settings::getInstance()->setBool("ScrapeVideos", scrape_videos->getState()); @@ -305,7 +305,7 @@ void GuiScraperMenu::openContentOptions() // Scrape screenshots images. auto scrape_screenshots = std::make_shared(mWindow); scrape_screenshots->setState(Settings::getInstance()->getBool("ScrapeScreenshots")); - s->addWithLabel("SCRAPE SCREENSHOT IMAGES", scrape_screenshots); + s->addWithLabel("SCREENSHOT IMAGES", scrape_screenshots); s->addSaveFunc([scrape_screenshots, s] { if (scrape_screenshots->getState() != Settings::getInstance()->getBool("ScrapeScreenshots")) { @@ -314,10 +314,22 @@ void GuiScraperMenu::openContentOptions() } }); - // Scrape cover images. + // Scrape title screen images. + auto scrapeTitleScreens = std::make_shared(mWindow); + 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 scrape_covers = std::make_shared(mWindow); scrape_covers->setState(Settings::getInstance()->getBool("ScrapeCovers")); - s->addWithLabel("SCRAPE BOX COVER IMAGES", scrape_covers); + s->addWithLabel("BOX COVER IMAGES", scrape_covers); s->addSaveFunc([scrape_covers, s] { if (scrape_covers->getState() != Settings::getInstance()->getBool("ScrapeCovers")) { Settings::getInstance()->setBool("ScrapeCovers", scrape_covers->getState()); @@ -325,10 +337,31 @@ void GuiScraperMenu::openContentOptions() } }); + // Scrape box back cover images. + auto scrapeBackCovers = std::make_shared(mWindow); + 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(); + } + }); + + // Box back cover images are not supported by TheGamesDB, so gray out the option if this + // scraper is selected. + if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { + scrapeBackCovers->setEnabled(false); + scrapeBackCovers->setOpacity(DISABLED_OPACITY); + scrapeBackCovers->getParent() + ->getChild(scrapeBackCovers->getChildIndex() - 1) + ->setOpacity(DISABLED_OPACITY); + } + // Scrape marquee images. auto scrape_marquees = std::make_shared(mWindow); scrape_marquees->setState(Settings::getInstance()->getBool("ScrapeMarquees")); - s->addWithLabel("SCRAPE MARQUEE (WHEEL) IMAGES", scrape_marquees); + s->addWithLabel("MARQUEE (WHEEL) IMAGES", scrape_marquees); s->addSaveFunc([scrape_marquees, s] { if (scrape_marquees->getState() != Settings::getInstance()->getBool("ScrapeMarquees")) { Settings::getInstance()->setBool("ScrapeMarquees", scrape_marquees->getState()); @@ -339,7 +372,7 @@ void GuiScraperMenu::openContentOptions() // Scrape 3D box images. auto scrape_3dboxes = std::make_shared(mWindow); scrape_3dboxes->setState(Settings::getInstance()->getBool("Scrape3DBoxes")); - s->addWithLabel("SCRAPE 3D BOX IMAGES", scrape_3dboxes); + s->addWithLabel("3D BOX IMAGES", scrape_3dboxes); s->addSaveFunc([scrape_3dboxes, s] { if (scrape_3dboxes->getState() != Settings::getInstance()->getBool("Scrape3DBoxes")) { Settings::getInstance()->setBool("Scrape3DBoxes", scrape_3dboxes->getState()); @@ -357,6 +390,29 @@ void GuiScraperMenu::openContentOptions() ->setOpacity(DISABLED_OPACITY); } + // Scrape physical media images. + auto scrapePhysicalMedia = std::make_shared(mWindow); + 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); + } + mWindow->pushGui(s); } @@ -884,10 +940,19 @@ void GuiScraperMenu::start() contentToScrape = true; break; } + if (Settings::getInstance()->getBool("ScrapeTitleScreens")) { + contentToScrape = true; + break; + } if (Settings::getInstance()->getBool("ScrapeCovers")) { contentToScrape = true; break; } + if (scraperService == "screenscraper" && + Settings::getInstance()->getBool("ScrapeBackCovers")) { + contentToScrape = true; + break; + } if (Settings::getInstance()->getBool("ScrapeMarquees")) { contentToScrape = true; break; @@ -897,6 +962,11 @@ void GuiScraperMenu::start() contentToScrape = true; break; } + if (scraperService == "screenscraper" && + Settings::getInstance()->getBool("ScrapePhysicalMedia")) { + contentToScrape = true; + break; + } } while (0); if (!contentToScrape) { diff --git a/es-app/src/guis/GuiScraperSearch.cpp b/es-app/src/guis/GuiScraperSearch.cpp index 83f0cb6fe..840fc2b4f 100644 --- a/es-app/src/guis/GuiScraperSearch.cpp +++ b/es-app/src/guis/GuiScraperSearch.cpp @@ -678,9 +678,12 @@ void GuiScraperSearch::update(int deltaTime) for (unsigned int i = 0; i < results_scrape.size(); i++) { if (results_scrape[i].gameID == it->gameID) { results_scrape[i].box3DUrl = it->box3DUrl; + results_scrape[i].backcoverUrl = it->backcoverUrl; results_scrape[i].coverUrl = it->coverUrl; results_scrape[i].marqueeUrl = it->marqueeUrl; results_scrape[i].screenshotUrl = it->screenshotUrl; + results_scrape[i].titlescreenUrl = it->titlescreenUrl; + results_scrape[i].physicalmediaUrl = it->physicalmediaUrl; results_scrape[i].videoUrl = it->videoUrl; results_scrape[i].scraperRequestAllowance = it->scraperRequestAllowance; results_scrape[i].mediaURLFetch = COMPLETED; diff --git a/es-app/src/scrapers/GamesDBJSONScraper.cpp b/es-app/src/scrapers/GamesDBJSONScraper.cpp index 12a4b7047..046009f70 100644 --- a/es-app/src/scrapers/GamesDBJSONScraper.cpp +++ b/es-app/src/scrapers/GamesDBJSONScraper.cpp @@ -378,6 +378,7 @@ void processMediaURLs(const Value& images, result.coverUrl = ""; result.marqueeUrl = ""; result.screenshotUrl = ""; + result.titlescreenUrl = ""; // Quite excessive testing for valid values, but you never know what the server has // returned and we don't want to crash the program due to malformed data. @@ -399,6 +400,9 @@ void processMediaURLs(const Value& images, if (mediatype == "screenshot") if (gameMedia[i]["filename"].IsString()) result.screenshotUrl = base_url + gameMedia[i]["filename"].GetString(); + if (mediatype == "titlescreen") + if (gameMedia[i]["filename"].IsString()) + result.titlescreenUrl = base_url + gameMedia[i]["filename"].GetString(); } } result.mediaURLFetch = COMPLETED; diff --git a/es-app/src/scrapers/Scraper.cpp b/es-app/src/scrapers/Scraper.cpp index c889f54b2..aa54264a3 100644 --- a/es-app/src/scrapers/Scraper.cpp +++ b/es-app/src/scrapers/Scraper.cpp @@ -185,6 +185,14 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, mediaFileInfo.resizeFile = true; scrapeFiles.push_back(mediaFileInfo); } + if (Settings::getInstance()->getBool("ScrapeBackCovers") && result.backcoverUrl != "") { + mediaFileInfo.fileURL = result.backcoverUrl; + mediaFileInfo.fileFormat = result.backcoverFormat; + mediaFileInfo.subDirectory = "backcovers"; + mediaFileInfo.existingMediaFile = search.game->getBackCoverPath(); + mediaFileInfo.resizeFile = true; + scrapeFiles.push_back(mediaFileInfo); + } if (Settings::getInstance()->getBool("ScrapeCovers") && result.coverUrl != "") { mediaFileInfo.fileURL = result.coverUrl; mediaFileInfo.fileFormat = result.coverFormat; @@ -193,6 +201,14 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, mediaFileInfo.resizeFile = true; scrapeFiles.push_back(mediaFileInfo); } + if (Settings::getInstance()->getBool("ScrapePhysicalMedia") && result.physicalmediaUrl != "") { + mediaFileInfo.fileURL = result.physicalmediaUrl; + mediaFileInfo.fileFormat = result.physicalmediaFormat; + mediaFileInfo.subDirectory = "physicalmedia"; + mediaFileInfo.existingMediaFile = search.game->getPhysicalMediaPath(); + mediaFileInfo.resizeFile = true; + scrapeFiles.push_back(mediaFileInfo); + } if (Settings::getInstance()->getBool("ScrapeMarquees") && result.marqueeUrl != "") { mediaFileInfo.fileURL = result.marqueeUrl; mediaFileInfo.fileFormat = result.marqueeFormat; @@ -209,6 +225,14 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, mediaFileInfo.resizeFile = true; scrapeFiles.push_back(mediaFileInfo); } + if (Settings::getInstance()->getBool("ScrapeTitleScreens") && result.titlescreenUrl != "") { + mediaFileInfo.fileURL = result.titlescreenUrl; + mediaFileInfo.fileFormat = result.titlescreenFormat; + mediaFileInfo.subDirectory = "titlescreens"; + mediaFileInfo.existingMediaFile = search.game->getTitleScreenPath(); + mediaFileInfo.resizeFile = true; + scrapeFiles.push_back(mediaFileInfo); + } if (Settings::getInstance()->getBool("ScrapeVideos") && result.videoUrl != "") { mediaFileInfo.fileURL = result.videoUrl; mediaFileInfo.fileFormat = result.videoFormat; @@ -401,6 +425,56 @@ void MediaDownloadHandle::update() // Download is done, save it to disk. + // There's an incredibly annoying issue where some box back covers at ScreenScraper only contain + // a single color, like pure black or more commonly pure green. The hack below checks if the + // same pixel value is set throughout the image and if so skips the file saving. This is not + // very efficient but these images are not technically corrupt so the actual pixel values need + // to be read and compared. + if (Settings::getInstance()->getString("Scraper") == "screenscraper" && + mMediaType == "backcovers") { + bool emptyImage = false; + FREE_IMAGE_FORMAT imageFormat = FIF_UNKNOWN; + std::string imageData = mReq->getContent(); + FIMEMORY* memoryStream = FreeImage_OpenMemory(reinterpret_cast(&imageData.at(0)), + static_cast(imageData.size())); + imageFormat = FreeImage_GetFileTypeFromMemory(memoryStream, 0); + + if (imageFormat != FIF_UNKNOWN) { + emptyImage = true; + + FIBITMAP* tempImage = FreeImage_LoadFromMemory(imageFormat, memoryStream); + RGBQUAD firstPixel; + RGBQUAD currPixel; + + FreeImage_GetPixelColor(tempImage, 0, 0, &firstPixel); + + for (unsigned int x = 0; x < FreeImage_GetWidth(tempImage); x++) { + if (!emptyImage) + break; + for (unsigned int y = 0; y < FreeImage_GetHeight(tempImage); y++) { + FreeImage_GetPixelColor(tempImage, x, y, &currPixel); + if (currPixel.rgbBlue != firstPixel.rgbBlue || + currPixel.rgbGreen != firstPixel.rgbGreen || + currPixel.rgbRed != firstPixel.rgbRed) { + emptyImage = false; + break; + } + } + } + + FreeImage_Unload(tempImage); + } + FreeImage_CloseMemory(memoryStream); + + if (emptyImage) { + LOG(LogWarning) << "ScreenScraper: Image does not seem to contain any data, not saving " + "it to disk: \"" + << mSavePath << "\""; + setStatus(ASYNC_DONE); + return; + } + } + // This is just a temporary workaround to avoid saving media files to disk that are // actually just containing error messages from the scraper service. The proper solution // is to implement file checksum checks to determine if the server response contains valid diff --git a/es-app/src/scrapers/Scraper.h b/es-app/src/scrapers/Scraper.h index 4218effc4..81dde8c60 100644 --- a/es-app/src/scrapers/Scraper.h +++ b/es-app/src/scrapers/Scraper.h @@ -60,16 +60,22 @@ struct ScraperSearchResult { std::string thumbnailImageUrl; std::string box3DUrl; + std::string backcoverUrl; std::string coverUrl; std::string marqueeUrl; + std::string physicalmediaUrl; std::string screenshotUrl; + std::string titlescreenUrl; std::string videoUrl; // Needed to pre-set the image type. std::string box3DFormat; + std::string backcoverFormat; std::string coverFormat; std::string marqueeFormat; + std::string physicalmediaFormat; std::string screenshotFormat; + std::string titlescreenFormat; std::string videoFormat; // Indicates whether any new media files were downloaded and saved. diff --git a/es-app/src/scrapers/ScreenScraper.cpp b/es-app/src/scrapers/ScreenScraper.cpp index d6caa4885..8fa80b582 100644 --- a/es-app/src/scrapers/ScreenScraper.cpp +++ b/es-app/src/scrapers/ScreenScraper.cpp @@ -524,15 +524,24 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, // 3D box. processMedia(result, media_list, ssConfig.media_3dbox, result.box3DUrl, result.box3DFormat, region); - // Cover. + // Box back cover. + processMedia(result, media_list, ssConfig.media_backcover, result.backcoverUrl, + result.backcoverFormat, region); + // Box cover. processMedia(result, media_list, ssConfig.media_cover, result.coverUrl, result.coverFormat, region); // Marquee (wheel). processMedia(result, media_list, ssConfig.media_marquee, result.marqueeUrl, result.marqueeFormat, region); + // Physical media. + processMedia(result, media_list, ssConfig.media_physicalmedia, result.physicalmediaUrl, + result.physicalmediaFormat, region); // Screenshot. processMedia(result, media_list, ssConfig.media_screenshot, result.screenshotUrl, result.screenshotFormat, region); + // Title screen. + processMedia(result, media_list, ssConfig.media_titlescreen, result.titlescreenUrl, + result.titlescreenFormat, region); // Video. processMedia(result, media_list, ssConfig.media_video, result.videoUrl, result.videoFormat, region); diff --git a/es-app/src/scrapers/ScreenScraper.h b/es-app/src/scrapers/ScreenScraper.h index 3e07bf61b..68e509733 100644 --- a/es-app/src/scrapers/ScreenScraper.h +++ b/es-app/src/scrapers/ScreenScraper.h @@ -71,9 +71,12 @@ public: // std::string media_3dbox = "box-3D"; + std::string media_backcover = "box-2D-back"; std::string media_cover = "box-2D"; std::string media_marquee = "wheel"; + std::string media_physicalmedia = "support-2D"; std::string media_screenshot = "ss"; + std::string media_titlescreen = "sstitle"; std::string media_video = "video"; bool isArcadeSystem; diff --git a/es-app/src/views/gamelist/BasicGameListView.cpp b/es-app/src/views/gamelist/BasicGameListView.cpp index f6d2a4002..632b89d61 100644 --- a/es-app/src/views/gamelist/BasicGameListView.cpp +++ b/es-app/src/views/gamelist/BasicGameListView.cpp @@ -250,6 +250,13 @@ void BasicGameListView::removeMedia(FileData* game) removeEmptyDirFunc(systemMediaDir, mediaType, path); } + if (Utils::FileSystem::exists(game->getTitleScreenPath())) { + mediaType = "titlescreens"; + path = game->getTitleScreenPath(); + Utils::FileSystem::removeFile(path); + removeEmptyDirFunc(systemMediaDir, mediaType, path); + } + if (Utils::FileSystem::exists(game->getCoverPath())) { mediaType = "covers"; path = game->getCoverPath(); @@ -257,6 +264,13 @@ void BasicGameListView::removeMedia(FileData* game) removeEmptyDirFunc(systemMediaDir, mediaType, path); } + if (Utils::FileSystem::exists(game->getBackCoverPath())) { + mediaType = "backcovers"; + path = game->getBackCoverPath(); + Utils::FileSystem::removeFile(path); + removeEmptyDirFunc(systemMediaDir, mediaType, path); + } + if (Utils::FileSystem::exists(game->getMarqueePath())) { mediaType = "marquees"; path = game->getMarqueePath(); @@ -271,6 +285,13 @@ void BasicGameListView::removeMedia(FileData* game) removeEmptyDirFunc(systemMediaDir, mediaType, path); } + if (Utils::FileSystem::exists(game->getPhysicalMediaPath())) { + mediaType = "physicalmedia"; + path = game->getPhysicalMediaPath(); + Utils::FileSystem::removeFile(path); + removeEmptyDirFunc(systemMediaDir, mediaType, path); + } + if (Utils::FileSystem::exists(game->getThumbnailPath())) { mediaType = "thumbnails"; path = game->getThumbnailPath(); diff --git a/es-app/src/views/gamelist/GridGameListView.cpp b/es-app/src/views/gamelist/GridGameListView.cpp index 284762deb..af429a7ce 100644 --- a/es-app/src/views/gamelist/GridGameListView.cpp +++ b/es-app/src/views/gamelist/GridGameListView.cpp @@ -600,6 +600,13 @@ void GridGameListView::removeMedia(FileData* game) removeEmptyDirFunc(systemMediaDir, mediaType, path); } + if (Utils::FileSystem::exists(game->getTitleScreenPath())) { + mediaType = "titlescreens"; + path = game->getTitleScreenPath(); + Utils::FileSystem::removeFile(path); + removeEmptyDirFunc(systemMediaDir, mediaType, path); + } + if (Utils::FileSystem::exists(game->getCoverPath())) { mediaType = "covers"; path = game->getCoverPath(); @@ -607,6 +614,13 @@ void GridGameListView::removeMedia(FileData* game) removeEmptyDirFunc(systemMediaDir, mediaType, path); } + if (Utils::FileSystem::exists(game->getBackCoverPath())) { + mediaType = "backcovers"; + path = game->getBackCoverPath(); + Utils::FileSystem::removeFile(path); + removeEmptyDirFunc(systemMediaDir, mediaType, path); + } + if (Utils::FileSystem::exists(game->getMarqueePath())) { mediaType = "marquees"; path = game->getMarqueePath(); @@ -621,6 +635,13 @@ void GridGameListView::removeMedia(FileData* game) removeEmptyDirFunc(systemMediaDir, mediaType, path); } + if (Utils::FileSystem::exists(game->getPhysicalMediaPath())) { + mediaType = "physicalmedia"; + path = game->getPhysicalMediaPath(); + Utils::FileSystem::removeFile(path); + removeEmptyDirFunc(systemMediaDir, mediaType, path); + } + if (Utils::FileSystem::exists(game->getThumbnailPath())) { mediaType = "thumbnails"; path = game->getThumbnailPath(); diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index 2d4d406c8..fc31311f7 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -108,9 +108,12 @@ void Settings::setDefaults() mBoolMap["ScrapeMetadata"] = {true, true}; mBoolMap["ScrapeVideos"] = {true, true}; mBoolMap["ScrapeScreenshots"] = {true, true}; + mBoolMap["ScrapeTitleScreens"] = {true, true}; mBoolMap["ScrapeCovers"] = {true, true}; + mBoolMap["ScrapeBackCovers"] = {true, true}; mBoolMap["ScrapeMarquees"] = {true, true}; mBoolMap["Scrape3DBoxes"] = {true, true}; + mBoolMap["ScrapePhysicalMedia"] = {true, true}; mStringMap["MiximageResolution"] = {"1280x960", "1280x960"}; mStringMap["MiximageScreenshotScaling"] = {"sharp", "sharp"};