mirror of
https://github.com/dev-claw/vripper-project.git
synced 2026-08-19 08:35:41 +02:00
wip
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
package me.vripper.model
|
||||
|
||||
data class DownloadRequest(val imageId: Long)
|
||||
@@ -0,0 +1,33 @@
|
||||
package me.vripper.model
|
||||
|
||||
data class ImageChunk(
|
||||
val missing: Boolean,
|
||||
val imageId: Long,
|
||||
val offset: Long,
|
||||
val data: ByteArray,
|
||||
val isLast: Boolean
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ImageChunk
|
||||
|
||||
if (missing != other.missing) return false
|
||||
if (imageId != other.imageId) return false
|
||||
if (offset != other.offset) return false
|
||||
if (isLast != other.isLast) return false
|
||||
if (!data.contentEquals(other.data)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = missing.hashCode()
|
||||
result = 31 * result + imageId.hashCode()
|
||||
result = 31 * result + offset.hashCode()
|
||||
result = 31 * result + isLast.hashCode()
|
||||
result = 31 * result + data.contentHashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import me.vripper.tasks.ThreadLookupTask
|
||||
import me.vripper.utilities.*
|
||||
import me.vripper.utilities.ApplicationProperties.VRIPPER_DIR
|
||||
import org.h2.jdbc.JdbcSQLNonTransientConnectionException
|
||||
import java.nio.file.Files
|
||||
import java.sql.DriverManager
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
@@ -25,6 +26,7 @@ import kotlin.concurrent.withLock
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import kotlin.math.min
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
|
||||
internal class AppEndpointService(
|
||||
@@ -173,11 +175,8 @@ internal class AppEndpointService(
|
||||
|
||||
override suspend fun renameToFirst(postEntityIds: List<Long>) {
|
||||
postEntityIds.forEach { postEntityId ->
|
||||
dataAccessService
|
||||
.findMetadataByPostEntityId(postEntityId)
|
||||
.map { it.data.resolvedNames }
|
||||
.filter { it.isNotEmpty() }
|
||||
.getOrNull()?.let { rename(postEntityId, it.first()) }
|
||||
dataAccessService.findMetadataByPostEntityId(postEntityId).map { it.data.resolvedNames }
|
||||
.filter { it.isNotEmpty() }.getOrNull()?.let { rename(postEntityId, it.first()) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,11 +328,55 @@ internal class AppEndpointService(
|
||||
downloadService.move(postEntityId, position)
|
||||
}
|
||||
|
||||
override fun downloadImage(downloadRequest: DownloadRequest): Flow<ImageChunk> {
|
||||
val imageEntity = dataAccessService.findImageById(downloadRequest.imageId).orElseThrow()
|
||||
val postEntity = dataAccessService.findPostByEntityId(imageEntity.postEntityId)
|
||||
val filePath = Path(postEntity.downloadDirectory).resolve(postEntity.folderName).resolve(imageEntity.filename)
|
||||
return if (filePath.exists()) {
|
||||
val chunkSize = 256 * 1024
|
||||
val bytes = Files.readAllBytes(filePath)
|
||||
flow {
|
||||
var offset = 0L
|
||||
while (offset < bytes.size) {
|
||||
val start = offset.toInt()
|
||||
val len = min(chunkSize, bytes.size - start)
|
||||
val part = bytes.copyOfRange(start, start + len)
|
||||
|
||||
val isLast = (start + len) >= bytes.size
|
||||
|
||||
emit(
|
||||
ImageChunk(
|
||||
missing = false,
|
||||
imageId = downloadRequest.imageId,
|
||||
offset = offset,
|
||||
data = part,
|
||||
isLast = isLast
|
||||
)
|
||||
)
|
||||
|
||||
offset += len.toLong()
|
||||
if (isLast) break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
flow {
|
||||
emit(
|
||||
ImageChunk(
|
||||
missing = true,
|
||||
imageId = downloadRequest.imageId,
|
||||
offset = 0,
|
||||
data = ByteArray(0),
|
||||
isLast = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun dbMigration(): String {
|
||||
|
||||
val conn = try {
|
||||
DriverManager
|
||||
.getConnection("jdbc:h2:file:$VRIPPER_DIR/vripper;DB_CLOSE_DELAY=-1;IFEXISTS=TRUE")
|
||||
DriverManager.getConnection("jdbc:h2:file:$VRIPPER_DIR/vripper;DB_CLOSE_DELAY=-1;IFEXISTS=TRUE")
|
||||
} catch (_: JdbcSQLNonTransientConnectionException) {
|
||||
return "Old database not found, nothing to do"
|
||||
}
|
||||
@@ -425,8 +468,7 @@ internal class AppEndpointService(
|
||||
if (set.next()) {
|
||||
val data = Json.decodeFromString(set.getString("DATA")) as MetadataEntity.Data
|
||||
val metadata = MetadataEntity(
|
||||
postIdRef = savedPost.id,
|
||||
data = data
|
||||
postIdRef = savedPost.id, data = data
|
||||
)
|
||||
dataAccessService.saveMetadata(metadata)
|
||||
}
|
||||
|
||||
@@ -47,5 +47,6 @@ interface IAppEndpointService {
|
||||
suspend fun dbMigration(): String
|
||||
suspend fun initLogger()
|
||||
suspend fun move(postEntityId: Long, position: MovePosition)
|
||||
fun downloadImage(downloadRequest: DownloadRequest): Flow<ImageChunk>
|
||||
fun connectionState(): String
|
||||
}
|
||||
@@ -94,6 +94,18 @@ message MovePositionMessage {
|
||||
MovePositionEnum position = 2;
|
||||
}
|
||||
|
||||
message DownloadRequest {
|
||||
int64 imageId = 1;
|
||||
}
|
||||
|
||||
message ImageChunk {
|
||||
bool missing = 1;
|
||||
int64 imageId = 2;
|
||||
int64 offset = 3;
|
||||
bytes data = 4;
|
||||
bool isLast = 5;
|
||||
}
|
||||
|
||||
service EndpointService {
|
||||
rpc scanLinks (Links) returns (EmptyResponse) {}
|
||||
rpc onNewPosts (EmptyRequest) returns (stream Post) {}
|
||||
@@ -136,5 +148,6 @@ service EndpointService {
|
||||
rpc getVersion (EmptyRequest) returns (Version) {}
|
||||
rpc dbMigration (EmptyRequest) returns (DBMigrationResponse) {}
|
||||
rpc initLogger (EmptyRequest) returns (EmptyResponse) {}
|
||||
rpc move (MovePositionMessage) returns (EmptyResponse) {}
|
||||
rpc move(MovePositionMessage) returns (EmptyResponse) {}
|
||||
rpc downloadImage(DownloadRequest) returns (stream ImageChunk);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package me.vripper.gui.components.fragments
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import me.vripper.model.ImageChunk
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
|
||||
interface ImageSource {
|
||||
suspend fun inputStream(): InputStream?
|
||||
fun fileName(): String
|
||||
}
|
||||
|
||||
data class BytesImageSource(
|
||||
val fileName: String,
|
||||
val downloadFunction: () -> Flow<ImageChunk>
|
||||
) : ImageSource {
|
||||
|
||||
override suspend fun inputStream(): InputStream? {
|
||||
val bos = ByteArrayOutputStream()
|
||||
var missing = false
|
||||
downloadFunction().collect {
|
||||
if (it.missing) {
|
||||
missing = true
|
||||
} else {
|
||||
bos.write(it.data)
|
||||
}
|
||||
}
|
||||
val bytes = bos.toByteArray()
|
||||
return if (missing) null else ByteArrayInputStream(bytes)
|
||||
}
|
||||
|
||||
override fun fileName(): String {
|
||||
return fileName
|
||||
}
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
package me.vripper.gui.components.fragments
|
||||
|
||||
import atlantafx.base.theme.Styles
|
||||
import javafx.beans.property.SimpleIntegerProperty
|
||||
import javafx.geometry.Pos
|
||||
import javafx.scene.control.Button
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.image.Image
|
||||
import javafx.scene.image.ImageView
|
||||
import javafx.scene.layout.BorderPane
|
||||
import javafx.scene.layout.HBox
|
||||
import kotlinx.coroutines.*
|
||||
import org.kordamp.ikonli.feather.Feather
|
||||
import org.kordamp.ikonli.javafx.FontIcon
|
||||
import tornadofx.*
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
class PhotoViewerFragment : Fragment("Image Viewer") {
|
||||
|
||||
val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
val sources: List<ImageSource> by param()
|
||||
val initialIndex: Int by param()
|
||||
|
||||
private val indexProperty = SimpleIntegerProperty(initialIndex)
|
||||
|
||||
private val imageView = ImageView().apply {
|
||||
isPreserveRatio = true
|
||||
isSmooth = true
|
||||
isCache = true
|
||||
}
|
||||
|
||||
private val cache = object : LinkedHashMap<Int, Image>(8, 0.75f, true) {
|
||||
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<Int, Image>?): Boolean = size > 3
|
||||
}
|
||||
|
||||
private var bottomBar: HBox
|
||||
|
||||
override val root = BorderPane().apply {
|
||||
center = imageView
|
||||
BorderPane.setAlignment(imageView, Pos.CENTER)
|
||||
|
||||
val prevButton = Button().apply {
|
||||
graphic = FontIcon.of(Feather.ARROW_LEFT)
|
||||
addClass(Styles.ACCENT)
|
||||
setOnAction {
|
||||
if (sources.isEmpty()) return@setOnAction
|
||||
coroutineScope.launch {
|
||||
goTo((indexProperty.get() - 1 + sources.size) % sources.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val nextButton = Button().apply {
|
||||
graphic = FontIcon.of(Feather.ARROW_RIGHT)
|
||||
addClass(Styles.ACCENT)
|
||||
setOnAction {
|
||||
if (sources.isEmpty()) return@setOnAction
|
||||
coroutineScope.launch {
|
||||
goTo((indexProperty.get() + 1) % sources.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val label = Label().apply {
|
||||
textProperty().bind(indexProperty.plus(1).asString().concat(" / ${sources.size}"))
|
||||
}
|
||||
|
||||
bottomBar = HBox(10.0, prevButton, label, nextButton).apply {
|
||||
alignment = Pos.CENTER
|
||||
paddingBottom = 5.0
|
||||
}
|
||||
bottom = bottomBar
|
||||
|
||||
fun scheduleResize() {
|
||||
runLater { resizeNoUpscale() }
|
||||
}
|
||||
|
||||
// Re-evaluate whenever container size or bottom bar size changes
|
||||
layoutBoundsProperty().addListener { _, _, _ -> scheduleResize() }
|
||||
bottomBar.layoutBoundsProperty().addListener { _, _, _ -> scheduleResize() }
|
||||
|
||||
// Re-evaluate whenever the image changes (so it works on prev/next)
|
||||
imageView.imageProperty().addListener { _, _, _ -> scheduleResize() }
|
||||
}
|
||||
|
||||
init {
|
||||
coroutineScope.launch {
|
||||
if (sources.isEmpty()) {
|
||||
return@launch
|
||||
}
|
||||
val image = loadImageFor(indexProperty.value)
|
||||
runLater {
|
||||
imageView.image = image
|
||||
title = sources[indexProperty.value].fileName()
|
||||
resizeNoUpscale()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private suspend fun goTo(newIndex: Int) {
|
||||
if (sources.isEmpty()) return
|
||||
|
||||
val image = loadImageFor(newIndex)
|
||||
runLater {
|
||||
imageView.image = image
|
||||
indexProperty.set(newIndex)
|
||||
title = sources[newIndex].fileName()
|
||||
}
|
||||
|
||||
// preload neighbors only
|
||||
val prev = (newIndex - 1 + sources.size) % sources.size
|
||||
val next = (newIndex + 1) % sources.size
|
||||
preload(prev)
|
||||
preload(next)
|
||||
}
|
||||
|
||||
private fun resizeNoUpscale() {
|
||||
val img = imageView.image ?: return
|
||||
val ih = img.height
|
||||
if (ih <= 0.0) return
|
||||
|
||||
val bottomH = bottomBar.layoutBounds.height
|
||||
val availableH = (root.height - bottomH - 10.0).coerceAtLeast(0.0)
|
||||
|
||||
val scale = min(1.0, availableH / ih) // never upscale
|
||||
imageView.isPreserveRatio = true
|
||||
imageView.fitHeight = ih * scale
|
||||
imageView.fitWidth = 0.0
|
||||
}
|
||||
|
||||
private suspend fun preload(i: Int) {
|
||||
if (i == indexProperty.get()) return
|
||||
if (cache.containsKey(i)) return
|
||||
cache[i] = loadImageFor(i)
|
||||
}
|
||||
|
||||
private suspend fun loadImageFor(i: Int): Image {
|
||||
cache[i]?.let { return it }
|
||||
val source = sources[i]
|
||||
val inputStream = source.inputStream()
|
||||
val img = if (inputStream == null) {
|
||||
missingPlaceholder
|
||||
} else {
|
||||
Image(inputStream, 0.0, 0.0, true, true)
|
||||
}
|
||||
cache[i] = img
|
||||
return img
|
||||
}
|
||||
|
||||
private val missingPlaceholder: Image by lazy {
|
||||
// 1x1 transparent PNG
|
||||
val transparentPngBase64 =
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/xcAAgMBgX7Y1z8AAAAASUVORK5CYII="
|
||||
val bytes = Base64.getDecoder().decode(transparentPngBase64)
|
||||
Image(ByteArrayInputStream(bytes), 0.0, 0.0, true, true)
|
||||
}
|
||||
|
||||
override fun onUndock() {
|
||||
coroutineScope.cancel()
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import javafx.collections.FXCollections
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.event.EventHandler
|
||||
import javafx.geometry.Pos
|
||||
import javafx.scene.Cursor
|
||||
import javafx.scene.control.*
|
||||
import javafx.scene.control.cell.TextFieldTableCell
|
||||
import javafx.scene.input.MouseButton
|
||||
@@ -17,6 +18,7 @@ import me.vripper.entities.Status
|
||||
import me.vripper.gui.components.cells.PreviewTableCell
|
||||
import me.vripper.gui.components.cells.ProgressTableCell
|
||||
import me.vripper.gui.components.cells.StatusTableCell
|
||||
import me.vripper.gui.components.fragments.PhotoViewerFragment
|
||||
import me.vripper.gui.controller.ImageController
|
||||
import me.vripper.gui.controller.WidgetsController
|
||||
import me.vripper.gui.model.ImageModel
|
||||
@@ -73,6 +75,7 @@ class ImagesTableView : View("Photos") {
|
||||
cellFactory = Callback {
|
||||
val cell = PreviewTableCell<ImageModel, String>()
|
||||
cell.onMouseExited = EventHandler {
|
||||
cursor = Cursor.DEFAULT
|
||||
preview.cleanup()
|
||||
}
|
||||
cell.onMouseMoved = EventHandler {
|
||||
@@ -82,6 +85,7 @@ class ImagesTableView : View("Photos") {
|
||||
}
|
||||
}
|
||||
cell.onMouseEntered = EventHandler { mouseEvent ->
|
||||
cursor = Cursor.HAND
|
||||
preview.cleanup()
|
||||
if (cell.tableRow.item != null && cell.tableRow.item.thumbUrl.isNotEmpty()) {
|
||||
preview.display(cell.tableRow.item.postEntityId, listOf(cell.tableRow.item.thumbUrl))
|
||||
@@ -91,6 +95,34 @@ class ImagesTableView : View("Photos") {
|
||||
}
|
||||
}
|
||||
}
|
||||
cell.onLeftClick {
|
||||
preview.cleanup()
|
||||
coroutineScope.launch {
|
||||
val imageSources = imageController.getImageSources(cell.tableRow.item)
|
||||
if (imageSources.none { it.key.id == cell.tableRow.item.id }) {
|
||||
return@launch
|
||||
}
|
||||
runLater {
|
||||
find<PhotoViewerFragment>(
|
||||
mapOf(
|
||||
PhotoViewerFragment::sources to imageSources.map { it.value },
|
||||
PhotoViewerFragment::initialIndex to cell.tableRow.item.index - 1,
|
||||
)
|
||||
).openModal()?.apply {
|
||||
val w = 800.0
|
||||
val h = 600.0
|
||||
minWidth = 100.0
|
||||
minHeight = 100.0
|
||||
width = w
|
||||
height = h
|
||||
val x = owner.x + (owner.width - w) / 2
|
||||
val y = owner.y + (owner.height - h) / 2
|
||||
this.x = x
|
||||
this.y = y
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cell
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import javafx.collections.FXCollections
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.event.EventHandler
|
||||
import javafx.geometry.Pos
|
||||
import javafx.scene.Cursor
|
||||
import javafx.scene.control.*
|
||||
import javafx.scene.control.cell.TextFieldTableCell
|
||||
import javafx.scene.input.KeyCode
|
||||
@@ -20,6 +21,7 @@ import me.vripper.gui.components.cells.PreviewTableCell
|
||||
import me.vripper.gui.components.cells.ProgressTableCell
|
||||
import me.vripper.gui.components.cells.StatusTableCell
|
||||
import me.vripper.gui.components.fragments.AddLinksFragment
|
||||
import me.vripper.gui.components.fragments.PhotoViewerFragment
|
||||
import me.vripper.gui.components.fragments.RenameFragment
|
||||
import me.vripper.gui.controller.PostController
|
||||
import me.vripper.gui.controller.WidgetsController
|
||||
@@ -253,6 +255,7 @@ class PostsTableView : View() {
|
||||
cellFactory = Callback {
|
||||
val cell = PreviewTableCell<PostModel, ObservableList<String>>()
|
||||
cell.onMouseExited = EventHandler {
|
||||
cursor = Cursor.DEFAULT
|
||||
preview.cleanup()
|
||||
}
|
||||
cell.onMouseMoved = EventHandler {
|
||||
@@ -262,6 +265,7 @@ class PostsTableView : View() {
|
||||
}
|
||||
}
|
||||
cell.onMouseEntered = EventHandler { mouseEvent ->
|
||||
cursor = Cursor.HAND
|
||||
preview.cleanup()
|
||||
if (cell.tableRow.item != null && cell.tableRow.item.previewList.isNotEmpty()) {
|
||||
preview.display(
|
||||
@@ -273,6 +277,34 @@ class PostsTableView : View() {
|
||||
}
|
||||
}
|
||||
}
|
||||
cell.onLeftClick {
|
||||
preview.cleanup()
|
||||
coroutineScope.launch {
|
||||
val imageSources = postController.getImageSources(cell.tableRow.item)
|
||||
if (imageSources.isEmpty()) {
|
||||
return@launch
|
||||
}
|
||||
runLater {
|
||||
find<PhotoViewerFragment>(
|
||||
mapOf(
|
||||
PhotoViewerFragment::sources to imageSources,
|
||||
PhotoViewerFragment::initialIndex to 0,
|
||||
)
|
||||
).openModal()?.apply {
|
||||
val w = 800.0
|
||||
val h = 600.0
|
||||
minWidth = 100.0
|
||||
minHeight = 100.0
|
||||
width = w
|
||||
height = h
|
||||
val x = owner.x + (owner.width - w) / 2
|
||||
val y = owner.y + (owner.height - h) / 2
|
||||
this.x = x
|
||||
this.y = y
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cell
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package me.vripper.gui.controller
|
||||
|
||||
import me.vripper.entities.ImageEntity
|
||||
import me.vripper.gui.components.fragments.BytesImageSource
|
||||
import me.vripper.gui.components.fragments.ImageSource
|
||||
import me.vripper.gui.model.ImageModel
|
||||
import me.vripper.gui.utils.AppEndpointManager.currentAppEndpointService
|
||||
import me.vripper.model.DownloadRequest
|
||||
import me.vripper.model.Image
|
||||
import tornadofx.Controller
|
||||
|
||||
@@ -42,4 +46,13 @@ class ImageController : Controller() {
|
||||
currentAppEndpointService().onUpdateImagesByPostEntityId(postId)
|
||||
|
||||
fun onStopped() = currentAppEndpointService().onStopped()
|
||||
|
||||
suspend fun getImageSources(item: ImageModel): Map<ImageEntity, ImageSource> {
|
||||
val imageEntities = currentAppEndpointService().findImagesByPostEntityId(item.postEntityId)
|
||||
return imageEntities.associateWith { imageEntity ->
|
||||
BytesImageSource(imageEntity.filename) {
|
||||
currentAppEndpointService().downloadImage(DownloadRequest(imageEntity.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
package me.vripper.gui.controller
|
||||
|
||||
import kotlinx.coroutines.flow.map
|
||||
import me.vripper.gui.components.fragments.BytesImageSource
|
||||
import me.vripper.gui.components.fragments.ImageSource
|
||||
import me.vripper.gui.model.PostModel
|
||||
import me.vripper.gui.utils.AppEndpointManager.currentAppEndpointService
|
||||
import me.vripper.gui.utils.AppEndpointManager.localAppEndpointService
|
||||
import me.vripper.gui.utils.AppEndpointManager.remoteAppEndpointService
|
||||
import me.vripper.gui.utils.ChannelFlowBuilder
|
||||
import me.vripper.model.DownloadRequest
|
||||
import me.vripper.model.Post
|
||||
import me.vripper.model.QueueState
|
||||
import me.vripper.services.download.MovePosition
|
||||
@@ -133,4 +136,13 @@ class PostController : Controller() {
|
||||
fun progress(total: Int, done: Int): Double {
|
||||
return if (done == 0 && total == 0) 0.0 else (done.toDouble() / total)
|
||||
}
|
||||
|
||||
suspend fun getImageSources(item: PostModel): List<ImageSource> {
|
||||
val imageEntities = currentAppEndpointService().findImagesByPostEntityId(item.id)
|
||||
return imageEntities.map { imageEntity ->
|
||||
BytesImageSource(imageEntity.filename) {
|
||||
currentAppEndpointService().downloadImage(DownloadRequest(imageEntity.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,10 @@ import kotlinx.coroutines.flow.map
|
||||
import me.vripper.entities.MetadataEntity
|
||||
import me.vripper.entities.Status
|
||||
import me.vripper.model.*
|
||||
import me.vripper.model.DownloadRequest
|
||||
import me.vripper.model.DownloadSpeed
|
||||
import me.vripper.model.ErrorCount
|
||||
import me.vripper.model.ImageChunk
|
||||
import me.vripper.model.PostSelection
|
||||
import me.vripper.model.QueueState
|
||||
import me.vripper.model.Rank
|
||||
@@ -252,6 +254,14 @@ internal class GrpcEndpointService : IAppEndpointService {
|
||||
override suspend fun dbMigration(): String =
|
||||
endpointServiceCoroutineStub!!.dbMigration(EmptyRequest.getDefaultInstance()).message
|
||||
|
||||
override fun downloadImage(downloadRequest: DownloadRequest): Flow<ImageChunk> =
|
||||
endpointServiceCoroutineStub!!.downloadImage(
|
||||
EndpointServiceOuterClass
|
||||
.DownloadRequest
|
||||
.newBuilder()
|
||||
.setImageId(downloadRequest.imageId).build()
|
||||
).map { ImageChunk(it.missing, it.imageId, it.offset, it.data.toByteArray(), it.isLast) }
|
||||
|
||||
private fun mapper(queueState: EndpointServiceOuterClass.QueueState) =
|
||||
QueueState(queueState.running, queueState.remaining, queueState.rankList.map {
|
||||
Rank(it.postEntityId, it.rank)
|
||||
|
||||
@@ -29,4 +29,8 @@ object AppEndpointManager : KoinComponent {
|
||||
else -> throw IllegalStateException("Unknown current state: $current")
|
||||
}
|
||||
}
|
||||
|
||||
fun currentAppState(): GUIEvent {
|
||||
return this.current
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.vripper.web.grpc
|
||||
|
||||
import com.google.protobuf.ByteString
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import me.vripper.entities.ImageEntity
|
||||
@@ -213,15 +214,13 @@ class GrpcServerAppEndpointService : EndpointServiceGrpcKt.EndpointServiceCorout
|
||||
enableClipboardMonitoring = request.systemSettings.enableClipboardMonitoring,
|
||||
clipboardPollingRate = request.systemSettings.clipboardPollingRate,
|
||||
maxEventLog = request.systemSettings.maxEventLog,
|
||||
),
|
||||
hostSettings = request.hostSettingsMap.entries.associate { hostSettings ->
|
||||
), hostSettings = request.hostSettingsMap.entries.associate { hostSettings ->
|
||||
HostName.valueOf(hostSettings.key) to hostSettings.value.settingsMap.entries.associate {
|
||||
HostSettingKey.valueOf(
|
||||
it.key
|
||||
) to it.value
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
)
|
||||
return EndpointServiceOuterClass.EmptyResponse.getDefaultInstance()
|
||||
}
|
||||
@@ -239,6 +238,18 @@ class GrpcServerAppEndpointService : EndpointServiceGrpcKt.EndpointServiceCorout
|
||||
override suspend fun dbMigration(request: EndpointServiceOuterClass.EmptyRequest): EndpointServiceOuterClass.DBMigrationResponse =
|
||||
EndpointServiceOuterClass.DBMigrationResponse.newBuilder().setMessage(appEndpointService.dbMigration()).build()
|
||||
|
||||
override fun downloadImage(request: EndpointServiceOuterClass.DownloadRequest): Flow<EndpointServiceOuterClass.ImageChunk> {
|
||||
return appEndpointService.downloadImage(DownloadRequest(request.imageId)).map {
|
||||
with(EndpointServiceOuterClass.ImageChunk.newBuilder()) {
|
||||
imageId = it.imageId
|
||||
offset = it.offset
|
||||
data = ByteString.copyFrom(it.data)
|
||||
isLast = it.isLast
|
||||
build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun mapper(queueState: QueueState): EndpointServiceOuterClass.QueueState {
|
||||
return with(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
spring.liquibase.enabled=false
|
||||
server.port=8080
|
||||
grpc.enabled=false
|
||||
grpc.passphrase=
|
||||
grpc.enabled=true
|
||||
grpc.passphrase=123
|
||||
grpc.port=30000
|
||||
Reference in New Issue
Block a user