diff --git a/backend/pom.xml b/backend/pom.xml
index 6066cdd..85270e6 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -7,7 +7,7 @@
gameyfin
de.grimsi
- 1.1.1
+ 1.1.2
gameyfin-backend
diff --git a/backend/src/main/java/de/grimsi/gameyfin/mapper/GameMapper.java b/backend/src/main/java/de/grimsi/gameyfin/mapper/GameMapper.java
index 70c9c5f..a61e068 100644
--- a/backend/src/main/java/de/grimsi/gameyfin/mapper/GameMapper.java
+++ b/backend/src/main/java/de/grimsi/gameyfin/mapper/GameMapper.java
@@ -19,6 +19,7 @@ import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.time.Instant;
import java.util.List;
import java.util.stream.Stream;
@@ -53,6 +54,7 @@ public class GameMapper {
.playerPerspectives(PlayerPerspectiveMapper.toPlayerPerspectives(g.getPlayerPerspectivesList()))
.path(path.toString())
.diskSize(calculateDiskSize(g, path))
+ .addedToLibrary(Instant.now())
.build();
}
diff --git a/backend/src/main/java/de/grimsi/gameyfin/service/DownloadService.java b/backend/src/main/java/de/grimsi/gameyfin/service/DownloadService.java
index 4e3013e..ecf6dea 100644
--- a/backend/src/main/java/de/grimsi/gameyfin/service/DownloadService.java
+++ b/backend/src/main/java/de/grimsi/gameyfin/service/DownloadService.java
@@ -116,7 +116,7 @@ public class DownloadService {
MultiValueMap gameToImageIds = new LinkedMultiValueMap<>(
detectedGameRepository.findAll().stream()
- .collect(Collectors.toMap(DetectedGame::getTitle, g -> Collections.singletonList(g.getCoverId()))));
+ .collect(Collectors.toMap(DetectedGame::getSlug, g -> Collections.singletonList(g.getCoverId()))));
int downloadCount = downloadImagesIntoCache(gameToImageIds, IgdbApiProperties.COVER_IMAGE_SIZE, "cover", "game");
@@ -133,7 +133,7 @@ public class DownloadService {
MultiValueMap gamesToImageIds = new LinkedMultiValueMap<>(
detectedGameRepository.findAll().stream()
- .collect(Collectors.toMap(DetectedGame::getTitle, DetectedGame::getScreenshotIds)));
+ .collect(Collectors.toMap(DetectedGame::getSlug, DetectedGame::getScreenshotIds)));
int downloadCount = downloadImagesIntoCache(gamesToImageIds, IgdbApiProperties.SCREENSHOT_IMAGE_SIZE, "screenshot", "game");
@@ -150,7 +150,7 @@ public class DownloadService {
Map> companyToLogoIdMap = detectedGameRepository.findAll().stream()
.flatMap(g -> g.getCompanies().stream())
- .collect(Collectors.toMap(Company::getName, c -> Collections.singletonList(c.getLogoId()), (c1, c2) -> c1));
+ .collect(Collectors.toMap(Company::getSlug, c -> Collections.singletonList(c.getLogoId()), (c1, c2) -> c1));
MultiValueMap companiesToLogoIds = new LinkedMultiValueMap<>(companyToLogoIdMap);
@@ -215,12 +215,24 @@ public class DownloadService {
String imgUrl = "t_%s/%s".formatted(imageSize, imgFileName);
if (Files.exists(Path.of(cacheFolderPath, imgFileName))) {
- log.debug("{} for {} '{}' already downloaded ({}), skipping.",
- imageType.substring(0, 1).toUpperCase() + imageType.substring(1).toLowerCase(),
- entityType,
- entry.getKey(),
- imgFileName);
- return;
+
+ Path existingImageFile = Path.of(cacheFolderPath, imgFileName);
+
+ try {
+ if(Files.size(existingImageFile) == 0L) {
+ log.info("File '{}' is corrupt, retrying download...", imgFileName);
+ Files.delete(existingImageFile);
+ } else {
+ log.debug("{} for {} '{}' already downloaded ({}), skipping.",
+ imageType.substring(0, 1).toUpperCase() + imageType.substring(1).toLowerCase(),
+ entityType,
+ entry.getKey(),
+ imgFileName);
+ return;
+ }
+ } catch (IOException e) {
+ log.error("Error while checking file '{}'.", existingImageFile);
+ }
}
Flux dataBuffer = igdbImageClient.get()
diff --git a/backend/src/main/java/de/grimsi/gameyfin/service/LibraryService.java b/backend/src/main/java/de/grimsi/gameyfin/service/LibraryService.java
index f5c7308..7e5dd67 100644
--- a/backend/src/main/java/de/grimsi/gameyfin/service/LibraryService.java
+++ b/backend/src/main/java/de/grimsi/gameyfin/service/LibraryService.java
@@ -19,6 +19,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import static de.grimsi.gameyfin.util.FilenameUtil.getFilenameWithoutExtension;
@@ -83,11 +84,17 @@ public class LibraryService {
.toList();
// For each new game, load the info from IGDB
- // 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
+ // 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
// If a game is not found on IGDB, blacklist the path, so we won't query the API later for the same path
List newDetectedGames = gameFiles.parallelStream()
.map(p -> {
Optional optionalGame = igdbWrapper.searchForGameByTitle(getFilenameWithoutExtension(p));
+
+ if(optionalGame.isPresent() && detectedGameRepository.existsBySlug(optionalGame.get().getSlug())) {
+ log.warn("Game with slug '{}' already exists in database", optionalGame.get().getSlug());
+ optionalGame = Optional.empty();
+ }
+
return optionalGame.map(game -> Map.entry(p, game)).or(() -> {
unmappableFileRepository.save(new UnmappableFile(p.toString()));
newUnmappedFilesCounter.getAndIncrement();
@@ -99,7 +106,11 @@ public class LibraryService {
.map(Optional::get)
.peek(e -> log.info("Mapped file '{}' to game '{}' (slug: {})", e.getKey(), e.getValue().getName(), e.getValue().getSlug()))
.map(e -> GameMapper.toDetectedGame(e.getValue(), e.getKey()))
- .toList();
+ .collect(Collectors.toList());
+
+ List duplicateGames = getDuplicates(newDetectedGames);
+ newUnmappedFilesCounter.getAndAdd(duplicateGames.size());
+ newDetectedGames.removeAll(duplicateGames);
newDetectedGames = detectedGameRepository.saveAll(newDetectedGames);
@@ -112,4 +123,13 @@ public class LibraryService {
public List getAutocompleteSuggestions(String searchTerm, int limit) {
return igdbWrapper.findPossibleMatchingTitles(searchTerm, limit);
}
+
+ private List getDuplicates(List gamesToFilter) {
+ return gamesToFilter.stream().filter(g -> Collections.frequency(gamesToFilter, g) >1)
+ .peek(d -> {
+ log.warn("Found duplicate for game '{}' under path '{}'. Mapping must be done manually.", d.getTitle(), d.getPath());
+ unmappableFileRepository.save(new UnmappableFile(d.getPath()));
+ })
+ .toList();
+ }
}
diff --git a/frontend/pom.xml b/frontend/pom.xml
index 1b7f07a..da6ec3d 100644
--- a/frontend/pom.xml
+++ b/frontend/pom.xml
@@ -5,7 +5,7 @@
gameyfin
de.grimsi
- 1.1.1
+ 1.1.2
4.0.0
diff --git a/pom.xml b/pom.xml
index 041f3dc..92cf638 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
de.grimsi
gameyfin
- 1.1.1
+ 1.1.2
gameyfin
gameyfin