diff --git a/.run/Rebuild all.run.xml b/.run/Rebuild all.run.xml
index 15b4c09..7e8ed26 100644
--- a/.run/Rebuild all.run.xml
+++ b/.run/Rebuild all.run.xml
@@ -10,6 +10,7 @@
diff --git a/.run/Rebuild plugins.run.xml b/.run/Rebuild plugins.run.xml
index ecc2410..caef427 100644
--- a/.run/Rebuild plugins.run.xml
+++ b/.run/Rebuild plugins.run.xml
@@ -10,6 +10,7 @@
+
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/management/GameyfinPluginManager.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/management/GameyfinPluginManager.kt
index a57906c..6f5b313 100644
--- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/management/GameyfinPluginManager.kt
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/management/GameyfinPluginManager.kt
@@ -94,19 +94,15 @@ class GameyfinPluginManager(
}
try {
- log.info { "${"Start plugin '{}'"} ${getPluginLabel(pluginWrapper.descriptor)}"}
+ log.info { "Start plugin '${getPluginLabel(pluginWrapper.descriptor)}'"}
pluginWrapper.plugin.start()
pluginWrapper.pluginState = PluginState.STARTED
pluginWrapper.failedException = null
startedPlugins.add(pluginWrapper)
- } catch (e: LinkageError) {
- pluginWrapper.pluginState = PluginState.FAILED
- pluginWrapper.failedException = e
- log.error { "${"Unable to start plugin '{}'"} ${getPluginLabel(pluginWrapper.descriptor)} $e"}
} catch (e: Exception) {
pluginWrapper.pluginState = PluginState.FAILED
pluginWrapper.failedException = e
- log.error { "${"Unable to start plugin '{}'"} ${getPluginLabel(pluginWrapper.descriptor)} $e"}
+ log.error { "Unable to start plugin '${getPluginLabel(pluginWrapper.descriptor)}': $e"}
} finally {
firePluginStateEvent(PluginStateEvent(this, pluginWrapper, pluginState))
}
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/management/PluginManagementEndpoint.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/management/PluginManagementEndpoint.kt
index 4b26d02..294f47e 100644
--- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/management/PluginManagementEndpoint.kt
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/management/PluginManagementEndpoint.kt
@@ -9,9 +9,9 @@ import jakarta.annotation.security.RolesAllowed
class PluginManagementEndpoint(
private val pluginManagementService: PluginManagementService
) {
- fun getPlugins() = pluginManagementService.getPlugins()
+ fun getPlugins() = pluginManagementService.getPluginDtos()
- fun getPlugin(pluginId: String) = pluginManagementService.getPlugin(pluginId)
+ fun getPlugin(pluginId: String) = pluginManagementService.getPluginDto(pluginId)
fun startPlugin(pluginId: String) = pluginManagementService.startPlugin(pluginId)
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/management/PluginManagementService.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/management/PluginManagementService.kt
index b0ed58e..f28cf45 100644
--- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/management/PluginManagementService.kt
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/management/PluginManagementService.kt
@@ -1,12 +1,15 @@
package de.grimsi.gameyfin.core.plugins.management
+import org.pf4j.ExtensionPoint
+import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
@Service
class PluginManagementService(
- private val pluginManager: GameyfinPluginManager
+ private val pluginManager: GameyfinPluginManager,
+ private val pluginManagementRepository: PluginManagementRepository
) {
- fun getPlugins(): List {
+ fun getPluginDtos(): List {
return pluginManager.plugins.map {
PluginDto(
it.pluginId,
@@ -18,7 +21,7 @@ class PluginManagementService(
}
}
- fun getPlugin(pluginId: String): PluginDto {
+ fun getPluginDto(pluginId: String): PluginDto {
val plugin = pluginManager.getPlugin(pluginId)
return PluginDto(
plugin.pluginId,
@@ -29,6 +32,17 @@ class PluginManagementService(
)
}
+ fun getPluginManagementEntry(pluginId: String): PluginManagementEntry {
+ return pluginManagementRepository.findByIdOrNull(pluginId)
+ ?: throw IllegalArgumentException("Plugin with ID $pluginId not found")
+ }
+
+ fun getPluginManagementEntry(clazz: Class): PluginManagementEntry {
+ val pluginWrapper = pluginManager.whichPlugin(clazz)
+ return pluginManagementRepository.findByIdOrNull(pluginWrapper.pluginId)
+ ?: throw IllegalArgumentException("Plugin with class $clazz not found")
+ }
+
fun startPlugin(pluginId: String) {
pluginManager.startPlugin(pluginId)
}
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/Game.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/Game.kt
deleted file mode 100644
index b8781e5..0000000
--- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/Game.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package de.grimsi.gameyfin.games
-
-import jakarta.persistence.*
-import java.time.Instant
-
-@Entity
-class Game(
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO)
- var id: Long? = null,
-
- val title: String,
-
- @Lob
- @Column(columnDefinition = "CLOB")
- val comment: String? = null,
-
- @Lob
- @Column(columnDefinition = "CLOB")
- val summary: String,
-
- val release: Instant,
-
- @ElementCollection
- val publishers: List,
-
- @ElementCollection
- val developers: List,
-
- @Column(unique = true)
- val path: String,
-
- val source: String
-)
\ No newline at end of file
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/GameService.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/GameService.kt
index 93b47bb..126ccbf 100644
--- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/GameService.kt
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/GameService.kt
@@ -1,5 +1,14 @@
package de.grimsi.gameyfin.games
+import de.grimsi.gameyfin.core.plugins.management.PluginManagementService
+import de.grimsi.gameyfin.games.entities.Company
+import de.grimsi.gameyfin.games.entities.CompanyType
+import de.grimsi.gameyfin.games.entities.Game
+import de.grimsi.gameyfin.games.entities.Screenshot
+import de.grimsi.gameyfin.games.repositories.CompanyRepository
+import de.grimsi.gameyfin.games.repositories.GameRepository
+import de.grimsi.gameyfin.games.repositories.ScreenshotContentStore
+import de.grimsi.gameyfin.games.repositories.ScreenshotRepository
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadata
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadataProvider
import io.github.oshai.kotlinlogging.KotlinLogging
@@ -9,12 +18,18 @@ import kotlinx.coroutines.runBlocking
import org.pf4j.PluginManager
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
+import java.net.URL
+import java.net.URLConnection
import java.nio.file.Path
@Service
class GameService(
+ private val pluginManager: PluginManager,
+ private val pluginManagementService: PluginManagementService,
private val gameRepository: GameRepository,
- private val pluginManager: PluginManager
+ private val companyRepository: CompanyRepository,
+ private val screenshotRepository: ScreenshotRepository,
+ private val screenshotContentStore: ScreenshotContentStore
) {
private val log = KotlinLogging.logger {}
@@ -47,15 +62,11 @@ class GameService(
val (plugin, metadata) = metadataResults.entries.firstOrNull { it.value != null }
?: throw NoMatchException("Could not match game at $path")
- val game = Game(
- title = metadata!!.title,
- summary = metadata.description,
- release = metadata.release,
- publishers = metadata.publishedBy,
- developers = metadata.developedBy,
- path = path.toString(),
- source = plugin.javaClass.name
- )
+ if (metadata == null) {
+ throw NoMatchException("Plugin ${plugin.javaClass} returned invalid metadata for game at $path")
+ }
+
+ val game = toEntity(metadata, path, plugin)
return createOrUpdate(game)
}
@@ -74,13 +85,48 @@ class GameService(
}
private fun toDto(game: Game): GameDto {
- if (game.id == null) {
- throw IllegalArgumentException("Game ID is null")
- }
+ val gameId = game.id ?: throw IllegalArgumentException("Game ID is null")
return GameDto(
- id = game.id!!,
+ id = gameId,
title = game.title
)
}
+
+ private fun toEntity(metadata: GameMetadata, path: Path, source: GameMetadataProvider): Game {
+ return Game(
+ title = metadata.title,
+ summary = metadata.description,
+ release = metadata.release,
+ publishers = metadata.publishedBy.map { toEntity(it, CompanyType.PUBLISHER) }.toSet(),
+ developers = metadata.developedBy.map { toEntity(it, CompanyType.DEVELOPER) }.toSet(),
+ genres = metadata.genres,
+ themes = metadata.themes,
+ keywords = metadata.keywords,
+ features = metadata.features,
+ perspectives = metadata.perspectives,
+ screenshots = metadata.screenshotUrls.map { downloadAndPersist(it) }.toSet(),
+ videoUrls = metadata.videoUrls,
+ path = path.toString(),
+ source = pluginManagementService.getPluginManagementEntry(source.javaClass)
+ )
+ }
+
+ private fun toEntity(companyName: String, companyType: CompanyType): Company {
+ companyRepository.findByNameAndType(companyName, companyType)?.let { return it }
+ val company = Company(name = companyName, type = companyType)
+ return companyRepository.save(company)
+ }
+
+ private fun downloadAndPersist(screenshotUrl: URL): Screenshot {
+ screenshotRepository.findByOriginalUrl(screenshotUrl)?.let { return it }
+
+ val screenshot = Screenshot(originalUrl = screenshotUrl)
+ screenshotUrl.openStream().use { input ->
+ val mimeType = URLConnection.guessContentTypeFromStream(input)
+ screenshot.mimeType = mimeType
+ screenshotContentStore.setContent(screenshot, input)
+ }
+ return screenshotRepository.save(screenshot)
+ }
}
\ No newline at end of file
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/entities/Company.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/entities/Company.kt
new file mode 100644
index 0000000..aba58bf
--- /dev/null
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/entities/Company.kt
@@ -0,0 +1,18 @@
+package de.grimsi.gameyfin.games.entities
+
+import jakarta.persistence.*
+
+@Entity
+@Table(uniqueConstraints = [UniqueConstraint(columnNames = ["name", "type"])])
+class Company(
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ var id: Long? = null,
+ val name: String,
+ val type: CompanyType
+)
+
+enum class CompanyType {
+ DEVELOPER,
+ PUBLISHER
+}
\ No newline at end of file
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/entities/Game.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/entities/Game.kt
new file mode 100644
index 0000000..f59c7d1
--- /dev/null
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/entities/Game.kt
@@ -0,0 +1,62 @@
+package de.grimsi.gameyfin.games.entities
+
+import de.grimsi.gameyfin.core.plugins.management.PluginManagementEntry
+import de.grimsi.gameyfin.pluginapi.gamemetadata.GameFeature
+import de.grimsi.gameyfin.pluginapi.gamemetadata.Genre
+import de.grimsi.gameyfin.pluginapi.gamemetadata.PlayerPerspective
+import de.grimsi.gameyfin.pluginapi.gamemetadata.Theme
+import jakarta.persistence.*
+import java.net.URL
+import java.time.Instant
+
+@Entity
+class Game(
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ var id: Long? = null,
+
+ val title: String,
+
+ @Lob
+ @Column(columnDefinition = "CLOB")
+ val comment: String? = null,
+
+ @Lob
+ @Column(columnDefinition = "CLOB")
+ val summary: String,
+
+ val release: Instant,
+
+ @OneToMany(cascade = [CascadeType.MERGE])
+ val publishers: Set,
+
+ @OneToMany(cascade = [CascadeType.MERGE])
+ val developers: Set,
+
+ @ElementCollection
+ val genres: Set,
+
+ @ElementCollection
+ val themes: Set,
+
+ @ElementCollection
+ val keywords: Set,
+
+ @ElementCollection
+ val features: Set,
+
+ @ElementCollection
+ val perspectives: Set,
+
+ @OneToMany(cascade = [CascadeType.MERGE])
+ val screenshots: Set,
+
+ @ElementCollection
+ val videoUrls: Set,
+
+ @Column(unique = true)
+ val path: String,
+
+ @ManyToOne
+ val source: PluginManagementEntry
+)
\ No newline at end of file
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/entities/Screenshot.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/entities/Screenshot.kt
new file mode 100644
index 0000000..c85ee2a
--- /dev/null
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/entities/Screenshot.kt
@@ -0,0 +1,32 @@
+package de.grimsi.gameyfin.games.entities
+
+import jakarta.annotation.Nullable
+import jakarta.persistence.Entity
+import jakarta.persistence.GeneratedValue
+import jakarta.persistence.GenerationType
+import jakarta.persistence.Id
+import org.springframework.content.commons.annotations.ContentId
+import org.springframework.content.commons.annotations.ContentLength
+import org.springframework.content.commons.annotations.MimeType
+import java.net.URL
+
+@Entity
+class Screenshot(
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ var id: Long? = null,
+
+ val originalUrl: URL,
+
+ @ContentId
+ @Nullable
+ var contentId: String? = null,
+
+ @ContentLength
+ @Nullable
+ var contentLength: Long? = null,
+
+ @MimeType
+ @Nullable
+ var mimeType: String? = null
+)
\ No newline at end of file
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/entities/Video.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/entities/Video.kt
new file mode 100644
index 0000000..d9d1d7d
--- /dev/null
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/entities/Video.kt
@@ -0,0 +1,4 @@
+package de.grimsi.gameyfin.games.entities
+
+class Video {
+}
\ No newline at end of file
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/repositories/CompanyRepository.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/repositories/CompanyRepository.kt
new file mode 100644
index 0000000..e0325a0
--- /dev/null
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/repositories/CompanyRepository.kt
@@ -0,0 +1,9 @@
+package de.grimsi.gameyfin.games.repositories
+
+import de.grimsi.gameyfin.games.entities.Company
+import de.grimsi.gameyfin.games.entities.CompanyType
+import org.springframework.data.jpa.repository.JpaRepository
+
+interface CompanyRepository : JpaRepository {
+ fun findByNameAndType(name: String, type: CompanyType): Company?
+}
\ No newline at end of file
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/GameRepository.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/repositories/GameRepository.kt
similarity index 62%
rename from gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/GameRepository.kt
rename to gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/repositories/GameRepository.kt
index 67257b2..91dc88d 100644
--- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/GameRepository.kt
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/repositories/GameRepository.kt
@@ -1,5 +1,6 @@
-package de.grimsi.gameyfin.games
+package de.grimsi.gameyfin.games.repositories
+import de.grimsi.gameyfin.games.entities.Game
import org.springframework.data.jpa.repository.JpaRepository
interface GameRepository : JpaRepository {
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/repositories/ScreenshotContentStore.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/repositories/ScreenshotContentStore.kt
new file mode 100644
index 0000000..f1e2ced
--- /dev/null
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/repositories/ScreenshotContentStore.kt
@@ -0,0 +1,8 @@
+package de.grimsi.gameyfin.games.repositories
+
+import de.grimsi.gameyfin.games.entities.Screenshot
+import org.springframework.content.commons.store.ContentStore
+import org.springframework.stereotype.Repository
+
+@Repository
+interface ScreenshotContentStore : ContentStore
\ No newline at end of file
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/repositories/ScreenshotRepository.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/repositories/ScreenshotRepository.kt
new file mode 100644
index 0000000..132b94b
--- /dev/null
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/repositories/ScreenshotRepository.kt
@@ -0,0 +1,9 @@
+package de.grimsi.gameyfin.games.repositories
+
+import de.grimsi.gameyfin.games.entities.Screenshot
+import org.springframework.data.jpa.repository.JpaRepository
+import java.net.URL
+
+interface ScreenshotRepository : JpaRepository {
+ fun findByOriginalUrl(originalUrl: URL): Screenshot?
+}
\ No newline at end of file
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/libraries/Library.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/libraries/Library.kt
index ef4b173..8ef3407 100644
--- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/libraries/Library.kt
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/libraries/Library.kt
@@ -1,6 +1,6 @@
package de.grimsi.gameyfin.libraries
-import de.grimsi.gameyfin.games.Game
+import de.grimsi.gameyfin.games.entities.Game
import jakarta.persistence.*
@Entity
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/libraries/LibraryEndpoint.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/libraries/LibraryEndpoint.kt
index e65a40d..d05e2d1 100644
--- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/libraries/LibraryEndpoint.kt
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/libraries/LibraryEndpoint.kt
@@ -2,7 +2,7 @@ package de.grimsi.gameyfin.libraries
import com.vaadin.hilla.Endpoint
import de.grimsi.gameyfin.core.Role
-import de.grimsi.gameyfin.games.Game
+import de.grimsi.gameyfin.games.entities.Game
import jakarta.annotation.security.RolesAllowed
@Endpoint
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/libraries/LibraryService.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/libraries/LibraryService.kt
index 7e0b38a..4232b58 100644
--- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/libraries/LibraryService.kt
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/libraries/LibraryService.kt
@@ -2,8 +2,8 @@ package de.grimsi.gameyfin.libraries
import de.grimsi.gameyfin.config.ConfigProperties
import de.grimsi.gameyfin.config.ConfigService
-import de.grimsi.gameyfin.games.Game
import de.grimsi.gameyfin.games.GameService
+import de.grimsi.gameyfin.games.entities.Game
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import java.nio.file.Path
diff --git a/plugin-api/src/main/kotlin/de/grimsi/gameyfin/pluginapi/gamemetadata/GameMetadata.kt b/plugin-api/src/main/kotlin/de/grimsi/gameyfin/pluginapi/gamemetadata/GameMetadata.kt
index a0432d3..c389173 100644
--- a/plugin-api/src/main/kotlin/de/grimsi/gameyfin/pluginapi/gamemetadata/GameMetadata.kt
+++ b/plugin-api/src/main/kotlin/de/grimsi/gameyfin/pluginapi/gamemetadata/GameMetadata.kt
@@ -9,14 +9,15 @@ class GameMetadata(
val release: Instant,
val userRating: Int?,
val criticRating: Int?,
- val developedBy: List,
- val publishedBy: List,
- val genres: List,
- val themes: List,
- val screenshotUrls: List,
- val videoUrls: List,
- val features: List,
- val perspectives: List
+ val developedBy: Set,
+ val publishedBy: Set,
+ val genres: Set,
+ val themes: Set,
+ val keywords: Set,
+ val screenshotUrls: Set,
+ val videoUrls: Set,
+ val features: Set,
+ val perspectives: Set
)
enum class Genre {
@@ -95,10 +96,12 @@ enum class GameFeature {
ONLINE_PVE,
LOCAL_PVP,
LOCAL_PVE,
- CROSSPLAY
+ CROSSPLAY,
+ SPLITSCREEN
}
enum class PlayerPerspective {
+ UNKNOWN,
FIRST_PERSON,
THIRD_PERSON,
BIRD_VIEW_ISOMETRIC,
diff --git a/plugins/igdb/src/main/kotlin/de/grimsi/gameyfin/plugins/igdb/IgdbPlugin.kt b/plugins/igdb/src/main/kotlin/de/grimsi/gameyfin/plugins/igdb/IgdbPlugin.kt
index 6783fa3..81bfa72 100644
--- a/plugins/igdb/src/main/kotlin/de/grimsi/gameyfin/plugins/igdb/IgdbPlugin.kt
+++ b/plugins/igdb/src/main/kotlin/de/grimsi/gameyfin/plugins/igdb/IgdbPlugin.kt
@@ -59,9 +59,43 @@ class IgdbPlugin(wrapper: PluginWrapper) : GameyfinPlugin(wrapper) {
@Extension
class IgdbMetadataProvider : GameMetadataProvider {
+
+ private val QUERY_FIELDS = listOf(
+ "slug",
+ "name",
+ "summary",
+ "first_release_date",
+ "rating",
+ "aggregated_rating",
+ "total_rating",
+ "category",
+ "multiplayer_modes.lancoop",
+ "game_modes.slug",
+ "game_modes.name",
+ "cover.image_id",
+ "screenshots.image_id",
+ "videos.video_id",
+ "involved_companies.company.slug",
+ "involved_companies.company.name",
+ "involved_companies.developer",
+ "involved_companies.publisher",
+ "involved_companies.company.logo.image_id",
+ "genres.slug",
+ "genres.name",
+ "keywords.slug",
+ "keywords.name",
+ "themes.slug",
+ "themes.name",
+ "player_perspectives.slug",
+ "player_perspectives.name",
+ "platforms.slug",
+ "platforms.name",
+ "platforms.platform_logo.image_id"
+ ).joinToString(",")
+
override fun fetchMetadata(gameId: String): GameMetadata? {
val findBySlugQuery = APICalypse()
- .fields("*")
+ .fields(QUERY_FIELDS)
.where("slug = \"${guessSlug(gameId)}\"")
// First step: Try to find the game by guessing the slug
@@ -70,7 +104,7 @@ class IgdbPlugin(wrapper: PluginWrapper) : GameyfinPlugin(wrapper) {
// Second step: Try a fuzzy search
if (game == null) {
val searchByNameQuery = APICalypse()
- .fields("*")
+ .fields(QUERY_FIELDS)
.limit(100)
.search(gameId)
@@ -91,14 +125,15 @@ class IgdbPlugin(wrapper: PluginWrapper) : GameyfinPlugin(wrapper) {
release = Instant.ofEpochSecond(game.firstReleaseDate.seconds),
userRating = game.rating.toInt(),
criticRating = game.aggregatedRating.toInt(),
- developedBy = game.involvedCompaniesList.filter { it.developer }.map { it.company.name },
- publishedBy = game.involvedCompaniesList.filter { it.publisher }.map { it.company.name },
- genres = game.genresList.map { Mapper.genre(it) },
- themes = game.themesList.map { Mapper.theme(it) },
- screenshotUrls = listOf(),
- videoUrls = listOf(),
- features = listOf(),
- perspectives = listOf()
+ developedBy = game.involvedCompaniesList.filter { it.developer }.map { it.company.name }.toSet(),
+ publishedBy = game.involvedCompaniesList.filter { it.publisher }.map { it.company.name }.toSet(),
+ genres = game.genresList.map { Mapper.genre(it) }.toSet(),
+ themes = game.themesList.map { Mapper.theme(it) }.toSet(),
+ keywords = game.keywordsList.map { it.name }.toSet(),
+ screenshotUrls = game.screenshotsList.map { Mapper.screenshot(it) }.toSet(),
+ videoUrls = game.videosList.map { Mapper.video(it) }.toSet(),
+ features = Mapper.gameFeatures(game),
+ perspectives = game.playerPerspectivesList.map { Mapper.playerPerspective(it) }.toSet()
)
}
diff --git a/plugins/igdb/src/main/kotlin/de/grimsi/gameyfin/plugins/igdb/Mapper.kt b/plugins/igdb/src/main/kotlin/de/grimsi/gameyfin/plugins/igdb/Mapper.kt
index 475975e..76b5d73 100644
--- a/plugins/igdb/src/main/kotlin/de/grimsi/gameyfin/plugins/igdb/Mapper.kt
+++ b/plugins/igdb/src/main/kotlin/de/grimsi/gameyfin/plugins/igdb/Mapper.kt
@@ -1,8 +1,15 @@
package de.grimsi.gameyfin.plugins.igdb
+import com.api.igdb.utils.ImageSize
+import com.api.igdb.utils.ImageType
+import com.api.igdb.utils.imageBuilder
+import de.grimsi.gameyfin.pluginapi.gamemetadata.GameFeature
import de.grimsi.gameyfin.pluginapi.gamemetadata.Genre
+import de.grimsi.gameyfin.pluginapi.gamemetadata.PlayerPerspective
import de.grimsi.gameyfin.pluginapi.gamemetadata.Theme
import org.slf4j.LoggerFactory
+import java.net.URI
+import java.net.URL
class Mapper {
companion object {
@@ -70,5 +77,49 @@ class Mapper {
}
}
}
+
+ fun playerPerspective(perspective: proto.PlayerPerspective): PlayerPerspective {
+ return when (perspective.slug) {
+ "first-person" -> PlayerPerspective.FIRST_PERSON
+ "third-person" -> PlayerPerspective.THIRD_PERSON
+ "bird-view-isometric" -> PlayerPerspective.BIRD_VIEW_ISOMETRIC
+ "side-view" -> PlayerPerspective.SIDE_VIEW
+ "text" -> PlayerPerspective.TEXT
+ "auditory" -> PlayerPerspective.AUDITORY
+ "virtual-reality" -> PlayerPerspective.VIRTUAL_REALITY
+ else -> {
+ log.warn("Unknown player perspective: {}", perspective.slug)
+ PlayerPerspective.UNKNOWN
+ }
+ }
+ }
+
+ fun screenshot(screenshot: proto.Screenshot): URL {
+ return URI(imageBuilder(screenshot.imageId, ImageSize.SCREENSHOT_HUGE, ImageType.PNG)).toURL()
+ }
+
+ fun video(video: proto.GameVideo): URL {
+ return URI("https://www.youtube.com/watch?v=${video.videoId}").toURL()
+ }
+
+ fun gameFeatures(game: proto.Game): Set {
+ var gameFeatures = mutableSetOf()
+
+ // Get LAN support from multiplayer modes
+ if (game.multiplayerModesList.any { it.lancoop }) gameFeatures.add(GameFeature.LOCAL_MULTIPLAYER)
+
+ for (gameMode in game.gameModesList) {
+ when (gameMode.slug) {
+ "single-player" -> gameFeatures.add(GameFeature.SINGLEPLAYER)
+ "multiplayer" -> gameFeatures.add(GameFeature.MULTIPLAYER)
+ "co-operative" -> gameFeatures.add(GameFeature.CO_OP)
+ "split-screen" -> gameFeatures.add(GameFeature.SPLITSCREEN)
+ else -> {
+ log.warn("Unknown game mode: {}", gameMode.slug)
+ }
+ }
+ }
+ return gameFeatures
+ }
}
}
\ No newline at end of file
diff --git a/plugins/steam/src/main/kotlin/de/grimsi/gameyfin/plugins/steam/SteamPlugin.kt b/plugins/steam/src/main/kotlin/de/grimsi/gameyfin/plugins/steam/SteamPlugin.kt
index 99eab6b..6dd1a0c 100644
--- a/plugins/steam/src/main/kotlin/de/grimsi/gameyfin/plugins/steam/SteamPlugin.kt
+++ b/plugins/steam/src/main/kotlin/de/grimsi/gameyfin/plugins/steam/SteamPlugin.kt
@@ -112,14 +112,15 @@ class SteamPlugin(wrapper: PluginWrapper) : GameyfinPlugin(wrapper) {
release = date(game["release_date"]?.jsonObject["date"]?.jsonPrimitive?.content!!),
userRating = 0,
criticRating = 0,
- developedBy = stringList(game, "developers"),
- publishedBy = stringList(game, "publishers"),
- genres = emptyList(),
- themes = emptyList(),
- screenshotUrls = emptyList(),
- videoUrls = emptyList(),
- features = emptyList(),
- perspectives = emptyList()
+ developedBy = stringList(game, "developers").toSet(),
+ publishedBy = stringList(game, "publishers").toSet(),
+ genres = emptySet(),
+ themes = emptySet(),
+ keywords = emptySet(),
+ screenshotUrls = emptySet(),
+ videoUrls = emptySet(),
+ features = emptySet(),
+ perspectives = emptySet()
)
return metadata