Load download options dynamically from backend

Minor layout and performance improvements
This commit is contained in:
grimsi
2025-05-19 17:59:22 +02:00
parent eab23d48e1
commit afd99d79df
10 changed files with 79 additions and 33 deletions
+9 -1
View File
@@ -20,6 +20,7 @@ import InvitationRegistrationView from "Frontend/views/InvitationRegistrationVie
import PluginManagement from "Frontend/components/administration/PluginManagement"; import PluginManagement from "Frontend/components/administration/PluginManagement";
import {SystemManagement} from "Frontend/components/administration/SystemManagement"; import {SystemManagement} from "Frontend/components/administration/SystemManagement";
import GameView from "Frontend/views/GameView"; import GameView from "Frontend/views/GameView";
import LibraryManagementView from "Frontend/views/LibraryManagementView";
export const routes = protectRoutes([ export const routes = protectRoutes([
{ {
@@ -49,7 +50,14 @@ export const routes = protectRoutes([
path: 'administration', path: 'administration',
element: <AdministrationView/>, element: <AdministrationView/>,
children: [ children: [
{path: 'libraries', element: <LibraryManagement/>}, {
path: 'libraries',
element: <LibraryManagement/>,
children: [{
path: 'library/:libraryId',
element: <LibraryManagementView/>
}]
},
{path: 'users', element: <UserManagement/>}, {path: 'users', element: <UserManagement/>},
{path: 'sso', element: <SsoManagement/>}, {path: 'sso', element: <SsoManagement/>},
{path: 'messages', element: <MessageManagement/>}, {path: 'messages', element: <MessageManagement/>},
+17 -18
View File
@@ -1,6 +1,6 @@
import {useEffect, useState} from "react"; import {useEffect, useState} from "react";
import GameDto from "Frontend/generated/de/grimsi/gameyfin/games/dto/GameDto"; 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 {useParams} from "react-router";
import {GameCover} from "Frontend/components/general/covers/GameCover"; import {GameCover} from "Frontend/components/general/covers/GameCover";
import ComboButton, {ComboButtonOption} from "Frontend/components/general/input/ComboButton"; import ComboButton, {ComboButtonOption} from "Frontend/components/general/input/ComboButton";
@@ -13,24 +13,23 @@ export default function GameView() {
const {gameId} = useParams(); const {gameId} = useParams();
const [game, setGame] = useState<GameDto>(); const [game, setGame] = useState<GameDto>();
const [downloadOptions, setDownloadOptions] = useState<Record<string, ComboButtonOption>>({});
const downloadOptions: Record<string, ComboButtonOption> = { useEffect(() => {
browser: { DownloadProviderEndpoint.getProviders().then((providers) => {
label: "Direct Download", const options: Record<string, ComboButtonOption> = providers.reduce((acc, provider) => {
description: "Download the game in this browser", acc[provider.key] = {
action: () => { label: provider.name,
DownloadEndpoint.downloadGame(parseInt(gameId!), "de.grimsi.gameyfin.plugins.directdownload.DirectDownloadPlugin$DirectDownloadProvider") description: provider.shortDescription ?? provider.description,
} action: () => {
}, if (gameId) DownloadEndpoint.downloadGame(parseInt(gameId!), provider.key);
torrent: { },
label: "Torrent Download", };
description: "Download the game as a torrent", return acc;
action: () => { }, {} as Record<string, ComboButtonOption>);
alert("Torrent download not yet implemented") setDownloadOptions(options);
}, });
isDisabled: true }, []);
}
}
useEffect(() => { useEffect(() => {
if (gameId) { if (gameId) {
@@ -0,0 +1,7 @@
import {useParams} from "react-router";
export default function LibraryManagementView() {
const {libraryId} = useParams();
return (<></>);
}
@@ -15,10 +15,6 @@ class DownloadEndpoint(
private val downloadService: DownloadService, private val downloadService: DownloadService,
private val gameService: GameService private val gameService: GameService
) { ) {
fun getProviders(): List<String> {
return downloadService.getProviders()
}
@GetMapping("/{gameId}") @GetMapping("/{gameId}")
fun downloadGame( fun downloadGame(
@PathVariable gameId: Long, @PathVariable gameId: Long,
@@ -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
)
@@ -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<DownloadProviderDto> {
return downloadService.getProviders()
}
}
@@ -1,5 +1,6 @@
package de.grimsi.gameyfin.core.download 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.core.plugins.management.GameyfinPluginManager
import de.grimsi.gameyfin.pluginapi.download.Download import de.grimsi.gameyfin.pluginapi.download.Download
import de.grimsi.gameyfin.pluginapi.download.DownloadProvider import de.grimsi.gameyfin.pluginapi.download.DownloadProvider
@@ -13,8 +14,18 @@ class DownloadService(
private val downloadPlugins: List<DownloadProvider> private val downloadPlugins: List<DownloadProvider>
get() = pluginManager.getExtensions(DownloadProvider::class.java) get() = pluginManager.getExtensions(DownloadProvider::class.java)
fun getProviders(): List<String> { fun getProviders(): List<DownloadProviderDto> {
return downloadPlugins.map { it.javaClass.name } 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 { fun getDownload(path: String, provider: String): Download {
@@ -59,11 +59,6 @@ class PluginService(
.map { toDto(it) } .map { toDto(it) }
} }
fun getPluginManagementEntry(pluginId: String): PluginManagementEntry {
return pluginManagementRepository.findByIdOrNull(pluginId)
?: throw IllegalArgumentException("Plugin with ID $pluginId not found")
}
fun getPluginManagementEntry(clazz: Class<ExtensionPoint>): PluginManagementEntry { fun getPluginManagementEntry(clazz: Class<ExtensionPoint>): PluginManagementEntry {
val pluginWrapper = pluginManager.whichPlugin(clazz) val pluginWrapper = pluginManager.whichPlugin(clazz)
return pluginManagementRepository.findByIdOrNull(pluginWrapper.pluginId) return pluginManagementRepository.findByIdOrNull(pluginWrapper.pluginId)
@@ -137,6 +132,11 @@ class PluginService(
return pluginManager.validatePluginConfig(pluginId, configToValidate) 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 { private fun toDto(pluginWrapper: PluginWrapper): PluginDto {
val pluginManagementEntry = getPluginManagementEntry(pluginWrapper.pluginId) val pluginManagementEntry = getPluginManagementEntry(pluginWrapper.pluginId)
@@ -183,7 +183,7 @@ class GameService(
// Sort results by plugin priority // Sort results by plugin priority
val sortedResults = results.entries.sortedByDescending { val sortedResults = results.entries.sortedByDescending {
pluginService.getPluginManagementEntry(it.key.javaClass).priority providerToManagementEntry[it.key]?.priority
} }
sortedResults.forEach { (provider, metadata) -> sortedResults.forEach { (provider, metadata) ->
@@ -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-Class: de.grimsi.gameyfin.plugins.directdownload.DirectDownloadPlugin
Plugin-Id: de.grimsi.gameyfin.directdownload Plugin-Id: de.grimsi.gameyfin.directdownload
Plugin-Name: Direct Download Plugin-Name: Direct Download
Plugin-Description: Downloads games directly in the browser.<br> Plugin-Description: Downloads games directly in the browser.<br>
If the game is contained in a folder, it will pack the folder into a zip file on the fly. 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-Author: grimsi
Plugin-License: MIT Plugin-License: MIT
Plugin-Url: https://github.com/gameyfin Plugin-Url: https://github.com/gameyfin