mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-13 16:40:01 +00:00
Implement field metadata
Implement result merging for GameMetadata
This commit is contained in:
+1
-1
@@ -10,5 +10,5 @@ data class PluginManagementEntry(
|
||||
|
||||
var enabled: Boolean = true,
|
||||
|
||||
var priority: Int = Int.MAX_VALUE
|
||||
var priority: Int = 0
|
||||
)
|
||||
@@ -44,18 +44,19 @@ class GameService(
|
||||
|
||||
fun createFromFile(path: Path): GameDto {
|
||||
val metadataResults = queryPlugins(path.fileName.toString())
|
||||
|
||||
val (plugin, metadata) = metadataResults.entries.firstOrNull { it.value != null }
|
||||
?: throw NoMatchException("Could not match game at $path")
|
||||
|
||||
if (metadata == null) {
|
||||
throw NoMatchException("Plugin ${plugin.javaClass} returned invalid metadata for game at $path")
|
||||
val validResults = metadataResults.filterValues { it != null }
|
||||
if (validResults.isEmpty()) {
|
||||
throw NoMatchException("Could not match game at $path")
|
||||
}
|
||||
|
||||
var game = toEntity(metadata, path, plugin)
|
||||
game = createOrUpdate(game)
|
||||
val sortedResults = validResults.entries.sortedByDescending {
|
||||
pluginManagementService.getPluginManagementEntry(it.key.javaClass).priority
|
||||
}
|
||||
|
||||
return toDto(game)
|
||||
val mergedGame = mergeResults(sortedResults, path)
|
||||
val savedGame = createOrUpdate(mergedGame)
|
||||
|
||||
return toDto(savedGame)
|
||||
}
|
||||
|
||||
fun getAllGames(): Collection<GameDto> {
|
||||
@@ -93,12 +94,112 @@ class GameService(
|
||||
}
|
||||
}
|
||||
|
||||
private fun mergeResults(results: List<Map.Entry<GameMetadataProvider, GameMetadata?>>, path: Path): Game {
|
||||
val mergedGame = Game(path = path.toString())
|
||||
val metadataMap = mutableMapOf<String, FieldMetadata>()
|
||||
|
||||
// Sort results by plugin priority
|
||||
val sortedResults = results.sortedByDescending {
|
||||
pluginManagementService.getPluginManagementEntry(it.key.javaClass).priority
|
||||
}
|
||||
|
||||
sortedResults.forEach { (provider, metadata) ->
|
||||
val sourcePlugin = pluginManagementService.getPluginManagementEntry(provider.javaClass)
|
||||
metadata?.let {
|
||||
it.title.takeIf { it.isNotBlank() }?.let { title ->
|
||||
if (!metadataMap.containsKey("title")) {
|
||||
mergedGame.title = title
|
||||
metadataMap["title"] = FieldMetadata(sourcePlugin)
|
||||
}
|
||||
}
|
||||
it.description?.takeIf { it.isNotBlank() }?.let { description ->
|
||||
if (!metadataMap.containsKey("summary")) {
|
||||
mergedGame.summary = description
|
||||
metadataMap["summary"] = FieldMetadata(sourcePlugin)
|
||||
}
|
||||
}
|
||||
it.coverUrl?.let { coverUrl ->
|
||||
if (!metadataMap.containsKey("coverImage")) {
|
||||
mergedGame.coverImage = downloadAndPersist(coverUrl, ImageType.COVER)
|
||||
metadataMap["coverImage"] = FieldMetadata(sourcePlugin)
|
||||
}
|
||||
}
|
||||
it.release?.let { release ->
|
||||
if (!metadataMap.containsKey("release")) {
|
||||
mergedGame.release = release
|
||||
metadataMap["release"] = FieldMetadata(sourcePlugin)
|
||||
}
|
||||
}
|
||||
it.publishedBy?.takeIf { it.isNotEmpty() }?.let { publishedBy ->
|
||||
if (!metadataMap.containsKey("publishers")) {
|
||||
mergedGame.publishers =
|
||||
publishedBy.map { name -> toEntity(name, CompanyType.PUBLISHER) }.toSet()
|
||||
metadataMap["publishers"] = FieldMetadata(sourcePlugin)
|
||||
}
|
||||
}
|
||||
it.developedBy?.takeIf { it.isNotEmpty() }?.let { developedBy ->
|
||||
if (!metadataMap.containsKey("developers")) {
|
||||
mergedGame.developers =
|
||||
developedBy.map { name -> toEntity(name, CompanyType.DEVELOPER) }.toSet()
|
||||
metadataMap["developers"] = FieldMetadata(sourcePlugin)
|
||||
}
|
||||
}
|
||||
it.genres?.takeIf { it.isNotEmpty() }?.let { genres ->
|
||||
if (!metadataMap.containsKey("genres")) {
|
||||
mergedGame.genres = genres
|
||||
metadataMap["genres"] = FieldMetadata(sourcePlugin)
|
||||
}
|
||||
}
|
||||
it.themes?.takeIf { it.isNotEmpty() }?.let { themes ->
|
||||
if (!metadataMap.containsKey("themes")) {
|
||||
mergedGame.themes = themes
|
||||
metadataMap["themes"] = FieldMetadata(sourcePlugin)
|
||||
}
|
||||
}
|
||||
it.keywords?.takeIf { it.isNotEmpty() }?.let { keywords ->
|
||||
if (!metadataMap.containsKey("keywords")) {
|
||||
mergedGame.keywords = keywords
|
||||
metadataMap["keywords"] = FieldMetadata(sourcePlugin)
|
||||
}
|
||||
}
|
||||
it.features?.takeIf { it.isNotEmpty() }?.let { features ->
|
||||
if (!metadataMap.containsKey("features")) {
|
||||
mergedGame.features = features
|
||||
metadataMap["features"] = FieldMetadata(sourcePlugin)
|
||||
}
|
||||
}
|
||||
it.perspectives?.takeIf { it.isNotEmpty() }?.let { perspectives ->
|
||||
if (!metadataMap.containsKey("perspectives")) {
|
||||
mergedGame.perspectives = perspectives
|
||||
metadataMap["perspectives"] = FieldMetadata(sourcePlugin)
|
||||
}
|
||||
}
|
||||
it.screenshotUrls?.takeIf { it.isNotEmpty() }?.let { screenshotUrls ->
|
||||
if (!metadataMap.containsKey("images")) {
|
||||
mergedGame.images =
|
||||
screenshotUrls.map { url -> downloadAndPersist(url, ImageType.SCREENSHOT) }.toSet()
|
||||
metadataMap["images"] = FieldMetadata(sourcePlugin)
|
||||
}
|
||||
}
|
||||
it.videoUrls?.takeIf { it.isNotEmpty() }?.let { videoUrls ->
|
||||
if (!metadataMap.containsKey("videoUrls")) {
|
||||
mergedGame.videoUrls = videoUrls
|
||||
metadataMap["videoUrls"] = FieldMetadata(sourcePlugin)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mergedGame.metadata = metadataMap
|
||||
return mergedGame
|
||||
}
|
||||
|
||||
private fun toDto(game: Game): GameDto {
|
||||
val gameId = game.id ?: throw IllegalArgumentException("Game ID is null")
|
||||
|
||||
return GameDto(
|
||||
id = gameId,
|
||||
title = game.title,
|
||||
title = game.title!!,
|
||||
coverId = game.coverImage?.id,
|
||||
comment = game.comment,
|
||||
summary = game.summary,
|
||||
@@ -128,28 +229,6 @@ class GameService(
|
||||
)
|
||||
}
|
||||
|
||||
private fun toEntity(metadata: GameMetadata, path: Path, source: GameMetadataProvider): Game {
|
||||
val sourcePlugin = pluginManagementService.getPluginManagementEntry(source.javaClass)
|
||||
|
||||
return Game(
|
||||
title = metadata.title,
|
||||
summary = metadata.description,
|
||||
coverImage = metadata.coverUrl?.let { downloadAndPersist(it, ImageType.COVER) },
|
||||
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,
|
||||
images = metadata.screenshotUrls?.map { downloadAndPersist(it, ImageType.SCREENSHOT) }?.toSet(),
|
||||
videoUrls = metadata.videoUrls,
|
||||
path = path.toString(),
|
||||
metadata = mapOf("title" to FieldMetadata(sourcePlugin))
|
||||
)
|
||||
}
|
||||
|
||||
private fun toEntity(companyName: String, companyType: CompanyType): Company {
|
||||
companyRepository.findByNameAndType(companyName, companyType)?.let { return it }
|
||||
val company = Company(name = companyName, type = companyType)
|
||||
|
||||
@@ -14,10 +14,10 @@ class Game(
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
var id: Long? = null,
|
||||
|
||||
val title: String,
|
||||
var title: String? = null,
|
||||
|
||||
@OneToOne(cascade = [CascadeType.MERGE])
|
||||
val coverImage: Image? = null,
|
||||
var coverImage: Image? = null,
|
||||
|
||||
@Lob
|
||||
@Column(columnDefinition = "CLOB")
|
||||
@@ -25,40 +25,40 @@ class Game(
|
||||
|
||||
@Lob
|
||||
@Column(columnDefinition = "CLOB")
|
||||
val summary: String? = null,
|
||||
var summary: String? = null,
|
||||
|
||||
val release: Instant? = null,
|
||||
var release: Instant? = null,
|
||||
|
||||
@ManyToMany(cascade = [CascadeType.MERGE])
|
||||
val publishers: Set<Company>? = null,
|
||||
var publishers: Set<Company>? = null,
|
||||
|
||||
@ManyToMany(cascade = [CascadeType.MERGE])
|
||||
val developers: Set<Company>? = null,
|
||||
var developers: Set<Company>? = null,
|
||||
|
||||
@ElementCollection(targetClass = Genre::class)
|
||||
var genres: Set<Genre>? = null,
|
||||
|
||||
@ElementCollection(targetClass = Theme::class)
|
||||
var themes: Set<Theme>? = null,
|
||||
|
||||
@ElementCollection
|
||||
val genres: Set<Genre>? = null,
|
||||
var keywords: Set<String>? = null,
|
||||
|
||||
@ElementCollection
|
||||
val themes: Set<Theme>? = null,
|
||||
@ElementCollection(targetClass = GameFeature::class)
|
||||
var features: Set<GameFeature>? = null,
|
||||
|
||||
@ElementCollection
|
||||
val keywords: Set<String>? = null,
|
||||
|
||||
@ElementCollection
|
||||
val features: Set<GameFeature>? = null,
|
||||
|
||||
@ElementCollection
|
||||
val perspectives: Set<PlayerPerspective>? = null,
|
||||
@ElementCollection(targetClass = PlayerPerspective::class)
|
||||
var perspectives: Set<PlayerPerspective>? = null,
|
||||
|
||||
@OneToMany(cascade = [CascadeType.MERGE])
|
||||
val images: Set<Image>? = null,
|
||||
var images: Set<Image>? = null,
|
||||
|
||||
@ElementCollection
|
||||
val videoUrls: Set<URI>? = null,
|
||||
var videoUrls: Set<URI>? = null,
|
||||
|
||||
@Column(unique = true)
|
||||
val path: String,
|
||||
|
||||
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true)
|
||||
val metadata: Map<String, FieldMetadata> = emptyMap()
|
||||
var metadata: Map<String, FieldMetadata> = emptyMap()
|
||||
)
|
||||
+5
-1
@@ -93,7 +93,11 @@ class UserPreferencesService(
|
||||
userPreference.value = castedValue.toString()
|
||||
}
|
||||
|
||||
userPreferenceRepository.save(userPreference)
|
||||
try {
|
||||
userPreferenceRepository.save(userPreference)
|
||||
} catch (e: Exception) {
|
||||
log.warn { "Error saving user preference '$key': ${e.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user