From 2a33fb1d6baaa8d7ac16999326bbc0b53014bd30 Mon Sep 17 00:00:00 2001 From: Simon Grimme <9295182+grimsi@users.noreply.github.com> Date: Sat, 16 Jul 2022 02:48:58 +0200 Subject: [PATCH] Implement manual mapping for files that could not be automatically mapped --- .../de/grimsi/gameyfin/igdb/IgdbWrapper.java | 20 ++++++++++++---- .../UnmappableFileRepository.java | 2 +- .../gameyfin/rest/GameyfinDevController.java | 12 ++++++---- .../gameyfin/service/FilesystemService.java | 4 ++-- .../grimsi/gameyfin/service/GameService.java | 24 +++++++++++++++++++ 5 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/grimsi/gameyfin/igdb/IgdbWrapper.java b/src/main/java/de/grimsi/gameyfin/igdb/IgdbWrapper.java index 73b9f7c..3d62ded 100644 --- a/src/main/java/de/grimsi/gameyfin/igdb/IgdbWrapper.java +++ b/src/main/java/de/grimsi/gameyfin/igdb/IgdbWrapper.java @@ -21,8 +21,6 @@ import java.util.Optional; @Service public class IgdbWrapper { - private static final int MAIN_GAME_CATEGORY_VALUE = 0; - @Value("${gameyfin.igdb.api.client-id}") private String clientId; @@ -70,7 +68,21 @@ public class IgdbWrapper { public Optional getGameById(Long id) { Igdb.GameResult gameResult = igdbApiClient.post() .uri("games.pb") - .bodyValue("fields *; where id = %d & category = %d; limit 1;".formatted(id, MAIN_GAME_CATEGORY_VALUE)) + .bodyValue("fields *; where id = %d; limit 1;".formatted(id)) + .retrieve() + .bodyToMono(Igdb.GameResult.class) + .transformDeferred(RateLimiterOperator.of(WebClientConfig.IGDB_RATE_LIMITER)) + .block(); + + if (gameResult == null) return Optional.empty(); + + return Optional.of(gameResult.getGames(0)); + } + + public Optional getGameBySlug(String slug) { + Igdb.GameResult gameResult = igdbApiClient.post() + .uri("games.pb") + .bodyValue("fields *; where slug = \"%s\"; limit 1;".formatted(slug)) .retrieve() .bodyToMono(Igdb.GameResult.class) .transformDeferred(RateLimiterOperator.of(WebClientConfig.IGDB_RATE_LIMITER)) @@ -84,7 +96,7 @@ public class IgdbWrapper { public Optional searchForGameByTitle(String searchTerm) { Igdb.GameResult gameResult = igdbApiClient.post() .uri("games.pb") - .bodyValue("fields *; search \"%s\"; where platforms = (%s) & category = %d;".formatted(searchTerm, preferredPlatforms, MAIN_GAME_CATEGORY_VALUE)) + .bodyValue("fields *; search \"%s\"; where platforms = (%s);".formatted(searchTerm, preferredPlatforms)) .retrieve() .bodyToMono(Igdb.GameResult.class) .transformDeferred(RateLimiterOperator.of(WebClientConfig.IGDB_RATE_LIMITER)) diff --git a/src/main/java/de/grimsi/gameyfin/repositories/UnmappableFileRepository.java b/src/main/java/de/grimsi/gameyfin/repositories/UnmappableFileRepository.java index 9ad79e8..420b033 100644 --- a/src/main/java/de/grimsi/gameyfin/repositories/UnmappableFileRepository.java +++ b/src/main/java/de/grimsi/gameyfin/repositories/UnmappableFileRepository.java @@ -3,7 +3,7 @@ package de.grimsi.gameyfin.repositories; import de.grimsi.gameyfin.entities.UnmappableFile; import org.springframework.data.jpa.repository.JpaRepository; -public interface UnmappableFileRepository extends JpaRepository { +public interface UnmappableFileRepository extends JpaRepository { boolean existsByPath(String path); } diff --git a/src/main/java/de/grimsi/gameyfin/rest/GameyfinDevController.java b/src/main/java/de/grimsi/gameyfin/rest/GameyfinDevController.java index e87d881..62f3f45 100644 --- a/src/main/java/de/grimsi/gameyfin/rest/GameyfinDevController.java +++ b/src/main/java/de/grimsi/gameyfin/rest/GameyfinDevController.java @@ -11,9 +11,7 @@ import de.grimsi.gameyfin.util.ProtobufUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ResponseStatusException; import java.nio.file.Path; @@ -64,7 +62,7 @@ public class GameyfinDevController { return gameService.getAllDetectedGames(); } - @GetMapping(value = "/dev/startScan", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = "/dev/scan", produces = MediaType.APPLICATION_JSON_VALUE) public void scanLibrary() { filesystemService.scanGameLibrary(); } @@ -74,4 +72,10 @@ public class GameyfinDevController { return gameService.getAllUnmappedFiles(); } + @PostMapping(value = "/dev/unmappedFiles/{unmappedGameId}/mapTo/{igdbSlug}", produces = MediaType.APPLICATION_JSON_VALUE) + public DetectedGame mapGameManually(@PathVariable Long unmappedGameId, @PathVariable String igdbSlug) { + return gameService.mapUnmappedFile(unmappedGameId, igdbSlug); + } + + } diff --git a/src/main/java/de/grimsi/gameyfin/service/FilesystemService.java b/src/main/java/de/grimsi/gameyfin/service/FilesystemService.java index ed28781..70527e7 100644 --- a/src/main/java/de/grimsi/gameyfin/service/FilesystemService.java +++ b/src/main/java/de/grimsi/gameyfin/service/FilesystemService.java @@ -73,7 +73,7 @@ public class FilesystemService { .toList(); // For each new game, load the info from IGDB - // If a game is not found on IGDB, blacklist the path so we won't query the API later on for the same path + // If a game is not found on IGDB, add it to the list of unmapped files so we won't query the API later on for the same path List newDetectedGames = gameFiles.parallelStream() .map(p -> { Optional optionalGame = igdbWrapper.searchForGameByTitle(getFilename(p)); @@ -91,7 +91,7 @@ public class FilesystemService { newDetectedGames = detectedGameRepository.saveAll(newDetectedGames); - log.info("Scan finished: Found {} new games, deleted {} games, backlisted {} files/folders, {} games total.", newDetectedGames.size(), "NOT_IMPLEMENTED_YET", newBlacklistCounter.get(), detectedGameRepository.count()); + log.info("Scan finished: Found {} new games, deleted {} games, could not map {} files/folders, {} games total.", newDetectedGames.size(), "NOT_IMPLEMENTED_YET", newBlacklistCounter.get(), detectedGameRepository.count()); } private String getFilename(Path p) { diff --git a/src/main/java/de/grimsi/gameyfin/service/GameService.java b/src/main/java/de/grimsi/gameyfin/service/GameService.java index 1e2cf0c..6ff55f6 100644 --- a/src/main/java/de/grimsi/gameyfin/service/GameService.java +++ b/src/main/java/de/grimsi/gameyfin/service/GameService.java @@ -1,17 +1,26 @@ package de.grimsi.gameyfin.service; +import com.igdb.proto.Igdb; import de.grimsi.gameyfin.entities.DetectedGame; import de.grimsi.gameyfin.entities.UnmappableFile; +import de.grimsi.gameyfin.igdb.IgdbWrapper; +import de.grimsi.gameyfin.mapper.GameMapper; import de.grimsi.gameyfin.repositories.UnmappableFileRepository; import de.grimsi.gameyfin.repositories.DetectedGameRepository; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; +import org.springframework.web.server.ResponseStatusException; +import java.nio.file.Path; import java.util.List; @Service public class GameService { + @Autowired + private IgdbWrapper igdbWrapper; + @Autowired private DetectedGameRepository detectedGameRepository; @@ -25,4 +34,19 @@ public class GameService { public List getAllUnmappedFiles() { return unmappableFileRepository.findAll(); } + + public DetectedGame mapUnmappedFile(Long unmappedGameId, String igdbSlug) { + UnmappableFile unmappableFile = unmappableFileRepository.findById(unmappedGameId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Unmapped file with id '%d' does not exist.".formatted(unmappedGameId))); + + Igdb.Game igdbGame = igdbWrapper.getGameBySlug(igdbSlug) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Game with slug '%s' does not exist on IGDB.".formatted(igdbSlug))); + + DetectedGame game = GameMapper.toDetectedGame(igdbGame, Path.of(unmappableFile.getPath())); + game = detectedGameRepository.save(game); + + unmappableFileRepository.delete(unmappableFile); + + return game; + } }