Use Protobuf endpoints (WIP)

This commit is contained in:
Simon Grimme
2022-07-14 16:47:11 +02:00
parent 9e1b510f48
commit 149e1cc7e1
8 changed files with 1042 additions and 32 deletions
+42
View File
@@ -18,6 +18,7 @@
<properties>
<java.version>18</java.version>
<protoc.plugin.version>3.11.4</protoc.plugin.version>
</properties>
<dependencies>
@@ -49,6 +50,19 @@
<version>2.11.0</version>
</dependency>
<!-- Protobuf dependencies -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.21.2</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>3.21.1</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -89,6 +103,34 @@
</excludes>
</configuration>
</plugin>
<!-- Protobuf source generation plugin -->
<plugin>
<groupId>com.github.os72</groupId>
<artifactId>protoc-jar-maven-plugin</artifactId>
<version>${protoc.plugin.version}</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<includeStdTypes>true</includeStdTypes>
<inputDirectories>
<include>${project.basedir}/src/main/resources/proto</include>
</inputDirectories>
<outputTargets>
<outputTarget>
<type>java</type>
<outputDirectory>${project.build.directory}/generated-sources/protobuf
</outputDirectory>
</outputTarget>
</outputTargets>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
@@ -0,0 +1,36 @@
package de.grimsi.gameyfin.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
@Slf4j
@Configuration
public class WebClientConfig implements WebClientCustomizer {
@Override
public void customize(WebClient.Builder webClientBuilder) {
webClientBuilder.filter(logResponse());
webClientBuilder.filter(logRequest());
webClientBuilder.clientConnector(new ReactorClientHttpConnector(HttpClient.create().proxyWithSystemProperties()));
}
private ExchangeFilterFunction logResponse() {
return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
log.debug("Response: {}", clientResponse.statusCode());
return Mono.just(clientResponse);
});
}
private ExchangeFilterFunction logRequest() {
return (clientRequest, next) -> {
log.debug("Request: {} {}", clientRequest.method(), clientRequest.url());
return next.exchange(clientRequest);
};
}
}
@@ -1,15 +1,17 @@
package de.grimsi.gameyfin.igdb;
import de.grimsi.gameyfin.igdb.dto.IgdbAccessToken;
import de.grimsi.gameyfin.igdb.dto.IgdbGame;
import com.google.protobuf.InvalidProtocolBufferException;
import com.igdb.proto.Game;
import de.grimsi.gameyfin.igdb.dto.TwitchOAuthTokenDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.netty.http.client.HttpClient;
import javax.annotation.PostConstruct;
import java.net.URI;
@@ -30,16 +32,18 @@ public class IgdbWrapper {
@Value("${gameyfin.igdb.config.preferred-platform}")
private int preferredPlatform;
private final WebClient twitchApiClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(HttpClient.create().proxyWithSystemProperties()))
.build();
@Autowired
private WebClient.Builder webclientBuilder;
private WebClient twitchApiClient;
private WebClient igdbApiClient;
private IgdbAccessToken accessToken;
private TwitchOAuthTokenDto accessToken;
@PostConstruct
public void init() {
twitchApiClient = webclientBuilder.build();
authenticate();
initIgdbClient();
}
@@ -57,25 +61,29 @@ public class IgdbWrapper {
.uri(url)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(IgdbAccessToken.class)
.bodyToMono(TwitchOAuthTokenDto.class)
.block();
log.info("Successfully authenticated.");
}
public IgdbGame findGameByTitle(String title) {
public Game 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")
public Optional<Game> getGameById(Long id) {
byte[] gameBytes = igdbApiClient.post()
.uri("games.pb")
.bodyValue("fields *; where id = %d;".formatted(id))
.retrieve()
.bodyToMono(IgdbGame.class)
.block()
);
.bodyToMono(byte[].class)
.block();
try {
return Optional.ofNullable(Game.parseFrom(gameBytes));
} catch (InvalidProtocolBufferException e) {
return Optional.empty();
}
}
private void initIgdbClient() {
@@ -83,22 +91,21 @@ public class IgdbWrapper {
authenticate();
}
igdbApiClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(HttpClient.create().proxyWithSystemProperties()))
igdbApiClient = webclientBuilder
.baseUrl("https://api.igdb.com/v4/")
.defaultHeader("Client-ID", clientId)
.defaultHeader("Authorization", "Bearer %s".formatted(accessToken.getAccessToken()))
.build();
}
private Optional<IgdbGame> searchForGameByTitle(String searchTerm) {
List<IgdbGame> games = new ArrayList<>();
private Optional<Game> searchForGameByTitle(String searchTerm) {
List<Game> games = new ArrayList<>();
igdbApiClient.post()
.uri("games")
.uri("games.pb")
.bodyValue("fields *; search \"%s\";".formatted(searchTerm))
.retrieve()
.bodyToFlux(IgdbGame.class)
.bodyToFlux(Game.class)
.doOnNext(games::add)
.blockLast();
@@ -112,10 +119,10 @@ public class IgdbWrapper {
// 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
Optional<IgdbGame> srExactTitleMatch = games.stream().filter(s -> s.getName().equals(searchTerm)).findFirst();
Optional<Game> srExactTitleMatch = games.stream().filter(s -> s.getName().equals(searchTerm)).findFirst();
if (srExactTitleMatch.isPresent()) return srExactTitleMatch;
Optional<IgdbGame> srTitleEndsWithMatch = games.stream().filter(s -> s.getName().endsWith(searchTerm)).findFirst();
Optional<Game> srTitleEndsWithMatch = games.stream().filter(s -> s.getName().endsWith(searchTerm)).findFirst();
if (srTitleEndsWithMatch.isPresent()) return srTitleEndsWithMatch;
return Optional.of(games.get(0));
@@ -8,7 +8,7 @@ import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class IgdbAccessToken {
public class TwitchOAuthTokenDto {
private String accessToken;
private Long expiresIn;
private String tokenType;
@@ -1,10 +1,10 @@
package de.grimsi.gameyfin.rest;
import com.igdb.proto.Game;
import de.grimsi.gameyfin.dto.GameDto;
import de.grimsi.gameyfin.igdb.IgdbWrapper;
import de.grimsi.gameyfin.igdb.dto.IgdbGame;
import de.grimsi.gameyfin.service.FilesystemService;
import org.apache.commons.io.FilenameUtils;
import de.grimsi.gameyfin.util.ProtobufUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@@ -15,7 +15,6 @@ import org.springframework.web.server.ResponseStatusException;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Collectors;
@RestController
public class GameyfinDevController {
@@ -28,7 +27,7 @@ public class GameyfinDevController {
@GetMapping(value = "/dev/findGameByTitle/{title}", produces = MediaType.APPLICATION_JSON_VALUE)
public GameDto findGameByTitle(@PathVariable("title") String title) {
IgdbGame game;
Game game;
try {
game = igdbWrapper.findGameByTitle(title);
@@ -36,7 +35,10 @@ public class GameyfinDevController {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage());
}
return GameDto.builder().name(game.getName()).releaseDate(game.getFirstReleaseDate()).build();
return GameDto.builder()
.name(game.getName())
.releaseDate(ProtobufUtils.toInstant(game.getFirstReleaseDate()))
.build();
}
@GetMapping(value = "/dev/gameFiles", produces = MediaType.APPLICATION_JSON_VALUE)
@@ -48,7 +50,10 @@ public class GameyfinDevController {
public List<GameDto> getAllGames() {
return filesystemService.getGameFileNames().stream()
.map(t -> igdbWrapper.findGameByTitle(t))
.map(g -> GameDto.builder().name(g.getName()).releaseDate(g.getFirstReleaseDate()).build())
.map(g -> GameDto.builder()
.name(g.getName())
.releaseDate(ProtobufUtils.toInstant(g.getFirstReleaseDate()))
.build())
.toList();
}
}
@@ -0,0 +1,11 @@
package de.grimsi.gameyfin.util;
import com.google.protobuf.Timestamp;
import java.time.Instant;
public class ProtobufUtils {
public static Instant toInstant(Timestamp t) {
return Instant.ofEpochSecond(t.getSeconds(), t.getNanos());
}
}
+2
View File
@@ -1,5 +1,7 @@
gameyfin:
root: C:\Projects\privat\gameyfin-library
cache: C:\Projects\privat\gameyfin-library\.gameyfin
db: C:\Projects\privat\gameyfin-library\.gameyfin
igdb:
api:
client-id: 23l3l5qshx4dwjuao6yb8jyf1qrd08
+907
View File
@@ -0,0 +1,907 @@
syntax = "proto3";
package com.igdb.proto;
import "google/protobuf/timestamp.proto";
//option java_outer_classname = "Igdb";
option java_multiple_files = true; // Must be true because of private access in files.
option optimize_for = CODE_SIZE;
message Count {
int64 count = 1;
}
message MultiQueryResult {
string name = 1;
repeated bytes results = 2;
int64 count = 3;
}
message MultiQueryResultArray {
repeated MultiQueryResult result = 1;
}
message AgeRatingResult {
repeated AgeRating ageratings = 1;
}
message AgeRating {
uint64 id = 1;
AgeRatingCategoryEnum category = 2;
repeated AgeRatingContentDescription content_descriptions = 3;
AgeRatingRatingEnum rating = 4;
string rating_cover_url = 5;
string synopsis = 6;
string checksum = 7;
}
enum AgeRatingCategoryEnum {
AGERATING_CATEGORY_NULL = 0;
ESRB = 1;
PEGI = 2;
CERO = 3;
USK = 4;
GRAC = 5;
CLASS_IND = 6;
ACB = 7;
}
enum AgeRatingRatingEnum {
AGERATING_RATING_NULL = 0;
THREE = 1;
SEVEN = 2;
TWELVE = 3;
SIXTEEN = 4;
EIGHTEEN = 5;
RP = 6;
EC = 7;
E = 8;
E10 = 9;
T = 10;
M = 11;
AO = 12;
CERO_A = 13;
CERO_B = 14;
CERO_C = 15;
CERO_D = 16;
CERO_Z = 17;
USK_0 = 18;
USK_6 = 19;
USK_12 = 20;
USK_18 = 21;
GRAC_ALL = 22;
GRAC_TWELVE = 23;
GRAC_FIFTEEN = 24;
GRAC_EIGHTEEN = 25;
GRAC_TESTING = 26;
CLASS_IND_L = 27;
CLASS_IND_TEN = 28;
CLASS_IND_TWELVE = 29;
CLASS_IND_FOURTEEN = 30;
CLASS_IND_SIXTEEN = 31;
CLASS_IND_EIGHTEEN = 32;
ACB_G = 33;
ACB_PG = 34;
ACB_M = 35;
ACB_MA15 = 36;
ACB_R18 = 37;
ACB_RC = 38;
}
message AgeRatingContentDescriptionResult {
repeated AgeRatingContentDescription ageratingcontentdescriptions = 1;
}
message AgeRatingContentDescription {
uint64 id = 1;
AgeRatingRatingEnum category = 2;
string description = 3;
string checksum = 4;
}
message AlternativeNameResult {
repeated AlternativeName alternativenames = 1;
}
message AlternativeName {
uint64 id = 1;
string comment = 2;
Game game = 3;
string name = 4;
string checksum = 5;
}
message ArtworkResult {
repeated Artwork artworks = 1;
}
message Artwork {
uint64 id = 1;
bool alpha_channel = 2;
bool animated = 3;
Game game = 4;
int32 height = 5;
string image_id = 6;
string url = 7;
int32 width = 8;
string checksum = 9;
}
message CharacterResult {
repeated Character characters = 1;
}
message Character {
uint64 id = 1;
repeated string akas = 2;
string country_name = 3;
google.protobuf.Timestamp created_at = 4;
string description = 5;
repeated Game games = 6;
GenderGenderEnum gender = 7;
CharacterMugShot mug_shot = 8;
string name = 9;
string slug = 10;
CharacterSpeciesEnum species = 11;
google.protobuf.Timestamp updated_at = 12;
string url = 13;
string checksum = 14;
}
enum GenderGenderEnum {
MALE = 0;
FEMALE = 1;
OTHER = 2;
}
enum CharacterSpeciesEnum {
CHARACTER_SPECIES_NULL = 0;
HUMAN = 1;
ALIEN = 2;
ANIMAL = 3;
ANDROID = 4;
UNKNOWN = 5;
}
message CharacterMugShotResult {
repeated CharacterMugShot charactermugshots = 1;
}
message CharacterMugShot {
uint64 id = 1;
bool alpha_channel = 2;
bool animated = 3;
int32 height = 4;
string image_id = 5;
string url = 6;
int32 width = 7;
string checksum = 8;
}
message CollectionResult {
repeated Collection collections = 1;
}
message Collection {
uint64 id = 1;
google.protobuf.Timestamp created_at = 2;
repeated Game games = 3;
string name = 4;
string slug = 5;
google.protobuf.Timestamp updated_at = 6;
string url = 7;
string checksum = 8;
}
message CompanyResult {
repeated Company companies = 1;
}
message Company {
uint64 id = 1;
google.protobuf.Timestamp change_date = 2;
DateFormatChangeDateCategoryEnum change_date_category = 3;
Company changed_company_id = 4;
int32 country = 5;
google.protobuf.Timestamp created_at = 6;
string description = 7;
repeated Game developed = 8;
CompanyLogo logo = 9;
string name = 10;
Company parent = 11;
repeated Game published = 12;
string slug = 13;
google.protobuf.Timestamp start_date = 14;
DateFormatChangeDateCategoryEnum start_date_category = 15;
google.protobuf.Timestamp updated_at = 16;
string url = 17;
repeated CompanyWebsite websites = 18;
string checksum = 19;
}
enum DateFormatChangeDateCategoryEnum {
YYYYMMMMDD = 0;
YYYYMMMM = 1;
YYYY = 2;
YYYYQ1 = 3;
YYYYQ2 = 4;
YYYYQ3 = 5;
YYYYQ4 = 6;
TBD = 7;
}
message CompanyLogoResult {
repeated CompanyLogo companylogos = 1;
}
message CompanyLogo {
uint64 id = 1;
bool alpha_channel = 2;
bool animated = 3;
int32 height = 4;
string image_id = 5;
string url = 6;
int32 width = 7;
string checksum = 8;
}
message CompanyWebsiteResult {
repeated CompanyWebsite companywebsites = 1;
}
message CompanyWebsite {
uint64 id = 1;
WebsiteCategoryEnum category = 2;
bool trusted = 3;
string url = 4;
string checksum = 5;
}
enum WebsiteCategoryEnum {
WEBSITE_CATEGORY_NULL = 0;
WEBSITE_OFFICIAL = 1;
WEBSITE_WIKIA = 2;
WEBSITE_WIKIPEDIA = 3;
WEBSITE_FACEBOOK = 4;
WEBSITE_TWITTER = 5;
WEBSITE_TWITCH = 6;
WEBSITE_INSTAGRAM = 8;
WEBSITE_YOUTUBE = 9;
WEBSITE_IPHONE = 10;
WEBSITE_IPAD = 11;
WEBSITE_ANDROID = 12;
WEBSITE_STEAM = 13;
WEBSITE_REDDIT = 14;
WEBSITE_ITCH = 15;
WEBSITE_EPICGAMES = 16;
WEBSITE_GOG = 17;
WEBSITE_DISCORD = 18;
}
message CoverResult {
repeated Cover covers = 1;
}
message Cover {
uint64 id = 1;
bool alpha_channel = 2;
bool animated = 3;
Game game = 4;
int32 height = 5;
string image_id = 6;
string url = 7;
int32 width = 8;
string checksum = 9;
}
message ExternalGameResult {
repeated ExternalGame externalgames = 1;
}
message ExternalGame {
uint64 id = 1;
ExternalGameCategoryEnum category = 2;
google.protobuf.Timestamp created_at = 3;
Game game = 4;
string name = 5;
string uid = 6;
google.protobuf.Timestamp updated_at = 7;
string url = 8;
int32 year = 9;
ExternalGameMediaEnum media = 10;
Platform platform = 11;
repeated int32 countries = 12;
string checksum = 13;
}
enum ExternalGameCategoryEnum {
EXTERNALGAME_CATEGORY_NULL = 0;
EXTERNALGAME_STEAM = 1;
EXTERNALGAME_GOG = 5;
EXTERNALGAME_YOUTUBE = 10;
EXTERNALGAME_MICROSOFT = 11;
EXTERNALGAME_APPLE = 13;
EXTERNALGAME_TWITCH = 14;
EXTERNALGAME_ANDROID = 15;
EXTERNALGAME_AMAZON_ASIN = 20;
EXTERNALGAME_AMAZON_LUNA = 22;
EXTERNALGAME_AMAZON_ADG = 23;
EXTERNALGAME_EPIC_GAME_STORE = 26;
EXTERNALGAME_OCULUS = 28;
}
enum ExternalGameMediaEnum {
EXTERNALGAME_MEDIA_NULL = 0;
EXTERNALGAME_DIGITAL = 1;
EXTERNALGAME_PHYSICAL = 2;
}
message FranchiseResult {
repeated Franchise franchises = 1;
}
message Franchise {
uint64 id = 1;
google.protobuf.Timestamp created_at = 2;
repeated Game games = 3;
string name = 4;
string slug = 5;
google.protobuf.Timestamp updated_at = 6;
string url = 7;
string checksum = 8;
}
message GameResult {
repeated Game games = 1;
}
message Game {
uint64 id = 1;
repeated AgeRating age_ratings = 2;
double aggregated_rating = 3;
int32 aggregated_rating_count = 4;
repeated AlternativeName alternative_names = 5;
repeated Artwork artworks = 6;
repeated Game bundles = 7;
GameCategoryEnum category = 8;
Collection collection = 9;
Cover cover = 10;
google.protobuf.Timestamp created_at = 11;
repeated Game dlcs = 12;
repeated Game expansions = 13;
repeated ExternalGame external_games = 14;
google.protobuf.Timestamp first_release_date = 15;
int32 follows = 16;
Franchise franchise = 17;
repeated Franchise franchises = 18;
repeated GameEngine game_engines = 19;
repeated GameMode game_modes = 20;
repeated Genre genres = 21;
int32 hypes = 22;
repeated InvolvedCompany involved_companies = 23;
repeated Keyword keywords = 24;
repeated MultiplayerMode multiplayer_modes = 25;
string name = 26;
Game parent_game = 27;
repeated Platform platforms = 28;
repeated PlayerPerspective player_perspectives = 29;
double rating = 30;
int32 rating_count = 31;
repeated ReleaseDate release_dates = 32;
repeated Screenshot screenshots = 33;
repeated Game similar_games = 34;
string slug = 35;
repeated Game standalone_expansions = 36;
GameStatusEnum status = 37;
string storyline = 38;
string summary = 39;
repeated int32 tags = 40;
repeated Theme themes = 41;
double total_rating = 42;
int32 total_rating_count = 43;
google.protobuf.Timestamp updated_at = 44;
string url = 45;
Game version_parent = 46;
string version_title = 47;
repeated GameVideo videos = 48;
repeated Website websites = 49;
string checksum = 50;
repeated Game remakes = 51;
repeated Game remasters = 52;
repeated Game expanded_games = 53;
repeated Game ports = 54;
repeated Game forks = 55;
}
enum GameCategoryEnum {
MAIN_GAME = 0;
DLC_ADDON = 1;
EXPANSION = 2;
BUNDLE = 3;
STANDALONE_EXPANSION = 4;
MOD = 5;
EPISODE = 6;
SEASON = 7;
REMAKE = 8;
REMASTER = 9;
EXPANDED_GAME = 10;
PORT = 11;
FORK = 12;
}
enum GameStatusEnum {
RELEASED = 0;
ALPHA = 2;
BETA = 3;
EARLY_ACCESS = 4;
OFFLINE = 5;
CANCELLED = 6;
RUMORED = 7;
DELISTED = 8;
}
message GameEngineResult {
repeated GameEngine gameengines = 1;
}
message GameEngine {
uint64 id = 1;
repeated Company companies = 2;
google.protobuf.Timestamp created_at = 3;
string description = 4;
GameEngineLogo logo = 5;
string name = 6;
repeated Platform platforms = 7;
string slug = 8;
google.protobuf.Timestamp updated_at = 9;
string url = 10;
string checksum = 11;
}
message GameEngineLogoResult {
repeated GameEngineLogo gameenginelogos = 1;
}
message GameEngineLogo {
uint64 id = 1;
bool alpha_channel = 2;
bool animated = 3;
int32 height = 4;
string image_id = 5;
string url = 6;
int32 width = 7;
string checksum = 8;
}
message GameModeResult {
repeated GameMode gamemodes = 1;
}
message GameMode {
uint64 id = 1;
google.protobuf.Timestamp created_at = 2;
string name = 3;
string slug = 4;
google.protobuf.Timestamp updated_at = 5;
string url = 6;
string checksum = 7;
}
message GameVersionResult {
repeated GameVersion gameversions = 1;
}
message GameVersion {
uint64 id = 1;
google.protobuf.Timestamp created_at = 2;
repeated GameVersionFeature features = 3;
Game game = 4;
repeated Game games = 5;
google.protobuf.Timestamp updated_at = 6;
string url = 7;
string checksum = 8;
}
message GameVersionFeatureResult {
repeated GameVersionFeature gameversionfeatures = 1;
}
message GameVersionFeature {
uint64 id = 1;
GameVersionFeatureCategoryEnum category = 2;
string description = 3;
int32 position = 4;
string title = 5;
repeated GameVersionFeatureValue values = 6;
string checksum = 7;
}
enum GameVersionFeatureCategoryEnum {
BOOLEAN = 0;
DESCRIPTION = 1;
}
message GameVersionFeatureValueResult {
repeated GameVersionFeatureValue gameversionfeaturevalues = 1;
}
message GameVersionFeatureValue {
uint64 id = 1;
Game game = 2;
GameVersionFeature game_feature = 3;
GameVersionFeatureValueIncludedFeatureEnum included_feature = 4;
string note = 5;
string checksum = 6;
}
enum GameVersionFeatureValueIncludedFeatureEnum {
NOT_INCLUDED = 0;
INCLUDED = 1;
PRE_ORDER_ONLY = 2;
}
message GameVideoResult {
repeated GameVideo gamevideos = 1;
}
message GameVideo {
uint64 id = 1;
Game game = 2;
string name = 3;
string video_id = 4;
string checksum = 5;
}
message GenreResult {
repeated Genre genres = 1;
}
message Genre {
uint64 id = 1;
google.protobuf.Timestamp created_at = 2;
string name = 3;
string slug = 4;
google.protobuf.Timestamp updated_at = 5;
string url = 6;
string checksum = 7;
}
message InvolvedCompanyResult {
repeated InvolvedCompany involvedcompanies = 1;
}
message InvolvedCompany {
uint64 id = 1;
Company company = 2;
google.protobuf.Timestamp created_at = 3;
bool developer = 4;
Game game = 5;
bool porting = 6;
bool publisher = 7;
bool supporting = 8;
google.protobuf.Timestamp updated_at = 9;
string checksum = 10;
}
message KeywordResult {
repeated Keyword keywords = 1;
}
message Keyword {
uint64 id = 1;
google.protobuf.Timestamp created_at = 2;
string name = 3;
string slug = 4;
google.protobuf.Timestamp updated_at = 5;
string url = 6;
string checksum = 7;
}
message MultiplayerModeResult {
repeated MultiplayerMode multiplayermodes = 1;
}
message MultiplayerMode {
uint64 id = 1;
bool campaigncoop = 2;
bool dropin = 3;
Game game = 4;
bool lancoop = 5;
bool offlinecoop = 6;
int32 offlinecoopmax = 7;
int32 offlinemax = 8;
bool onlinecoop = 9;
int32 onlinecoopmax = 10;
int32 onlinemax = 11;
Platform platform = 12;
bool splitscreen = 13;
bool splitscreenonline = 14;
string checksum = 15;
}
message PlatformResult {
repeated Platform platforms = 1;
}
message Platform {
uint64 id = 1;
string abbreviation = 2;
string alternative_name = 3;
PlatformCategoryEnum category = 4;
google.protobuf.Timestamp created_at = 5;
int32 generation = 6;
string name = 7;
PlatformLogo platform_logo = 8;
PlatformFamily platform_family = 9;
string slug = 10;
string summary = 11;
google.protobuf.Timestamp updated_at = 12;
string url = 13;
repeated PlatformVersion versions = 14;
repeated PlatformWebsite websites = 15;
string checksum = 16;
}
enum PlatformCategoryEnum {
PLATFORM_CATEGORY_NULL = 0;
CONSOLE = 1;
ARCADE = 2;
PLATFORM = 3;
OPERATING_SYSTEM = 4;
PORTABLE_CONSOLE = 5;
COMPUTER = 6;
}
message PlatformFamilyResult {
repeated PlatformFamily platformfamilies = 1;
}
message PlatformFamily {
uint64 id = 1;
string name = 2;
string slug = 3;
string checksum = 4;
}
message PlatformLogoResult {
repeated PlatformLogo platformlogos = 1;
}
message PlatformLogo {
uint64 id = 1;
bool alpha_channel = 2;
bool animated = 3;
int32 height = 4;
string image_id = 5;
string url = 6;
int32 width = 7;
string checksum = 8;
}
message PlatformVersionResult {
repeated PlatformVersion platformversions = 1;
}
message PlatformVersion {
uint64 id = 1;
repeated PlatformVersionCompany companies = 2;
string connectivity = 3;
string cpu = 4;
string graphics = 5;
PlatformVersionCompany main_manufacturer = 6;
string media = 7;
string memory = 8;
string name = 9;
string online = 10;
string os = 11;
string output = 12;
PlatformLogo platform_logo = 13;
repeated PlatformVersionReleaseDate platform_version_release_dates = 14;
string resolutions = 15;
string slug = 16;
string sound = 17;
string storage = 18;
string summary = 19;
string url = 20;
string checksum = 21;
}
message PlatformVersionCompanyResult {
repeated PlatformVersionCompany platformversioncompanies = 1;
}
message PlatformVersionCompany {
uint64 id = 1;
string comment = 2;
Company company = 3;
bool developer = 4;
bool manufacturer = 5;
string checksum = 6;
}
message PlatformVersionReleaseDateResult {
repeated PlatformVersionReleaseDate platformversionreleasedates = 1;
}
message PlatformVersionReleaseDate {
uint64 id = 1;
DateFormatChangeDateCategoryEnum category = 2;
google.protobuf.Timestamp created_at = 3;
google.protobuf.Timestamp date = 4;
string human = 5;
int32 m = 6;
PlatformVersion platform_version = 7;
RegionRegionEnum region = 8;
google.protobuf.Timestamp updated_at = 9;
int32 y = 10;
string checksum = 11;
}
enum RegionRegionEnum {
REGION_REGION_NULL = 0;
EUROPE = 1;
NORTH_AMERICA = 2;
AUSTRALIA = 3;
NEW_ZEALAND = 4;
JAPAN = 5;
CHINA = 6;
ASIA = 7;
WORLDWIDE = 8;
KOREA = 9;
BRAZIL = 10;
}
message PlatformWebsiteResult {
repeated PlatformWebsite platformwebsites = 1;
}
message PlatformWebsite {
uint64 id = 1;
WebsiteCategoryEnum category = 2;
bool trusted = 3;
string url = 4;
string checksum = 5;
}
message PlayerPerspectiveResult {
repeated PlayerPerspective playerperspectives = 1;
}
message PlayerPerspective {
uint64 id = 1;
google.protobuf.Timestamp created_at = 2;
string name = 3;
string slug = 4;
google.protobuf.Timestamp updated_at = 5;
string url = 6;
string checksum = 7;
}
message ReleaseDateResult {
repeated ReleaseDate releasedates = 1;
}
message ReleaseDate {
uint64 id = 1;
DateFormatChangeDateCategoryEnum category = 2;
google.protobuf.Timestamp created_at = 3;
google.protobuf.Timestamp date = 4;
Game game = 5;
string human = 6;
int32 m = 7;
Platform platform = 8;
RegionRegionEnum region = 9;
google.protobuf.Timestamp updated_at = 10;
int32 y = 11;
string checksum = 12;
}
message ScreenshotResult {
repeated Screenshot screenshots = 1;
}
message Screenshot {
uint64 id = 1;
bool alpha_channel = 2;
bool animated = 3;
Game game = 4;
int32 height = 5;
string image_id = 6;
string url = 7;
int32 width = 8;
string checksum = 9;
}
message SearchResult {
repeated Search searches = 1;
}
message Search {
uint64 id = 1;
string alternative_name = 2;
Character character = 3;
Collection collection = 4;
Company company = 5;
string description = 6;
Game game = 7;
string name = 8;
Platform platform = 9;
google.protobuf.Timestamp published_at = 10;
TestDummy test_dummy = 11;
Theme theme = 12;
string checksum = 13;
}
message TestDummyResult {
repeated TestDummy testdummies = 1;
}
message TestDummy {
uint64 id = 1;
bool bool_value = 2;
google.protobuf.Timestamp created_at = 3;
TestDummyEnumTestEnum enum_test = 4;
double float_value = 5;
Game game = 6;
repeated int32 integer_array = 7;
int32 integer_value = 8;
string name = 9;
int32 new_integer_value = 10;
bool private = 11;
string slug = 12;
repeated string string_array = 13;
repeated TestDummy test_dummies = 14;
TestDummy test_dummy = 15;
google.protobuf.Timestamp updated_at = 16;
string url = 17;
string checksum = 18;
}
enum TestDummyEnumTestEnum {
TESTDUMMY_ENUM_TEST_NULL = 0;
ENUM1 = 1;
ENUM2 = 2;
}
message ThemeResult {
repeated Theme themes = 1;
}
message Theme {
uint64 id = 1;
google.protobuf.Timestamp created_at = 2;
string name = 3;
string slug = 4;
google.protobuf.Timestamp updated_at = 5;
string url = 6;
string checksum = 7;
}
message WebsiteResult {
repeated Website websites = 1;
}
message Website {
uint64 id = 1;
WebsiteCategoryEnum category = 2;
Game game = 3;
bool trusted = 4;
string url = 5;
string checksum = 6;
}