mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-16 16:20:04 +00:00
Initial commit 3: Will it work this time?
This commit is contained in:
@@ -31,3 +31,4 @@ build/
|
|||||||
|
|
||||||
### VS Code ###
|
### VS Code ###
|
||||||
.vscode/
|
.vscode/
|
||||||
|
/.mvn/
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<!-- Spring Boot -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
@@ -34,17 +35,27 @@
|
|||||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Persistence -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Test -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Dev -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-devtools</artifactId>
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.h2database</groupId>
|
|
||||||
<artifactId>h2</artifactId>
|
|
||||||
<scope>runtime</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
@@ -55,11 +66,6 @@
|
|||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package de.grimsi.gameyfin.dto;
|
package de.grimsi.gameyfin.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@@ -9,6 +11,8 @@ import java.util.List;
|
|||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
public class GameDto {
|
public class GameDto {
|
||||||
private String name;
|
private String name;
|
||||||
private String publisher;
|
private String publisher;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package de.grimsi.gameyfin.igdb;
|
package de.grimsi.gameyfin.igdb;
|
||||||
|
|
||||||
import de.grimsi.gameyfin.dto.GameDto;
|
import de.grimsi.gameyfin.igdb.dto.IgdbAccessToken;
|
||||||
|
import de.grimsi.gameyfin.igdb.dto.IgdbGame;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -10,7 +11,6 @@ import org.springframework.web.util.UriComponentsBuilder;
|
|||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -37,6 +37,7 @@ public class IgdbWrapper {
|
|||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
authenticate();
|
authenticate();
|
||||||
|
initIgdbClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void authenticate() {
|
public void authenticate() {
|
||||||
@@ -58,8 +59,23 @@ public class IgdbWrapper {
|
|||||||
log.info("Successfully authenticated.");
|
log.info("Successfully authenticated.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IgdbGame findGameByTitle(String title) {
|
||||||
|
return searchForGameByTitle(title).orElseThrow(() -> new RuntimeException("Could not find game with title: \"%s\"".formatted(title)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<IgdbGame> getGameById(Long id) {
|
||||||
|
return Optional.ofNullable(
|
||||||
|
igdbApiClient.post()
|
||||||
|
.uri("games")
|
||||||
|
.bodyValue("fields *; where id = %d;".formatted(id))
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(IgdbGame.class)
|
||||||
|
.block()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private void initIgdbClient() {
|
private void initIgdbClient() {
|
||||||
if(accessToken == null) {
|
if (accessToken == null) {
|
||||||
authenticate();
|
authenticate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,46 +86,33 @@ public class IgdbWrapper {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameDto findGameByTitle(String title) {
|
private Optional<IgdbGame> searchForGameByTitle(String searchTerm) {
|
||||||
if (igdbApiClient == null) {
|
List<IgdbGame> games = new ArrayList<>();
|
||||||
initIgdbClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
IgdbSearchResultDto searchResult = searchForGameByTitle(title).orElseThrow(() -> new RuntimeException("Could not find game with title : \"%s\"".formatted(title)));
|
|
||||||
|
|
||||||
return GameDto.builder()
|
|
||||||
.name(searchResult.getName())
|
|
||||||
.releaseDate(Instant.ofEpochSecond(searchResult.getPublishedAt()))
|
|
||||||
.igdbGameId(searchResult.getGame())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<IgdbSearchResultDto> searchForGameByTitle(String searchTerm) {
|
|
||||||
List<IgdbSearchResultDto> searchResults = new ArrayList<>();
|
|
||||||
|
|
||||||
igdbApiClient.post()
|
igdbApiClient.post()
|
||||||
.uri("search")
|
.uri("games")
|
||||||
.bodyValue("fields *; search \"%s\"; limit 50;".formatted(searchTerm))
|
.bodyValue("fields *; search \"%s\";".formatted(searchTerm))
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.bodyToFlux(IgdbSearchResultDto.class)
|
.bodyToFlux(IgdbGame.class)
|
||||||
.doOnNext(searchResults::add)
|
.doOnNext(games::add)
|
||||||
.blockLast();
|
.blockLast();
|
||||||
|
|
||||||
if(searchResults.isEmpty()) return Optional.empty();
|
if (games.isEmpty()) return Optional.empty();
|
||||||
|
|
||||||
// First check if there are any matches with the exact search term
|
// First check if there are any matches with the exact search term
|
||||||
// If no exact match has been found, check if there are matches where the name ends with the search term
|
// If no exact match has been found, check if there are matches where the name ends with the search term
|
||||||
// This will filter out most DLCs and similiar stuff, but will detect a game even when your search term is not exactly the title
|
// This will filter out most DLCs and similiar stuff, but will detect a game even when your search term is not exactly the title
|
||||||
|
// If that also returns nothing, just return the first search result
|
||||||
//
|
//
|
||||||
// Example: Searching for "Rainbow Six Siege" will result in returning "Tom Clancy's Rainbow Six Siege" (the game we want)
|
// Example: Searching for "Rainbow Six Siege" will result in returning "Tom Clancy's Rainbow Six Siege" (the game we want)
|
||||||
// If we just used the first result from IGDB we would get something like "Tom Clancy's Rainbow Six Siege Demon Veil" as a result
|
// If we just used the first result from IGDB we would get something like "Tom Clancy's Rainbow Six Siege Demon Veil" as a result
|
||||||
|
|
||||||
Optional<IgdbSearchResultDto> srExactTitleMatch = searchResults.stream().filter(s -> s.getName().equals(searchTerm)).findFirst();
|
Optional<IgdbGame> srExactTitleMatch = games.stream().filter(s -> s.getName().equals(searchTerm)).findFirst();
|
||||||
if(srExactTitleMatch.isPresent()) return srExactTitleMatch;
|
if (srExactTitleMatch.isPresent()) return srExactTitleMatch;
|
||||||
|
|
||||||
Optional<IgdbSearchResultDto> srTitleEndsWithMatch = searchResults.stream().filter(s -> s.getName().endsWith(searchTerm)).findFirst();
|
Optional<IgdbGame> srTitleEndsWithMatch = games.stream().filter(s -> s.getName().endsWith(searchTerm)).findFirst();
|
||||||
if(srTitleEndsWithMatch.isPresent()) return srTitleEndsWithMatch;
|
if (srTitleEndsWithMatch.isPresent()) return srTitleEndsWithMatch;
|
||||||
|
|
||||||
return Optional.of(searchResults.get(0));
|
return Optional.of(games.get(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.grimsi.gameyfin.igdb;
|
package de.grimsi.gameyfin.igdb.dto;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonNaming;
|
import com.fasterxml.jackson.databind.annotation.JsonNaming;
|
||||||
|
|||||||
@@ -1,2 +1,45 @@
|
|||||||
package de.grimsi.gameyfin.igdb.dto;public class IgdbGame {
|
package de.grimsi.gameyfin.igdb.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonNaming;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
|
||||||
|
public class IgdbGame {
|
||||||
|
private Long id;
|
||||||
|
private List<Long> alternativeNames;
|
||||||
|
private Long category;
|
||||||
|
private Long cover;
|
||||||
|
private Instant createdAt;
|
||||||
|
private List<Long> externalGames;
|
||||||
|
private Instant firstReleaseDate;
|
||||||
|
private Long follows;
|
||||||
|
private List<Long> gameModes;
|
||||||
|
private List<Long> genres;
|
||||||
|
private Long hypes;
|
||||||
|
private List<Long> involvedCompanies;
|
||||||
|
private List<Long> keywords;
|
||||||
|
private List<Long> multiplayerModes;
|
||||||
|
private String name;
|
||||||
|
private List<Long> platforms;
|
||||||
|
private List<Long> playerPerspectives;
|
||||||
|
private Float rating;
|
||||||
|
private Long ratingCount;
|
||||||
|
private List<Long> releaseDates;
|
||||||
|
private List<Long> screenshots;
|
||||||
|
private List<Long> similiarGames;
|
||||||
|
private String slug;
|
||||||
|
private String storyline;
|
||||||
|
private String summary;
|
||||||
|
private List<Long> tags;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,16 +9,15 @@ import org.springframework.http.MediaType;
|
|||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.client.HttpStatusCodeException;
|
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class GameyfinController {
|
public class GameyfinDevController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
IgdbWrapper igdbWrapper;
|
IgdbWrapper igdbWrapper;
|
||||||
|
|
||||||
@GetMapping(value = "/findGameByTitle/{title}", produces = MediaType.APPLICATION_JSON_VALUE)
|
@GetMapping(value = "/dev/findGameByTitle/{title}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
public GameDto findGameByTitle(@PathVariable("title") String title) {
|
public GameDto findGameByTitle(@PathVariable("title") String title) {
|
||||||
IgdbGame game;
|
IgdbGame game;
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
package de.grimsi.gameyfin.service;public class FilesystemService {
|
package de.grimsi.gameyfin.service;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class FilesystemService {
|
||||||
|
@Value("${gameyfin.root}")
|
||||||
|
private String rootFolderPath;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
gameyfin:
|
gameyfin:
|
||||||
root: F:\Spiele
|
root: D:\Games
|
||||||
igdb:
|
igdb:
|
||||||
api:
|
api:
|
||||||
client-id: 23l3l5qshx4dwjuao6yb8jyf1qrd08
|
client-id: 23l3l5qshx4dwjuao6yb8jyf1qrd08
|
||||||
|
|||||||
Reference in New Issue
Block a user