mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-13 16:40:01 +00:00
Update Plugin API (and plugins)
Fix issue with persisting new games
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
package de.grimsi.gameyfin.games
|
||||
|
||||
data class GameDto(
|
||||
val id: Long,
|
||||
val title: String,
|
||||
)
|
||||
@@ -1,14 +1,12 @@
|
||||
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.dto.GameDto
|
||||
import de.grimsi.gameyfin.games.entities.*
|
||||
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.games.repositories.ImageContentStore
|
||||
import de.grimsi.gameyfin.games.repositories.ImageRepository
|
||||
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadata
|
||||
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadataProvider
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
@@ -28,8 +26,8 @@ class GameService(
|
||||
private val pluginManagementService: PluginManagementService,
|
||||
private val gameRepository: GameRepository,
|
||||
private val companyRepository: CompanyRepository,
|
||||
private val screenshotRepository: ScreenshotRepository,
|
||||
private val screenshotContentStore: ScreenshotContentStore
|
||||
private val imageRepository: ImageRepository,
|
||||
private val imageContentStore: ImageContentStore
|
||||
) {
|
||||
private val log = KotlinLogging.logger {}
|
||||
|
||||
@@ -43,7 +41,7 @@ class GameService(
|
||||
return gameRepository.save(game)
|
||||
}
|
||||
|
||||
fun createFromFile(path: Path): Game {
|
||||
fun createFromFile(path: Path): GameDto {
|
||||
val metadataResults: Map<GameMetadataProvider, GameMetadata?> = runBlocking {
|
||||
coroutineScope {
|
||||
metadataPlugins.associateWith {
|
||||
@@ -66,9 +64,10 @@ class GameService(
|
||||
throw NoMatchException("Plugin ${plugin.javaClass} returned invalid metadata for game at $path")
|
||||
}
|
||||
|
||||
val game = toEntity(metadata, path, plugin)
|
||||
|
||||
return createOrUpdate(game)
|
||||
var game = toEntity(metadata, path, plugin)
|
||||
game = createOrUpdate(game)
|
||||
|
||||
return toDto(game)
|
||||
}
|
||||
|
||||
fun getAllGames(): Collection<GameDto> {
|
||||
@@ -89,7 +88,21 @@ class GameService(
|
||||
|
||||
return GameDto(
|
||||
id = gameId,
|
||||
title = game.title
|
||||
title = game.title,
|
||||
coverImageUrl = game.coverImage.contentId,
|
||||
comment = game.comment,
|
||||
summary = game.summary,
|
||||
release = game.release,
|
||||
publishers = game.publishers.map { it.name },
|
||||
developers = game.developers.map { it.name },
|
||||
genres = game.genres.map { it.name },
|
||||
themes = game.themes.map { it.name },
|
||||
keywords = game.keywords.toList(),
|
||||
features = game.features.map { it.name },
|
||||
perspectives = game.perspectives.map { it.name },
|
||||
images = game.images.mapNotNull { it.contentId },
|
||||
videoUrls = game.videoUrls.map { it.toString() },
|
||||
source = game.source.pluginId
|
||||
)
|
||||
}
|
||||
|
||||
@@ -97,6 +110,7 @@ class GameService(
|
||||
return Game(
|
||||
title = metadata.title,
|
||||
summary = metadata.description,
|
||||
coverImage = downloadAndPersist(metadata.coverUrl, ImageType.COVER),
|
||||
release = metadata.release,
|
||||
publishers = metadata.publishedBy.map { toEntity(it, CompanyType.PUBLISHER) }.toSet(),
|
||||
developers = metadata.developedBy.map { toEntity(it, CompanyType.DEVELOPER) }.toSet(),
|
||||
@@ -105,7 +119,7 @@ class GameService(
|
||||
keywords = metadata.keywords,
|
||||
features = metadata.features,
|
||||
perspectives = metadata.perspectives,
|
||||
screenshots = metadata.screenshotUrls.map { downloadAndPersist(it) }.toSet(),
|
||||
images = metadata.screenshotUrls.map { downloadAndPersist(it, ImageType.SCREENSHOT) }.toSet(),
|
||||
videoUrls = metadata.videoUrls,
|
||||
path = path.toString(),
|
||||
source = pluginManagementService.getPluginManagementEntry(source.javaClass)
|
||||
@@ -118,15 +132,14 @@ class GameService(
|
||||
return companyRepository.save(company)
|
||||
}
|
||||
|
||||
private fun downloadAndPersist(screenshotUrl: URL): Screenshot {
|
||||
screenshotRepository.findByOriginalUrl(screenshotUrl)?.let { return it }
|
||||
private fun downloadAndPersist(imageUrl: URL, type: ImageType): Image {
|
||||
imageRepository.findByOriginalUrl(imageUrl)?.let { return it }
|
||||
|
||||
val screenshot = Screenshot(originalUrl = screenshotUrl)
|
||||
screenshotUrl.openStream().use { input ->
|
||||
val mimeType = URLConnection.guessContentTypeFromStream(input)
|
||||
screenshot.mimeType = mimeType
|
||||
screenshotContentStore.setContent(screenshot, input)
|
||||
val image = Image(originalUrl = imageUrl, type = type)
|
||||
imageUrl.openStream().use { input ->
|
||||
image.mimeType = URLConnection.guessContentTypeFromStream(input)
|
||||
imageContentStore.setContent(image, input)
|
||||
}
|
||||
return screenshotRepository.save(screenshot)
|
||||
return imageRepository.save(image)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package de.grimsi.gameyfin.games.dto
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
class GameDto(
|
||||
val id: Long,
|
||||
val title: String,
|
||||
val coverImageUrl: String?,
|
||||
val comment: String?,
|
||||
val summary: String?,
|
||||
val release: Instant?,
|
||||
val publishers: List<String>?,
|
||||
val developers: List<String>?,
|
||||
val genres: List<String>?,
|
||||
val themes: List<String>?,
|
||||
val keywords: List<String>?,
|
||||
val features: List<String>?,
|
||||
val perspectives: List<String>?,
|
||||
val images: List<String>?,
|
||||
val videoUrls: List<String>?,
|
||||
val source: String?
|
||||
)
|
||||
@@ -17,6 +17,9 @@ class Game(
|
||||
|
||||
val title: String,
|
||||
|
||||
@OneToOne(cascade = [CascadeType.MERGE])
|
||||
val coverImage: Image,
|
||||
|
||||
@Lob
|
||||
@Column(columnDefinition = "CLOB")
|
||||
val comment: String? = null,
|
||||
@@ -27,10 +30,10 @@ class Game(
|
||||
|
||||
val release: Instant,
|
||||
|
||||
@OneToMany(cascade = [CascadeType.MERGE])
|
||||
@ManyToMany(cascade = [CascadeType.MERGE])
|
||||
val publishers: Set<Company>,
|
||||
|
||||
@OneToMany(cascade = [CascadeType.MERGE])
|
||||
@ManyToMany(cascade = [CascadeType.MERGE])
|
||||
val developers: Set<Company>,
|
||||
|
||||
@ElementCollection
|
||||
@@ -49,7 +52,7 @@ class Game(
|
||||
val perspectives: Set<PlayerPerspective>,
|
||||
|
||||
@OneToMany(cascade = [CascadeType.MERGE])
|
||||
val screenshots: Set<Screenshot>,
|
||||
val images: Set<Image>,
|
||||
|
||||
@ElementCollection
|
||||
val videoUrls: Set<URL>,
|
||||
|
||||
+9
-2
@@ -11,13 +11,15 @@ import org.springframework.content.commons.annotations.MimeType
|
||||
import java.net.URL
|
||||
|
||||
@Entity
|
||||
class Screenshot(
|
||||
class Image(
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
var id: Long? = null,
|
||||
|
||||
val originalUrl: URL,
|
||||
|
||||
val type: ImageType,
|
||||
|
||||
@ContentId
|
||||
@Nullable
|
||||
var contentId: String? = null,
|
||||
@@ -29,4 +31,9 @@ class Screenshot(
|
||||
@MimeType
|
||||
@Nullable
|
||||
var mimeType: String? = null
|
||||
)
|
||||
)
|
||||
|
||||
enum class ImageType {
|
||||
COVER,
|
||||
SCREENSHOT
|
||||
}
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
package de.grimsi.gameyfin.games.repositories
|
||||
|
||||
import de.grimsi.gameyfin.games.entities.Screenshot
|
||||
import de.grimsi.gameyfin.games.entities.Image
|
||||
import org.springframework.content.commons.store.ContentStore
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
interface ScreenshotContentStore : ContentStore<Screenshot, String>
|
||||
interface ImageContentStore : ContentStore<Image, String>
|
||||
@@ -0,0 +1,9 @@
|
||||
package de.grimsi.gameyfin.games.repositories
|
||||
|
||||
import de.grimsi.gameyfin.games.entities.Image
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import java.net.URL
|
||||
|
||||
interface ImageRepository : JpaRepository<Image, Long> {
|
||||
fun findByOriginalUrl(originalUrl: URL): Image?
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
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<Screenshot, Long> {
|
||||
fun findByOriginalUrl(originalUrl: URL): Screenshot?
|
||||
}
|
||||
@@ -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.entities.Game
|
||||
import de.grimsi.gameyfin.games.dto.GameDto
|
||||
import jakarta.annotation.security.RolesAllowed
|
||||
|
||||
@Endpoint
|
||||
@@ -20,7 +20,7 @@ class LibraryEndpoint(
|
||||
}
|
||||
|
||||
@RolesAllowed(Role.Names.ADMIN)
|
||||
fun test(testString: String): Game {
|
||||
fun test(testString: String): GameDto {
|
||||
return libraryService.test(testString)
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package de.grimsi.gameyfin.libraries
|
||||
import de.grimsi.gameyfin.config.ConfigProperties
|
||||
import de.grimsi.gameyfin.config.ConfigService
|
||||
import de.grimsi.gameyfin.games.GameService
|
||||
import de.grimsi.gameyfin.games.entities.Game
|
||||
import de.grimsi.gameyfin.games.dto.GameDto
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
import org.springframework.stereotype.Service
|
||||
import java.nio.file.Path
|
||||
@@ -18,7 +18,7 @@ class LibraryService(
|
||||
private val gameService: GameService,
|
||||
private val config: ConfigService
|
||||
) {
|
||||
fun test(testString: String): Game {
|
||||
fun test(testString: String): GameDto {
|
||||
return gameService.createFromFile(Path(testString))
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.time.Instant
|
||||
class GameMetadata(
|
||||
val title: String,
|
||||
val description: String,
|
||||
val coverUrl: URL,
|
||||
val release: Instant,
|
||||
val userRating: Int?,
|
||||
val criticRating: Int?,
|
||||
|
||||
@@ -122,6 +122,7 @@ class IgdbPlugin(wrapper: PluginWrapper) : GameyfinPlugin(wrapper) {
|
||||
return GameMetadata(
|
||||
title = game.name,
|
||||
description = game.summary,
|
||||
coverUrl = Mapper.cover(game.cover),
|
||||
release = Instant.ofEpochSecond(game.firstReleaseDate.seconds),
|
||||
userRating = game.rating.toInt(),
|
||||
criticRating = game.aggregatedRating.toInt(),
|
||||
|
||||
@@ -95,7 +95,11 @@ class Mapper {
|
||||
}
|
||||
|
||||
fun screenshot(screenshot: proto.Screenshot): URL {
|
||||
return URI(imageBuilder(screenshot.imageId, ImageSize.SCREENSHOT_HUGE, ImageType.PNG)).toURL()
|
||||
return URI(imageBuilder(screenshot.imageId, ImageSize.FHD, ImageType.PNG)).toURL()
|
||||
}
|
||||
|
||||
fun cover(cover: proto.Cover): URL {
|
||||
return URI(imageBuilder(cover.imageId, ImageSize.COVER_BIG, ImageType.PNG)).toURL()
|
||||
}
|
||||
|
||||
fun video(video: proto.GameVideo): URL {
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.pf4j.Extension
|
||||
import org.pf4j.PluginWrapper
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.net.URI
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.time.Instant
|
||||
@@ -109,6 +110,7 @@ class SteamPlugin(wrapper: PluginWrapper) : GameyfinPlugin(wrapper) {
|
||||
val metadata = GameMetadata(
|
||||
title = string(game, "name"),
|
||||
description = string(game, "detailed_description"),
|
||||
coverUrl = URI("").toURL(),
|
||||
release = date(game["release_date"]?.jsonObject["date"]?.jsonPrimitive?.content!!),
|
||||
userRating = 0,
|
||||
criticRating = 0,
|
||||
|
||||
Reference in New Issue
Block a user