mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-10 06:42:05 +01:00
Massive rewrite
- Replace toothpick with kotlin-inject - Create a data module - Fix bugs from last rewrite - Start replacing java.nio.Path with okio.Path
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
import Config.migrationCode
|
||||
import Config.serverCode
|
||||
import Config.tachideskVersion
|
||||
import com.codingfeline.buildkonfig.compiler.FieldSpec.Type
|
||||
|
||||
plugins {
|
||||
kotlin("multiplatform") version "1.6.10" apply false
|
||||
kotlin("kapt") version "1.6.10" apply false
|
||||
kotlin("plugin.serialization") version "1.6.10" apply false
|
||||
id("com.android.library") version "7.0.4" apply false
|
||||
id("com.android.application") version "7.0.4" apply false
|
||||
id("org.jetbrains.compose") version "1.0.1" apply false
|
||||
id("com.google.devtools.ksp") version "1.6.10-1.0.2"
|
||||
id("com.github.gmazzo.buildconfig") version "3.0.3" apply false
|
||||
id("com.codingfeline.buildkonfig") version "0.11.0" apply false
|
||||
id("dev.icerock.mobile.multiplatform-resources") version "0.18.0" apply false
|
||||
id("org.jmailen.kotlinter") version "3.8.0" apply false
|
||||
id("com.github.ben-manes.versions") version "0.41.0"
|
||||
@@ -34,6 +40,21 @@ allprojects {
|
||||
}
|
||||
|
||||
subprojects {
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = freeCompilerArgs + listOf(
|
||||
"-Xjvm-default=compatibility",
|
||||
)
|
||||
}
|
||||
}
|
||||
tasks.withType<org.jmailen.gradle.kotlinter.tasks.LintTask> {
|
||||
source(files("src"))
|
||||
exclude("ca/gosyer/*/build")
|
||||
}
|
||||
tasks.withType<org.jmailen.gradle.kotlinter.tasks.FormatTask> {
|
||||
source(files("src"))
|
||||
exclude("ca/gosyer/*/build")
|
||||
}
|
||||
plugins.withType<com.android.build.gradle.BasePlugin> {
|
||||
configure<com.android.build.gradle.BaseExtension> {
|
||||
compileSdkVersion(31)
|
||||
@@ -47,7 +68,7 @@ subprojects {
|
||||
}*/
|
||||
}
|
||||
compileOptions {
|
||||
//isCoreLibraryDesugaringEnabled = true
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
sourceCompatibility(JavaVersion.VERSION_11)
|
||||
targetCompatibility(JavaVersion.VERSION_11)
|
||||
}
|
||||
@@ -60,7 +81,38 @@ subprojects {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
//add("coreLibraryDesugaring", Deps.desugarJdkLibs)
|
||||
add("coreLibraryDesugaring", libs.desugarJdkLibs)
|
||||
}
|
||||
}
|
||||
}
|
||||
plugins.withType<com.codingfeline.buildkonfig.gradle.BuildKonfigPlugin> {
|
||||
configure<com.codingfeline.buildkonfig.gradle.BuildKonfigExtension> {
|
||||
defaultConfigs {
|
||||
buildConfigField(Type.STRING, "NAME", rootProject.name)
|
||||
buildConfigField(Type.STRING, "VERSION", project.version.toString())
|
||||
buildConfigField(Type.INT, "MIGRATION_CODE", migrationCode.toString())
|
||||
buildConfigField(Type.BOOLEAN, "DEBUG", project.hasProperty("debugApp").toString())
|
||||
buildConfigField(Type.BOOLEAN, "IS_PREVIEW", project.hasProperty("preview").toString())
|
||||
buildConfigField(Type.INT, "PREVIEW_BUILD", project.properties["preview"]?.toString()?.trim('"') ?: 0.toString())
|
||||
|
||||
// Tachidesk
|
||||
buildConfigField(Type.STRING, "TACHIDESK_SP_VERSION", tachideskVersion)
|
||||
buildConfigField(Type.INT, "SERVER_CODE", serverCode.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
plugins.withType<org.jmailen.gradle.kotlinter.KotlinterPlugin> {
|
||||
configure<org.jmailen.gradle.kotlinter.KotlinterExtension> {
|
||||
experimentalRules = true
|
||||
disabledRules = arrayOf("experimental:argument-list-wrapping", "experimental:trailing-comma")
|
||||
}
|
||||
}
|
||||
|
||||
plugins.withType<com.google.devtools.ksp.gradle.KspGradleSubplugin> {
|
||||
configure<com.google.devtools.ksp.gradle.KspExtension> {
|
||||
arg("me.tatarka.inject.generateCompanionExtensions", "true")
|
||||
if (project.hasProperty("debugApp")) {
|
||||
arg("me.tatarka.inject.dumpGraph", "true")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,5 +10,5 @@ object Config {
|
||||
const val preview = true
|
||||
const val previewCommit = "b714abddae9f13e91bc53c5daac54aeae564cd2a"
|
||||
|
||||
val jvmTarget = JavaVersion.VERSION_16
|
||||
val desktopJvmTarget = JavaVersion.VERSION_16
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile as KotlinJvmCompile
|
||||
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
kotlin("kapt")
|
||||
id("com.android.library")
|
||||
id("com.google.devtools.ksp")
|
||||
id("com.codingfeline.buildkonfig")
|
||||
id("org.jmailen.kotlinter")
|
||||
}
|
||||
|
||||
group = "ca.gosyer"
|
||||
@@ -15,7 +15,13 @@ repositories {
|
||||
|
||||
kotlin {
|
||||
android()
|
||||
jvm("desktop")
|
||||
jvm("desktop") {
|
||||
compilations {
|
||||
all {
|
||||
kotlinOptions.jvmTarget = Config.desktopJvmTarget.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
all {
|
||||
@@ -27,20 +33,23 @@ kotlin {
|
||||
}
|
||||
}
|
||||
val commonMain by getting {
|
||||
kotlin.srcDir("build/generated/ksp/commonMain/kotlin")
|
||||
dependencies {
|
||||
api(kotlin("stdlib-common"))
|
||||
api(libs.coroutinesCore)
|
||||
api(libs.json)
|
||||
api(libs.toothpickKsp)
|
||||
api(libs.kotlinInjectRuntime)
|
||||
api(libs.ktorCore)
|
||||
api(libs.ktorSerialization)
|
||||
api(libs.okio)
|
||||
api(libs.ktlogging)
|
||||
api(libs.multiplatformSettingsCore)
|
||||
api(libs.multiplatformSettingsCoroutines)
|
||||
api(libs.multiplatformSettingsSerialization)
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
kotlin.srcDir("build/generated/ksp/commonTest/kotlin")
|
||||
dependencies {
|
||||
implementation(kotlin("test-common"))
|
||||
implementation(kotlin("test-annotations-common"))
|
||||
@@ -49,43 +58,36 @@ kotlin {
|
||||
|
||||
val desktopMain by getting {
|
||||
kotlin.srcDir("src/jvmMain/kotlin")
|
||||
kotlin.srcDir("build/generated/ksp/desktopMain/kotlin")
|
||||
dependencies {
|
||||
api(kotlin("stdlib-jdk8"))
|
||||
api(libs.appDirs)
|
||||
}
|
||||
}
|
||||
val desktopTest by getting {
|
||||
kotlin.srcDir("src/jvmTest/kotlin")
|
||||
kotlin.srcDir("build/generated/ksp/desktopTest/kotlin")
|
||||
}
|
||||
|
||||
val androidMain by getting {
|
||||
kotlin.srcDir("src/jvmMain/kotlin")
|
||||
kotlin.srcDir("build/generated/ksp/androidMain/kotlin")
|
||||
dependencies {
|
||||
api(kotlin("stdlib-jdk8"))
|
||||
}
|
||||
}
|
||||
val androidTest by getting {
|
||||
kotlin.srcDir("src/jvmTest/kotlin")
|
||||
kotlin.srcDir("build/generated/ksp/androidTest/kotlin")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
add("kapt", libs.toothpickCompiler)
|
||||
add("kspDesktop", libs.kotlinInjectCompiler)
|
||||
add("kspAndroid", libs.kotlinInjectCompiler)
|
||||
}
|
||||
|
||||
tasks {
|
||||
withType<KotlinJvmCompile> {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = listOf("-Xjvm-default=compatibility")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = 31
|
||||
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
targetSdk = 31
|
||||
}
|
||||
buildkonfig {
|
||||
packageName = "ca.gosyer.core.build"
|
||||
}
|
||||
@@ -9,6 +9,9 @@ package ca.gosyer.core.prefs
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
/**
|
||||
* A wrapper around application preferences without knowing implementation details. Instances of
|
||||
@@ -57,3 +60,10 @@ interface Preference<T> {
|
||||
*/
|
||||
fun stateIn(scope: CoroutineScope): StateFlow<T>
|
||||
}
|
||||
|
||||
fun <T> Preference<T>.getAsFlow(action: (suspend (T) -> Unit)? = null): Flow<T> {
|
||||
val flow = merge(flowOf(get()), changes())
|
||||
return if (action != null) {
|
||||
flow.onEach(action = action)
|
||||
} else flow
|
||||
}
|
||||
|
||||
24
core/src/desktopMain/kotlin/ca/gosyer/core/io/AppDirs.kt
Normal file
24
core/src/desktopMain/kotlin/ca/gosyer/core/io/AppDirs.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ca.gosyer.core.io
|
||||
|
||||
import ca.gosyer.core.build.BuildKonfig
|
||||
import mu.KotlinLogging
|
||||
import net.harawata.appdirs.AppDirsFactory
|
||||
import okio.FileSystem
|
||||
import okio.Path
|
||||
import okio.Path.Companion.toPath
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
val userDataDir: Path by lazy {
|
||||
AppDirsFactory.getInstance().getUserDataDir(BuildKonfig.NAME, null, null).toPath().also {
|
||||
if (!FileSystem.SYSTEM.exists(it)) {
|
||||
logger.info("Attempted to create app data dir, result: {}", FileSystem.SYSTEM.createDirectories(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ca.gosyer.util.system
|
||||
package ca.gosyer.core.logging
|
||||
|
||||
import mu.KLogger
|
||||
import mu.KotlinLogging
|
||||
@@ -6,25 +6,8 @@
|
||||
|
||||
package ca.gosyer.core.di
|
||||
|
||||
import toothpick.Scope
|
||||
import toothpick.ktp.KTP
|
||||
import me.tatarka.inject.annotations.Scope
|
||||
|
||||
/**
|
||||
* The global scope for dependency injection that will provide all the application level components.
|
||||
*/
|
||||
object AppScope : Scope by KTP.openRootScope() {
|
||||
|
||||
/**
|
||||
* Returns a new subscope inheriting the root scope.
|
||||
*/
|
||||
fun subscope(any: Any): Scope {
|
||||
return openSubScope(any)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of [T] from the root scope.
|
||||
*/
|
||||
inline fun <reified T> getInstance(): T {
|
||||
return getInstance(T::class.java)
|
||||
}
|
||||
}
|
||||
@Scope
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER)
|
||||
annotation class AppScope
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ca.gosyer.core.di
|
||||
|
||||
import toothpick.Scope
|
||||
import javax.inject.Provider
|
||||
|
||||
class GenericsProvider<T>(private val cls: Class<T>, val scope: Scope = AppScope) : Provider<T> {
|
||||
|
||||
override fun get(): T {
|
||||
return scope.getInstance(cls)
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ca.gosyer.core.di
|
||||
|
||||
import toothpick.config.Module
|
||||
|
||||
/**
|
||||
* Binds the given [instance] to its class.
|
||||
*/
|
||||
inline fun <reified B> Module.bindInstance(instance: B) {
|
||||
bind(B::class.java).toInstance(instance)
|
||||
}
|
||||
90
data/build.gradle.kts
Normal file
90
data/build.gradle.kts
Normal file
@@ -0,0 +1,90 @@
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
id("com.google.devtools.ksp")
|
||||
kotlin("plugin.serialization")
|
||||
id("com.android.library")
|
||||
id("com.codingfeline.buildkonfig")
|
||||
id("org.jmailen.kotlinter")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
android()
|
||||
jvm("desktop") {
|
||||
compilations {
|
||||
all {
|
||||
kotlinOptions.jvmTarget = Config.desktopJvmTarget.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
all {
|
||||
languageSettings {
|
||||
optIn("kotlin.RequiresOptIn")
|
||||
optIn("kotlinx.coroutines.ExperimentalCoroutinesApi")
|
||||
}
|
||||
}
|
||||
val commonMain by getting {
|
||||
kotlin.srcDir("build/generated/ksp/commonMain/kotlin")
|
||||
dependencies {
|
||||
api(kotlin("stdlib-common"))
|
||||
api(libs.coroutinesCore)
|
||||
api(libs.json)
|
||||
api(libs.kotlinInjectRuntime)
|
||||
api(libs.ktorCore)
|
||||
api(libs.ktorSerialization)
|
||||
api(libs.ktorAuth)
|
||||
api(libs.ktorLogging)
|
||||
api(libs.ktorWebsockets)
|
||||
api(libs.ktorOkHttp)
|
||||
api(libs.okio)
|
||||
api(project(":core"))
|
||||
api(project(":i18n"))
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
kotlin.srcDir("build/generated/ksp/commonTest/kotlin")
|
||||
dependencies {
|
||||
implementation(kotlin("test-common"))
|
||||
implementation(kotlin("test-annotations-common"))
|
||||
}
|
||||
}
|
||||
|
||||
val desktopMain by getting {
|
||||
kotlin.srcDir("src/jvmMain/kotlin")
|
||||
kotlin.srcDir("build/generated/ksp/desktopMain/kotlin")
|
||||
dependencies {
|
||||
api(kotlin("stdlib-jdk8"))
|
||||
}
|
||||
}
|
||||
val desktopTest by getting {
|
||||
kotlin.srcDir("src/jvmTest/kotlin")
|
||||
kotlin.srcDir("build/generated/ksp/desktopTest/kotlin")
|
||||
}
|
||||
|
||||
val androidMain by getting {
|
||||
kotlin.srcDir("src/jvmMain/kotlin")
|
||||
kotlin.srcDir("build/generated/ksp/androidMain/kotlin")
|
||||
dependencies {
|
||||
api(kotlin("stdlib-jdk8"))
|
||||
}
|
||||
}
|
||||
val androidTest by getting {
|
||||
kotlin.srcDir("src/jvmTest/kotlin")
|
||||
kotlin.srcDir("build/generated/ksp/androidTest/kotlin")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
add("kspDesktop", libs.kotlinInjectCompiler)
|
||||
add("kspAndroid", libs.kotlinInjectCompiler)
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
||||
}
|
||||
|
||||
buildkonfig {
|
||||
packageName = "ca.gosyer.data.build"
|
||||
}
|
||||
2
data/src/androidMain/AndroidManifest.xml
Normal file
2
data/src/androidMain/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="ca.gosyer.data"/>
|
||||
99
data/src/desktopMain/kotlin/ca/gosyer/data/DataComponent.kt
Normal file
99
data/src/desktopMain/kotlin/ca/gosyer/data/DataComponent.kt
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ca.gosyer.data
|
||||
|
||||
import ca.gosyer.core.di.AppScope
|
||||
import ca.gosyer.core.prefs.PreferenceStoreFactory
|
||||
import ca.gosyer.data.catalog.CatalogPreferences
|
||||
import ca.gosyer.data.download.DownloadService
|
||||
import ca.gosyer.data.extension.ExtensionPreferences
|
||||
import ca.gosyer.data.library.LibraryPreferences
|
||||
import ca.gosyer.data.library.LibraryUpdateService
|
||||
import ca.gosyer.data.migration.MigrationPreferences
|
||||
import ca.gosyer.data.migration.Migrations
|
||||
import ca.gosyer.data.reader.ReaderPreferences
|
||||
import ca.gosyer.data.server.Http
|
||||
import ca.gosyer.data.server.HttpProvider
|
||||
import ca.gosyer.data.server.ServerHostPreferences
|
||||
import ca.gosyer.data.server.ServerPreferences
|
||||
import ca.gosyer.data.server.ServerService
|
||||
import ca.gosyer.data.ui.UiPreferences
|
||||
import ca.gosyer.data.update.UpdateChecker
|
||||
import ca.gosyer.data.update.UpdatePreferences
|
||||
import me.tatarka.inject.annotations.Component
|
||||
import me.tatarka.inject.annotations.Provides
|
||||
|
||||
@AppScope
|
||||
@Component
|
||||
abstract class DataComponent {
|
||||
private val preferenceFactory = PreferenceStoreFactory()
|
||||
protected abstract val httpProvider: HttpProvider
|
||||
|
||||
abstract val serverService: ServerService
|
||||
|
||||
abstract val downloadService: DownloadService
|
||||
|
||||
abstract val libraryUpdateService: LibraryUpdateService
|
||||
|
||||
abstract val migrations: Migrations
|
||||
|
||||
abstract val updateChecker: UpdateChecker
|
||||
|
||||
@get:AppScope
|
||||
@get:Provides
|
||||
val serverPreferences: ServerPreferences
|
||||
get() = ServerPreferences(preferenceFactory.create("server"))
|
||||
|
||||
@get:AppScope
|
||||
@get:Provides
|
||||
val serverHostPreferences: ServerHostPreferences
|
||||
get() = ServerHostPreferences(preferenceFactory.create("host"))
|
||||
|
||||
@get:AppScope
|
||||
@get:Provides
|
||||
val extensionPreferences: ExtensionPreferences
|
||||
get() = ExtensionPreferences(preferenceFactory.create("extension"))
|
||||
|
||||
@get:AppScope
|
||||
@get:Provides
|
||||
val catalogPreferences: CatalogPreferences
|
||||
get() = CatalogPreferences(preferenceFactory.create("catalog"))
|
||||
|
||||
@get:AppScope
|
||||
@get:Provides
|
||||
val libraryPreferences: LibraryPreferences
|
||||
get() = LibraryPreferences(preferenceFactory.create("library"))
|
||||
|
||||
@get:AppScope
|
||||
@get:Provides
|
||||
val readerPreferences: ReaderPreferences
|
||||
get() = ReaderPreferences(preferenceFactory.create("reader")) { name ->
|
||||
preferenceFactory.create("reader", name)
|
||||
}
|
||||
|
||||
@get:AppScope
|
||||
@get:Provides
|
||||
val uiPreferences: UiPreferences
|
||||
get() = UiPreferences(preferenceFactory.create("ui"))
|
||||
|
||||
@get:AppScope
|
||||
@get:Provides
|
||||
val migrationPreferences: MigrationPreferences
|
||||
get() = MigrationPreferences(preferenceFactory.create("migration"))
|
||||
|
||||
@get:AppScope
|
||||
@get:Provides
|
||||
val updatePreferences: UpdatePreferences
|
||||
get() = UpdatePreferences(preferenceFactory.create("update"))
|
||||
|
||||
@get:AppScope
|
||||
@get:Provides
|
||||
val http: Http
|
||||
get() = httpProvider.get(serverPreferences)
|
||||
|
||||
companion object
|
||||
}
|
||||
@@ -4,13 +4,13 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ca.gosyer.core.service
|
||||
package ca.gosyer.data.base
|
||||
|
||||
import ca.gosyer.build.BuildConfig
|
||||
import ca.gosyer.core.lang.throwIfCancellation
|
||||
import ca.gosyer.core.logging.CKLogger
|
||||
import ca.gosyer.data.build.BuildKonfig
|
||||
import ca.gosyer.data.server.Http
|
||||
import ca.gosyer.data.server.ServerPreferences
|
||||
import ca.gosyer.util.system.CKLogger
|
||||
import io.ktor.client.features.websocket.ws
|
||||
import io.ktor.http.cio.websocket.Frame
|
||||
import kotlinx.coroutines.CancellationException
|
||||
@@ -33,7 +33,7 @@ abstract class WebsocketService(
|
||||
protected val client: Http
|
||||
) {
|
||||
protected val json = Json {
|
||||
ignoreUnknownKeys = !BuildConfig.DEBUG
|
||||
ignoreUnknownKeys = !BuildKonfig.DEBUG
|
||||
}
|
||||
private val _status = MutableStateFlow(Status.STARTING)
|
||||
val status = _status.asStateFlow()
|
||||
@@ -6,14 +6,14 @@
|
||||
|
||||
package ca.gosyer.data.download
|
||||
|
||||
import ca.gosyer.core.service.WebsocketService
|
||||
import ca.gosyer.core.logging.CKLogger
|
||||
import ca.gosyer.data.base.WebsocketService
|
||||
import ca.gosyer.data.download.model.DownloadChapter
|
||||
import ca.gosyer.data.download.model.DownloadStatus
|
||||
import ca.gosyer.data.download.model.DownloaderStatus
|
||||
import ca.gosyer.data.server.Http
|
||||
import ca.gosyer.data.server.ServerPreferences
|
||||
import ca.gosyer.data.server.requests.downloadsQuery
|
||||
import ca.gosyer.util.system.CKLogger
|
||||
import io.ktor.http.cio.websocket.Frame
|
||||
import io.ktor.http.cio.websocket.readText
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
@@ -22,7 +22,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import javax.inject.Inject
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
class DownloadService @Inject constructor(
|
||||
@@ -6,17 +6,17 @@
|
||||
|
||||
package ca.gosyer.data.library
|
||||
|
||||
import ca.gosyer.core.service.WebsocketService
|
||||
import ca.gosyer.core.logging.CKLogger
|
||||
import ca.gosyer.data.base.WebsocketService
|
||||
import ca.gosyer.data.library.model.UpdateStatus
|
||||
import ca.gosyer.data.server.Http
|
||||
import ca.gosyer.data.server.ServerPreferences
|
||||
import ca.gosyer.data.server.requests.updatesQuery
|
||||
import ca.gosyer.util.system.CKLogger
|
||||
import io.ktor.http.cio.websocket.Frame
|
||||
import io.ktor.http.cio.websocket.readText
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import javax.inject.Inject
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
class LibraryUpdateService @Inject constructor(
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
package ca.gosyer.data.migration
|
||||
|
||||
import ca.gosyer.build.BuildConfig
|
||||
import ca.gosyer.data.build.BuildKonfig
|
||||
import ca.gosyer.data.reader.ReaderPreferences
|
||||
import javax.inject.Inject
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
|
||||
class Migrations @Inject constructor(
|
||||
private val migrationPreferences: MigrationPreferences,
|
||||
@@ -21,7 +21,7 @@ class Migrations @Inject constructor(
|
||||
readerPreferences.modes().get().forEach {
|
||||
readerPreferences.getMode(it).direction().delete()
|
||||
}
|
||||
migrationPreferences.version().set(BuildConfig.MIGRATION_CODE)
|
||||
migrationPreferences.version().set(BuildKonfig.MIGRATION_CODE)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
package ca.gosyer.data.reader
|
||||
|
||||
import ca.gosyer.util.system.getAsFlow
|
||||
import ca.gosyer.core.prefs.getAsFlow
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
package ca.gosyer.data.server
|
||||
|
||||
import ca.gosyer.build.BuildConfig
|
||||
import ca.gosyer.data.build.BuildKonfig
|
||||
import ca.gosyer.data.server.model.Auth
|
||||
import ca.gosyer.data.server.model.Proxy
|
||||
import io.ktor.client.HttpClient
|
||||
@@ -24,16 +24,13 @@ import io.ktor.client.features.logging.Logging
|
||||
import io.ktor.client.features.websocket.WebSockets
|
||||
import io.ktor.http.URLBuilder
|
||||
import kotlinx.serialization.json.Json
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
import io.ktor.client.features.auth.Auth as AuthFeature
|
||||
|
||||
typealias Http = HttpClient
|
||||
|
||||
internal class HttpProvider @Inject constructor(
|
||||
private val serverPreferences: ServerPreferences
|
||||
) : Provider<Http> {
|
||||
override fun get(): Http {
|
||||
class HttpProvider @Inject constructor() {
|
||||
fun get(serverPreferences: ServerPreferences): Http {
|
||||
return HttpClient(OkHttp) {
|
||||
engine {
|
||||
proxy = when (serverPreferences.proxy().get()) {
|
||||
@@ -85,7 +82,7 @@ internal class HttpProvider @Inject constructor(
|
||||
}
|
||||
install(WebSockets)
|
||||
install(Logging) {
|
||||
level = if (BuildConfig.DEBUG) {
|
||||
level = if (BuildKonfig.DEBUG) {
|
||||
LogLevel.HEADERS
|
||||
} else {
|
||||
LogLevel.INFO
|
||||
@@ -6,10 +6,11 @@
|
||||
|
||||
package ca.gosyer.data.server
|
||||
|
||||
import ca.gosyer.build.BuildConfig
|
||||
import ca.gosyer.core.io.copyTo
|
||||
import ca.gosyer.core.io.userDataDir
|
||||
import ca.gosyer.core.lang.withIOContext
|
||||
import ca.gosyer.util.system.CKLogger
|
||||
import ca.gosyer.util.system.userDataDir
|
||||
import ca.gosyer.core.logging.CKLogger
|
||||
import ca.gosyer.data.build.BuildKonfig
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
@@ -20,23 +21,22 @@ import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.launch
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
import mu.KotlinLogging
|
||||
import okio.FileSystem
|
||||
import okio.Path
|
||||
import okio.Path.Companion.toPath
|
||||
import okio.buffer
|
||||
import okio.source
|
||||
import java.io.File.pathSeparatorChar
|
||||
import java.io.IOException
|
||||
import java.io.Reader
|
||||
import java.nio.file.Path
|
||||
import java.util.jar.Attributes
|
||||
import java.util.jar.JarInputStream
|
||||
import javax.inject.Inject
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolutePathString
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.inputStream
|
||||
import kotlin.io.path.isExecutable
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.outputStream
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
class ServerService @Inject constructor(
|
||||
@@ -64,17 +64,14 @@ class ServerService @Inject constructor(
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun copyJar(jarFile: Path) {
|
||||
javaClass.getResourceAsStream("/Tachidesk.jar")?.buffered()?.use { input ->
|
||||
jarFile.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
private suspend fun copyJar(jarFile: Path) {
|
||||
javaClass.getResourceAsStream("/Tachidesk.jar")?.source()
|
||||
?.copyTo(FileSystem.SYSTEM.sink(jarFile).buffer())
|
||||
}
|
||||
|
||||
private fun getJavaFromPath(javaPath: Path): String? {
|
||||
val javaExeFile = javaPath.resolve("java.exe")
|
||||
val javaUnixFile = javaPath.resolve("java")
|
||||
val javaExeFile = javaPath.resolve("java.exe").toNioPath()
|
||||
val javaUnixFile = javaPath.resolve("java").toNioPath()
|
||||
return when {
|
||||
javaExeFile.exists() && javaExeFile.isExecutable() -> javaExeFile.absolutePathString()
|
||||
javaUnixFile.exists() && javaUnixFile.isExecutable() -> javaUnixFile.absolutePathString()
|
||||
@@ -83,7 +80,7 @@ class ServerService @Inject constructor(
|
||||
}
|
||||
|
||||
private fun getRuntimeJava(): String? {
|
||||
return System.getProperty("java.home")?.let { getJavaFromPath(Path(it).resolve("bin")) }
|
||||
return System.getProperty("java.home")?.let { getJavaFromPath(it.toPath().resolve("bin")) }
|
||||
}
|
||||
|
||||
private fun getPossibleJava(): String? {
|
||||
@@ -91,8 +88,8 @@ class ServerService @Inject constructor(
|
||||
.orEmpty()
|
||||
.asSequence()
|
||||
.mapNotNull {
|
||||
val file = Path(it)
|
||||
if (file.absolutePathString().contains("java") || file.absolutePathString().contains("jdk")) {
|
||||
val file = it.toPath()
|
||||
if (file.toString().contains("java") || file.toString().contains("jdk")) {
|
||||
if (file.name.equals("bin", true)) {
|
||||
file
|
||||
} else file.resolve("bin")
|
||||
@@ -126,25 +123,25 @@ class ServerService @Inject constructor(
|
||||
}
|
||||
}
|
||||
GlobalScope.launch(handler) {
|
||||
val jarFile = userDataDir.also { it.createDirectories() }.resolve("Tachidesk.jar")
|
||||
if (!jarFile.exists()) {
|
||||
val jarFile = userDataDir / "Tachidesk.jar"
|
||||
if (!FileSystem.SYSTEM.exists(jarFile)) {
|
||||
info { "Copying server to resources" }
|
||||
withIOContext { copyJar(jarFile) }
|
||||
} else {
|
||||
try {
|
||||
val jarVersion = withIOContext {
|
||||
JarInputStream(jarFile.inputStream()).use { jar ->
|
||||
JarInputStream(FileSystem.SYSTEM.source(jarFile).buffer().inputStream()).use { jar ->
|
||||
jar.manifest?.mainAttributes?.getValue(Attributes.Name.IMPLEMENTATION_VERSION)?.toIntOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
if (jarVersion != BuildConfig.SERVER_CODE) {
|
||||
if (jarVersion != BuildKonfig.SERVER_CODE) {
|
||||
info { "Updating server file from resources" }
|
||||
withIOContext { copyJar(jarFile) }
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
error(e) {
|
||||
"Error accessing server jar, cannot update server, ${BuildConfig.NAME} may not work properly"
|
||||
"Error accessing server jar, cannot update server, ${BuildKonfig.NAME} may not work properly"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,7 +153,7 @@ class ServerService @Inject constructor(
|
||||
|
||||
withIOContext {
|
||||
val reader: Reader
|
||||
process = ProcessBuilder(javaPath, *properties, "-jar", jarFile.absolutePathString())
|
||||
process = ProcessBuilder(javaPath, *properties, "-jar", jarFile.toString())
|
||||
.redirectErrorStream(true)
|
||||
.start()
|
||||
.also {
|
||||
@@ -21,9 +21,10 @@ import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.Headers
|
||||
import io.ktor.http.HttpHeaders
|
||||
import java.nio.file.Path
|
||||
import javax.inject.Inject
|
||||
import kotlin.io.path.readBytes
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
import okio.FileSystem
|
||||
import okio.Path
|
||||
import okio.buffer
|
||||
|
||||
class BackupInteractionHandler @Inject constructor(
|
||||
client: Http,
|
||||
@@ -35,7 +36,7 @@ class BackupInteractionHandler @Inject constructor(
|
||||
serverUrl + backupFileImportRequest(),
|
||||
formData = formData {
|
||||
append(
|
||||
"backup.proto.gz", file.readBytes(),
|
||||
"backup.proto.gz", FileSystem.SYSTEM.source(file).buffer().readByteArray(),
|
||||
Headers.build {
|
||||
append(HttpHeaders.ContentType, ContentType.MultiPart.FormData.toString())
|
||||
append(HttpHeaders.ContentDisposition, "filename=backup.proto.gz")
|
||||
@@ -51,7 +52,7 @@ class BackupInteractionHandler @Inject constructor(
|
||||
serverUrl + validateBackupFileRequest(),
|
||||
formData = formData {
|
||||
append(
|
||||
"backup.proto.gz", file.readBytes(),
|
||||
"backup.proto.gz", FileSystem.SYSTEM.source(file).buffer().readByteArray(),
|
||||
Headers.build {
|
||||
append(HttpHeaders.ContentType, ContentType.MultiPart.FormData.toString())
|
||||
append(HttpHeaders.ContentDisposition, "filename=backup.proto.gz")
|
||||
@@ -26,7 +26,7 @@ import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.Parameters
|
||||
import javax.inject.Inject
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
|
||||
class CategoryInteractionHandler @Inject constructor(
|
||||
client: Http,
|
||||
@@ -19,7 +19,6 @@ import ca.gosyer.data.server.requests.queueDownloadChapterRequest
|
||||
import ca.gosyer.data.server.requests.stopDownloadingChapterRequest
|
||||
import ca.gosyer.data.server.requests.updateChapterMetaRequest
|
||||
import ca.gosyer.data.server.requests.updateChapterRequest
|
||||
import ca.gosyer.util.compose.imageFromUrl
|
||||
import io.ktor.client.request.HttpRequestBuilder
|
||||
import io.ktor.client.request.delete
|
||||
import io.ktor.client.request.forms.submitForm
|
||||
@@ -28,7 +27,8 @@ import io.ktor.client.request.parameter
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.Parameters
|
||||
import javax.inject.Inject
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
|
||||
class ChapterInteractionHandler @Inject constructor(
|
||||
client: Http,
|
||||
@@ -123,8 +123,7 @@ class ChapterInteractionHandler @Inject constructor(
|
||||
)
|
||||
|
||||
suspend fun getPage(mangaId: Long, chapterIndex: Int, pageNum: Int, block: HttpRequestBuilder.() -> Unit) = withIOContext {
|
||||
imageFromUrl(
|
||||
client,
|
||||
client.get<ByteReadChannel>(
|
||||
serverUrl + getPageQuery(mangaId, chapterIndex, pageNum),
|
||||
block
|
||||
)
|
||||
@@ -14,7 +14,7 @@ import ca.gosyer.data.server.requests.downloadsStartRequest
|
||||
import ca.gosyer.data.server.requests.downloadsStopRequest
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import javax.inject.Inject
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
|
||||
class DownloadInteractionHandler @Inject constructor(
|
||||
client: Http,
|
||||
@@ -15,11 +15,11 @@ import ca.gosyer.data.server.requests.apkInstallQuery
|
||||
import ca.gosyer.data.server.requests.apkUninstallQuery
|
||||
import ca.gosyer.data.server.requests.apkUpdateQuery
|
||||
import ca.gosyer.data.server.requests.extensionListQuery
|
||||
import ca.gosyer.util.compose.imageFromUrl
|
||||
import io.ktor.client.request.HttpRequestBuilder
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import javax.inject.Inject
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
|
||||
class ExtensionInteractionHandler @Inject constructor(
|
||||
client: Http,
|
||||
@@ -51,8 +51,7 @@ class ExtensionInteractionHandler @Inject constructor(
|
||||
}
|
||||
|
||||
suspend fun getApkIcon(extension: Extension, block: HttpRequestBuilder.() -> Unit) = withIOContext {
|
||||
imageFromUrl(
|
||||
client,
|
||||
client.get<ByteReadChannel>(
|
||||
serverUrl + apkIconQuery(extension.apkName),
|
||||
block
|
||||
)
|
||||
@@ -15,7 +15,7 @@ import ca.gosyer.data.server.requests.removeMangaFromLibraryRequest
|
||||
import io.ktor.client.request.delete
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import javax.inject.Inject
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
|
||||
class LibraryInteractionHandler @Inject constructor(
|
||||
client: Http,
|
||||
@@ -13,7 +13,6 @@ import ca.gosyer.data.server.ServerPreferences
|
||||
import ca.gosyer.data.server.requests.mangaQuery
|
||||
import ca.gosyer.data.server.requests.mangaThumbnailQuery
|
||||
import ca.gosyer.data.server.requests.updateMangaMetaRequest
|
||||
import ca.gosyer.util.compose.imageFromUrl
|
||||
import io.ktor.client.request.HttpRequestBuilder
|
||||
import io.ktor.client.request.forms.submitForm
|
||||
import io.ktor.client.request.get
|
||||
@@ -21,7 +20,8 @@ import io.ktor.client.request.parameter
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.Parameters
|
||||
import javax.inject.Inject
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
|
||||
class MangaInteractionHandler @Inject constructor(
|
||||
client: Http,
|
||||
@@ -43,8 +43,7 @@ class MangaInteractionHandler @Inject constructor(
|
||||
suspend fun getManga(manga: Manga, refresh: Boolean = false) = getManga(manga.id, refresh)
|
||||
|
||||
suspend fun getMangaThumbnail(mangaId: Long, block: HttpRequestBuilder.() -> Unit) = withIOContext {
|
||||
imageFromUrl(
|
||||
client,
|
||||
client.get<ByteReadChannel>(
|
||||
serverUrl + mangaThumbnailQuery(mangaId),
|
||||
block
|
||||
)
|
||||
@@ -33,7 +33,7 @@ import io.ktor.http.ContentType
|
||||
import io.ktor.http.contentType
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import javax.inject.Inject
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
|
||||
class SourceInteractionHandler @Inject constructor(
|
||||
client: Http,
|
||||
@@ -18,7 +18,7 @@ import io.ktor.client.request.get
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.http.Parameters
|
||||
import javax.inject.Inject
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
|
||||
class UpdatesInteractionHandler @Inject constructor(
|
||||
client: Http,
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ca.gosyer.data.ui.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class WindowSettings(
|
||||
val x: Int? = null,
|
||||
val y: Int? = null,
|
||||
val width: Int? = null,
|
||||
val height: Int? = null,
|
||||
val maximized: Boolean? = null,
|
||||
val fullscreen: Boolean? = null
|
||||
)
|
||||
@@ -6,16 +6,16 @@
|
||||
|
||||
package ca.gosyer.data.update
|
||||
|
||||
import ca.gosyer.build.BuildConfig
|
||||
import ca.gosyer.core.lang.launch
|
||||
import ca.gosyer.core.lang.withIOContext
|
||||
import ca.gosyer.data.build.BuildKonfig
|
||||
import ca.gosyer.data.server.Http
|
||||
import ca.gosyer.data.update.model.GithubRelease
|
||||
import io.ktor.client.request.get
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import javax.inject.Inject
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
|
||||
class UpdateChecker @Inject constructor(
|
||||
private val updatePreferences: UpdatePreferences,
|
||||
@@ -42,19 +42,19 @@ class UpdateChecker @Inject constructor(
|
||||
// Removes prefixes like "r" or "v"
|
||||
val newVersion = versionTag.replace("[^\\d.]".toRegex(), "")
|
||||
|
||||
return if (BuildConfig.IS_PREVIEW) {
|
||||
return if (BuildKonfig.IS_PREVIEW) {
|
||||
// Preview builds: based on releases in "Suwayomi/Tachidesk-JUI-preview" repo
|
||||
// tagged as something like "r123"
|
||||
newVersion.toInt() > BuildConfig.PREVIEW_BUILD
|
||||
newVersion.toInt() > BuildKonfig.PREVIEW_BUILD
|
||||
} else {
|
||||
// Release builds: based on releases in "Suwayomi/Tachidesk-JUI" repo
|
||||
// tagged as something like "v1.1.2"
|
||||
newVersion != BuildConfig.VERSION
|
||||
newVersion != BuildKonfig.VERSION
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val GITHUB_REPO = if (BuildConfig.IS_PREVIEW) {
|
||||
private val GITHUB_REPO = if (BuildKonfig.IS_PREVIEW) {
|
||||
"Suwayomi/Tachidesk-JUI-preview"
|
||||
} else {
|
||||
"Suwayomi/Tachidesk-JUI"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user