Skip to content

Commit 6b9546d

Browse files
committed
Add config defaults, k8scp: remove debug from K8scpSource.stat, dav: configurable nextcloud upload chunk size, fix no update on source change, sftp: add home
1 parent de60961 commit 6b9546d

File tree

13 files changed

+54
-40
lines changed

13 files changed

+54
-40
lines changed

src/main/kotlin/com/valaphee/blit/Main.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ fun main(arguments: Array<String>) {
4242
val injector = Guice.createInjector(DataModule(), object : AbstractModule() {
4343
@Singleton
4444
@Provides
45-
fun locale(config: Config, locales: Map<String, @JvmSuppressWildcards Locale>) = config.locale?.let { locales[it] ?: locales["en_us"] } ?: locales["en_us"]
45+
fun locale(config: Config, locales: Map<String, @JvmSuppressWildcards Locale>) = locales[config.locale] ?: locales["en_us"]
4646
})
4747

4848
FX.dicontainer = object : DIContainer {

src/main/kotlin/com/valaphee/blit/MainView.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,15 @@ class MainView : View("Blit") {
128128
}
129129

130130
inner class Pane<T : Entry<T>> : VBox() {
131-
private val source = SimpleObjectProperty<Source<T>>().apply { onChange { it?.let { navigate(it.home) } } }
132-
private lateinit var _path: String
131+
private val source = SimpleObjectProperty<Source<T>>().apply {
132+
onChange {
133+
it?.let {
134+
_path = null
135+
navigate(it.home)
136+
}
137+
}
138+
}
139+
private var _path: String? = null
133140
private val name = SimpleStringProperty()
134141
private val tree = Tree<T>()
135142

@@ -183,7 +190,7 @@ class MainView : View("Blit") {
183190
var canonicalPath = path.toCanonicalPath().joinToString("/")
184191
if (!canonicalPath.endsWith('/')) canonicalPath = "$canonicalPath/"
185192

186-
if (::_path.isInitialized && canonicalPath == _path) return
193+
if (canonicalPath == _path) return
187194

188195
source.value?.let {
189196
worker.launch(locale["main.navigator.task.navigate.name", canonicalPath]) {

src/main/kotlin/com/valaphee/blit/data/DataModule.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.valaphee.blit.data
1818

19+
import com.fasterxml.jackson.annotation.JsonInclude
1920
import com.fasterxml.jackson.databind.DeserializationFeature
2021
import com.fasterxml.jackson.databind.ObjectMapper
2122
import com.fasterxml.jackson.databind.PropertyNamingStrategies
@@ -42,6 +43,8 @@ class DataModule(
4243
if (!::objectMapper.isInitialized) objectMapper = jacksonObjectMapper().apply {
4344
registerModule(AfterburnerModule())
4445
propertyNamingStrategy = PropertyNamingStrategies.SNAKE_CASE
46+
setSerializationInclusion(JsonInclude.Include.NON_DEFAULT)
47+
enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
4548
disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
4649
}.also { bind(ObjectMapper::class.java).toInstance(it) }
4750

src/main/kotlin/com/valaphee/blit/data/config/Config.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import com.valaphee.blit.source.Source
2424
import com.valaphee.blit.source.local.LocalSource
2525
import java.lang.Long.signum
2626
import java.text.StringCharacterIterator
27+
import java.util.Locale
2728
import kotlin.math.abs
2829

2930
/**
@@ -32,7 +33,7 @@ import kotlin.math.abs
3233
@Singleton
3334
@DataType("config")
3435
class Config(
35-
@get:JsonProperty("locale") val locale: String? = null,
36+
@get:JsonProperty("locale") val locale: String = Locale.getDefault().toLanguageTag().replace('-', '_'),
3637
@get:JsonProperty("data_size_unit") val dataSizeUnit: DataSizeUnit = DataSizeUnit.IEC,
3738
@get:JsonProperty("sources") val sources: List<Source<*>> = listOf(LocalSource("local"))
3839
) : Data {

src/main/kotlin/com/valaphee/blit/source/dav/DavEntry.kt

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import io.ktor.client.request.put
2828
import io.ktor.client.request.request
2929
import io.ktor.client.statement.HttpResponse
3030
import io.ktor.client.statement.readBytes
31+
import io.ktor.client.statement.request
3132
import io.ktor.http.HttpStatusCode
3233
import io.ktor.http.URLBuilder
3334
import io.ktor.http.content.OutgoingContent
@@ -53,20 +54,20 @@ class DavEntry(
5354
override val modifyTime get() = prop.getlastmodified?.time ?: 0
5455
override val directory get() = prop.resourcetype?.collection != null
5556

56-
override suspend fun list() = if (directory) {
57+
override suspend fun list(): List<DavEntry> = if (directory) {
58+
val path = if (path.startsWith('/')) path.substring(1) else path // Unix path correction, "." ("") and "/" are the same
5759
val httpResponse = davSource.httpClient.request<HttpResponse>("${davSource._url}/$path") { method = httpMethodPropfind }
58-
if (httpResponse.status == HttpStatusCode.MultiStatus) {
59-
var first = true
60-
xmlMapper.readValue<Multistatus>(httpResponse.readBytes()).response.mapNotNull {
61-
if (first) {
62-
first = false
63-
null
64-
} else {
60+
when (httpResponse.status) {
61+
HttpStatusCode.MultiStatus -> {
62+
val href = httpResponse.request.url.encodedPath
63+
xmlMapper.readValue<Multistatus>(httpResponse.readBytes()).response.filter { !it.href.equals(href, true) }.mapNotNull {
6564
val name = URLDecoder.decode(it.href.removeSuffix("/").split('/').last(), "UTF-8")
66-
/*if (name != this.name) */DavEntry(davSource, "${if (path == "/") "" else path}/${name}", it.propstat.first().prop)/* else null*/
67-
} // TODO: it is not defined in the standard that the first entry has to be the entry itself
65+
it.propstat.find { it.status == "HTTP/1.1 200 OK" }?.prop?.let { DavEntry(davSource, "$path/$name", it) }
66+
}
6867
}
69-
} else emptyList()
68+
HttpStatusCode.NotFound -> throw NotFoundException(path)
69+
else -> TODO()
70+
}
7071
} else emptyList()
7172

7273
override suspend fun transferTo(stream: OutputStream) {
@@ -78,15 +79,15 @@ class DavEntry(
7879
}
7980

8081
override suspend fun transferFrom(name: String, stream: InputStream, length: Long) {
81-
if (davSource.nextcloud && length > chunkSize) {
82+
if (davSource.nextcloud && length > davSource.nextcloudUploadChunkSize) {
8283
val id = "blit-${UUID.randomUUID()}"
8384
davSource.httpClient.request<Unit>("${davSource.url}/uploads/${davSource.username}/$id") { method = httpMethodMkcol }
8485

8586
/*val jobs = mutableListOf<Job>()*/
86-
val chunkCount = ceil(length / chunkSize.toDouble())
87+
val chunkCount = ceil(length / davSource.nextcloudUploadChunkSize.toDouble())
8788
for (i in 0..chunkCount) {
8889
/*jobs += ioScope.launch { */
89-
davSource.httpClient.put<Unit>("${davSource.url}/uploads/${davSource.username}/$id/${i * chunkSize}") { body = stream.readNBytes(chunkSize.toInt()) }
90+
davSource.httpClient.put<Unit>("${davSource.url}/uploads/${davSource.username}/$id/${i * davSource.nextcloudUploadChunkSize}") { body = stream.readNBytes(davSource.nextcloudUploadChunkSize.toInt()) }
9091
coroutineContext.progress = i / chunkCount.toDouble() // TODO: more precise progress
9192
/*}*/
9293
}
@@ -124,8 +125,6 @@ class DavEntry(
124125
}
125126

126127
companion object {
127-
private const val chunkSize = 10L * 1024 * 1024
128-
129128
private fun ceil(value: Double): Int {
130129
val valueInt = (value + 1).toInt()
131130
return if (value >= valueInt) valueInt else valueInt - 1

src/main/kotlin/com/valaphee/blit/source/dav/DavSource.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,12 @@ import javax.net.ssl.X509TrustManager
4747
*/
4848
@JsonTypeName("dav")
4949
class DavSource(
50-
name: String,
51-
@get:JsonProperty("url") val url: String,
52-
@get:JsonProperty("username") val username: String,
53-
@get:JsonProperty("password") val password: String,
54-
@get:JsonProperty("nextcloud") val nextcloud: Boolean
50+
name: String = "",
51+
@get:JsonProperty("url") val url: String = "",
52+
@get:JsonProperty("username") val username: String = "",
53+
@get:JsonProperty("password") val password: String = "",
54+
@get:JsonProperty("nextcloud") val nextcloud: Boolean = false,
55+
@get:JsonProperty("nextcloud-upload-chunk-size") val nextcloudUploadChunkSize: Long = 10L * 1024 * 1024
5556
) : AbstractSource<DavEntry>(name) {
5657
@get:JsonIgnore internal val httpClient by lazy {
5758
HttpClient(OkHttp) {

src/main/kotlin/com/valaphee/blit/source/dav/DavSourceUi.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import javafx.scene.control.TextField
2424
import tornadofx.Field
2525
import tornadofx.checkbox
2626
import tornadofx.field
27+
import tornadofx.filterInput
28+
import tornadofx.isLong
2729
import tornadofx.passwordfield
2830
import tornadofx.textfield
2931
import tornadofx.toProperty
@@ -43,6 +45,7 @@ object DavSourceUi : SourceUi {
4345
field("Username") { textfield(davSource?.username ?: "") },
4446
field("Password") { passwordfield(davSource?.password ?: "") },
4547
field("Nextcloud") { checkbox(property = davSource?.nextcloud.toProperty()) },
48+
field("Nextcloud Upload Chunk Size") { textfield(davSource?.nextcloudUploadChunkSize?.toString() ?: "") { filterInput { it.controlNewText.isLong() } } }
4649
)
4750
}
4851

@@ -52,5 +55,6 @@ object DavSourceUi : SourceUi {
5255
(fields[2].inputs[0] as TextField).text,
5356
(fields[3].inputs[0] as TextField).text,
5457
(fields[4].inputs[0] as CheckBox).isSelected,
58+
(fields[5].inputs[0] as TextField).text.toLong()
5559
)
5660
}

src/main/kotlin/com/valaphee/blit/source/k8scp/K8scpSource.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ import java.io.InputStreamReader
3131
*/
3232
@JsonTypeName("k8scp")
3333
class K8scpSource(
34-
name: String,
35-
@get:JsonProperty("namespace") val namespace: String,
36-
@get:JsonProperty("pod") val pod: String
34+
name: String = "",
35+
@get:JsonProperty("namespace") val namespace: String = "",
36+
@get:JsonProperty("pod") val pod: String = ""
3737
) : AbstractSource<K8scpEntry>(name) {
3838
override val home: String get() = if (namespace.isNotEmpty() && pod.isNotEmpty()) {
3939
val process = copy.exec(namespace, pod, arrayOf("pwd", toString()), false)

src/main/kotlin/com/valaphee/blit/source/k8scp/K8scpUtil.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ internal fun K8scpSource.stat(path: String): SftpClient.Attributes? {
3131
val (namespace, pod, path) = getNamespacePodAndPath(path)
3232
return if (namespace != null) {
3333
if (pod != null) {
34-
println("$namespace - $pod - $path")
3534
val process = K8scpSource.copy.exec(namespace, pod, arrayOf("stat", "--format", "%A 0 %U %G %s %y %n", path), false)
3635
val attributes = BufferedReader(InputStreamReader(process.inputStream)).use { parseLsEntry(it.readText())?.second }
3736
process.waitFor()

src/main/kotlin/com/valaphee/blit/source/local/LocalSource.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import java.io.File
2626
*/
2727
@JsonTypeName("local")
2828
class LocalSource(
29-
name: String
29+
name: String = ""
3030
) : AbstractSource<LocalEntry>(name) {
3131
override val home: String get() = File(System.getProperty("user.home")).absolutePath
3232

0 commit comments

Comments
 (0)