Unstable Feature
Gameyfin's plugin API is currently in an unstable state.
Expect breaking changes until the release of 2.0.0 (probably even after that).
Gameyfin 2.5.0 introduces breaking changes in the plugin API.
Update existing plugins to the new interfaces before compiling them against the latest plugin-api artifact.
How to create a Plugin
The plugin system is based on PF4J which allows you to create plugins in Java or Kotlin.
Depending on the type of plugin you want to create, you will need to implement the appropriate interfaces.
The Gameyfin Plugin API is published as a Maven artifact
on Maven Central, so you can easily include it in your
project.
For a quick start, you can follow the tutorial.
Structure of a Plugin
A Gameyfin plugin typically consists of the following components:
Plugin Manifest
The plugin manifest is a Manifest file that contains metadata about the plugin.
It should be located in the META-INF/MANIFEST.MF file of your plugin JAR file.
| Field | Description |
|---|---|
| Plugin-Version | Version of the plugin in SemVer format |
| Plugin-Class | Fully qualified class name of your plugin wrapper class |
| Plugin-Id | Unique ID for your plugin. It is recommended to either use a unique reverse-DNS identifier or a UUID. |
| Plugin-Name | Name of your plugin |
| Plugin-Description | Description of the functionality of your plugin in Markdown format. Use <br> for line breaks and start suceeding lines with one space. |
| Plugin-Author | Name(s) of the plugin authors (if the plugin has multiple authors, use a comma-separated string) |
| Plugin-Short-Description1 | Shorter description of your plugin (not in Markdown). Used primarily for display in various smaller UI elements (falls back to "Plugin-Description" if empty). |
| Plugin-Url1 | URL to the website of your plugin (e.g. the GitHub repository containing the plugin source code) |
| Plugin-License1 | Name of the license for the plugin (e.g. "MIT", "AGLPv3", ...) |
Source Code
Gameyfin plugins are written in Java or Kotlin (although any language that compiles to JVM bytecode can be used in theory). The source code of the plugin should be organized in a way that follows the standard Java/Kotlin project structure.
Plugin Wrapper Class
The plugin wrapper class is the main class of your plugin that extends either GameyfinPlugin or
ConfigurableGameyfinPlugin (depending on whether your plugin requires configuration).
Your wrapper class should contain one or more inner classes that implement the necessary interfaces for your plugin
type.
These inner classes will be used by Gameyfin to interact with your plugin and should be annotated with @Extension.
Depending on the type of plugin, you will need to implement specific methods that Gameyfin will call to interact with
your plugin.
Configurable Plugins
If your plugin requires configuration, you should extend ConfigurableGameyfinPlugin instead of GameyfinPlugin.
This also means that your plugin will have a configuration page in the Admin UI.
In order for Gameyfin to be able to display the configuration page, your plugin needs to implement two things:
- A
configMetadataproperty of typeConfigMetadatathat contains the metadata of your plugin configuration. - The
validateConfigmethod that validates the configuration and returns aPluginConfigValidationResult.
For an example of how to implement plugin configuration, take a look at the tutorial.
Reading data from Gameyfin
Gameyfin plugins can read a snapshot of the host application's data through GameyfinDataService.
The service is available via PluginContext.dataService and GameyfinPlugin.dataService, and it exposes read-only
snapshots of games, libraries and collections.
This is especially useful for plugins that register tasks, because tasks can inspect the current host state without mutating it. For example, a task can count all games in the installation, filter them by IDs selected in the task UI, or generate reports based on the configured libraries.
Available methods include:
getGames()/getGames(ids)/getGame(id)getGamesOfLibrary(libraryId)getLibraries()/getLibrary(id)getCollections()/getCollection(id)
Registering Tasks
Starting with Gameyfin 2.5.0, plugins can contribute background tasks by implementing GameyfinTask and annotating
the implementation with @Extension.
A task can define:
- a stable
keythat is used for scheduling and persisted settings, - a human-readable
nameanddescription, - an optional default cron schedule,
- settings that administrators can change in the UI,
- parameters that admins can pass when manually starting the task,
- and the
execute(context)method that performs the work.
Task implementations receive a TaskContext that provides:
setting(key)andparameter(key)accessors,- cancellation checks via
isCancellationRequested/throwIfCancelled(), - progress reporting with
progress(...), - and
wrap(...)helpers to keep logs attached to work done on other threads.
See the tutorial for a small end-to-end example that combines a metadata provider with a task that reads host data.