Implement manual mapping for files that could not be automatically mapped

This commit is contained in:
Simon Grimme
2022-07-16 02:48:58 +02:00
parent 132d4d3694
commit 2a33fb1d6b
5 changed files with 51 additions and 11 deletions
@@ -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<Igdb.Game> 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<Igdb.Game> 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<Igdb.Game> 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))
@@ -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<UnmappableFile, String> {
public interface UnmappableFileRepository extends JpaRepository<UnmappableFile, Long> {
boolean existsByPath(String path);
}
@@ -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);
}
}
@@ -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<DetectedGame> newDetectedGames = gameFiles.parallelStream()
.map(p -> {
Optional<Igdb.Game> 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) {
@@ -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<UnmappableFile> 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;
}
}