Release v2.2.0 (#741)

* Migrate to TailwindCSS v4 (#740)

* Remove "material-tailwind" dependencies due to incompatibility of Stepper component with Tailwind v4

* Clean up Tailwind configs before upgrade

* Run HeroUI upgrade

* Run TailwindCSS upgrade

* Replace PostCSS with Vite

* Migrate custom styles to v4

* Remove tailwind.config.ts

* Add heroui.ts
Add tailwind vite plugin

* Fix small UI color inconsistency

* Fix theming system
Rename purple theme to pink

* Re-implement stepper in HeroUI

* Fix RoleChip colors

* Migrate icon names (#743)

* Add migration script for phosphor-icons

* Migrate icon usages

* Update version to 2.2.0-preview

* Revert accidental rename of menu title

* Bump stefanzweifel/git-auto-commit-action from 6 to 7 (#750)

Bumps [stefanzweifel/git-auto-commit-action](https://github.com/stefanzweifel/git-auto-commit-action) from 6 to 7.
- [Release notes](https://github.com/stefanzweifel/git-auto-commit-action/releases)
- [Changelog](https://github.com/stefanzweifel/git-auto-commit-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stefanzweifel/git-auto-commit-action/compare/v6...v7)

---
updated-dependencies:
- dependency-name: stefanzweifel/git-auto-commit-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Improve library scanning (#749)

* Update script to generate example libraries using SteamSpy API

* Refactor library scanning process

* Display Flyway startup log by default

* Fix race condition in CompanyService

* Fix race condition in ImageService
Remove obsolete table

* Fix SMTP config requiring an email as username (#755)

* Disable length limit for config values (#757)

* Deprecate DockerHub image (#759)

* Remove deprecation warning from web UI

* Reworked the CICD pipelines

* Optimize container image (#761)

* Fix Gradle warning

* Rework Docker image to improve layer caching

* Bump stefanzweifel/git-auto-commit-action from 6 to 7 (#765)

Bumps [stefanzweifel/git-auto-commit-action](https://github.com/stefanzweifel/git-auto-commit-action) from 6 to 7.
- [Release notes](https://github.com/stefanzweifel/git-auto-commit-action/releases)
- [Changelog](https://github.com/stefanzweifel/git-auto-commit-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stefanzweifel/git-auto-commit-action/compare/v6...v7)

---
updated-dependencies:
- dependency-name: stefanzweifel/git-auto-commit-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Multi platform support (#764)

* Remove migrate-phosphor-icons.js since migration has been successful
* Refactor GameMetadata into separate files
* Add Platform enum
* Implement platform support in Plugin API
* Implement platform support in Steam Plugin
* Implement platform support in IGDB Plugin
* Add database migration for platform support
* Implement platform support in GameService
* Implement platform support on most endpoints and features, some are still missing
Implemented platform support in all bundled plugins (although not finished polishing yet)
* Implement platforms in UI
* Make GameRequest platform aware
* Return headerImages from IGDB
* Implement proper PlatformMapper for IGDB plugin
* Fix various smaller issues and inconsistencies

* Replace placeholder in LibraryOverviewCard (#767)

* Bump actions/download-artifact from 5 to 6 (#769)

* Bump actions/upload-artifact from 4 to 5 (#770)

* Multi platform support (#773)

* Fix bug in Plugin API related to state loading/saving

* Hide Flyway query logs by default

* Extend migration script for multi platform tables

* Plugins now store their data and state in ./plugindata

* Add "plugindata" directory to entrypoint scripts

* Improve download handling (#756)

* Process download in background thread to avoid session timeout affecting it

* Increase default session timeout to 24h

* Use virtual thread pool for download task in background

* Make KSP extensions.idx generation more robust

* Implement download bandwidth limiter
Implement SliderInput
Refactor NumberInput

* Implement download bandwidth throttling
Implement real-time download monitoring

* Improve UI for DownloadManagement
Track more stats in SessionStats

* Update Hilla
Use React 19

* Implement real-time graph to track bandwidth usage
Implement downloaded data sum over last day
Small bug fixes
Small refactorings

* Update docker-compose.example.yml

* Improve DownloadSessionCard (#784)

* Fix unit on y-axis of download graph

* Show game size and library in tooltip
Make game chips interactive in DownloadSessionCard (leads to game page when clicked)
Optimize graph settings

* Migrate torrent plugin to libtorrent (#775)

* Disable TorrentDownloadPlugin in Alpine based Docker image

* Improve test coverage (#785)

* Fix potential divide by zero bug

* Add mockk dependency

* Add tests for org.gameyfin.app.core.download

* Add tests for Filesytem package
Fix DownloadServiceTest

* Fix FilesystemServiceTest

* Add tests for "job" package

* Upgrade Gradle wrapper
Enable Gradle config cache

* Added more tests

* Added tests for the "security" package

* Add tests for "game" package

* Fix AsyncFileTailer not shutting down properly on Windows

* Fix GameServiceTest

* Added tests for "libraries" package

* Added tests for "media" package

* Fix warning in ImageService

* Add tests fpr "messages" package
Make sure transport is closed even in case an exception is thrown

* Add tests for "platforms" package

* Add tests for "requests" package

* Moved "token" package to "core" package (from "shared")

* Add tests for "token" package

* Fix issue in RoleEnum.safeValueOf() throwing Exception

* Fix potential issue in UserEndpoint.getUserInfo() when auth is null

* Added tests for "user" package

* Migrate package for "token" in FE

* Publish test report in CI

* Fix workflow permissions

* Remove test because of timing issue in CI

* Replaced "unmatched paths" with "ignored paths" (#791)

* Use new "AutoComplete" component (#793)

* Use ArrayInputAutocomplete in EditGameMetadataModal

* Add test for getEnumPropertyValues

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
Simon
2025-11-17 08:45:39 +01:00
committed by GitHub
parent dd3b18e5e3
commit 717a423449
357 changed files with 39213 additions and 7918 deletions
+3
View File
@@ -12,6 +12,9 @@ dependencies {
// PF4J (shared)
api("org.pf4j:pf4j:${rootProject.extra["pf4jVersion"]}")
// Logging
implementation("io.github.oshai:kotlin-logging-jvm:7.0.3")
// JSON serialization
compileOnly("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion")
@@ -82,8 +82,7 @@ abstract class ConfigurableGameyfinPlugin(wrapper: PluginWrapper) : GameyfinPlug
*/
override fun <T : Serializable> optionalConfig(key: String): T? {
val meta = resolveMetadata(key)
val value = resolveValue(key)
if (value == null) return null
val value = resolveValue(key) ?: return null
return try {
castConfigValue(meta, value) as T
@@ -101,9 +100,7 @@ abstract class ConfigurableGameyfinPlugin(wrapper: PluginWrapper) : GameyfinPlug
*/
override fun <T : Serializable> config(key: String): T {
val value = optionalConfig<T>(key)
if (value == null) {
throw PluginConfigError("Required configuration key '$key' is missing or has no value")
}
?: throw PluginConfigError("Required configuration key '$key' is missing or has no value")
return value
}
@@ -4,9 +4,10 @@ import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import org.pf4j.Plugin
import org.pf4j.PluginWrapper
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.createFile
import java.nio.file.StandardOpenOption
import kotlin.io.path.exists
import kotlin.io.path.fileSize
@@ -37,7 +38,12 @@ abstract class GameyfinPlugin(wrapper: PluginWrapper) : Plugin(wrapper) {
/**
* State file for the plugin, used to persist plugin-specific state.
*/
val stateFile: Path = Path.of("${wrapper.pluginId}.state.json")
val stateFile: Path = Path.of("plugindata/${wrapper.pluginId}/state.json")
/**
* Plugin data directory for storing plugin-specific files.
*/
val dataDirectory: Path = Path.of("plugindata/${wrapper.pluginId}")
/**
* JSON serializer for serializing and deserializing plugin state.
@@ -53,11 +59,8 @@ abstract class GameyfinPlugin(wrapper: PluginWrapper) : Plugin(wrapper) {
for (format in SUPPORTED_LOGO_FORMATS) {
val resourcePath = "$LOGO_FILE_NAME.$format"
val inputStream = wrapper.pluginClassLoader.getResourceAsStream(resourcePath)
if (inputStream != null) {
return true
}
if (inputStream != null) return true
}
return false
}
@@ -70,25 +73,37 @@ abstract class GameyfinPlugin(wrapper: PluginWrapper) : Plugin(wrapper) {
for (format in SUPPORTED_LOGO_FORMATS) {
val resourcePath = "$LOGO_FILE_NAME.$format"
val inputStream = wrapper.pluginClassLoader.getResourceAsStream(resourcePath)
if (inputStream != null) {
return inputStream.readAllBytes()
}
if (inputStream != null) return inputStream.readAllBytes()
}
return null
}
inline fun <reified T> loadState(): T? {
if (!stateFile.exists() || stateFile.fileSize() == 0L) return null
return Files.newBufferedReader(stateFile).use {
objectMapper.readValue(it.readText(), T::class.java)
return try {
Files.newBufferedReader(stateFile).use {
objectMapper.readValue(it.readText(), T::class.java)
}
} catch (_: Exception) {
null
}
}
inline fun <reified T> saveState(state: T) {
if (!stateFile.exists()) stateFile.createFile()
Files.newBufferedWriter(stateFile).use {
it.write(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(state))
try {
// Ensure parent directories always exist
Files.createDirectories(stateFile.parent)
// Write JSON, creating or truncating the file atomically
Files.newBufferedWriter(
stateFile,
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING,
StandardOpenOption.WRITE
).use {
it.write(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(state))
}
} catch (e: IOException) {
throw IllegalStateException("Failed to persist plugin state to $stateFile", e)
}
}
}
@@ -0,0 +1,33 @@
package org.gameyfin.pluginapi.gamemetadata
/**
* Enum representing the features of a game.
*/
enum class GameFeature(
val displayName: String
) {
SINGLEPLAYER("Singleplayer"),
MULTIPLAYER("Multiplayer"),
CO_OP("Co-op"),
CROSS_PLATFORM("Cross-Platform"),
MODDING("Modding"),
VR("VR"),
AR("AR"),
CLOUD_SAVES("Cloud Saves"),
CLOUD_PLAY("Cloud Play"),
ACHIEVEMENTS("Achievements"),
LEADERBOARDS("Leaderboards"),
WORKSHOP("Workshop"),
CONTROLLER_SUPPORT("Controller Support"),
REMOTE_PLAY("Remote Play"),
LOCAL_MULTIPLAYER("Local Multiplayer"),
LOCAL_CO_OP("Local Co-op"),
ONLINE_MULTIPLAYER("Online Multiplayer"),
ONLINE_CO_OP("Online Co-op"),
ONLINE_PVP("Online PvP"),
ONLINE_PVE("Online PvE"),
LOCAL_PVP("Local PvP"),
LOCAL_PVE("Local PvE"),
CROSSPLAY("Crossplay"),
SPLITSCREEN("Splitscreen")
}
@@ -27,9 +27,10 @@ import java.time.Instant
data class GameMetadata(
val originalId: String,
val title: String,
val platforms: Set<Platform>? = null,
val description: String? = null,
val coverUrls: List<URI>? = null,
val headerUrls: List<URI>? = null,
val coverUrls: Set<URI>? = null,
val headerUrls: Set<URI>? = null,
val release: Instant? = null,
val userRating: Int? = null,
val criticRating: Int? = null,
@@ -42,103 +43,4 @@ data class GameMetadata(
val videoUrls: Set<URI>? = null,
val features: Set<GameFeature>? = null,
val perspectives: Set<PlayerPerspective>? = null
)
/**
* Enum representing the genre of a game.
*/
enum class Genre {
UNKNOWN,
ACTION,
PINBALL,
ADVENTURE,
INDIE,
ARCADE,
VISUAL_NOVEL,
CARD_AND_BOARD_GAME,
MOBA,
MMO,
POINT_AND_CLICK,
FIGHTING,
SHOOTER,
MUSIC,
PLATFORM,
PUZZLE,
RACING,
REAL_TIME_STRATEGY,
ROLE_PLAYING,
SIMULATOR,
SPORT,
STRATEGY,
TURN_BASED_STRATEGY,
TACTICAL,
HACK_AND_SLASH_BEAT_EM_UP,
QUIZ_TRIVIA
}
/**
* Enum representing the theme of a game.
*/
enum class Theme {
UNKNOWN,
ACTION,
FANTASY,
SCIENCE_FICTION,
HORROR,
THRILLER,
SURVIVAL,
HISTORICAL,
STEALTH,
COMEDY,
BUSINESS,
DRAMA,
NON_FICTION,
SANDBOX,
EDUCATIONAL,
KIDS,
OPEN_WORLD,
WARFARE,
PARTY,
FOUR_X,
MYSTERY,
EROTIC,
ROMANCE
}
enum class GameFeature {
SINGLEPLAYER,
MULTIPLAYER,
CO_OP,
CROSS_PLATFORM,
MODDING,
VR,
AR,
CLOUD_SAVES,
CLOUD_PLAY,
ACHIEVEMENTS,
LEADERBOARDS,
WORKSHOP,
CONTROLLER_SUPPORT,
REMOTE_PLAY,
LOCAL_MULTIPLAYER,
LOCAL_CO_OP,
ONLINE_MULTIPLAYER,
ONLINE_CO_OP,
ONLINE_PVP,
ONLINE_PVE,
LOCAL_PVP,
LOCAL_PVE,
CROSSPLAY,
SPLITSCREEN
}
enum class PlayerPerspective {
UNKNOWN,
FIRST_PERSON,
THIRD_PERSON,
BIRD_VIEW_ISOMETRIC,
SIDE_VIEW,
TEXT,
AUDITORY,
VIRTUAL_REALITY
}
)
@@ -9,14 +9,23 @@ import org.pf4j.ExtensionPoint
* This is typically used to allow plugins to provide metadata for games from various sources.
*/
interface GameMetadataProvider : ExtensionPoint {
/**
* Returns the set of platforms supported by this metadata provider.
*
* @return A set of [Platform] enums indicating supported platforms.
*/
val supportedPlatforms: Set<Platform>
/**
* Fetches a list of game metadata entries matching the given game title.
*
* @param gameTitle The title of the game to search for.
* @param platformFilter A set of [Platform] enums to filter the search results. Only games available on these platforms should be returned.
* @param maxResults The maximum number of results to return. Defaults to 1.
* @return A list of [GameMetadata] objects matching the title, or an empty list if none found.
*/
fun fetchByTitle(gameTitle: String, maxResults: Int = 1): List<GameMetadata>
fun fetchByTitle(gameTitle: String, platformFilter: Set<Platform>, maxResults: Int = 1): List<GameMetadata>
/**
* Fetches game metadata by its unique identifier.
@@ -0,0 +1,35 @@
package org.gameyfin.pluginapi.gamemetadata
/**
* Enum representing the genres of a game.
*/
enum class Genre(
val displayName: String
) {
UNKNOWN("Unknown"),
ACTION("Action"),
PINBALL("Pinball"),
ADVENTURE("Adventure"),
INDIE("Indie"),
ARCADE("Arcade"),
VISUAL_NOVEL("Visual Novel"),
CARD_AND_BOARD_GAME("Card & Board Game"),
MOBA("MOBA"),
MMO("MMO"),
POINT_AND_CLICK("Point-and-Click"),
FIGHTING("Fighting"),
SHOOTER("Shooter"),
MUSIC("Music"),
PLATFORM("Platform"),
PUZZLE("Puzzle"),
RACING("Racing"),
REAL_TIME_STRATEGY("Real-Time Strategy"),
ROLE_PLAYING("Role-Playing"),
SIMULATOR("Simulator"),
SPORT("Sport"),
STRATEGY("Strategy"),
TURN_BASED_STRATEGY("Turn-Based Strategy"),
TACTICAL("Tactical"),
HACK_AND_SLASH_BEAT_EM_UP("Hack and Slash/Beat 'em up"),
QUIZ_TRIVIA("Quiz/Trivia")
}
@@ -0,0 +1,277 @@
package org.gameyfin.pluginapi.gamemetadata
/**
* Enum representing the platforms of a game.
* Source: https://api.igdb.com/v4/platforms
*/
@Suppress("Unused", "EnumEntryName")
enum class Platform(
val displayName: String
) {
// 0-9 (prefixed with underscore because TypeScript enum entries cannot start with a number)
_1292_ADVANCED_PROGRAMMABLE_VIDEO_SYSTEM("1292 Advanced Programmable Video System"),
_3DO_INTERACTIVE_MULTIPLAYER("3DO Interactive Multiplayer"),
_64DD("64DD"),
// A
ACORN_ARCHIMEDES("Acorn Archimedes"),
ACORN_ELECTRON("Acorn Electron"),
ADVANCED_PICO_BEENA("Advanced Pico Beena"),
AIRCONSOLE("AirConsole"),
AMAZON_FIRE_TV("Amazon Fire TV"),
AMIGA("Amiga"),
AMIGA_CD32("Amiga CD32"),
AMSTRAD_CPC("Amstrad CPC"),
AMSTRAD_GX4000("Amstrad GX4000"),
AMSTRAD_PCW("Amstrad PCW"),
ANDROID("Android"),
ANALOGUE_ELECTRONICS("Analogue electronics"),
ARCADE("Arcade"),
ARCADIA_2001("Arcadia 2001"),
ARDUBOY("Arduboy"),
APPLE_II("Apple II"),
APPLE_IIGS("Apple IIGS"),
APPLE_PIPPIN("Apple Pippin"),
ATARI_2600("Atari 2600"),
ATARI_5200("Atari 5200"),
ATARI_7800("Atari 7800"),
ATARI_8_BIT("Atari 8-bit"),
ATARI_JAGUAR("Atari Jaguar"),
ATARI_JAGUAR_CD("Atari Jaguar CD"),
ATARI_LYNX("Atari Lynx"),
ATARI_ST_STE("Atari ST/STE"),
AY_3_8500("AY-3-8500"),
AY_3_8603("AY-3-8603"),
AY_3_8605("AY-3-8605"),
AY_3_8606("AY-3-8606"),
AY_3_8607("AY-3-8607"),
AY_3_8610("AY-3-8610"),
AY_3_8710("AY-3-8710"),
AY_3_8760("AY-3-8760"),
// B
BALLY_ASTROCADE("Bally Astrocade"),
BBC_MICROCOMPUTER_SYSTEM("BBC Microcomputer System"),
BLACKBERRY_OS("BlackBerry OS"),
BLU_RAY_PLAYER("Blu-ray Player"),
// C
CALL_A_COMPUTER_TIME_SHARED_MAINFRAME_COMPUTER_SYSTEM("Call-A-Computer time-shared mainframe computer system"),
CASIO_LOOPY("Casio Loopy"),
CDC_CYBER_70("CDC Cyber 70"),
COLECOVISION("ColecoVision"),
COMMODORE_16("Commodore 16"),
COMMODORE_C64_128_MAX("Commodore C64/128/MAX"),
COMMODORE_CDTV("Commodore CDTV"),
COMMODORE_PET("Commodore PET"),
COMMODORE_PLUS_4("Commodore Plus/4"),
COMMODORE_VIC_20("Commodore VIC-20"),
// D
DAYDREAM("Daydream"),
DEC_GT40("DEC GT40"),
DIGIBLAST("Digiblast"),
DONNER_MODEL_30("Donner Model 30"),
DOS("DOS"),
DRAGON_32_64("Dragon 32/64"),
DREAMCAST("Dreamcast"),
DUPLICATE_STADIA("DUPLICATE Stadia"),
DVD_PLAYER("DVD Player"),
// E
EDSAC("EDSAC"),
ELEKTOR_TV_GAMES_COMPUTER("Elektor TV Games Computer"),
EPOCH_CASSETTE_VISION("Epoch Cassette Vision"),
EPOCH_SUPER_CASSETTE_VISION("Epoch Super Cassette Vision"),
EVERCADE("Evercade"),
EXIDY_SORCERER("Exidy Sorcerer"),
E_READER_CARD_E_READER("e-Reader / Card-e Reader"),
// F
FAMILY_COMPUTER("Family Computer"),
FAMILY_COMPUTER_DISK_SYSTEM("Family Computer Disk System"),
FAIRCHILD_CHANNEL_F("Fairchild Channel F"),
FERRANTI_NIMROD_COMPUTER("Ferranti Nimrod Computer"),
FM_TOWNS("FM Towns"),
FM_7("FM-7"),
// G
GAME_AND_WATCH("Game & Watch"),
GAMATE("Gamate"),
GAME_BOY("Game Boy"),
GAME_BOY_ADVANCE("Game Boy Advance"),
GAME_BOY_COLOR("Game Boy Color"),
GAME_COM("Game.com"),
GEAR_VR("Gear VR"),
GIZMONDO("Gizmondo"),
GOOGLE_STADIA("Google Stadia"),
// H
HANDHELD_ELECTRONIC_LCD("Handheld Electronic LCD"),
HP_2100("HP 2100"),
HP_3000("HP 3000"),
HYPER_NEO_GEO_64("Hyper Neo Geo 64"),
HYPERSCAN("HyperScan"),
// I
IMLAC_PDS_1("Imlac PDS-1"),
INTELLIVISION("Intellivision"),
INTELLIVISION_AMICO("Intellivision Amico"),
IOS("iOS"),
// L
LASERACTIVE("LaserActive"),
LEAPSTER("Leapster"),
LEAPSTER_EXPLORER_LEADPAD_EXPLORER("Leapster Explorer/LeadPad Explorer"),
LEAPTV("LeapTV"),
LEGACY_COMPUTER("Legacy Computer"),
LEGACY_MOBILE_DEVICE("Legacy Mobile Device"),
LINUX("Linux"),
// M
MAC("Mac"),
MEGA_DUCK_COUGAR_BOY("Mega Duck/Cougar Boy"),
META_QUEST_2("Meta Quest 2"),
META_QUEST_3("Meta Quest 3"),
MICROCOMPUTER("Microcomputer"),
MICROVISION("Microvision"),
MSX("MSX"),
MSX2("MSX2"),
// N
N_GAGE("N-Gage"),
NEC_PC_6000_SERIES("NEC PC-6000 Series"),
NEO_GEO_AES("Neo Geo AES"),
NEO_GEO_CD("Neo Geo CD"),
NEO_GEO_MVS("Neo Geo MVS"),
NEO_GEO_POCKET("Neo Geo Pocket"),
NEO_GEO_POCKET_COLOR("Neo Geo Pocket Color"),
NEW_NINTENDO_3DS("New Nintendo 3DS"),
NINTENDO_3DS("Nintendo 3DS"),
NINTENDO_64("Nintendo 64"),
NINTENDO_DS("Nintendo DS"),
NINTENDO_DSI("Nintendo DSi"),
NINTENDO_ENTERTAINMENT_SYSTEM("Nintendo Entertainment System"),
NINTENDO_GAMECUBE("Nintendo GameCube"),
NINTENDO_SWITCH("Nintendo Switch"),
NINTENDO_SWITCH_2("Nintendo Switch 2"),
NUON("Nuon"),
// O
OCULUS_GO("Oculus Go"),
OCULUS_QUEST("Oculus Quest"),
OCULUS_RIFT("Oculus Rift"),
OCULUS_VR("Oculus VR"),
ODYSSEY("Odyssey"),
ODYSSEY_2_VIDEOPAC_G7000("Odyssey 2 / Videopac G7000"),
ONLIVE_GAME_SYSTEM("OnLive Game System"),
OOPARTS("OOParts"),
OUYA("Ouya"),
// P
PALM_OS("Palm OS"),
PANASONIC_JUNGLE("Panasonic Jungle"),
PANASONIC_M2("Panasonic M2"),
PC_MICROSOFT_WINDOWS("PC (Microsoft Windows)"),
PC_50X_FAMILY("PC-50X Family"),
PC_8800_SERIES("PC-8800 Series"),
PC_9800_SERIES("PC-9800 Series"),
PC_ENGINE_SUPERGRAFX("PC Engine SuperGrafx"),
PC_FX("PC-FX"),
PDP_1("PDP-1"),
PDP_7("PDP-7"),
PDP_8("PDP-8"),
PDP_10("PDP-10"),
PDP_11("PDP-11"),
PHILIPS_CD_I("Philips CD-i"),
PLATO("PLATO"),
PLAYDATE("Playdate"),
PLAYDIA("Playdia"),
PLAYSTATION("PlayStation"),
PLAYSTATION_2("PlayStation 2"),
PLAYSTATION_3("PlayStation 3"),
PLAYSTATION_4("PlayStation 4"),
PLAYSTATION_5("PlayStation 5"),
PLAYSTATION_PORTABLE("PlayStation Portable"),
PLAYSTATION_VITA("PlayStation Vita"),
PLAYSTATION_VR("PlayStation VR"),
PLAYSTATION_VR2("PlayStation VR2"),
PLUG_AND_PLAY("Plug & Play"),
POCKETSTATION("PocketStation"),
POKEMON_MINI("Pokémon mini"),
POLYMEGA("Polymega"),
// R
R_ZONE("R-Zone"),
// S
SATELLAVIEW("Satellaview"),
SDS_SIGMA_7("SDS Sigma 7"),
SEGA_32X("Sega 32X"),
SEGA_CD("Sega CD"),
SEGA_CD_32X("Sega CD 32X"),
SEGA_GAME_GEAR("Sega Game Gear"),
SEGA_MASTER_SYSTEM_MARK_III("Sega Master System/Mark III"),
SEGA_MEGA_DRIVE_GENESIS("Sega Mega Drive/Genesis"),
SEGA_PICO("Sega Pico"),
SEGA_SATURN("Sega Saturn"),
SG_1000("SG-1000"),
SHARP_MZ_2200("Sharp MZ-2200"),
SHARP_X1("Sharp X1"),
SHARP_X68000("Sharp X68000"),
SINCLAIR_QL("Sinclair QL"),
SINCLAIR_ZX81("Sinclair ZX81"),
SOL_20("Sol-20"),
STEAMVR("SteamVR"),
SUPER_ACAN("Super A'Can"),
SUPER_FAMICOM("Super Famicom"),
SUPER_NES_CD_ROM_SYSTEM("Super NES CD-ROM System"),
SUPER_NINTENDO_ENTERTAINMENT_SYSTEM("Super Nintendo Entertainment System"),
SWANCRYSTAL("SwanCrystal"),
// T
TAPWAVE_ZODIAC("Tapwave Zodiac"),
TATUNG_EINSTEIN("Tatung Einstein"),
TEXAS_INSTRUMENTS_TI_99("Texas Instruments TI-99"),
TEREBIKKO_SEE_N_SAY_VIDEO_PHONE("Terebikko / See 'n Say Video Phone"),
THOMSON_MO5("Thomson MO5"),
TOMY_TUTOR_PYUTA_GRANDSTAND_TUTOR("Tomy Tutor / Pyuta / Grandstand Tutor"),
TRS_80("TRS-80"),
TRS_80_COLOR_COMPUTER("TRS-80 Color Computer"),
TURBOGRAFX_16_PC_ENGINE_CD("Turbografx-16/PC Engine CD"),
TURBOGRAFX_16_PC_ENGINE("TurboGrafx-16/PC Engine"),
// U
UZEBOX("Uzebox"),
// V
V_SMILE("V.Smile"),
VC_4000("VC 4000"),
VECTREX("Vectrex"),
VIRTUAL_BOY("Virtual Boy"),
VIRTUAL_CONSOLE("Virtual Console"),
VISIONOS("visionOS"),
VISUAL_MEMORY_UNIT_VISUAL_MEMORY_SYSTEM("Visual Memory Unit / Visual Memory System"),
// W
WATARA_QUICKSHOT_SUPERVISION("Watara/QuickShot Supervision"),
WEB_BROWSER("Web browser"),
WII("Wii"),
WII_U("Wii U"),
WINDOWS_MOBILE("Windows Mobile"),
WINDOWS_MIXED_REALITY("Windows Mixed Reality"),
WINDOWS_PHONE("Windows Phone"),
WONDERSWAN("WonderSwan"),
WONDERSWAN_COLOR("WonderSwan Color"),
// X
XBOX("Xbox"),
XBOX_360("Xbox 360"),
XBOX_ONE("Xbox One"),
XBOX_SERIES_X_S("Xbox Series X|S"),
// Z
ZEEBO("Zeebo"),
ZX_SPECTRUM("ZX Spectrum");
}
@@ -0,0 +1,17 @@
package org.gameyfin.pluginapi.gamemetadata
/**
* Enum representing the perspectives of a game.
*/
enum class PlayerPerspective(
val displayName: String
) {
UNKNOWN("Unknown"),
FIRST_PERSON("First-Person"),
THIRD_PERSON("Third-Person"),
BIRD_VIEW_ISOMETRIC("Bird View/Isometric"),
SIDE_VIEW("Side View"),
TEXT("Text"),
AUDITORY("Auditory"),
VIRTUAL_REALITY("Virtual Reality")
}
@@ -0,0 +1,32 @@
package org.gameyfin.pluginapi.gamemetadata
/**
* Enum representing the themes of a game.
*/
enum class Theme(
val displayName: String
) {
UNKNOWN("Unknown"),
ACTION("Action"),
FANTASY("Fantasy"),
SCIENCE_FICTION("Science Fiction"),
HORROR("Horror"),
THRILLER("Thriller"),
SURVIVAL("Survival"),
HISTORICAL("Historical"),
STEALTH("Stealth"),
COMEDY("Comedy"),
BUSINESS("Business"),
DRAMA("Drama"),
NON_FICTION("Non-Fiction"),
SANDBOX("Sandbox"),
EDUCATIONAL("Educational"),
KIDS("Kids"),
OPEN_WORLD("Open World"),
WARFARE("Warfare"),
PARTY("Party"),
FOUR_X("4X"),
MYSTERY("Mystery"),
EROTIC("Erotic"),
ROMANCE("Romance")
}