mirror of
https://github.com/dev-claw/vripper-project.git
synced 2026-08-19 08:35:41 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0108b670c5 | ||
|
|
378d493e2a | ||
|
|
00468b7ed3 | ||
|
|
0978ff37b0 | ||
|
|
5e52af0e00 | ||
|
|
0dec4bd3f3 | ||
|
|
bb053ca314 |
@@ -39,7 +39,7 @@ The following hosts are supported:
|
||||
|
||||
<img src="https://github.com/stashapp/stash/raw/develop/docs/readme_assets/windows_logo.svg" width="100%" height="75"> Windows | <img src="https://github.com/stashapp/stash/raw/develop/docs/readme_assets/mac_logo.svg" width="100%" height="75"> macOS (Intel) | <img src="https://github.com/stashapp/stash/raw/develop/docs/readme_assets/mac_logo.svg" width="100%" height="75"> macOS (Apple silicon) | <img src="https://github.com/stashapp/stash/raw/develop/docs/readme_assets/linux_logo.svg" width="100%" height="75"> Linux | <img src="https://images.vexels.com/media/users/3/166401/isolated/preview/b82aa7ac3f736dd78570dd3fa3fa9e24-java-programming-language-icon-by-vexels.png" width="100%" height="75"> Java
|
||||
:---:|:---:|:---:|:---:|:---:
|
||||
[Installer (EXE)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-windows-installer-6.5.8.exe) <br /> [Installer (MSI)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-windows-installer-6.5.8.msi) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-windows-portable-6.5.8.zip) | [Installer (DMG)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-macos-6.5.8.x86_64.dmg) <br /> [Installer (PKG)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-macos-6.5.8.x86_64.pkg) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-macos-portable-6.5.8.x86_64.zip) | [Installer (DMG)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-macos-6.5.8.arm64.dmg) <br /> [Installer (PKG)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-macos-6.5.8.arm64.pkg) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-macos-portable-6.5.8.arm64.zip) | [Linux (amd64) (DEB)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-linux-6.5.3_amd64.deb) <br /> [Linux (x86_64) (RPM)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-linux-6.5.8.x86_64.rpm) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-linux-portable-6.5.8.zip) | [Java GUI (noarch)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-noarch-gui-6.5.8.jar) <br /> [Java Web (noarch)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-noarch-web-6.5.8.jar)
|
||||
[Installer (EXE)](https://github.com/dev-claw/vripper-project/releases/download/6.10.0/vripper-windows-installer-6.10.0.exe) <br /> [Installer (MSI)](https://github.com/dev-claw/vripper-project/releases/download/6.10.0/vripper-windows-installer-6.10.0.msi) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.10.0/vripper-windows-portable-6.10.0.zip) | [Installer (DMG)](https://github.com/dev-claw/vripper-project/releases/download/6.10.0/vripper-macos-6.10.0.x86_64.dmg) <br /> [Installer (PKG)](https://github.com/dev-claw/vripper-project/releases/download/6.10.0/vripper-macos-6.10.0.x86_64.pkg) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.10.0/vripper-macos-portable-6.10.0.x86_64.zip) | [Installer (DMG)](https://github.com/dev-claw/vripper-project/releases/download/6.10.0/vripper-macos-6.10.0.arm64.dmg) <br /> [Installer (PKG)](https://github.com/dev-claw/vripper-project/releases/download/6.10.0/vripper-macos-6.10.0.arm64.pkg) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.10.0/vripper-macos-portable-6.10.0.arm64.zip) | [Linux (amd64) (DEB)](https://github.com/dev-claw/vripper-project/releases/download/6.10.0/vripper-linux-6.5.3_amd64.deb) <br /> [Linux (x86_64) (RPM)](https://github.com/dev-claw/vripper-project/releases/download/6.10.0/vripper-linux-6.10.0.x86_64.rpm) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.10.0/vripper-linux-portable-6.10.0.zip) | [Java GUI (noarch)](https://github.com/dev-claw/vripper-project/releases/download/6.10.0/vripper-noarch-gui-6.10.0.jar) <br /> [Java Web (noarch)](https://github.com/dev-claw/vripper-project/releases/download/6.10.0/vripper-noarch-web-6.10.0.jar)
|
||||
|
||||
Source code and previous versions are available on
|
||||
the [Releases page](https://github.com/dev-claw/vripper-project/releases).
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<revision>6.9.0</revision>
|
||||
<app-version>6.9.0</app-version>
|
||||
<revision>6.10.0</revision>
|
||||
<app-version>6.10.0</app-version>
|
||||
<kotlin.version>2.2.20</kotlin.version>
|
||||
<slf4j.version>2.0.16</slf4j.version>
|
||||
<logback.version>1.5.12</logback.version>
|
||||
|
||||
@@ -79,6 +79,8 @@ internal class ImxHost : Host("imx.to", 8) {
|
||||
"https://image.imx.to/u/i/" + url.replace("https://t.imx.to/t/", "")
|
||||
} else if (url.startsWith("https://imx.to/upload/small/")) {
|
||||
"https://image.imx.to/u/i/" + url.replace("https://imx.to/upload/small/", "")
|
||||
} else if (url.startsWith("https://i.imx.to/t/")) {
|
||||
"https://image.imx.to/u/i/" + url.replace("https://i.imx.to/t/", "")
|
||||
} else if (url.startsWith("https://image.imx.to/u/i/")) {
|
||||
url
|
||||
} else {
|
||||
|
||||
@@ -41,7 +41,7 @@ internal class HTTPService {
|
||||
connectionExpiryJob = coroutineScope.launch {
|
||||
while (isActive) {
|
||||
it.closeExpired()
|
||||
delay(60_000)
|
||||
delay(300_000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-10
@@ -42,16 +42,18 @@ internal class ImageDownloadRunnable(
|
||||
class Context(val imageEntity: ImageEntity, val settings: Settings) {
|
||||
|
||||
private val log by LoggerDelegate()
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
||||
private lateinit var coroutineScope: CoroutineScope
|
||||
private val jobs = mutableListOf<Job>()
|
||||
|
||||
val httpContext: HttpClientContext = HttpClientContext.create().apply {
|
||||
cookieStore = HTTPService.cookieStore
|
||||
}
|
||||
val requests = mutableListOf<HttpUriRequestBase>()
|
||||
val headers = mutableMapOf<String, String>()
|
||||
var completed = false
|
||||
var stopped = false
|
||||
|
||||
init {
|
||||
fun init() {
|
||||
coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
||||
ApplicationProperties.VRIPPER_DIR.listDirectoryEntries()
|
||||
.filter { it.fileName.pathString.startsWith("cookies") }.forEach { cookiesPath ->
|
||||
loadCookies(cookiesPath).also { cookies ->
|
||||
@@ -83,6 +85,7 @@ internal class ImageDownloadRunnable(
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun clear() {
|
||||
runBlocking {
|
||||
requests.forEach { it.abort() }
|
||||
@@ -102,8 +105,6 @@ internal class ImageDownloadRunnable(
|
||||
private val vgAuthService: VGAuthService by inject()
|
||||
private val hosts: List<Host> = getKoin().getAll()
|
||||
val context = Context(imageEntity, settings)
|
||||
var completed = false
|
||||
var stopped = false
|
||||
|
||||
fun download() {
|
||||
try {
|
||||
@@ -140,7 +141,7 @@ internal class ImageDownloadRunnable(
|
||||
dataAccessService.updateImage(context.imageEntity)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (stopped) {
|
||||
if (context.stopped) {
|
||||
return
|
||||
}
|
||||
context.imageEntity.status = Status.ERROR
|
||||
@@ -189,20 +190,24 @@ internal class ImageDownloadRunnable(
|
||||
|
||||
override fun run() {
|
||||
try {
|
||||
if (stopped) {
|
||||
context.init()
|
||||
if (context.stopped) {
|
||||
return
|
||||
}
|
||||
download()
|
||||
} finally {
|
||||
completed = true
|
||||
context.completed = true
|
||||
context.clear()
|
||||
if (context.stopped && context.imageEntity.downloaded != context.imageEntity.size) {
|
||||
context.imageEntity.status = Status.STOPPED
|
||||
}
|
||||
dataAccessService.updateImage(context.imageEntity)
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
stopped = true
|
||||
context.stopped = true
|
||||
context.clear()
|
||||
dataAccessService.updateImage(context.imageEntity)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
|
||||
@@ -55,7 +55,7 @@ internal class QueueManager(
|
||||
val toProcess =
|
||||
if (postEntityId != null) running.filter { it.context.imageEntity.postEntityId == postEntityId } else running
|
||||
toProcess.forEach { it.stop() }
|
||||
while (toProcess.count { !it.completed } > 0) {
|
||||
while (toProcess.count { !it.context.completed } > 0) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
running.removeAll(toProcess)
|
||||
@@ -158,7 +158,7 @@ internal class QueueManager(
|
||||
downloadManagerLock.withLock {
|
||||
val image = imageDownloadRunnable.context.imageEntity
|
||||
clearRunningRunnable(imageDownloadRunnable.context.imageEntity.id)
|
||||
if (!isPending(image.postEntityId) && !isRunning(image.postEntityId) && !imageDownloadRunnable.stopped) {
|
||||
if (!isPending(image.postEntityId) && !isRunning(image.postEntityId) && !imageDownloadRunnable.context.stopped) {
|
||||
dataAccessService.finishPost(image.postEntityId, true)
|
||||
}
|
||||
reportQueueState()
|
||||
|
||||
@@ -25,6 +25,18 @@
|
||||
|
||||
<appender name="RING" class="me.vripper.RingAppender"/>
|
||||
|
||||
<appender name="ASYNC-STDOUT" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</appender>
|
||||
|
||||
<appender name="ASYNC-FILE" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<appender-ref ref="FILE"/>
|
||||
</appender>
|
||||
|
||||
<appender name="ASYNC-RING" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<appender-ref ref="RING"/>
|
||||
</appender>
|
||||
|
||||
<logger name="me.vripper" level="info"/>
|
||||
<logger name="Exposed" level="off"/>
|
||||
<logger name="org.springframework.web.socket.config.WebSocketMessageBrokerStats" level="off"/>
|
||||
@@ -32,8 +44,8 @@
|
||||
<Logger name="org.apache.hc.client5.http.wire" level="off"/>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
<appender-ref ref="FILE"/>
|
||||
<appender-ref ref="RING"/>
|
||||
<appender-ref ref="ASYNC-STDOUT"/>
|
||||
<appender-ref ref="ASYNC-FILE"/>
|
||||
<appender-ref ref="ASYNC-RING"/>
|
||||
</root>
|
||||
</configuration>
|
||||
|
||||
@@ -2,11 +2,19 @@ package me.vripper.gui.components.views
|
||||
|
||||
import atlantafx.base.theme.*
|
||||
import javafx.application.Application.setUserAgentStylesheet
|
||||
import javafx.event.EventHandler
|
||||
import javafx.scene.input.TransferMode
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import me.vripper.gui.controller.AppController
|
||||
import me.vripper.gui.controller.MainController
|
||||
import me.vripper.gui.controller.WidgetsController
|
||||
import tornadofx.View
|
||||
import tornadofx.onChange
|
||||
import tornadofx.vbox
|
||||
import java.net.URI
|
||||
|
||||
class AppView : View() {
|
||||
|
||||
@@ -17,6 +25,8 @@ class AppView : View() {
|
||||
private val statusBarView: StatusBarView by inject()
|
||||
private val actionBarView: ActionBarView by inject()
|
||||
private val widgetsController: WidgetsController by inject()
|
||||
private val appController: AppController by inject()
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
init {
|
||||
title = "VRipper ${mainController.version}"
|
||||
with(root) {
|
||||
@@ -30,6 +40,24 @@ class AppView : View() {
|
||||
}
|
||||
prefWidth = widgetsController.currentSettings.width
|
||||
prefHeight = widgetsController.currentSettings.height
|
||||
onDragOver = EventHandler { event ->
|
||||
if (event.dragboard.hasString()) {
|
||||
event.acceptTransferModes(TransferMode.COPY)
|
||||
}
|
||||
event.consume()
|
||||
}
|
||||
onDragDropped = EventHandler { event ->
|
||||
val dragBoard = event.dragboard
|
||||
if (dragBoard != null && dragBoard.hasString()) {
|
||||
val url = dragBoard.string.trim()
|
||||
runCatching {
|
||||
val uri = URI(url)
|
||||
coroutineScope.launch {
|
||||
appController.scan(uri.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
widgetsController.currentSettings.visibleToolbarPanelProperty.onChange { it ->
|
||||
|
||||
@@ -33,9 +33,11 @@ class LoadingView : View("VRipper") {
|
||||
|
||||
init {
|
||||
DatabaseManager.connect()
|
||||
ClipboardManager.init()
|
||||
coroutineScope.launch {
|
||||
GuiEventBus.events.filterIsInstance(GuiEventBus.ApplicationInitialized::class).collect {
|
||||
runLater {
|
||||
ClipboardManager.init()
|
||||
}
|
||||
message.set("")
|
||||
AppManager.stop()
|
||||
grpcEndpointService.disconnect()
|
||||
|
||||
@@ -200,6 +200,11 @@ class LogTableView : View() {
|
||||
tableView.placeholder = Label("Loading")
|
||||
}
|
||||
|
||||
is GuiEventBus.RemoteError -> runLater {
|
||||
items.clear()
|
||||
tableView.placeholder = Label("Connection Failure")
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,6 +516,11 @@ class PostsTableView : View() {
|
||||
items.clear()
|
||||
}
|
||||
|
||||
is GuiEventBus.RemoteError -> runLater {
|
||||
items.clear()
|
||||
tableView.placeholder = Label("Connection Failure")
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,15 +29,15 @@ class StatusBarView : View("Status bar") {
|
||||
GuiEventBus.events.collect {
|
||||
when (it) {
|
||||
GuiEventBus.LocalSession, GuiEventBus.RemoteSession -> {
|
||||
while (isActive) {
|
||||
val result = runCatching { statusBarController.loggedInUser() }
|
||||
if (result.isSuccess) {
|
||||
runLater {
|
||||
loggedUser.set(result.getOrNull())
|
||||
}
|
||||
break
|
||||
val result = runCatching { statusBarController.loggedInUser() }
|
||||
if (result.isSuccess) {
|
||||
runLater {
|
||||
loggedUser.set(result.getOrNull())
|
||||
}
|
||||
} else {
|
||||
runLater {
|
||||
loggedUser.set(null)
|
||||
}
|
||||
delay(1000)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -214,6 +214,11 @@ class ThreadTableView : View() {
|
||||
items.clear()
|
||||
}
|
||||
|
||||
is GuiEventBus.RemoteError -> runLater {
|
||||
items.clear()
|
||||
tableView.placeholder = Label("Connection Failure")
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package me.vripper.gui.controller
|
||||
|
||||
import me.vripper.gui.utils.AppEndpointManager.currentAppEndpointService
|
||||
import tornadofx.Controller
|
||||
|
||||
class AppController : Controller() {
|
||||
suspend fun scan(postLinks: String) {
|
||||
runCatching { currentAppEndpointService().scanLinks(postLinks) }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package me.vripper.gui.controller
|
||||
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.retryWhen
|
||||
import me.vripper.gui.model.ImageModel
|
||||
import me.vripper.gui.utils.AppEndpointManager.currentAppEndpointService
|
||||
import me.vripper.model.Image
|
||||
@@ -41,7 +39,7 @@ class ImageController : Controller() {
|
||||
}
|
||||
|
||||
fun onUpdateImages(postId: Long) =
|
||||
currentAppEndpointService().onUpdateImagesByPostEntityId(postId).retryWhen { _, _ -> delay(1000); true }
|
||||
currentAppEndpointService().onUpdateImagesByPostEntityId(postId)
|
||||
|
||||
fun onStopped() = currentAppEndpointService().onStopped().retryWhen { _, _ -> delay(1000);true }
|
||||
fun onStopped() = currentAppEndpointService().onStopped()
|
||||
}
|
||||
@@ -12,7 +12,7 @@ class StatusBarController : Controller() {
|
||||
}
|
||||
|
||||
fun connectionState(): String {
|
||||
return currentAppEndpointService().connectionState()
|
||||
return runCatching { currentAppEndpointService().connectionState() }.getOrElse { "" }
|
||||
}
|
||||
|
||||
suspend fun getVersion(): String {
|
||||
|
||||
@@ -17,6 +17,8 @@ object GuiEventBus {
|
||||
object ChangingSession : GUIEvent
|
||||
object LocalSession : GUIEvent
|
||||
object RemoteSession : GUIEvent
|
||||
object Undefined : GUIEvent
|
||||
data class RemoteError(val source: String, val message: String) : GUIEvent
|
||||
}
|
||||
|
||||
sealed interface GUIEvent
|
||||
@@ -402,7 +402,7 @@ internal class GrpcEndpointService : IAppEndpointService {
|
||||
|
||||
val result = runCatching {
|
||||
val version = getVersion()
|
||||
version >= "6.6.0"
|
||||
isAtLeastWithAlphaBeta(version, "6.6.0")
|
||||
}
|
||||
|
||||
return if (result.isSuccess) {
|
||||
@@ -412,5 +412,26 @@ internal class GrpcEndpointService : IAppEndpointService {
|
||||
}
|
||||
}
|
||||
|
||||
override fun connectionState(): String = channel?.getState(true)?.name ?: ConnectivityState.SHUTDOWN.name
|
||||
}
|
||||
fun isAtLeastWithAlphaBeta(actual: String, min: String): Boolean {
|
||||
fun core(s: String) = s.substringBefore('-')
|
||||
fun suffix(s: String): String? {
|
||||
val i = s.indexOf('-')
|
||||
return if (i >= 0 && i + 1 < s.length) s.substring(i + 1) else null
|
||||
}
|
||||
|
||||
val aCore = core(actual).split('.').map { it.toInt() }
|
||||
val mCore = core(min).split('.').map { it.toInt() }
|
||||
|
||||
val maxLen = maxOf(aCore.size, mCore.size)
|
||||
for (i in 0 until maxLen) {
|
||||
val ai = aCore.getOrElse(i) { 0 }
|
||||
val mi = mCore.getOrElse(i) { 0 }
|
||||
if (ai != mi) return ai > mi
|
||||
}
|
||||
|
||||
// numeric parts equal => alpha/beta is NOT >= final
|
||||
return suffix(actual) == null
|
||||
}
|
||||
|
||||
override fun connectionState(): String = channel?.getState(false)?.name ?: ConnectivityState.SHUTDOWN.name
|
||||
}
|
||||
@@ -16,7 +16,7 @@ object AppEndpointManager : KoinComponent {
|
||||
val localAppEndpointService: IAppEndpointService by inject(named("localAppEndpointService"))
|
||||
val remoteAppEndpointService: IAppEndpointService by inject(named("remoteAppEndpointService"))
|
||||
|
||||
private lateinit var current: GUIEvent
|
||||
private var current: GUIEvent = GuiEventBus.Undefined
|
||||
|
||||
fun set(event: GUIEvent) {
|
||||
this.current = event
|
||||
|
||||
@@ -4,12 +4,11 @@ import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.cancellable
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.retryWhen
|
||||
import me.vripper.gui.event.GuiEventBus
|
||||
|
||||
object ChannelFlowBuilder {
|
||||
|
||||
fun <T> build(localFlow: () -> Flow<T>, remoteFlow: () -> Flow<T>): Flow<T> {
|
||||
fun <T> build(localFlow: () -> Flow<T>, remoteFlow: () -> Flow<T>, source: String = "remote"): Flow<T> {
|
||||
return channelFlow {
|
||||
var job: Job? = null
|
||||
|
||||
@@ -20,10 +19,14 @@ object ChannelFlowBuilder {
|
||||
}
|
||||
} else {
|
||||
launch {
|
||||
remoteFlow().cancellable().retryWhen { _, _ ->
|
||||
delay(1000)
|
||||
true
|
||||
}.collect { if (isActive) send(it) }
|
||||
try {
|
||||
remoteFlow().cancellable().collect { if (isActive) send(it) }
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (t: Throwable) {
|
||||
t.printStackTrace()
|
||||
GuiEventBus.publishEvent(GuiEventBus.RemoteError(source, t.message ?: t.toString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@ package me.vripper.gui.utils
|
||||
|
||||
import javafx.scene.input.Clipboard
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.retryWhen
|
||||
import me.vripper.gui.event.GuiEventBus
|
||||
import me.vripper.model.Settings
|
||||
import me.vripper.services.IAppEndpointService
|
||||
import me.vripper.utilities.LoggerDelegate
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import org.koin.core.qualifier.named
|
||||
import tornadofx.runLater
|
||||
|
||||
object ClipboardManager : KoinComponent {
|
||||
@@ -14,35 +16,44 @@ object ClipboardManager : KoinComponent {
|
||||
private var current: String? = null
|
||||
private var coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
private var pollJob: Job? = null
|
||||
private var settingsUpdateJob: Job? = null
|
||||
private val systemClipboard = Clipboard.getSystemClipboard()
|
||||
val localAppEndpointService: IAppEndpointService by inject(named("localAppEndpointService"))
|
||||
val remoteAppEndpointService: IAppEndpointService by inject(named("remoteAppEndpointService"))
|
||||
var initialized = false
|
||||
|
||||
@Synchronized
|
||||
fun init() {
|
||||
coroutineScope.launch {
|
||||
GuiEventBus.events.collect { event ->
|
||||
when (event) {
|
||||
GuiEventBus.LocalSession, GuiEventBus.RemoteSession -> {
|
||||
logger.info("Clipboard manager initialized")
|
||||
while (isActive) {
|
||||
if (initialized) {
|
||||
return
|
||||
}
|
||||
runBlocking {
|
||||
val updateSettings = ChannelFlowBuilder.build(
|
||||
localAppEndpointService::onUpdateSettings,
|
||||
remoteAppEndpointService::onUpdateSettings
|
||||
)
|
||||
updateSettings.let { flow ->
|
||||
coroutineScope.launch {
|
||||
flow.collect {
|
||||
update(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
coroutineScope.launch {
|
||||
GuiEventBus.events.collect { event ->
|
||||
when (event) {
|
||||
GuiEventBus.LocalSession, GuiEventBus.RemoteSession -> {
|
||||
logger.info("Clipboard manager initialized")
|
||||
val result = runCatching { AppEndpointManager.currentAppEndpointService().getSettings() }
|
||||
if (result.isSuccess) {
|
||||
update(result.getOrNull()!!)
|
||||
break
|
||||
}
|
||||
delay(1000)
|
||||
}
|
||||
settingsUpdateJob?.cancelAndJoin()
|
||||
settingsUpdateJob = launch {
|
||||
AppEndpointManager.currentAppEndpointService().onUpdateSettings()
|
||||
.retryWhen { _, _ -> delay(1000); true }.collect {
|
||||
update(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
initialized = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user