Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ jobs:
name: test-results-ios
path: '**/build/test-results/**/TEST-*.xml'

wasmjs-tests:
name: WasmJs Tests
js-tests:
name: JS Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
Expand All @@ -98,20 +98,20 @@ jobs:

- uses: gradle/actions/setup-gradle@v5

- name: Run WasmJs tests
run: ./gradlew wasmJsBrowserTest wasmJsNodeTest
- name: Run JS tests
run: ./gradlew jsNodeTest

- name: Upload test results
if: always()
uses: actions/upload-artifact@v6
with:
name: test-results-wasmjs
name: test-results-js
path: '**/build/test-results/**/TEST-*.xml'

publish-results:
name: Publish Test Results
runs-on: ubuntu-latest
needs: [ jvm-tests, android-tests, ios-tests, wasmjs-tests ]
needs: [ jvm-tests, android-tests, ios-tests, js-tests ]
if: always()
steps:
- name: Download test results
Expand Down
3 changes: 3 additions & 0 deletions app/android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ dependencies {
implementation(projects.app.shared)
implementation(projects.ai.discover)
implementation(projects.library.core)
implementation(projects.library.ktor)
implementation(projects.library.ftp)
implementation(projects.library.torrent)
implementation(projects.library.sqlite)
implementation(projects.library.server)
implementation(libs.androidx.activity.compose)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,21 @@ import com.linroid.ketch.ai.AiConfig
import com.linroid.ketch.ai.AiModule
import com.linroid.ketch.ai.LlmConfig
import com.linroid.ketch.api.log.KetchLogger
import com.linroid.ketch.api.log.Logger
import com.linroid.ketch.app.instance.InstanceFactory
import com.linroid.ketch.app.instance.InstanceManager
import com.linroid.ketch.app.instance.LocalServerHandle
import com.linroid.ketch.app.instance.ServerState
import com.linroid.ketch.app.state.AiDiscoveryProvider
import com.linroid.ketch.app.state.EmbeddedAiDiscoveryProvider
import com.linroid.ketch.config.FileConfigStore
import com.linroid.ketch.core.Ketch
import com.linroid.ketch.engine.KtorHttpEngine
import com.linroid.ketch.ftp.FtpDownloadSource
import com.linroid.ketch.server.KetchServer
import com.linroid.ketch.sqlite.DriverFactory
import com.linroid.ketch.sqlite.createSqliteTaskStore
import com.linroid.ketch.torrent.TorrentDownloadSource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
Expand Down Expand Up @@ -86,9 +91,20 @@ class KetchService : Service() {
?: android.os.Build.MODEL
instanceManager = InstanceManager(
factory = InstanceFactory(
taskStore = taskStore,
downloadConfig = downloadConfig,
deviceName = instanceName,
embeddedFactory = {
Ketch(
httpEngine = KtorHttpEngine(),
taskStore = taskStore,
config = downloadConfig,
name = instanceName,
logger = Logger.console(),
additionalSources = listOf(
FtpDownloadSource(),
TorrentDownloadSource(),
),
)
},
localServerFactory = { ketchApi ->
val serverConfig = config.server
log.i { "Starting local server on port ${serverConfig.port}" }
Expand Down
5 changes: 4 additions & 1 deletion app/desktop/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ dependencies {
implementation(projects.config)
implementation(projects.app.shared)
implementation(projects.ai.discover)
implementation(projects.library.server)
implementation(projects.library.core)
implementation(projects.library.ktor)
implementation(projects.library.ftp)
implementation(projects.library.torrent)
implementation(projects.library.server)
implementation(projects.library.sqlite)
implementation(compose.desktop.currentOs)
implementation(libs.kotlinx.coroutinesSwing)
Expand Down
20 changes: 18 additions & 2 deletions app/desktop/src/main/kotlin/com/linroid/ketch/app/desktop/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@ import androidx.compose.ui.window.application
import com.linroid.ketch.ai.AiConfig
import com.linroid.ketch.ai.AiModule
import com.linroid.ketch.ai.LlmConfig
import com.linroid.ketch.api.log.Logger
import com.linroid.ketch.app.App
import com.linroid.ketch.app.instance.InstanceFactory
import com.linroid.ketch.app.instance.InstanceManager
import com.linroid.ketch.app.instance.LocalServerHandle
import com.linroid.ketch.app.state.EmbeddedAiDiscoveryProvider
import com.linroid.ketch.config.FileConfigStore
import com.linroid.ketch.config.defaultConfigDir
import com.linroid.ketch.core.Ketch
import com.linroid.ketch.engine.KtorHttpEngine
import com.linroid.ketch.ftp.FtpDownloadSource
import com.linroid.ketch.server.KetchServer
import com.linroid.ketch.sqlite.DriverFactory
import com.linroid.ketch.sqlite.createSqliteTaskStore
import com.linroid.ketch.torrent.TorrentDownloadSource
import java.io.File
import java.net.InetAddress

Expand All @@ -40,9 +45,20 @@ fun main() = application {
?: InetAddress.getLocalHost().hostName.removeSuffix(".local")
InstanceManager(
factory = InstanceFactory(
taskStore = taskStore,
downloadConfig = downloadConfig,
deviceName = instanceName,
embeddedFactory = {
Ketch(
httpEngine = KtorHttpEngine(),
taskStore = taskStore,
config = downloadConfig,
name = instanceName,
logger = Logger.console(),
additionalSources = listOf(
FtpDownloadSource(),
TorrentDownloadSource(),
),
)
},
localServerFactory = { ketchApi ->
val serverConfig = config.server
val server = KetchServer(
Expand Down
9 changes: 7 additions & 2 deletions app/shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@ kotlin {
sourceSets {
commonMain.dependencies {
implementation(projects.config)
implementation(projects.library.core)
implementation(projects.library.remote)
implementation(projects.library.ktor)

implementation(libs.kotlinx.coroutines.core)
implementation(libs.compose.runtime)
implementation(libs.compose.foundation)
Expand All @@ -61,6 +60,8 @@ kotlin {
implementation(libs.kotlinx.coroutines.test)
}
androidMain.dependencies {
implementation(projects.library.core)
implementation(projects.library.ktor)
implementation(projects.ai.discover)
implementation(projects.library.ftp)
implementation(projects.library.torrent)
Expand All @@ -69,13 +70,17 @@ kotlin {
implementation(libs.dnssd)
}
iosMain.dependencies {
implementation(projects.library.core)
implementation(projects.library.ktor)
implementation(projects.library.ftp)
implementation(projects.library.torrent)
implementation(projects.library.sqlite)
implementation(libs.ktor.client.darwin)
implementation(libs.dnssd)
}
jvmMain.dependencies {
implementation(projects.library.core)
implementation(projects.library.ktor)
implementation(projects.ai.discover)
implementation(projects.library.ftp)
implementation(projects.library.torrent)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.linroid.ketch.app.instance

import com.linroid.ketch.api.KetchApi
import com.linroid.ketch.config.RemoteConfig
import com.linroid.ketch.core.Ketch
import com.linroid.ketch.remote.ConnectionState
import com.linroid.ketch.remote.RemoteKetch
import kotlinx.coroutines.flow.StateFlow
Expand All @@ -13,7 +12,7 @@ interface InstanceEntry {
}

data class EmbeddedInstance(
override val instance: Ketch,
override val instance: KetchApi,
override val label: String,
) : InstanceEntry

Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,27 @@
package com.linroid.ketch.app.instance

import com.linroid.ketch.api.KetchApi
import com.linroid.ketch.api.DownloadConfig
import com.linroid.ketch.api.log.Logger
import com.linroid.ketch.config.RemoteConfig
import com.linroid.ketch.core.Ketch
import com.linroid.ketch.core.engine.DownloadSource
import com.linroid.ketch.core.task.TaskStore
import com.linroid.ketch.engine.KtorHttpEngine
import com.linroid.ketch.remote.RemoteKetch

/**
* Creates [KetchApi] instances for each instance type.
*
* @param taskStore persistent storage for download task records.
* Required when using the default embedded instance. Pass `null`
* for remote-only mode (e.g. wasmJs/web).
* @param deviceName label for the embedded instance (e.g. device
* model on Android, hostname on desktop).
* @param embeddedFactory factory for creating the embedded Ketch
* @param embeddedFactory factory for creating the embedded [KetchApi]
* instance. When `null`, no embedded instance is available and
* [InstanceManager] starts in remote-only mode.
* Override in tests to inject fakes.
* [InstanceManager] starts in remote-only mode (e.g. wasmJs/web).
* Each platform provides its own factory that wires up the core
* engine with platform-specific dependencies.
* @param localServerFactory optional factory that starts an HTTP
* server exposing the embedded [KetchApi]. Receives port,
* optional API token, and the embedded KetchApi instance.
* When non-null, server controls appear in the Embedded
* instance entry. Provided by Android and JVM/Desktop.
* server exposing the embedded [KetchApi]. Receives the embedded
* KetchApi instance. When non-null, server controls appear in
* the Embedded instance entry. Provided by Android and JVM/Desktop.
*/
class InstanceFactory(
taskStore: TaskStore? = null,
defaultDirectory: String? = null,
downloadConfig: DownloadConfig = DownloadConfig(
defaultDirectory = defaultDirectory,
),
val deviceName: String = "Embedded",
additionalSources: List<DownloadSource> = platformAdditionalSources(),
private val embeddedFactory: (() -> Ketch)? = taskStore?.let { ts ->
{
createDefaultEmbeddedKetch(
ts, downloadConfig, deviceName, additionalSources,
)
}
},
private val embeddedFactory: (() -> KetchApi)? = null,
private val localServerFactory: ((KetchApi) -> LocalServerHandle)? = null,
) {
/** Whether an embedded instance is available. */
Expand All @@ -54,7 +33,7 @@ class InstanceFactory(

private var localServer: LocalServerHandle? = null

/** Create the embedded Ketch instance. */
/** Create the embedded [KetchApi] instance. */
fun createEmbedded(): EmbeddedInstance {
val ketch = embeddedFactory?.invoke()
?: throw UnsupportedOperationException(
Expand Down Expand Up @@ -106,21 +85,3 @@ class InstanceFactory(
localServer = null
}
}

internal expect fun platformAdditionalSources(): List<DownloadSource>

private fun createDefaultEmbeddedKetch(
taskStore: TaskStore,
config: DownloadConfig,
name: String,
additionalSources: List<DownloadSource>,
): Ketch {
return Ketch(
httpEngine = KtorHttpEngine(),
taskStore = taskStore,
config = config,
name = name,
logger = Logger.console(),
additionalSources = additionalSources,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import com.linroid.ketch.api.ResolvedSource
import com.linroid.ketch.api.DownloadConfig
import com.linroid.ketch.config.ConfigStore
import com.linroid.ketch.config.RemoteConfig
import com.linroid.ketch.core.Ketch
import com.linroid.ketch.remote.RemoteKetch
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -24,7 +23,7 @@ import kotlinx.coroutines.launch
* instance, and lifecycle transitions.
*
* When [InstanceFactory.hasEmbedded] is `true` (Android, iOS,
* JVM/Desktop), an embedded [Ketch] instance is created once
* JVM/Desktop), an embedded [KetchApi] instance is created once
* and reused. An optional HTTP server can be started/stopped
* to expose the same instance over the network.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.linroid.ketch.api.KetchApi
import com.linroid.ketch.config.RemoteConfig
import com.linroid.ketch.app.instance.EmbeddedInstance
import com.linroid.ketch.app.instance.RemoteInstance
import com.linroid.ketch.core.Ketch
import com.linroid.ketch.remote.RemoteKetch

/**
Expand Down Expand Up @@ -50,12 +49,8 @@ class FakeInstanceFactory(
)
}
val api = embeddedFactory()
// In tests we use FakeKetchApi which is not a real Ketch,
// so we cast unsafely. Real tests needing Ketch type
// should provide a real Ketch instance.
@Suppress("UNCHECKED_CAST")
return EmbeddedInstance(
instance = api as Ketch,
instance = api,
label = label,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@ package com.linroid.ketch.app
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.window.ComposeUIViewController
import com.linroid.ketch.config.FileConfigStore
import com.linroid.ketch.api.log.Logger
import com.linroid.ketch.app.instance.InstanceFactory
import com.linroid.ketch.app.instance.InstanceManager
import com.linroid.ketch.config.FileConfigStore
import com.linroid.ketch.core.Ketch
import com.linroid.ketch.engine.KtorHttpEngine
import com.linroid.ketch.ftp.FtpDownloadSource
import com.linroid.ketch.sqlite.DriverFactory
import com.linroid.ketch.sqlite.createSqliteTaskStore
import com.linroid.ketch.torrent.TorrentDownloadSource
import platform.Foundation.NSDocumentDirectory
import platform.Foundation.NSSearchPathForDirectoriesInDomains
import platform.Foundation.NSUserDomainMask
Expand All @@ -31,9 +36,20 @@ fun MainViewController() = ComposeUIViewController {
?: UIDevice.currentDevice.name
InstanceManager(
factory = InstanceFactory(
taskStore = taskStore,
downloadConfig = downloadConfig,
deviceName = instanceName,
embeddedFactory = {
Ketch(
httpEngine = KtorHttpEngine(),
taskStore = taskStore,
config = downloadConfig,
name = instanceName,
logger = Logger.console(),
additionalSources = listOf(
FtpDownloadSource(),
TorrentDownloadSource(),
),
)
},
),
initialRemotes = config.remotes,
configStore = configStore,
Expand Down

This file was deleted.

This file was deleted.

Loading
Loading