mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-16 08:15:48 +00:00
Implement entity listeners for Game and Library
This commit is contained in:
@@ -15,7 +15,7 @@ class GameEndpoint(
|
|||||||
private val gameService: GameService
|
private val gameService: GameService
|
||||||
) {
|
) {
|
||||||
fun subscribe(): Flux<GameEvent> {
|
fun subscribe(): Flux<GameEvent> {
|
||||||
return gameService.subscribe()
|
return GameService.subscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAll(): List<GameDto> = gameService.getAll()
|
fun getAll(): List<GameDto> = gameService.getAll()
|
||||||
@@ -25,5 +25,4 @@ class GameEndpoint(
|
|||||||
|
|
||||||
@RolesAllowed(Role.Names.ADMIN)
|
@RolesAllowed(Role.Names.ADMIN)
|
||||||
fun deleteGame(gameId: Long) = gameService.delete(gameId)
|
fun deleteGame(gameId: Long) = gameService.delete(gameId)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,6 @@ import de.grimsi.gameyfin.libraries.Library
|
|||||||
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadata
|
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadata
|
||||||
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadataProvider
|
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadataProvider
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import jakarta.persistence.EntityManager
|
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
@@ -37,26 +36,31 @@ class GameService(
|
|||||||
private val pluginService: PluginService,
|
private val pluginService: PluginService,
|
||||||
private val config: ConfigService,
|
private val config: ConfigService,
|
||||||
private val companyService: CompanyService,
|
private val companyService: CompanyService,
|
||||||
private val gameRepository: GameRepository,
|
private val gameRepository: GameRepository
|
||||||
private val entityManager: EntityManager
|
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = KotlinLogging.logger {}
|
private val log = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
/* Websockets */
|
||||||
|
private val gameEvents = Sinks.many().multicast().onBackpressureBuffer<GameEvent>(1024, false)
|
||||||
|
|
||||||
|
fun subscribe(): Flux<GameEvent> {
|
||||||
|
log.debug { "New subscription for gameUpdates" }
|
||||||
|
return gameEvents.asFlux()
|
||||||
|
.doOnSubscribe { log.debug { "Subscriber added to gameEvents [${gameEvents.currentSubscriberCount()}]" } }
|
||||||
|
.doFinally {
|
||||||
|
log.debug { "Subscriber removed from gameEvents with signal type $it [${gameEvents.currentSubscriberCount()}]" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun emit(event: GameEvent) {
|
||||||
|
gameEvents.tryEmitNext(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val metadataPlugins: List<GameMetadataProvider>
|
private val metadataPlugins: List<GameMetadataProvider>
|
||||||
get() = pluginManager.getExtensions(GameMetadataProvider::class.java)
|
get() = pluginManager.getExtensions(GameMetadataProvider::class.java)
|
||||||
|
|
||||||
private val gameEvents = Sinks.many().multicast().onBackpressureBuffer<GameEvent>(1024, false)
|
|
||||||
|
|
||||||
fun subscribe(): Flux<GameEvent> {
|
|
||||||
log.debug { "New subscription for gameUpdates" }
|
|
||||||
return gameEvents.asFlux()
|
|
||||||
.doOnSubscribe { log.debug { "Subscriber added to gameEvents [${gameEvents.currentSubscriberCount()}]" } }
|
|
||||||
.doFinally {
|
|
||||||
log.debug { "Subscriber removed from gameEvents with signal type $it [${gameEvents.currentSubscriberCount()}]" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getAll(): List<GameDto> {
|
fun getAll(): List<GameDto> {
|
||||||
val entities = gameRepository.findAll()
|
val entities = gameRepository.findAll()
|
||||||
@@ -73,16 +77,7 @@ class GameService(
|
|||||||
game
|
game
|
||||||
}
|
}
|
||||||
|
|
||||||
val games = gameRepository.saveAll(gamesToBePersisted)
|
return gameRepository.saveAll(gamesToBePersisted)
|
||||||
|
|
||||||
// force flush to populate creation and update timestamp
|
|
||||||
entityManager.flush()
|
|
||||||
|
|
||||||
games.forEach { game ->
|
|
||||||
val gameDto = game.toDto()
|
|
||||||
gameEvents.tryEmitNext(GameEvent.Created(gameDto))
|
|
||||||
}
|
|
||||||
return games
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun update(gameUpdateDto: GameUpdateDto) {
|
fun update(gameUpdateDto: GameUpdateDto) {
|
||||||
@@ -94,18 +89,11 @@ class GameService(
|
|||||||
gameUpdateDto.comment?.let { existingGame.comment = it }
|
gameUpdateDto.comment?.let { existingGame.comment = it }
|
||||||
gameUpdateDto.summary?.let { existingGame.summary = it }
|
gameUpdateDto.summary?.let { existingGame.summary = it }
|
||||||
|
|
||||||
val updatedGame = gameRepository.save(existingGame)
|
gameRepository.save(existingGame)
|
||||||
val updatedGameDto = updatedGame.toDto()
|
|
||||||
gameEvents.tryEmitNext(GameEvent.Updated(updatedGameDto))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun delete(gameId: Long) {
|
fun delete(gameId: Long) {
|
||||||
gameRepository.deleteById(gameId)
|
gameRepository.deleteById(gameId)
|
||||||
gameEvents.tryEmitNext(GameEvent.Deleted(gameId))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun emitDeletionEvent(gameId: Long) {
|
|
||||||
gameEvents.tryEmitNext(GameEvent.Deleted(gameId))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun matchFromFile(path: Path, library: Library): Game? {
|
fun matchFromFile(path: Path, library: Library): Game? {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import java.net.URI
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
|
@EntityListeners(GameEntityListener::class)
|
||||||
class Game(
|
class Game(
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package de.grimsi.gameyfin.games.entities
|
||||||
|
|
||||||
|
import de.grimsi.gameyfin.games.GameService
|
||||||
|
import de.grimsi.gameyfin.games.dto.GameEvent
|
||||||
|
import de.grimsi.gameyfin.games.toDto
|
||||||
|
import jakarta.persistence.PostPersist
|
||||||
|
import jakarta.persistence.PostRemove
|
||||||
|
import jakarta.persistence.PostUpdate
|
||||||
|
|
||||||
|
class GameEntityListener {
|
||||||
|
@PostPersist
|
||||||
|
fun created(game: Game) {
|
||||||
|
val event = GameEvent.Created(game.toDto())
|
||||||
|
GameService.emit(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostUpdate
|
||||||
|
fun updated(game: Game) {
|
||||||
|
val event = GameEvent.Updated(game.toDto())
|
||||||
|
GameService.emit(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostRemove
|
||||||
|
fun deleted(game: Game) {
|
||||||
|
val event = GameEvent.Deleted(game.id!!)
|
||||||
|
GameService.emit(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package de.grimsi.gameyfin.games.entities
|
||||||
|
|
||||||
|
import de.grimsi.gameyfin.libraries.Library
|
||||||
|
import de.grimsi.gameyfin.libraries.LibraryService
|
||||||
|
import de.grimsi.gameyfin.libraries.dto.LibraryEvent
|
||||||
|
import de.grimsi.gameyfin.libraries.toDto
|
||||||
|
import jakarta.persistence.PostPersist
|
||||||
|
import jakarta.persistence.PostRemove
|
||||||
|
import jakarta.persistence.PostUpdate
|
||||||
|
|
||||||
|
class LibraryEntityListener {
|
||||||
|
@PostPersist
|
||||||
|
fun created(library: Library) {
|
||||||
|
val event = LibraryEvent.Created(library.toDto())
|
||||||
|
LibraryService.emit(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostUpdate
|
||||||
|
fun updated(library: Library) {
|
||||||
|
val event = LibraryEvent.Updated(library.toDto())
|
||||||
|
LibraryService.emit(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostRemove
|
||||||
|
fun deleted(library: Library) {
|
||||||
|
val event = LibraryEvent.Deleted(library.id!!)
|
||||||
|
LibraryService.emit(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
package de.grimsi.gameyfin.libraries
|
package de.grimsi.gameyfin.libraries
|
||||||
|
|
||||||
import de.grimsi.gameyfin.games.entities.Game
|
import de.grimsi.gameyfin.games.entities.Game
|
||||||
|
import de.grimsi.gameyfin.games.entities.LibraryEntityListener
|
||||||
import jakarta.persistence.*
|
import jakarta.persistence.*
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
|
@EntityListeners(LibraryEntityListener::class)
|
||||||
class Library(
|
class Library(
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class LibraryEndpoint(
|
|||||||
private val libraryService: LibraryService
|
private val libraryService: LibraryService
|
||||||
) {
|
) {
|
||||||
fun subscribe(): Flux<LibraryEvent> {
|
fun subscribe(): Flux<LibraryEvent> {
|
||||||
return libraryService.subscribe()
|
return LibraryService.subscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAll() = libraryService.getAll()
|
fun getAll() = libraryService.getAll()
|
||||||
|
|||||||
@@ -28,25 +28,31 @@ class LibraryService(
|
|||||||
companion object {
|
companion object {
|
||||||
private val log = KotlinLogging.logger {}
|
private val log = KotlinLogging.logger {}
|
||||||
private val executor = Executors.newVirtualThreadPerTaskExecutor()
|
private val executor = Executors.newVirtualThreadPerTaskExecutor()
|
||||||
|
|
||||||
|
/* Websockets */
|
||||||
|
private val libraryEvents = Sinks.many().multicast().onBackpressureBuffer<LibraryEvent>(1024, false)
|
||||||
|
|
||||||
|
fun subscribe(): Flux<LibraryEvent> {
|
||||||
|
log.debug { "New subscription for libraryEvents" }
|
||||||
|
return libraryEvents.asFlux()
|
||||||
|
.doOnSubscribe { log.debug { "Subscriber added to libraryEvents [${libraryEvents.currentSubscriberCount()}]" } }
|
||||||
|
.doFinally {
|
||||||
|
log.debug { "Subscriber removed from libraryEvents with signal type $it [${libraryEvents.currentSubscriberCount()}]" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun emit(event: LibraryEvent) {
|
||||||
|
libraryEvents.tryEmitNext(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val libraryEvents = Sinks.many().multicast().onBackpressureBuffer<LibraryEvent>(1024, false)
|
|
||||||
|
|
||||||
fun subscribe(): Flux<LibraryEvent> {
|
|
||||||
log.debug { "New subscription for libraryEvents" }
|
|
||||||
return libraryEvents.asFlux()
|
|
||||||
.doOnSubscribe { log.debug { "Subscriber added to libraryEvents [${libraryEvents.currentSubscriberCount()}]" } }
|
|
||||||
.doFinally {
|
|
||||||
log.debug { "Subscriber removed from libraryEvents with signal type $it [${libraryEvents.currentSubscriberCount()}]" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves all libraries from the repository.
|
* Retrieves all libraries from the repository.
|
||||||
*/
|
*/
|
||||||
fun getAll(): List<LibraryDto> {
|
fun getAll(): List<LibraryDto> {
|
||||||
val entities = libraryRepository.findAll()
|
val entities = libraryRepository.findAll()
|
||||||
return entities.map { toDto(it) }
|
return entities.map { it.toDto() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,8 +63,6 @@ class LibraryService(
|
|||||||
*/
|
*/
|
||||||
fun create(library: LibraryDto) {
|
fun create(library: LibraryDto) {
|
||||||
val entity = libraryRepository.save(toEntity(library))
|
val entity = libraryRepository.save(toEntity(library))
|
||||||
val libraryDto = toDto(entity)
|
|
||||||
libraryEvents.tryEmitNext(LibraryEvent.Created(libraryDto))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,9 +85,7 @@ class LibraryService(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val updatedLibrary = libraryRepository.save(existingLibrary)
|
libraryRepository.save(existingLibrary)
|
||||||
val updatedLibraryDto = toDto(updatedLibrary)
|
|
||||||
libraryEvents.tryEmitNext(LibraryEvent.Updated(updatedLibraryDto))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,13 +94,7 @@ class LibraryService(
|
|||||||
* @param libraryId: ID of the library to delete.
|
* @param libraryId: ID of the library to delete.
|
||||||
*/
|
*/
|
||||||
fun delete(libraryId: Long) {
|
fun delete(libraryId: Long) {
|
||||||
val gameIds = libraryRepository.findByIdOrNull(libraryId)?.games?.mapNotNull { it.id }
|
|
||||||
?: throw IllegalArgumentException("Library with ID $libraryId not found")
|
|
||||||
|
|
||||||
libraryRepository.deleteById(libraryId)
|
libraryRepository.deleteById(libraryId)
|
||||||
|
|
||||||
libraryEvents.tryEmitNext(LibraryEvent.Deleted(libraryId))
|
|
||||||
gameIds.forEach { gameService.emitDeletionEvent(it) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -256,29 +252,6 @@ class LibraryService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a Library entity to a LibraryDto.
|
|
||||||
*
|
|
||||||
* @param library: The Library entity to convert.
|
|
||||||
* @return The converted LibraryDto.
|
|
||||||
*/
|
|
||||||
private fun toDto(library: Library): LibraryDto {
|
|
||||||
val libraryId = library.id ?: throw IllegalArgumentException("Library ID is null")
|
|
||||||
|
|
||||||
val statsDto = LibraryStatsDto(
|
|
||||||
gamesCount = library.games.size,
|
|
||||||
downloadedGamesCount = library.games.sumOf { it.downloadCount }
|
|
||||||
)
|
|
||||||
|
|
||||||
return LibraryDto(
|
|
||||||
id = libraryId,
|
|
||||||
name = library.name,
|
|
||||||
directories = library.directories.map { DirectoryMappingDto(it.internalPath, it.externalPath) },
|
|
||||||
games = library.games.mapNotNull { it.id },
|
|
||||||
stats = statsDto
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a collection of games to the library.
|
* Adds a collection of games to the library.
|
||||||
*
|
*
|
||||||
@@ -306,4 +279,21 @@ class LibraryService(
|
|||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Library.toDto(): LibraryDto {
|
||||||
|
val libraryId = this.id ?: throw IllegalArgumentException("Library ID is null")
|
||||||
|
|
||||||
|
val statsDto = LibraryStatsDto(
|
||||||
|
gamesCount = this.games.size,
|
||||||
|
downloadedGamesCount = this.games.sumOf { it.downloadCount }
|
||||||
|
)
|
||||||
|
|
||||||
|
return LibraryDto(
|
||||||
|
id = libraryId,
|
||||||
|
name = this.name,
|
||||||
|
directories = this.directories.map { DirectoryMappingDto(it.internalPath, it.externalPath) },
|
||||||
|
games = this.games.mapNotNull { it.id },
|
||||||
|
stats = statsDto
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user