This commit is contained in:
death-claw
2023-05-21 15:55:42 +01:00
parent f8370801c2
commit f42d412b86
9 changed files with 190 additions and 88 deletions
@@ -1,11 +1,11 @@
package me.mnlr.vripper.model
class Settings {
var desktopClipboard: Boolean = false
var maxEventLog: Int = 1_000
var connectionSettings: ConnectionSettings = ConnectionSettings()
var downloadSettings: DownloadSettings = DownloadSettings()
var viperSettings: ViperSettings = ViperSettings()
var connectionSettings = ConnectionSettings()
var downloadSettings = DownloadSettings()
var viperSettings = ViperSettings()
var clipboardSettings = ClipboardSettings()
}
data class ViperSettings(
@@ -32,4 +32,6 @@ data class ConnectionSettings(
var maxTotalThreads: Int = 0,
var timeout: Int = 30,
var maxAttempts: Int = 3,
)
)
data class ClipboardSettings(var enable: Boolean = false, var pollingRate: Int = 500)
@@ -7,16 +7,16 @@ import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import jakarta.annotation.PostConstruct
import jakarta.annotation.PreDestroy
import org.apache.commons.codec.digest.DigestUtils
import org.springframework.beans.factory.annotation.Value
import org.springframework.core.io.Resource
import org.springframework.stereotype.Service
import me.mnlr.vripper.SpringContext
import me.mnlr.vripper.delegate.LoggerDelegate
import me.mnlr.vripper.event.Event
import me.mnlr.vripper.event.EventBus
import me.mnlr.vripper.exception.ValidationException
import me.mnlr.vripper.model.Settings
import org.apache.commons.codec.digest.DigestUtils
import org.springframework.beans.factory.annotation.Value
import org.springframework.core.io.Resource
import org.springframework.stereotype.Service
import java.io.FileWriter
import java.io.IOException
import java.nio.file.*
@@ -92,7 +92,8 @@ class SettingsService(
fun newSettings(settings: Settings) {
if (settings.viperSettings.login) {
if (this.settings.viperSettings.password != settings.viperSettings.password) {
settings.viperSettings.password = DigestUtils.md5Hex(settings.viperSettings.password)
settings.viperSettings.password =
DigestUtils.md5Hex(settings.viperSettings.password)
}
} else {
settings.viperSettings.username = ""
@@ -156,55 +157,54 @@ class SettingsService(
val path: Path = try {
Paths.get(settings.downloadSettings.downloadPath)
} catch (e: InvalidPathException) {
throw ValidationException(String.format("%s is invalid", settings.downloadSettings.downloadPath))
throw ValidationException(
String.format(
"%s is invalid", settings.downloadSettings.downloadPath
)
)
}
if (!Files.exists(path)) {
throw ValidationException(String.format("%s does not exist", settings.downloadSettings.downloadPath))
throw ValidationException(
String.format(
"%s does not exist", settings.downloadSettings.downloadPath
)
)
} else if (!Files.isDirectory(path)) {
throw ValidationException(String.format("%s is not a directory", settings.downloadSettings.downloadPath))
throw ValidationException(
String.format(
"%s is not a directory", settings.downloadSettings.downloadPath
)
)
}
if(settings.downloadSettings.autoQueueThreshold < 0) {
if (settings.downloadSettings.autoQueueThreshold < 0) {
throw ValidationException("Invalid auto queue settings, value must be a positive integer")
}
if (settings.connectionSettings.maxTotalThreads < 0 || settings.connectionSettings.maxTotalThreads > 12) {
throw ValidationException(
String.format(
"Invalid max global concurrent download settings, values must be in [%d,%d]",
0,
12
)
"Invalid max global concurrent download settings, values must be in [0,12]"
)
}
if (settings.connectionSettings.maxThreads < 1 || settings.connectionSettings.maxThreads > 4) {
throw ValidationException(
String.format(
"Invalid max concurrent download settings, values must be in [%d,%d]", 1, 4
)
)
throw ValidationException("Invalid max concurrent download settings, values must be in [1,4]")
}
if (settings.connectionSettings.timeout < 1 || settings.connectionSettings.timeout > 300) {
throw ValidationException(
String.format(
"Invalid connection timeout settings, values must be in [%d,%d]", 1, 300
)
"Invalid connection timeout settings, values must be in [1,300]"
)
}
if (settings.connectionSettings.maxAttempts < 1 || settings.connectionSettings.maxAttempts > 10) {
throw ValidationException(
String.format(
"Invalid maximum attempts settings, values must be in [%d,%d]", 1, 10
)
)
throw ValidationException("Invalid maximum attempts settings, values must be in [1,10]")
}
if (settings.maxEventLog < 100 || settings.maxEventLog > 10000) {
throw ValidationException(
String.format(
"Invalid maximum event log record settings, values must be in [%d,%d]",
100,
10000
)
"Invalid maximum event log record settings, values must be in [100,10000]"
)
}
if (settings.clipboardSettings.pollingRate < 500) {
throw ValidationException(
"Invalid clipboard monitoring polling rate settings, values must be >= 500"
)
}
}
@@ -1,42 +1,78 @@
package me.mnlr.vripper.clipboard
import jakarta.annotation.PostConstruct
import jakarta.annotation.PreDestroy
import javafx.scene.input.Clipboard
import me.mnlr.vripper.AppEndpointService
import org.springframework.scheduling.annotation.Scheduled
import me.mnlr.vripper.event.Event
import me.mnlr.vripper.event.EventBus
import me.mnlr.vripper.services.SettingsService
import org.springframework.stereotype.Service
import reactor.core.Disposable
import reactor.core.publisher.Sinks
import reactor.core.scheduler.Schedulers
import tornadofx.*
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
@Service
class ClipboardService(private val appEndpointService: AppEndpointService) {
class ClipboardService(
private val appEndpointService: AppEndpointService,
private val settingsService: SettingsService,
eventBus: EventBus
) {
private val eventBusDisposable: Disposable
private val sink = Sinks.many().unicast().onBackpressureBuffer<String>()
private val scheduler = Executors.newSingleThreadScheduledExecutor()
private var current: String? = null
private var scheduledFuture: ScheduledFuture<*>? = null
@PostConstruct
fun init() {
init {
runLater {
this.current = if (Clipboard.getSystemClipboard()
.hasString()
) Clipboard.getSystemClipboard().string else null
}
eventBusDisposable = eventBus
.flux()
.filter { it.kind == Event.Kind.SETTINGS_UPDATE }
.subscribe { init() }
sink.asFlux().subscribeOn(Schedulers.single()).subscribe {
this.appEndpointService.scanLinks(it)
}
runLater {
this.current = if(Clipboard.getSystemClipboard().hasString()) Clipboard.getSystemClipboard().string else null
}
@PostConstruct
fun init() {
scheduledFuture?.cancel(true)
if(settingsService.settings.clipboardSettings.enable) {
scheduledFuture = scheduler.scheduleWithFixedDelay({
poll()
}, 500, settingsService.settings.clipboardSettings.pollingRate.toLong(), TimeUnit.MILLISECONDS)
} else {
current = null
}
}
@Scheduled(fixedDelay = 500)
fun poll() {
runLater {
val clipboard = Clipboard.getSystemClipboard()
if(clipboard.hasString()) {
if (clipboard.hasString()) {
val value: String? = clipboard.string
if(!value.isNullOrBlank() && value != this.current) {
if (!value.isNullOrBlank() && value != this.current) {
this.current = value
sink.emitNext(value) { _,_ ->
sink.emitNext(value) { _, _ ->
true
}
}
}
}
}
@PreDestroy
fun destroy() {
scheduledFuture?.cancel(true)
scheduler.shutdown()
}
}
@@ -1,18 +1,12 @@
package me.mnlr.vripper.controller
import javafx.scene.control.Alert
import me.mnlr.vripper.exception.ValidationException
import me.mnlr.vripper.model.ConnectionSettings
import me.mnlr.vripper.model.DownloadSettings
import me.mnlr.vripper.model.settings.DownloadSettingsModel
import me.mnlr.vripper.model.Settings
import me.mnlr.vripper.model.ViperSettings
import me.mnlr.vripper.model.*
import me.mnlr.vripper.model.settings.ClipboardSettingsModel
import me.mnlr.vripper.model.settings.ConnectionSettingsModel
import me.mnlr.vripper.model.settings.DownloadSettingsModel
import me.mnlr.vripper.model.settings.ViperSettingsModel
import me.mnlr.vripper.services.SettingsService
import tornadofx.Controller
import tornadofx.alert
import tornadofx.warning
import tornadofx.*
class SettingsController : Controller() {
@@ -30,36 +24,46 @@ class SettingsController : Controller() {
return settingsService.settings.viperSettings
}
fun findClipboardSettings(): ClipboardSettings {
return settingsService.settings.clipboardSettings
}
fun saveNewSettings(
downloadSettingsModel: DownloadSettingsModel,
connectionSettingsModel: ConnectionSettingsModel,
viperSettingsModel: ViperSettingsModel
viperSettingsModel: ViperSettingsModel,
clipboardSettingsModel: ClipboardSettingsModel
) {
settingsService.newSettings(Settings().apply {
downloadSettings = DownloadSettings(
downloadSettingsModel.downloadPath,
downloadSettingsModel.autoStart,
downloadSettingsModel.autoQueueThreshold,
downloadSettingsModel.forceOrder,
downloadSettingsModel.forumSubfolder,
downloadSettingsModel.threadSubLocation,
downloadSettingsModel.clearCompleted,
downloadSettingsModel.appendPostId
settingsService.newSettings(Settings().apply {
downloadSettings = DownloadSettings(
downloadSettingsModel.downloadPath,
downloadSettingsModel.autoStart,
downloadSettingsModel.autoQueueThreshold,
downloadSettingsModel.forceOrder,
downloadSettingsModel.forumSubfolder,
downloadSettingsModel.threadSubLocation,
downloadSettingsModel.clearCompleted,
downloadSettingsModel.appendPostId
)
connectionSettings = ConnectionSettings(
connectionSettingsModel.maxThreads,
connectionSettingsModel.maxTotalThreads,
connectionSettingsModel.timeout,
connectionSettingsModel.maxAttempts,
)
viperSettings = ViperSettings(
viperSettingsModel.login,
viperSettingsModel.username,
viperSettingsModel.password,
viperSettingsModel.thanks,
viperSettingsModel.host,
)
clipboardSettings =
ClipboardSettings(
clipboardSettingsModel.enable,
if (clipboardSettingsModel.pollingRate.isBlank()) 500 else clipboardSettingsModel.pollingRate.toInt()
)
connectionSettings = ConnectionSettings(
connectionSettingsModel.maxThreads,
connectionSettingsModel.maxTotalThreads,
connectionSettingsModel.timeout,
connectionSettingsModel.maxAttempts,
)
viperSettings = ViperSettings(
viperSettingsModel.login,
viperSettingsModel.username,
viperSettingsModel.password,
viperSettingsModel.thanks,
viperSettingsModel.host,
)
})
})
}
@@ -0,0 +1,13 @@
package me.mnlr.vripper.model.settings
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleStringProperty
import tornadofx.*
class ClipboardSettingsModel {
val enableProperty = SimpleBooleanProperty()
var enable: Boolean by enableProperty
val pollingRateProperty = SimpleStringProperty()
var pollingRate: String by pollingRateProperty
}
@@ -0,0 +1,39 @@
package me.mnlr.vripper.view.settings
import me.mnlr.vripper.controller.SettingsController
import me.mnlr.vripper.model.settings.ClipboardSettingsModel
import tornadofx.*
class ClipboardSettingsView : View("Clipboard Settings") {
private val settingsController: SettingsController by inject()
val clipboardSettingsModel = ClipboardSettingsModel()
override fun onDock() {
val clipboardSettings = settingsController.findClipboardSettings()
clipboardSettingsModel.enable = clipboardSettings.enable
clipboardSettingsModel.pollingRate = clipboardSettings.pollingRate.toString()
}
override val root = vbox {
form {
fieldset {
field("Enable") {
checkbox {
bind(clipboardSettingsModel.enableProperty)
}
}
fieldset {
visibleWhen(clipboardSettingsModel.enableProperty)
field("Polling rate (ms)") {
textfield(clipboardSettingsModel.pollingRateProperty) {
filterInput {
it.controlNewText.isInt()
}
}
}
}
}
}
}
}
@@ -29,7 +29,7 @@ class ConnectionSettingsView : View("Connection settings") {
filterInput { it.controlNewText.isInt() }
}
}
field("Connection timeout") {
field("Connection timeout (s)") {
textfield(connectionSettingsModel.timeoutProperty) {
filterInput { it.controlNewText.isInt() }
}
@@ -18,25 +18,32 @@ class SettingsView : Fragment("Settings") {
private val downloadSettingsView: DownloadSettingsView by inject()
private val connectionSettingsView: ConnectionSettingsView by inject()
private val viperSettingsView: ViperSettingsView by inject()
private val clipboardSettingsView: ClipboardSettingsView by inject()
override val root = vbox(alignment = Pos.CENTER_RIGHT) {
spacing = 5.0
tabpane {
tabClosingPolicy = TabPane.TabClosingPolicy.UNAVAILABLE
VBox.setVgrow(this, Priority.ALWAYS)
tab<DownloadSettingsView>() {
tab<DownloadSettingsView> {
val imageView = ImageView("downloads-folder.png")
imageView.fitWidth = 18.0
imageView.fitHeight = 18.0
graphic = imageView
}
tab<ConnectionSettingsView>() {
tab<ConnectionSettingsView> {
val imageView = ImageView("data-transfer.png")
imageView.fitWidth = 18.0
imageView.fitHeight = 18.0
graphic = imageView
}
tab<ViperSettingsView>() {
tab<ClipboardSettingsView> {
val imageView = ImageView("clipboard.png")
imageView.fitWidth = 18.0
imageView.fitHeight = 18.0
graphic = imageView
}
tab<ViperSettingsView> {
val imageView = ImageView("icons/32x32.png")
imageView.fitWidth = 18.0
imageView.fitHeight = 18.0
@@ -56,7 +63,8 @@ class SettingsView : Fragment("Settings") {
settingsController.saveNewSettings(
downloadSettingsView.downloadSettingsModel,
connectionSettingsView.connectionSettingsModel,
viperSettingsView.viperSettingsModel
viperSettingsView.viperSettingsModel,
clipboardSettingsView.clipboardSettingsModel
)
close()
} catch (e: ValidationException) {
Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B