mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-17 08:15:44 +00:00
@@ -52,3 +52,4 @@ out/
|
|||||||
/app/src/main/frontend/**/*.js.map
|
/app/src/main/frontend/**/*.js.map
|
||||||
/app/src/main/frontend/generated/
|
/app/src/main/frontend/generated/
|
||||||
/torrent_dotfiles/
|
/torrent_dotfiles/
|
||||||
|
*.state.json
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import com.vanniktech.maven.publish.SonatypeHost
|
import com.vanniktech.maven.publish.SonatypeHost
|
||||||
|
|
||||||
|
val jacksonVersion = "2.19.1"
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm")
|
kotlin("jvm")
|
||||||
`java-library`
|
`java-library`
|
||||||
@@ -11,6 +13,10 @@ group = "org.gameyfin"
|
|||||||
dependencies {
|
dependencies {
|
||||||
// PF4J (shared)
|
// PF4J (shared)
|
||||||
api("org.pf4j:pf4j:${rootProject.extra["pf4jVersion"]}")
|
api("org.pf4j:pf4j:${rootProject.extra["pf4jVersion"]}")
|
||||||
|
|
||||||
|
// JSON serialization
|
||||||
|
compileOnly("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
|
||||||
|
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion")
|
||||||
}
|
}
|
||||||
|
|
||||||
mavenPublishing {
|
mavenPublishing {
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
package org.gameyfin.pluginapi.core.wrapper
|
package org.gameyfin.pluginapi.core.wrapper
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
import org.pf4j.Plugin
|
import org.pf4j.Plugin
|
||||||
import org.pf4j.PluginWrapper
|
import org.pf4j.PluginWrapper
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.createFile
|
||||||
|
import kotlin.io.path.exists
|
||||||
|
import kotlin.io.path.fileSize
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for all Gameyfin plugins.
|
* Abstract base class for all Gameyfin plugins.
|
||||||
@@ -25,20 +32,17 @@ abstract class GameyfinPlugin(wrapper: PluginWrapper) : Plugin(wrapper) {
|
|||||||
* Supported logo file formats.
|
* Supported logo file formats.
|
||||||
*/
|
*/
|
||||||
val SUPPORTED_LOGO_FORMATS: List<String> = listOf("png", "jpg", "jpeg", "gif", "svg", "webp")
|
val SUPPORTED_LOGO_FORMATS: List<String> = listOf("png", "jpg", "jpeg", "gif", "svg", "webp")
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to the current plugin instance.
|
|
||||||
*/
|
|
||||||
lateinit var plugin: GameyfinPlugin
|
|
||||||
private set
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the plugin and sets the static plugin reference.
|
* State file for the plugin, used to persist plugin-specific state.
|
||||||
*/
|
*/
|
||||||
init {
|
val stateFile: Path = Path.of("${wrapper.pluginId}.state.json")
|
||||||
plugin = this
|
|
||||||
}
|
/**
|
||||||
|
* JSON serializer for serializing and deserializing plugin state.
|
||||||
|
*/
|
||||||
|
val objectMapper: ObjectMapper = ObjectMapper().registerModule(KotlinModule.Builder().build())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the plugin contains a logo file in any supported format.
|
* Checks if the plugin contains a logo file in any supported format.
|
||||||
@@ -73,4 +77,18 @@ abstract class GameyfinPlugin(wrapper: PluginWrapper) : Plugin(wrapper) {
|
|||||||
|
|
||||||
return null
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T> saveState(state: T) {
|
||||||
|
if (!stateFile.exists()) stateFile.createFile()
|
||||||
|
Files.newBufferedWriter(stateFile).use {
|
||||||
|
it.write(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(state))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,7 @@ subprojects {
|
|||||||
apply(plugin = "org.jetbrains.kotlin.jvm")
|
apply(plugin = "org.jetbrains.kotlin.jvm")
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
compileOnly(kotlin("stdlib"))
|
||||||
compileOnly(project(":plugin-api"))
|
compileOnly(project(":plugin-api"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+43
-17
@@ -33,6 +33,8 @@ class TorrentDownloadPlugin(wrapper: PluginWrapper) : ConfigurableGameyfinPlugin
|
|||||||
private lateinit var communicationManager: CommunicationManager
|
private lateinit var communicationManager: CommunicationManager
|
||||||
|
|
||||||
private lateinit var plugin: TorrentDownloadPlugin
|
private lateinit var plugin: TorrentDownloadPlugin
|
||||||
|
|
||||||
|
private lateinit var state: TorrentDownloadPluginState
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -70,14 +72,8 @@ class TorrentDownloadPlugin(wrapper: PluginWrapper) : ConfigurableGameyfinPlugin
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@OptIn(ExperimentalPathApi::class)
|
|
||||||
override fun start() {
|
override fun start() {
|
||||||
// Currently Gameyfin does not support storing plugin state
|
|
||||||
// and since we can't associate the torrent files with a game path after a restart
|
|
||||||
// we just delete the directory on startup.
|
|
||||||
if (Files.exists(TORRENT_FILE_DIRECTORY)) {
|
|
||||||
TORRENT_FILE_DIRECTORY.deleteRecursively()
|
|
||||||
}
|
|
||||||
Files.createDirectories(TORRENT_FILE_DIRECTORY)
|
Files.createDirectories(TORRENT_FILE_DIRECTORY)
|
||||||
|
|
||||||
tracker = Tracker(config("trackerPort"), getTrackerUri().toString())
|
tracker = Tracker(config("trackerPort"), getTrackerUri().toString())
|
||||||
@@ -95,6 +91,27 @@ class TorrentDownloadPlugin(wrapper: PluginWrapper) : ConfigurableGameyfinPlugin
|
|||||||
SelectorFactoryImpl(),
|
SelectorFactoryImpl(),
|
||||||
FirstAvailableChannel(clientPort, clientPort)
|
FirstAvailableChannel(clientPort, clientPort)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
state = loadState<TorrentDownloadPluginState>() ?: TorrentDownloadPluginState()
|
||||||
|
|
||||||
|
state.torrentFilesMetadata.forEach {
|
||||||
|
// Check if the torrent and game files exist and
|
||||||
|
// that the game files have not been modified since the torrent file was created
|
||||||
|
if (Files.exists(it.torrentFile) && Files.exists(it.gameFile) &&
|
||||||
|
it.gameFile.getLastModifiedTime().toInstant().isBefore(it.torrentFile.getLastModifiedTime().toInstant())
|
||||||
|
) {
|
||||||
|
tracker.announce(TrackedTorrent.load(it.torrentFile.toFile()))
|
||||||
|
communicationManager.addTorrent(
|
||||||
|
it.torrentFile.toString(),
|
||||||
|
getRootPath(it.gameFile).toString(),
|
||||||
|
FullyPieceStorageFactory.INSTANCE
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
state.torrentFilesMetadata.remove(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveState(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stop() {
|
override fun stop() {
|
||||||
@@ -146,6 +163,14 @@ class TorrentDownloadPlugin(wrapper: PluginWrapper) : ConfigurableGameyfinPlugin
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getRootPath(gameFilesPath: Path): Path {
|
||||||
|
return if (gameFilesPath.isDirectory()) {
|
||||||
|
gameFilesPath
|
||||||
|
} else {
|
||||||
|
gameFilesPath.parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Extension(ordinal = 2)
|
@Extension(ordinal = 2)
|
||||||
class TorrentDownloadProvider : DownloadProvider {
|
class TorrentDownloadProvider : DownloadProvider {
|
||||||
private val log = LoggerFactory.getLogger(TorrentDownloadProvider::class.java)
|
private val log = LoggerFactory.getLogger(TorrentDownloadProvider::class.java)
|
||||||
@@ -180,10 +205,19 @@ class TorrentDownloadPlugin(wrapper: PluginWrapper) : ConfigurableGameyfinPlugin
|
|||||||
tracker.announce(TrackedTorrent.load(torrentFile.toFile()))
|
tracker.announce(TrackedTorrent.load(torrentFile.toFile()))
|
||||||
communicationManager.addTorrent(
|
communicationManager.addTorrent(
|
||||||
torrentFile.toString(),
|
torrentFile.toString(),
|
||||||
getRootPath(gameFilesPath).toString(),
|
plugin.getRootPath(gameFilesPath).toString(),
|
||||||
FullyPieceStorageFactory.INSTANCE
|
FullyPieceStorageFactory.INSTANCE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
state.torrentFilesMetadata.add(
|
||||||
|
TorrentFileMetadata(
|
||||||
|
torrentFile = torrentFile,
|
||||||
|
gameFile = gameFilesPath
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
plugin.saveState(state)
|
||||||
|
|
||||||
return torrentFile
|
return torrentFile
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,18 +226,10 @@ class TorrentDownloadPlugin(wrapper: PluginWrapper) : ConfigurableGameyfinPlugin
|
|||||||
.numHashingThreads(Runtime.getRuntime().availableProcessors() * 2)
|
.numHashingThreads(Runtime.getRuntime().availableProcessors() * 2)
|
||||||
.createdBy(plugin.javaClass.name)
|
.createdBy(plugin.javaClass.name)
|
||||||
.addFile(gameFilesPath)
|
.addFile(gameFilesPath)
|
||||||
.rootPath(getRootPath(gameFilesPath))
|
.rootPath(plugin.getRootPath(gameFilesPath))
|
||||||
.announce(plugin.getTrackerUri().toString())
|
.announce(plugin.getTrackerUri().toString())
|
||||||
.privateFlag(plugin.config("privateMode"))
|
.privateFlag(plugin.config("privateMode"))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getRootPath(gameFilesPath: Path): Path {
|
|
||||||
return if (gameFilesPath.isDirectory()) {
|
|
||||||
gameFilesPath
|
|
||||||
} else {
|
|
||||||
gameFilesPath.parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package org.gameyfin.plugins.download.torrent
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
data class TorrentDownloadPluginState(
|
||||||
|
val torrentFilesMetadata: MutableList<TorrentFileMetadata> = mutableListOf()
|
||||||
|
)
|
||||||
|
|
||||||
|
data class TorrentFileMetadata(
|
||||||
|
val torrentFile: Path,
|
||||||
|
val gameFile: Path
|
||||||
|
)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
Plugin-Version: 1.0.0.beta1
|
Plugin-Version: 1.0.0.beta2
|
||||||
Plugin-Class: org.gameyfin.plugins.download.torrent.TorrentDownloadPlugin
|
Plugin-Class: org.gameyfin.plugins.download.torrent.TorrentDownloadPlugin
|
||||||
Plugin-Id: org.gameyfin.plugins.download.torrent
|
Plugin-Id: org.gameyfin.plugins.download.torrent
|
||||||
Plugin-Name: Torrent Download
|
Plugin-Name: Torrent Download
|
||||||
|
|||||||
Reference in New Issue
Block a user