diff --git a/.gitignore b/.gitignore index 32de00b..6fc8d71 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ build/ ### VS Code ### .vscode/ /.mvn/ + +### Custom ### +/data/ diff --git a/src/main/java/de/grimsi/gameyfin/igdb/IgdbApiProperties.java b/src/main/java/de/grimsi/gameyfin/igdb/IgdbApiProperties.java index 4dcec99..b2cd8fc 100644 --- a/src/main/java/de/grimsi/gameyfin/igdb/IgdbApiProperties.java +++ b/src/main/java/de/grimsi/gameyfin/igdb/IgdbApiProperties.java @@ -5,8 +5,8 @@ import java.util.List; public class IgdbApiProperties { public static final String IGDB_ENPOINT_GAMES_PROTOBUF = "games.pb"; - public static final List GAME_QUERY_FIELDS = List.of( - "slug", "name", "summary", "first_release_date", "rating", "aggregated_rating", "total_rating", "category", "multiplayer_modes", "cover", "screenshots", "videos", // All top-level fields + private static final List GAME_QUERY_FIELDS = List.of( + "slug", "name", "summary", "first_release_date", "rating", "aggregated_rating", "total_rating", "category", "multiplayer_modes", "cover", "screenshots", "videos", "involved_companies.company.slug", "involved_companies.company.name", "involved_companies.company.logo.id", "genres.slug", "genres.name", "keywords.slug", "keywords.name", @@ -14,4 +14,6 @@ public class IgdbApiProperties { "player_perspectives.slug", "player_perspectives.name" ); + public static final String GAME_QUERY_FIELDS_STRING = String.join(",", GAME_QUERY_FIELDS); + } diff --git a/src/main/java/de/grimsi/gameyfin/igdb/IgdbWrapper.java b/src/main/java/de/grimsi/gameyfin/igdb/IgdbWrapper.java index 1961172..e3fa551 100644 --- a/src/main/java/de/grimsi/gameyfin/igdb/IgdbWrapper.java +++ b/src/main/java/de/grimsi/gameyfin/igdb/IgdbWrapper.java @@ -68,7 +68,7 @@ public class IgdbWrapper { public Optional getGameById(Long id) { Igdb.GameResult gameResult = queryIgdbApi( IgdbApiProperties.IGDB_ENPOINT_GAMES_PROTOBUF, - "fields *; where id = %d; limit 1;".formatted(id), + "fields %s; where id = %d; limit 1;".formatted(IgdbApiProperties.GAME_QUERY_FIELDS_STRING, id), Igdb.GameResult.class ); @@ -80,7 +80,7 @@ public class IgdbWrapper { public Optional getGameBySlug(String slug) { Igdb.GameResult gameResult = queryIgdbApi( IgdbApiProperties.IGDB_ENPOINT_GAMES_PROTOBUF, - "fields *; where slug = \"%s\"; limit 1;".formatted(slug), + "fields %s; where slug = \"%s\"; limit 1;".formatted(IgdbApiProperties.GAME_QUERY_FIELDS_STRING, slug), Igdb.GameResult.class ); @@ -93,7 +93,7 @@ public class IgdbWrapper { Igdb.GameResult gameResult = queryIgdbApi( IgdbApiProperties.IGDB_ENPOINT_GAMES_PROTOBUF, "search \"%s\"; fields %s; where platforms = (%s);" - .formatted(searchTerm, String.join(",", IgdbApiProperties.GAME_QUERY_FIELDS), preferredPlatforms), + .formatted(searchTerm, IgdbApiProperties.GAME_QUERY_FIELDS_STRING, preferredPlatforms), Igdb.GameResult.class ); diff --git a/src/main/java/de/grimsi/gameyfin/repositories/UnmappableFileRepository.java b/src/main/java/de/grimsi/gameyfin/repositories/UnmappableFileRepository.java index 420b033..eea336d 100644 --- a/src/main/java/de/grimsi/gameyfin/repositories/UnmappableFileRepository.java +++ b/src/main/java/de/grimsi/gameyfin/repositories/UnmappableFileRepository.java @@ -1,9 +1,21 @@ package de.grimsi.gameyfin.repositories; +import de.grimsi.gameyfin.entities.DetectedGame; import de.grimsi.gameyfin.entities.UnmappableFile; import org.springframework.data.jpa.repository.JpaRepository; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; + public interface UnmappableFileRepository extends JpaRepository { boolean existsByPath(String path); + + List getAllByPathNotIn(Collection paths); + + default List getAllByPathNotIn(List paths) { + List pathStrings = paths.stream().map(Path::toString).toList(); + return getAllByPathNotIn(pathStrings); + } } diff --git a/src/main/java/de/grimsi/gameyfin/service/FilesystemService.java b/src/main/java/de/grimsi/gameyfin/service/FilesystemService.java index a5ecb59..9b80813 100644 --- a/src/main/java/de/grimsi/gameyfin/service/FilesystemService.java +++ b/src/main/java/de/grimsi/gameyfin/service/FilesystemService.java @@ -74,6 +74,12 @@ public class FilesystemService { // This would include renamed files, but they will be re-detected by the next step List deletedGames = detectedGameRepository.getAllByPathNotIn(gameFiles); detectedGameRepository.deleteAll(deletedGames); + deletedGames.forEach(g -> log.info("Game '{}' has been moved or deleted.", g.getPath())); + + // Now check if there are any unmapped files that have been removed from the file system + List deletedUnmappableFiles = unmappableFileRepository.getAllByPathNotIn(gameFiles); + unmappableFileRepository.deleteAll(deletedUnmappableFiles); + deletedUnmappableFiles.forEach(g -> log.info("Unmapped file '{}' has been moved or deleted.", g.getPath())); // Filter out the games we already know and the ones we already tried to map to a game without success gameFiles = gameFiles.stream() @@ -104,13 +110,8 @@ public class FilesystemService { stopWatch.stop(); - String scanDuration = "%dmin : %ds".formatted( - TimeUnit.MILLISECONDS.toMinutes(stopWatch.getLastTaskTimeMillis()), - TimeUnit.MILLISECONDS.toSeconds(stopWatch.getLastTaskTimeMillis()) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(stopWatch.getLastTaskTimeMillis())) - ); - - log.info("Scan finished in {}: Found {} new games, deleted {} games, could not map {} files/folders, {} games total.", - scanDuration, newDetectedGames.size(), deletedGames.size(), newUnmappedFilesCounter.get(), detectedGameRepository.count()); + log.info("Scan finished in {} seconds: Found {} new games, deleted {} games, could not map {} files/folders, {} games total.", + (int) stopWatch.getTotalTimeSeconds(), newDetectedGames.size(), deletedGames.size() + deletedUnmappableFiles.size(), newUnmappedFilesCounter.get(), detectedGameRepository.count()); } private String getFilename(Path p) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index afc5f26..df8a107 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,5 +1,17 @@ -server.port: 8080 -spring.jackson.default-property-inclusion: non_null +server: + port: 8080 + error.include-stacktrace: never + +spring: + jackson.default-property-inclusion: non_null + datasource.db-name: gameyfin_db + datasource.url: jdbc:h2:file:./data/${spring.datasource.db-name};AUTO_SERVER=TRUE + datasource.username: gfadmin + datasource.password: gameyfin + datasource.driverClassName: org.h2.Driver + jpa.database-platform: org.hibernate.dialect.H2Dialect + jpa.hibernate.ddl-auto: update + jpa.open-in-view: true gameyfin: root: ""