From afd99d79df2dc780d235f38401c567da1752b175 Mon Sep 17 00:00:00 2001
From: grimsi <9295182+grimsi@users.noreply.github.com>
Date: Mon, 19 May 2025 17:59:22 +0200
Subject: [PATCH] Load download options dynamically from backend Minor layout
and performance improvements
---
gameyfin/src/main/frontend/routes.tsx | 10 +++++-
gameyfin/src/main/frontend/views/GameView.tsx | 35 +++++++++----------
.../frontend/views/LibraryManagementView.tsx | 7 ++++
.../core/download/DownloadEndpoint.kt | 4 ---
.../core/download/DownloadProviderDto.kt | 11 ++++++
.../core/download/DownloadProviderEndpoint.kt | 14 ++++++++
.../gameyfin/core/download/DownloadService.kt | 15 ++++++--
.../gameyfin/core/plugins/PluginService.kt | 10 +++---
.../de/grimsi/gameyfin/games/GameService.kt | 2 +-
.../src/main/resources/MANIFEST.MF | 4 +--
10 files changed, 79 insertions(+), 33 deletions(-)
create mode 100644 gameyfin/src/main/frontend/views/LibraryManagementView.tsx
create mode 100644 gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadProviderDto.kt
create mode 100644 gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadProviderEndpoint.kt
diff --git a/gameyfin/src/main/frontend/routes.tsx b/gameyfin/src/main/frontend/routes.tsx
index 42c591f..40388e7 100644
--- a/gameyfin/src/main/frontend/routes.tsx
+++ b/gameyfin/src/main/frontend/routes.tsx
@@ -20,6 +20,7 @@ import InvitationRegistrationView from "Frontend/views/InvitationRegistrationVie
import PluginManagement from "Frontend/components/administration/PluginManagement";
import {SystemManagement} from "Frontend/components/administration/SystemManagement";
import GameView from "Frontend/views/GameView";
+import LibraryManagementView from "Frontend/views/LibraryManagementView";
export const routes = protectRoutes([
{
@@ -49,7 +50,14 @@ export const routes = protectRoutes([
path: 'administration',
element: ,
children: [
- {path: 'libraries', element: },
+ {
+ path: 'libraries',
+ element: ,
+ children: [{
+ path: 'library/:libraryId',
+ element:
+ }]
+ },
{path: 'users', element: },
{path: 'sso', element: },
{path: 'messages', element: },
diff --git a/gameyfin/src/main/frontend/views/GameView.tsx b/gameyfin/src/main/frontend/views/GameView.tsx
index 42f7fbe..0c4a120 100644
--- a/gameyfin/src/main/frontend/views/GameView.tsx
+++ b/gameyfin/src/main/frontend/views/GameView.tsx
@@ -1,6 +1,6 @@
import {useEffect, useState} from "react";
import GameDto from "Frontend/generated/de/grimsi/gameyfin/games/dto/GameDto";
-import {GameEndpoint} from "Frontend/generated/endpoints";
+import {DownloadProviderEndpoint, GameEndpoint} from "Frontend/generated/endpoints";
import {useParams} from "react-router";
import {GameCover} from "Frontend/components/general/covers/GameCover";
import ComboButton, {ComboButtonOption} from "Frontend/components/general/input/ComboButton";
@@ -13,24 +13,23 @@ export default function GameView() {
const {gameId} = useParams();
const [game, setGame] = useState();
+ const [downloadOptions, setDownloadOptions] = useState>({});
- const downloadOptions: Record = {
- browser: {
- label: "Direct Download",
- description: "Download the game in this browser",
- action: () => {
- DownloadEndpoint.downloadGame(parseInt(gameId!), "de.grimsi.gameyfin.plugins.directdownload.DirectDownloadPlugin$DirectDownloadProvider")
- }
- },
- torrent: {
- label: "Torrent Download",
- description: "Download the game as a torrent",
- action: () => {
- alert("Torrent download not yet implemented")
- },
- isDisabled: true
- }
- }
+ useEffect(() => {
+ DownloadProviderEndpoint.getProviders().then((providers) => {
+ const options: Record = providers.reduce((acc, provider) => {
+ acc[provider.key] = {
+ label: provider.name,
+ description: provider.shortDescription ?? provider.description,
+ action: () => {
+ if (gameId) DownloadEndpoint.downloadGame(parseInt(gameId!), provider.key);
+ },
+ };
+ return acc;
+ }, {} as Record);
+ setDownloadOptions(options);
+ });
+ }, []);
useEffect(() => {
if (gameId) {
diff --git a/gameyfin/src/main/frontend/views/LibraryManagementView.tsx b/gameyfin/src/main/frontend/views/LibraryManagementView.tsx
new file mode 100644
index 0000000..2d7cacb
--- /dev/null
+++ b/gameyfin/src/main/frontend/views/LibraryManagementView.tsx
@@ -0,0 +1,7 @@
+import {useParams} from "react-router";
+
+export default function LibraryManagementView() {
+ const {libraryId} = useParams();
+
+ return (<>>);
+}
\ No newline at end of file
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadEndpoint.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadEndpoint.kt
index 1695895..4d6ca24 100644
--- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadEndpoint.kt
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadEndpoint.kt
@@ -15,10 +15,6 @@ class DownloadEndpoint(
private val downloadService: DownloadService,
private val gameService: GameService
) {
- fun getProviders(): List {
- return downloadService.getProviders()
- }
-
@GetMapping("/{gameId}")
fun downloadGame(
@PathVariable gameId: Long,
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadProviderDto.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadProviderDto.kt
new file mode 100644
index 0000000..db7c7dc
--- /dev/null
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadProviderDto.kt
@@ -0,0 +1,11 @@
+package de.grimsi.gameyfin.core.download
+
+import com.fasterxml.jackson.annotation.JsonInclude
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+data class DownloadProviderDto(
+ val key: String,
+ val name: String,
+ val description: String,
+ val shortDescription: String? = null
+)
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadProviderEndpoint.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadProviderEndpoint.kt
new file mode 100644
index 0000000..d38aacb
--- /dev/null
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadProviderEndpoint.kt
@@ -0,0 +1,14 @@
+package de.grimsi.gameyfin.core.download
+
+import com.vaadin.hilla.Endpoint
+import jakarta.annotation.security.PermitAll
+
+@Endpoint
+@PermitAll
+class DownloadProviderEndpoint(
+ private val downloadService: DownloadService
+) {
+ fun getProviders(): List {
+ return downloadService.getProviders()
+ }
+}
\ No newline at end of file
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadService.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadService.kt
index d86fe4e..81b9ab4 100644
--- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadService.kt
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/download/DownloadService.kt
@@ -1,5 +1,6 @@
package de.grimsi.gameyfin.core.download
+import de.grimsi.gameyfin.core.plugins.management.GameyfinPluginDescriptor
import de.grimsi.gameyfin.core.plugins.management.GameyfinPluginManager
import de.grimsi.gameyfin.pluginapi.download.Download
import de.grimsi.gameyfin.pluginapi.download.DownloadProvider
@@ -13,8 +14,18 @@ class DownloadService(
private val downloadPlugins: List
get() = pluginManager.getExtensions(DownloadProvider::class.java)
- fun getProviders(): List {
- return downloadPlugins.map { it.javaClass.name }
+ fun getProviders(): List {
+ return downloadPlugins.map {
+ val plugin = pluginManager.whichPlugin(it.javaClass.enclosingClass)
+ val descriptor = plugin.descriptor as GameyfinPluginDescriptor
+
+ DownloadProviderDto(
+ key = it.javaClass.name,
+ name = descriptor.pluginName,
+ description = descriptor.pluginDescription,
+ shortDescription = descriptor.pluginShortDescription,
+ )
+ }
}
fun getDownload(path: String, provider: String): Download {
diff --git a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/PluginService.kt b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/PluginService.kt
index f3b89ce..7ce1d0c 100644
--- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/PluginService.kt
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/core/plugins/PluginService.kt
@@ -59,11 +59,6 @@ class PluginService(
.map { toDto(it) }
}
- 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)
@@ -137,6 +132,11 @@ class PluginService(
return pluginManager.validatePluginConfig(pluginId, configToValidate)
}
+ private fun getPluginManagementEntry(pluginId: String): PluginManagementEntry {
+ return pluginManagementRepository.findByIdOrNull(pluginId)
+ ?: throw IllegalArgumentException("Plugin with ID $pluginId not found")
+ }
+
private fun toDto(pluginWrapper: PluginWrapper): PluginDto {
val pluginManagementEntry = getPluginManagementEntry(pluginWrapper.pluginId)
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 aafdeb3..99e8a05 100644
--- a/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/GameService.kt
+++ b/gameyfin/src/main/kotlin/de/grimsi/gameyfin/games/GameService.kt
@@ -183,7 +183,7 @@ class GameService(
// Sort results by plugin priority
val sortedResults = results.entries.sortedByDescending {
- pluginService.getPluginManagementEntry(it.key.javaClass).priority
+ providerToManagementEntry[it.key]?.priority
}
sortedResults.forEach { (provider, metadata) ->
diff --git a/plugins/directdownload/src/main/resources/MANIFEST.MF b/plugins/directdownload/src/main/resources/MANIFEST.MF
index 36a2de7..252addc 100644
--- a/plugins/directdownload/src/main/resources/MANIFEST.MF
+++ b/plugins/directdownload/src/main/resources/MANIFEST.MF
@@ -1,10 +1,10 @@
-Plugin-Version: 1.0.0-alpha2
+Plugin-Version: 1.0.0-alpha3
Plugin-Class: de.grimsi.gameyfin.plugins.directdownload.DirectDownloadPlugin
Plugin-Id: de.grimsi.gameyfin.directdownload
Plugin-Name: Direct Download
Plugin-Description: Downloads games directly in the browser.
If the game is contained in a folder, it will pack the folder into a zip file on the fly.
-Plugin-Short-Description: Download games directly in the browser
+Plugin-Short-Description: Download directly in the browser
Plugin-Author: grimsi
Plugin-License: MIT
Plugin-Url: https://github.com/gameyfin