mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-16 16:20:04 +00:00
Finish plugin support implementation
Small refactoring
This commit is contained in:
@@ -1,69 +1,29 @@
|
|||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import Section from "Frontend/components/general/Section";
|
import {PluginManagementEndpoint} from "Frontend/generated/endpoints";
|
||||||
import {PluginConfigEndpoint} from "Frontend/generated/endpoints";
|
import PluginDto from "Frontend/generated/de/grimsi/gameyfin/core/plugins/management/PluginDto";
|
||||||
import {Form, Formik} from "formik";
|
import {PluginManagementCard} from "Frontend/components/general/PluginManagementCard";
|
||||||
import {Check} from "@phosphor-icons/react";
|
import {Divider} from "@nextui-org/react";
|
||||||
import {Button} from "@nextui-org/react";
|
|
||||||
import Input from "Frontend/components/general/Input";
|
|
||||||
|
|
||||||
export default function PluginManagement() {
|
export default function PluginManagement() {
|
||||||
const [configSaved, setConfigSaved] = useState(false);
|
const [plugins, setPlugins] = useState<PluginDto[]>([]);
|
||||||
const [igdbConfigMeta, setIgdbConfigMeta] = useState<any>();
|
|
||||||
const [igdbConfig, setIgdbConfig] = useState<any>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
PluginConfigEndpoint.getConfigMetadata("igdb").then(setIgdbConfigMeta);
|
PluginManagementEndpoint.getPlugins().then((response) => {
|
||||||
PluginConfigEndpoint.getConfig("igdb").then(setIgdbConfig);
|
if (response === undefined) return;
|
||||||
|
setPlugins(response as PluginDto[]);
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (configSaved) {
|
|
||||||
setTimeout(() => setConfigSaved(false), 2000);
|
|
||||||
}
|
|
||||||
}, [configSaved])
|
|
||||||
|
|
||||||
async function handleSubmit(values: any) {
|
|
||||||
await PluginConfigEndpoint.setConfigEntries("igdb", values);
|
|
||||||
setConfigSaved(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="flex flex-col">
|
||||||
<Formik
|
<div className="flex flex-row flex-grow justify-between mb-8">
|
||||||
initialValues={{
|
<h2 className="text-2xl font-bold">Plugins</h2>
|
||||||
clientId: igdbConfig?.clientId,
|
</div>
|
||||||
clientSecret: igdbConfig?.clientSecret
|
|
||||||
}}
|
|
||||||
enableReinitialize={true}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
>
|
|
||||||
{(formik: { values: any; isSubmitting: any; }) => (
|
|
||||||
<Form>
|
|
||||||
<div className="flex flex-row flex-grow justify-between mb-8">
|
|
||||||
<h2 className="text-2xl font-bold">Plugins</h2>
|
|
||||||
<div className="flex flex-row items-center gap-4">
|
|
||||||
<Button
|
|
||||||
color="primary"
|
|
||||||
isLoading={formik.isSubmitting}
|
|
||||||
disabled={formik.isSubmitting || configSaved}
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
{formik.isSubmitting ? "" : configSaved ? <Check/> : "Save"}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-row flex-1 justify-between gap-16">
|
<Divider className="mb-4"/>
|
||||||
<div className="flex flex-col flex-grow">
|
<div className="grid grid-cols-300px gap-4">
|
||||||
<Section title="IGDB"/>
|
{plugins.map((plugin) => <PluginManagementCard plugin={plugin} key={plugin.name}/>)}
|
||||||
{igdbConfigMeta && igdbConfigMeta.map((entry: any) => (
|
</div>
|
||||||
<Input key={entry.key} name={entry.key} label={entry.name} type="text"/>
|
</div>
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
)}
|
|
||||||
</Formik>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -26,11 +26,7 @@ interface Role {
|
|||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AssignRolesModal({
|
export default function AssignRolesModal({isOpen, onOpenChange, user}: AssignRolesModalProps) {
|
||||||
isOpen,
|
|
||||||
onOpenChange,
|
|
||||||
user
|
|
||||||
}: AssignRolesModalProps) {
|
|
||||||
const [availableRoles, setAvailableRoles] = useState<Role[]>([]);
|
const [availableRoles, setAvailableRoles] = useState<Role[]>([]);
|
||||||
const [selectedRole, setSelectedRole] = useState<Selection>();
|
const [selectedRole, setSelectedRole] = useState<Selection>();
|
||||||
const [error, setError] = useState<string>();
|
const [error, setError] = useState<string>();
|
||||||
|
|||||||
@@ -9,11 +9,7 @@ interface ConfirmUserDeletionModalProps {
|
|||||||
user: UserInfoDto;
|
user: UserInfoDto;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ConfirmUserDeletionModal({
|
export default function ConfirmUserDeletionModal({isOpen, onOpenChange, user}: ConfirmUserDeletionModalProps) {
|
||||||
isOpen,
|
|
||||||
onOpenChange,
|
|
||||||
user
|
|
||||||
}: ConfirmUserDeletionModalProps) {
|
|
||||||
const [confirmUsername, setConfirmUsername] = useState<string>("");
|
const [confirmUsername, setConfirmUsername] = useState<string>("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -8,10 +8,7 @@ interface InviteUserModalProps {
|
|||||||
onOpenChange: () => void;
|
onOpenChange: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function InviteUserModal({
|
export default function InviteUserModal({isOpen, onOpenChange}: InviteUserModalProps) {
|
||||||
isOpen,
|
|
||||||
onOpenChange
|
|
||||||
}: InviteUserModalProps) {
|
|
||||||
const [email, setEmail] = useState<string | null>();
|
const [email, setEmail] = useState<string | null>();
|
||||||
const [error, setError] = useState<string | null>();
|
const [error, setError] = useState<string | null>();
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,7 @@ interface PasswordResetTokenModalProps {
|
|||||||
token: TokenDto;
|
token: TokenDto;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PasswordResetTokenModal({
|
export default function PasswordResetTokenModal({isOpen, onOpenChange, token}: PasswordResetTokenModalProps) {
|
||||||
isOpen,
|
|
||||||
onOpenChange,
|
|
||||||
token
|
|
||||||
}: PasswordResetTokenModalProps) {
|
|
||||||
const [timeUntilExpiry, setTimeUntilExpiry] = useState<string>("");
|
const [timeUntilExpiry, setTimeUntilExpiry] = useState<string>("");
|
||||||
|
|
||||||
const timeoutRefresh = setInterval(updateTimeUntilExpiry, 1000);
|
const timeoutRefresh = setInterval(updateTimeUntilExpiry, 1000);
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import React, {useEffect, useState} from "react";
|
||||||
|
import {Button, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader} from "@nextui-org/react";
|
||||||
|
import {toast} from "sonner";
|
||||||
|
import {Form, Formik} from "formik";
|
||||||
|
import {PluginConfigEndpoint} from "Frontend/generated/endpoints";
|
||||||
|
import PluginDto from "Frontend/generated/de/grimsi/gameyfin/core/plugins/management/PluginDto";
|
||||||
|
import PluginConfigElement from "Frontend/generated/de/grimsi/gameyfin/pluginapi/core/PluginConfigElement";
|
||||||
|
import Input from "Frontend/components/general/Input";
|
||||||
|
|
||||||
|
interface PluginConfigurationModalProps {
|
||||||
|
plugin: PluginDto;
|
||||||
|
isOpen: boolean;
|
||||||
|
onOpenChange: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PluginConfigurationModal({plugin, isOpen, onOpenChange}: PluginConfigurationModalProps) {
|
||||||
|
const [pluginConfigMeta, setPluginConfigMeta] = useState<(PluginConfigElement)[]>();
|
||||||
|
const [pluginConfig, setPluginConfig] = useState<Record<string, string>>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
PluginConfigEndpoint.getConfigMetadata(plugin.id).then(response => {
|
||||||
|
if (response === undefined) return;
|
||||||
|
setPluginConfigMeta(response as PluginConfigElement[]);
|
||||||
|
});
|
||||||
|
PluginConfigEndpoint.getConfig(plugin.id).then(response => {
|
||||||
|
if (response === undefined) return;
|
||||||
|
setPluginConfig(response as Record<string, string>);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
async function saveConfig(values: Record<string, string>) {
|
||||||
|
await PluginConfigEndpoint.setConfigEntries(plugin.id, values);
|
||||||
|
toast.success(`Configuration for ${plugin.name} saved!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} onOpenChange={onOpenChange} backdrop="opaque" size="lg">
|
||||||
|
<ModalContent>
|
||||||
|
{(onClose) => (
|
||||||
|
<Formik initialValues={pluginConfig}
|
||||||
|
enableReinitialize={true}
|
||||||
|
onSubmit={async (values: any) => {
|
||||||
|
await saveConfig(values);
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{(formik: { isSubmitting: any; }) => (
|
||||||
|
<Form>
|
||||||
|
<ModalHeader className="flex flex-col gap-1">{plugin.name} configuration</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
{pluginConfigMeta && pluginConfigMeta.map((entry: any) => (
|
||||||
|
<Input key={entry.key} name={entry.key} label={entry.name} type="text"/>
|
||||||
|
))}
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button variant="light" onPress={onClose}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
isLoading={formik.isSubmitting}
|
||||||
|
disabled={formik.isSubmitting}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
{formik.isSubmitting ? "" : "Save"}
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
)}
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import {Card, Chip, Tooltip, useDisclosure} from "@nextui-org/react";
|
||||||
|
import {PuzzlePiece} from "@phosphor-icons/react";
|
||||||
|
import PluginDto from "Frontend/generated/de/grimsi/gameyfin/core/plugins/management/PluginDto";
|
||||||
|
import PluginState from "Frontend/generated/org/pf4j/PluginState";
|
||||||
|
import React from "react";
|
||||||
|
import PluginConfigurationModal from "Frontend/components/general/PluginConfigurationModal";
|
||||||
|
|
||||||
|
export function PluginManagementCard({plugin}: { plugin: PluginDto }) {
|
||||||
|
const pluginConfigurationModal = useDisclosure();
|
||||||
|
|
||||||
|
function stateToColor(state: PluginState | undefined): string {
|
||||||
|
switch (state) {
|
||||||
|
case PluginState.STARTED:
|
||||||
|
return "success";
|
||||||
|
case PluginState.DISABLED:
|
||||||
|
return "warning";
|
||||||
|
case PluginState.STOPPED:
|
||||||
|
return "danger";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Card className="flex flex-row justify-between p-2"
|
||||||
|
isPressable={true} onPress={pluginConfigurationModal.onOpen}>
|
||||||
|
<div className="flex flex-row items-center gap-4">
|
||||||
|
<Tooltip placement="right" content={`Plugin ${plugin.state!.toLowerCase()}`}>
|
||||||
|
<PuzzlePiece size={64} weight="duotone" className={`text-${stateToColor(plugin.state)}`}/>
|
||||||
|
</Tooltip>
|
||||||
|
<div className="flex flex-col items-start gap-1">
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<p className="font-semibold">{plugin.name}</p>
|
||||||
|
<div className="text-sm">
|
||||||
|
<Chip size="sm" radius="sm" className="text-xs">{plugin.version}</Chip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm">Author: {plugin.author}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
<PluginConfigurationModal plugin={plugin}
|
||||||
|
isOpen={pluginConfigurationModal.isOpen}
|
||||||
|
onOpenChange={pluginConfigurationModal.onOpenChange}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package de.grimsi.gameyfin.core.plugins
|
package de.grimsi.gameyfin.core.plugins.config
|
||||||
|
|
||||||
import com.vaadin.hilla.Endpoint
|
import com.vaadin.hilla.Endpoint
|
||||||
import de.grimsi.gameyfin.core.Role
|
import de.grimsi.gameyfin.core.Role
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package de.grimsi.gameyfin.core.plugins
|
package de.grimsi.gameyfin.core.plugins.config
|
||||||
|
|
||||||
import de.grimsi.gameyfin.core.security.EncryptionConverter
|
import de.grimsi.gameyfin.core.security.EncryptionConverter
|
||||||
import jakarta.persistence.Column
|
import jakarta.persistence.Column
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package de.grimsi.gameyfin.core.plugins
|
package de.grimsi.gameyfin.core.plugins.config
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
|
||||||
+9
-1
@@ -1,7 +1,9 @@
|
|||||||
package de.grimsi.gameyfin.core.plugins
|
package de.grimsi.gameyfin.core.plugins.config
|
||||||
|
|
||||||
|
import de.grimsi.gameyfin.core.plugins.management.GameyfinPluginManager
|
||||||
import de.grimsi.gameyfin.pluginapi.core.GameyfinPlugin
|
import de.grimsi.gameyfin.pluginapi.core.GameyfinPlugin
|
||||||
import de.grimsi.gameyfin.pluginapi.core.PluginConfigElement
|
import de.grimsi.gameyfin.pluginapi.core.PluginConfigElement
|
||||||
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -10,22 +12,28 @@ class PluginConfigService(
|
|||||||
private val pluginManager: GameyfinPluginManager
|
private val pluginManager: GameyfinPluginManager
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
private val log = KotlinLogging.logger {}
|
||||||
|
|
||||||
fun getConfigMetadata(pluginId: String): List<PluginConfigElement> {
|
fun getConfigMetadata(pluginId: String): List<PluginConfigElement> {
|
||||||
|
log.info { "Getting config metadata for plugin $pluginId" }
|
||||||
val plugin = pluginManager.getPlugin(pluginId).plugin as GameyfinPlugin
|
val plugin = pluginManager.getPlugin(pluginId).plugin as GameyfinPlugin
|
||||||
return plugin.configMetadata
|
return plugin.configMetadata
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getConfig(pluginId: String): Map<String, String?> {
|
fun getConfig(pluginId: String): Map<String, String?> {
|
||||||
|
log.info { "Getting config for plugin $pluginId" }
|
||||||
return pluginConfigRepository.findAllById_PluginId(pluginId).associate { it.id.key to it.value }
|
return pluginConfigRepository.findAllById_PluginId(pluginId).associate { it.id.key to it.value }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setConfigEntries(pluginId: String, config: Map<String, String>) {
|
fun setConfigEntries(pluginId: String, config: Map<String, String>) {
|
||||||
|
log.info { "Setting config entries for plugin $pluginId" }
|
||||||
val entries = config.map { PluginConfigEntry(PluginConfigEntryKey(pluginId, it.key), it.value) }
|
val entries = config.map { PluginConfigEntry(PluginConfigEntryKey(pluginId, it.key), it.value) }
|
||||||
pluginConfigRepository.saveAll(entries)
|
pluginConfigRepository.saveAll(entries)
|
||||||
pluginManager.restart(pluginId)
|
pluginManager.restart(pluginId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setConfigEntry(pluginId: String, key: String, value: String) {
|
fun setConfigEntry(pluginId: String, key: String, value: String) {
|
||||||
|
log.info { "Setting config entry $key for plugin $pluginId" }
|
||||||
val entry = PluginConfigEntry(PluginConfigEntryKey(pluginId, key), value)
|
val entry = PluginConfigEntry(PluginConfigEntryKey(pluginId, key), value)
|
||||||
pluginConfigRepository.save(entry)
|
pluginConfigRepository.save(entry)
|
||||||
pluginManager.restart(pluginId)
|
pluginManager.restart(pluginId)
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package de.grimsi.gameyfin.core.plugins
|
package de.grimsi.gameyfin.core.plugins.management
|
||||||
|
|
||||||
import org.pf4j.DevelopmentPluginLoader
|
import org.pf4j.DevelopmentPluginLoader
|
||||||
import org.pf4j.PluginClassLoader
|
import org.pf4j.PluginClassLoader
|
||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
package de.grimsi.gameyfin.core.plugins
|
package de.grimsi.gameyfin.core.plugins.management
|
||||||
|
|
||||||
|
import de.grimsi.gameyfin.core.plugins.config.PluginConfigRepository
|
||||||
import de.grimsi.gameyfin.pluginapi.core.GameyfinPlugin
|
import de.grimsi.gameyfin.pluginapi.core.GameyfinPlugin
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import org.pf4j.CompoundPluginLoader
|
import org.pf4j.CompoundPluginLoader
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package de.grimsi.gameyfin.core.plugins.management
|
||||||
|
|
||||||
|
import org.pf4j.PluginState
|
||||||
|
|
||||||
|
data class PluginDto(
|
||||||
|
val id: String,
|
||||||
|
val name: String,
|
||||||
|
val version: String,
|
||||||
|
val author: String,
|
||||||
|
val state: PluginState
|
||||||
|
)
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package de.grimsi.gameyfin.core.plugins.management
|
||||||
|
|
||||||
|
import com.vaadin.hilla.Endpoint
|
||||||
|
import de.grimsi.gameyfin.core.Role
|
||||||
|
import jakarta.annotation.security.RolesAllowed
|
||||||
|
|
||||||
|
@Endpoint
|
||||||
|
@RolesAllowed(Role.Names.ADMIN)
|
||||||
|
class PluginManagementEndpoint(
|
||||||
|
private val pluginManagementService: PluginManagementService
|
||||||
|
) {
|
||||||
|
fun getPlugins() = pluginManagementService.getPlugins()
|
||||||
|
|
||||||
|
fun startPlugin(pluginId: String) = pluginManagementService.startPlugin(pluginId)
|
||||||
|
|
||||||
|
fun stopPlugin(pluginId: String) = pluginManagementService.stopPlugin(pluginId)
|
||||||
|
|
||||||
|
fun restartPlugin(pluginId: String) = pluginManagementService.restartPlugin(pluginId)
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
package de.grimsi.gameyfin.core.plugins.management
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class PluginManagementService(
|
||||||
|
private val pluginManager: GameyfinPluginManager
|
||||||
|
) {
|
||||||
|
fun getPlugins(): List<PluginDto> {
|
||||||
|
return pluginManager.plugins.map {
|
||||||
|
PluginDto(
|
||||||
|
it.pluginId,
|
||||||
|
it.descriptor.pluginDescription,
|
||||||
|
it.descriptor.version,
|
||||||
|
it.descriptor.provider,
|
||||||
|
it.pluginState
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startPlugin(pluginId: String) {
|
||||||
|
pluginManager.startPlugin(pluginId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopPlugin(pluginId: String) {
|
||||||
|
pluginManager.stopPlugin(pluginId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun restartPlugin(pluginId: String) {
|
||||||
|
pluginManager.restart(pluginId)
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
package de.grimsi.gameyfin.core.plugins
|
package de.grimsi.gameyfin.core.plugins.management
|
||||||
|
|
||||||
|
import de.grimsi.gameyfin.core.plugins.config.PluginConfigRepository
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import org.springframework.boot.context.event.ApplicationReadyEvent
|
import org.springframework.boot.context.event.ApplicationReadyEvent
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
@@ -9,11 +9,8 @@ import de.grimsi.gameyfin.pluginapi.core.PluginConfigElement
|
|||||||
import de.grimsi.gameyfin.pluginapi.core.PluginConfigError
|
import de.grimsi.gameyfin.pluginapi.core.PluginConfigError
|
||||||
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadata
|
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadata
|
||||||
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadataFetcher
|
import de.grimsi.gameyfin.pluginapi.gamemetadata.GameMetadataFetcher
|
||||||
import de.grimsi.gameyfin.pluginapi.gamemetadata.Genre
|
|
||||||
import de.grimsi.gameyfin.pluginapi.gamemetadata.Theme
|
|
||||||
import org.pf4j.Extension
|
import org.pf4j.Extension
|
||||||
import org.pf4j.PluginWrapper
|
import org.pf4j.PluginWrapper
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import kotlin.collections.filter
|
import kotlin.collections.filter
|
||||||
|
|
||||||
@@ -52,8 +49,6 @@ class IgdbPlugin(wrapper: PluginWrapper) : GameyfinPlugin(wrapper) {
|
|||||||
|
|
||||||
@Extension
|
@Extension
|
||||||
class IgdbMetadataFetcher : GameMetadataFetcher {
|
class IgdbMetadataFetcher : GameMetadataFetcher {
|
||||||
private val log = LoggerFactory.getLogger(javaClass)
|
|
||||||
|
|
||||||
override fun fetchMetadata(gameId: String): GameMetadata {
|
override fun fetchMetadata(gameId: String): GameMetadata {
|
||||||
val findGameByName = APICalypse()
|
val findGameByName = APICalypse()
|
||||||
.fields("*")
|
.fields("*")
|
||||||
@@ -71,76 +66,13 @@ class IgdbPlugin(wrapper: PluginWrapper) : GameyfinPlugin(wrapper) {
|
|||||||
criticRating = game.aggregatedRating.toInt(),
|
criticRating = game.aggregatedRating.toInt(),
|
||||||
developedBy = game.involvedCompaniesList.filter { it.developer }.map { it.company.name },
|
developedBy = game.involvedCompaniesList.filter { it.developer }.map { it.company.name },
|
||||||
publishedBy = game.involvedCompaniesList.filter { it.publisher }.map { it.company.name },
|
publishedBy = game.involvedCompaniesList.filter { it.publisher }.map { it.company.name },
|
||||||
genres = game.genresList.map { mapGenre(it) },
|
genres = game.genresList.map { Mapper.genre(it) },
|
||||||
themes = game.themesList.map { mapTheme(it) },
|
themes = game.themesList.map { Mapper.theme(it) },
|
||||||
screenshotUrls = listOf(),
|
screenshotUrls = listOf(),
|
||||||
videoUrls = listOf(),
|
videoUrls = listOf(),
|
||||||
features = listOf(),
|
features = listOf(),
|
||||||
perspectives = listOf()
|
perspectives = listOf()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mapGenre(genre: proto.Genre): Genre {
|
|
||||||
return when (genre.slug) {
|
|
||||||
"pinball" -> Genre.PINBALL
|
|
||||||
"adventure" -> Genre.ADVENTURE
|
|
||||||
"indie" -> Genre.INDIE
|
|
||||||
"arcade" -> Genre.ARCADE
|
|
||||||
"visual-novel" -> Genre.VISUAL_NOVEL
|
|
||||||
"card-and-board-game" -> Genre.CARD_AND_BOARD_GAME
|
|
||||||
"moba" -> Genre.MOBA
|
|
||||||
"point-and-click" -> Genre.POINT_AND_CLICK
|
|
||||||
"fighting" -> Genre.FIGHTING
|
|
||||||
"shooter" -> Genre.SHOOTER
|
|
||||||
"music" -> Genre.MUSIC
|
|
||||||
"platform" -> Genre.PLATFORM
|
|
||||||
"puzzle" -> Genre.PUZZLE
|
|
||||||
"racing" -> Genre.RACING
|
|
||||||
"real-time-strategy-rts" -> Genre.REAL_TIME_STRATEGY
|
|
||||||
"role-playing-rpg" -> Genre.ROLE_PLAYING
|
|
||||||
"simulator" -> Genre.SIMULATOR
|
|
||||||
"sport" -> Genre.SPORT
|
|
||||||
"strategy" -> Genre.STRATEGY
|
|
||||||
"turn-based-strategy-tbs" -> Genre.TURN_BASED_STRATEGY
|
|
||||||
"tactical" -> Genre.TACTICAL
|
|
||||||
"hack-and-slash-beat-em-up" -> Genre.HACK_AND_SLASH_BEAT_EM_UP
|
|
||||||
"quiz-trivia" -> Genre.QUIZ_TRIVIA
|
|
||||||
else -> {
|
|
||||||
log.warn("Unknown genre: {}", genre.slug)
|
|
||||||
Genre.UNKNOWN
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun mapTheme(theme: proto.Theme): Theme {
|
|
||||||
return when (theme.slug) {
|
|
||||||
"action" -> Theme.ACTION
|
|
||||||
"fantasy" -> Theme.FANTASY
|
|
||||||
"horror" -> Theme.HORROR
|
|
||||||
"sci-fi" -> Theme.SCIENCE_FICTION
|
|
||||||
"mystery" -> Theme.MYSTERY
|
|
||||||
"thriller" -> Theme.THRILLER
|
|
||||||
"survival" -> Theme.SURVIVAL
|
|
||||||
"historical" -> Theme.HISTORICAL
|
|
||||||
"stealth" -> Theme.STEALTH
|
|
||||||
"comedy" -> Theme.COMEDY
|
|
||||||
"business" -> Theme.BUSINESS
|
|
||||||
"drama" -> Theme.DRAMA
|
|
||||||
"non-fiction" -> Theme.NON_FICTION
|
|
||||||
"sandbox" -> Theme.SANDBOX
|
|
||||||
"educational" -> Theme.EDUCATIONAL
|
|
||||||
"kids" -> Theme.KIDS
|
|
||||||
"open-world" -> Theme.OPEN_WORLD
|
|
||||||
"warfare" -> Theme.WARFARE
|
|
||||||
"party" -> Theme.PARTY
|
|
||||||
"4x-explore-expand-exploit-and-exterminate" -> Theme.FOUR_X
|
|
||||||
"erotic" -> Theme.EROTIC
|
|
||||||
"romance" -> Theme.ROMANCE
|
|
||||||
else -> {
|
|
||||||
log.warn("Unknown theme: {}", theme.slug)
|
|
||||||
Theme.UNKNOWN
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package de.grimsi.gameyfin.plugins.igdb
|
||||||
|
|
||||||
|
import de.grimsi.gameyfin.pluginapi.gamemetadata.Genre
|
||||||
|
import de.grimsi.gameyfin.pluginapi.gamemetadata.Theme
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
class Mapper {
|
||||||
|
companion object {
|
||||||
|
private val log = LoggerFactory.getLogger(javaClass)
|
||||||
|
|
||||||
|
fun genre(genre: proto.Genre): Genre {
|
||||||
|
return when (genre.slug) {
|
||||||
|
"pinball" -> Genre.PINBALL
|
||||||
|
"adventure" -> Genre.ADVENTURE
|
||||||
|
"indie" -> Genre.INDIE
|
||||||
|
"arcade" -> Genre.ARCADE
|
||||||
|
"visual-novel" -> Genre.VISUAL_NOVEL
|
||||||
|
"card-and-board-game" -> Genre.CARD_AND_BOARD_GAME
|
||||||
|
"moba" -> Genre.MOBA
|
||||||
|
"point-and-click" -> Genre.POINT_AND_CLICK
|
||||||
|
"fighting" -> Genre.FIGHTING
|
||||||
|
"shooter" -> Genre.SHOOTER
|
||||||
|
"music" -> Genre.MUSIC
|
||||||
|
"platform" -> Genre.PLATFORM
|
||||||
|
"puzzle" -> Genre.PUZZLE
|
||||||
|
"racing" -> Genre.RACING
|
||||||
|
"real-time-strategy-rts" -> Genre.REAL_TIME_STRATEGY
|
||||||
|
"role-playing-rpg" -> Genre.ROLE_PLAYING
|
||||||
|
"simulator" -> Genre.SIMULATOR
|
||||||
|
"sport" -> Genre.SPORT
|
||||||
|
"strategy" -> Genre.STRATEGY
|
||||||
|
"turn-based-strategy-tbs" -> Genre.TURN_BASED_STRATEGY
|
||||||
|
"tactical" -> Genre.TACTICAL
|
||||||
|
"hack-and-slash-beat-em-up" -> Genre.HACK_AND_SLASH_BEAT_EM_UP
|
||||||
|
"quiz-trivia" -> Genre.QUIZ_TRIVIA
|
||||||
|
else -> {
|
||||||
|
log.warn("Unknown genre: {}", genre.slug)
|
||||||
|
Genre.UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun theme(theme: proto.Theme): Theme {
|
||||||
|
return when (theme.slug) {
|
||||||
|
"action" -> Theme.ACTION
|
||||||
|
"fantasy" -> Theme.FANTASY
|
||||||
|
"horror" -> Theme.HORROR
|
||||||
|
"sci-fi" -> Theme.SCIENCE_FICTION
|
||||||
|
"mystery" -> Theme.MYSTERY
|
||||||
|
"thriller" -> Theme.THRILLER
|
||||||
|
"survival" -> Theme.SURVIVAL
|
||||||
|
"historical" -> Theme.HISTORICAL
|
||||||
|
"stealth" -> Theme.STEALTH
|
||||||
|
"comedy" -> Theme.COMEDY
|
||||||
|
"business" -> Theme.BUSINESS
|
||||||
|
"drama" -> Theme.DRAMA
|
||||||
|
"non-fiction" -> Theme.NON_FICTION
|
||||||
|
"sandbox" -> Theme.SANDBOX
|
||||||
|
"educational" -> Theme.EDUCATIONAL
|
||||||
|
"kids" -> Theme.KIDS
|
||||||
|
"open-world" -> Theme.OPEN_WORLD
|
||||||
|
"warfare" -> Theme.WARFARE
|
||||||
|
"party" -> Theme.PARTY
|
||||||
|
"4x-explore-expand-exploit-and-exterminate" -> Theme.FOUR_X
|
||||||
|
"erotic" -> Theme.EROTIC
|
||||||
|
"romance" -> Theme.ROMANCE
|
||||||
|
else -> {
|
||||||
|
log.warn("Unknown theme: {}", theme.slug)
|
||||||
|
Theme.UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
-7
@@ -1,7 +0,0 @@
|
|||||||
package de.grimsi.gameyfin.plugins.igdb.dto
|
|
||||||
|
|
||||||
data class TwitchOAuthTokenDto(
|
|
||||||
val accessToken: String,
|
|
||||||
val expiresIn: Int,
|
|
||||||
val tokenType: String
|
|
||||||
)
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
Manifest-Version: 1.0
|
Manifest-Version: 1.0
|
||||||
Plugin-Id: igdb
|
|
||||||
Plugin-Class: de.grimsi.gameyfin.plugins.igdb.IgdbPlugin
|
Plugin-Class: de.grimsi.gameyfin.plugins.igdb.IgdbPlugin
|
||||||
Plugin-Version: 1.0.0-SNAPSHOT
|
Plugin-Id: igdb
|
||||||
|
Plugin-Description: IGDB Plugin
|
||||||
|
Plugin-Version: 1.0.0-alpha1
|
||||||
Plugin-Provider: grimsi
|
Plugin-Provider: grimsi
|
||||||
|
|||||||
Reference in New Issue
Block a user