Switch from Kodein to Koin (#1112)

* Switch from Kodein to Koin

* Ktlint
This commit is contained in:
Mitchell Syer
2024-11-14 18:08:19 -05:00
committed by GitHub
parent aa1e98544b
commit 0670f298cd
51 changed files with 341 additions and 465 deletions

View File

@@ -1,13 +0,0 @@
package xyz.nulldev.ts.config
import org.kodein.di.DI
import org.kodein.di.bind
import org.kodein.di.singleton
class ConfigKodeinModule {
fun create() =
DI.Module("ConfigManager") {
// Config module
bind<ConfigManager>() with singleton { GlobalConfigManager }
}
}

View File

@@ -0,0 +1,9 @@
package xyz.nulldev.ts.config
import org.koin.core.module.Module
import org.koin.dsl.module
fun configManagerModule(): Module =
module {
single<ConfigManager> { GlobalConfigManager }
}

View File

@@ -25,7 +25,7 @@ import android.os.IBinder;
import android.util.Log;
import kotlin.NotImplementedError;
import xyz.nulldev.androidcompat.service.ServiceSupport;
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
import xyz.nulldev.androidcompat.util.KoinGlobalHelper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -299,7 +299,7 @@ import java.lang.annotation.RetentionPolicy;
*/
public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
private static final ServiceSupport serviceSupport = KodeinGlobalHelper.instance(ServiceSupport.class);
private static final ServiceSupport serviceSupport = KoinGlobalHelper.instance(ServiceSupport.class);
private static final String TAG = "Service";
/**
@@ -328,7 +328,7 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
public Service() {
//==================[THIS LINE MODIFIED FROM ANDROID SOURCE!]==================
//Service must be initialized with a base context!
super(KodeinGlobalHelper.instance(Context.class));
super(KoinGlobalHelper.instance(Context.class));
}
/** Return the application that owns this service. */
public final Application getApplication() {

View File

@@ -1,7 +1,7 @@
package android.os;
import xyz.nulldev.androidcompat.io.AndroidFiles;
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
import xyz.nulldev.androidcompat.util.KoinGlobalHelper;
import java.io.File;
@@ -9,7 +9,7 @@ import java.io.File;
* Android compatibility layer for files
*/
public class Environment {
private static AndroidFiles androidFiles = KodeinGlobalHelper.instance(AndroidFiles.class);
private static AndroidFiles androidFiles = KoinGlobalHelper.instance(AndroidFiles.class);
public static String DIRECTORY_ALARMS = getHomeDirectory("Alarms").getAbsolutePath();
public static String DIRECTORY_DCIM = getHomeDirectory("DCIM").getAbsolutePath();

View File

@@ -17,7 +17,7 @@ package dalvik.system;
import org.jetbrains.annotations.Nullable;
import xyz.nulldev.androidcompat.pm.PackageController;
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
import xyz.nulldev.androidcompat.util.KoinGlobalHelper;
import java.io.File;
import java.io.IOException;
@@ -33,7 +33,7 @@ import java.util.Enumeration;
* {@link ClassLoader} implementations.
*/
public class BaseDexClassLoader extends ClassLoader {
private PackageController controller = KodeinGlobalHelper.instance(PackageController.class);
private PackageController controller = KoinGlobalHelper.instance(PackageController.class);
private final URLClassLoader realClassloader;

View File

@@ -1,13 +1,11 @@
package xyz.nulldev.androidcompat
import android.app.Application
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import org.koin.mp.KoinPlatformTools
import xyz.nulldev.androidcompat.androidimpl.CustomContext
class AndroidCompat {
val context: CustomContext by DI.global.instance()
val context: CustomContext by KoinPlatformTools.defaultContext().get().inject()
fun startApp(application: Application) {
application.attach(context)

View File

@@ -1,7 +1,5 @@
package xyz.nulldev.androidcompat
import org.kodein.di.DI
import org.kodein.di.conf.global
import xyz.nulldev.androidcompat.config.ApplicationInfoConfigModule
import xyz.nulldev.androidcompat.config.FilesConfigModule
import xyz.nulldev.androidcompat.config.SystemConfigModule
@@ -12,8 +10,6 @@ import xyz.nulldev.ts.config.GlobalConfigManager
*/
class AndroidCompatInitializer {
fun init() {
DI.global.addImport(AndroidCompatModule().create())
// Register config modules
GlobalConfigManager.registerModules(
FilesConfigModule.register(GlobalConfigManager.config),

View File

@@ -1,11 +1,8 @@
package xyz.nulldev.androidcompat
import android.content.Context
import org.kodein.di.DI
import org.kodein.di.bind
import org.kodein.di.conf.global
import org.kodein.di.instance
import org.kodein.di.singleton
import org.koin.core.module.Module
import org.koin.dsl.module
import xyz.nulldev.androidcompat.androidimpl.CustomContext
import xyz.nulldev.androidcompat.androidimpl.FakePackageManager
import xyz.nulldev.androidcompat.info.ApplicationInfoImpl
@@ -17,25 +14,19 @@ import xyz.nulldev.androidcompat.service.ServiceSupport
* AndroidCompatModule
*/
class AndroidCompatModule {
fun create() =
DI.Module("AndroidCompat") {
bind<AndroidFiles>() with singleton { AndroidFiles() }
fun androidCompatModule(): Module =
module {
single { AndroidFiles() }
bind<ApplicationInfoImpl>() with singleton { ApplicationInfoImpl() }
single { ApplicationInfoImpl(get()) }
bind<ServiceSupport>() with singleton { ServiceSupport() }
single { ServiceSupport() }
bind<FakePackageManager>() with singleton { FakePackageManager() }
single { FakePackageManager() }
bind<PackageController>() with singleton { PackageController() }
single { PackageController() }
// Context
bind<CustomContext>() with singleton { CustomContext() }
bind<Context>() with
singleton {
val context: Context by DI.global.instance<CustomContext>()
context
}
}
}
single { CustomContext() }
single<Context> { get<CustomContext>() }
}

View File

@@ -32,15 +32,14 @@ import android.os.*;
import android.view.Display;
import android.view.DisplayAdjustments;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.kodein.di.*;
import org.koin.core.Koin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.nulldev.androidcompat.info.ApplicationInfoImpl;
import xyz.nulldev.androidcompat.io.AndroidFiles;
import xyz.nulldev.androidcompat.io.sharedprefs.JavaSharedPreferences;
import xyz.nulldev.androidcompat.service.ServiceSupport;
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
import xyz.nulldev.androidcompat.util.KoinGlobalHelper;
import java.io.*;
import java.util.HashMap;
@@ -51,26 +50,25 @@ import java.util.Map;
* Custom context implementation.
*
*/
public class CustomContext extends Context implements DIAware {
private final DI kodein;
public class CustomContext extends Context {
private final Koin koin;
public CustomContext() {
this(KodeinGlobalHelper.kodein());
this(KoinGlobalHelper.koin());
}
public CustomContext(DI kodein) {
this.kodein = kodein;
public CustomContext(Koin koin) {
this.koin = koin;
//Init configs
androidFiles = KodeinGlobalHelper.instance(AndroidFiles.class, getDi());
applicationInfo = KodeinGlobalHelper.instance(ApplicationInfoImpl.class, getDi());
serviceSupport = KodeinGlobalHelper.instance(ServiceSupport.class, getDi());
fakePackageManager = KodeinGlobalHelper.instance(FakePackageManager.class, getDi());
androidFiles = KoinGlobalHelper.instance(AndroidFiles.class, getDi());
applicationInfo = KoinGlobalHelper.instance(ApplicationInfoImpl.class, getDi());
serviceSupport = KoinGlobalHelper.instance(ServiceSupport.class, getDi());
fakePackageManager = KoinGlobalHelper.instance(FakePackageManager.class, getDi());
}
@NotNull
@Override
public DI getDi() {
return kodein;
public Koin getDi() {
return koin;
}
private AndroidFiles androidFiles;
@@ -719,17 +717,5 @@ public class CustomContext extends Context implements DIAware {
public boolean isCredentialProtectedStorage() {
return false;
}
@NotNull
@Override
public DIContext<?> getDiContext() {
return getDi().getDiContext();
}
@Nullable
@Override
public DITrigger getDiTrigger() {
return null;
}
}

View File

@@ -16,14 +16,14 @@ import android.os.UserHandle;
import kotlin.NotImplementedError;
import xyz.nulldev.androidcompat.pm.InstalledPackage;
import xyz.nulldev.androidcompat.pm.PackageController;
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
import xyz.nulldev.androidcompat.util.KoinGlobalHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class FakePackageManager extends PackageManager {
private PackageController controller = KodeinGlobalHelper.instance(PackageController.class);
private PackageController controller = KoinGlobalHelper.instance(PackageController.class);
@Override
public PackageInfo getPackageInfo(String packageName, int flags) throws NameNotFoundException {

View File

@@ -1,19 +1,12 @@
package xyz.nulldev.androidcompat.info
import android.content.pm.ApplicationInfo
import org.kodein.di.DI
import org.kodein.di.DIAware
import org.kodein.di.conf.global
import org.kodein.di.instance
import xyz.nulldev.androidcompat.config.ApplicationInfoConfigModule
import xyz.nulldev.ts.config.ConfigManager
class ApplicationInfoImpl(
override val di: DI = DI.global,
) : ApplicationInfo(),
DIAware {
val configManager: ConfigManager by di.instance()
private val configManager: ConfigManager,
) : ApplicationInfo() {
val appInfoConfig: ApplicationInfoConfigModule
get() = configManager.module()

View File

@@ -1,14 +1,12 @@
package xyz.nulldev.androidcompat.pm
import net.dongliu.apk.parser.ApkParsers
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import org.koin.mp.KoinPlatformTools
import xyz.nulldev.androidcompat.io.AndroidFiles
import java.io.File
class PackageController {
private val androidFiles by DI.global.instance<AndroidFiles>()
private val androidFiles: AndroidFiles by KoinPlatformTools.defaultContext().get().inject()
private val uninstallListeners = mutableListOf<(String) -> Unit>()
fun registerUninstallListener(listener: (String) -> Unit) {

View File

@@ -1,7 +1,7 @@
package xyz.nulldev.androidcompat.res;
import xyz.nulldev.androidcompat.info.ApplicationInfoImpl;
import xyz.nulldev.androidcompat.util.KodeinGlobalHelper;
import xyz.nulldev.androidcompat.util.KoinGlobalHelper;
import java.text.SimpleDateFormat;
import java.util.Calendar;
@@ -10,7 +10,7 @@ import java.util.Calendar;
* BuildConfig compat class.
*/
public class BuildConfigCompat {
private static ApplicationInfoImpl applicationInfo = KodeinGlobalHelper.instance(ApplicationInfoImpl.class);
private static ApplicationInfoImpl applicationInfo = KoinGlobalHelper.instance(ApplicationInfoImpl.class);
public static final boolean DEBUG = applicationInfo.getDebug();

View File

@@ -1,67 +0,0 @@
package xyz.nulldev.androidcompat.util
import android.content.Context
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import xyz.nulldev.androidcompat.androidimpl.CustomContext
import xyz.nulldev.androidcompat.androidimpl.FakePackageManager
import xyz.nulldev.androidcompat.info.ApplicationInfoImpl
import xyz.nulldev.androidcompat.io.AndroidFiles
import xyz.nulldev.androidcompat.pm.PackageController
import xyz.nulldev.androidcompat.service.ServiceSupport
/**
* Helper class to allow access to Kodein from Java
*/
object KodeinGlobalHelper {
/**
* Get the Kodein object
*/
@JvmStatic
fun kodein() = DI.global
/**
* Get a dependency
*/
@JvmStatic
@Suppress("UNCHECKED_CAST")
fun <T : Any> instance(
type: Class<T>,
kodein: DI? = null,
): T =
when (type) {
AndroidFiles::class.java -> {
val instance: AndroidFiles by (kodein ?: kodein()).instance()
instance as T
}
ApplicationInfoImpl::class.java -> {
val instance: ApplicationInfoImpl by (kodein ?: kodein()).instance()
instance as T
}
ServiceSupport::class.java -> {
val instance: ServiceSupport by (kodein ?: kodein()).instance()
instance as T
}
FakePackageManager::class.java -> {
val instance: FakePackageManager by (kodein ?: kodein()).instance()
instance as T
}
PackageController::class.java -> {
val instance: PackageController by (kodein ?: kodein()).instance()
instance as T
}
CustomContext::class.java -> {
val instance: CustomContext by (kodein ?: kodein()).instance()
instance as T
}
Context::class.java -> {
val instance: Context by (kodein ?: kodein()).instance()
instance as T
}
else -> throw IllegalArgumentException("Kodein instance not found")
}
@JvmStatic
fun <T : Any> instance(type: Class<T>): T = instance(type, null)
}

View File

@@ -0,0 +1,27 @@
package xyz.nulldev.androidcompat.util
import org.koin.core.Koin
import org.koin.mp.KoinPlatformTools
/**
* Helper class to allow access to Kodein from Java
*/
object KoinGlobalHelper {
/**
* Get the Kodein object
*/
@JvmStatic
fun koin() = KoinPlatformTools.defaultContext().get()
/**
* Get a dependency
*/
@JvmStatic
fun <T : Any> instance(
type: Class<T>,
koin: Koin? = null,
): T = (koin ?: koin()).get(type.kotlin)
@JvmStatic
fun <T : Any> instance(type: Class<T>): T = instance(type, null)
}

View File

@@ -13,7 +13,7 @@ twelvemonkeys = "3.11.0"
graphqlkotlin = "6.8.5"
xmlserialization = "0.90.3"
ktlint = "1.3.1"
koin-bom = "4.0.0"
koin = "4.0.0"
[libraries]
# Kotlin
@@ -68,10 +68,7 @@ h2 = "com.h2database:h2:1.4.200" # current database driver, can't update to h2 v
exposed-migrations = "com.github.Suwayomi:exposed-migrations:3.2.0"
# Dependency Injection
kodein = "org.kodein.di:kodein-di-conf-jvm:7.20.2"
koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin-bom" }
koin-core = { module = "io.insert-koin:koin-core" }
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
# tray icon
systemtray-core = "com.dorkbox:SystemTray:4.4"
@@ -175,7 +172,7 @@ shared = [
"serialization-json",
"serialization-json-okio",
"serialization-protobuf",
"kodein",
"koin-core",
"slf4japi",
"logback",
"kotlinlogging",

View File

@@ -92,10 +92,6 @@ dependencies {
implementation(libs.cron4j)
implementation(libs.cronUtils)
// koin
implementation(project.dependencies.platform(libs.koin.bom))
implementation(libs.koin.core)
}
application {

View File

@@ -9,15 +9,10 @@ package eu.kanade.tachiyomi
import android.app.Application
import android.content.Context
import org.koin.core.context.startKoin
open class App : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
modules(createAppModule(this@App))
}
// if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree())
}

View File

@@ -20,10 +20,9 @@ import eu.kanade.tachiyomi.network.JavaScriptEngine
import eu.kanade.tachiyomi.network.NetworkHelper
import kotlinx.serialization.json.Json
import kotlinx.serialization.protobuf.ProtoBuf
import nl.adaptivity.xmlutil.XmlDeclMode
import nl.adaptivity.xmlutil.core.XmlVersion
import nl.adaptivity.xmlutil.serialization.XML
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import org.koin.core.module.Module
import org.koin.dsl.module
@@ -54,18 +53,26 @@ fun createAppModule(app: Application): Module {
// addSingletonFactory { LibrarySyncManager(app) }
single {
val json by DI.global.instance<Json>()
json
Json {
ignoreUnknownKeys = true
explicitNulls = false
}
}
single {
val xml by DI.global.instance<XML>()
xml
XML {
defaultPolicy {
ignoreUnknownChildren()
}
autoPolymorphic = true
xmlDeclMode = XmlDeclMode.Charset
indent = 2
xmlVersion = XmlVersion.XML10
}
}
single {
val protobuf by DI.global.instance<ProtoBuf>()
protobuf
ProtoBuf
}
}

View File

@@ -40,9 +40,6 @@ import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.insertAndGetId
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.registerCatalogueSource
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil
import suwayomi.tachidesk.manga.model.table.ExtensionTable
@@ -441,7 +438,7 @@ class LocalSource(
private val logger = KotlinLogging.logger {}
private val applicationDirs by DI.global.instance<ApplicationDirs>()
private val applicationDirs: ApplicationDirs by injectLazy()
val pageCache: MutableMap<String, List<() -> InputStream>> = mutableMapOf()

View File

@@ -1,12 +1,10 @@
package suwayomi.tachidesk.graphql.mutations
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse
import suwayomi.tachidesk.server.ApplicationDirs
import uy.kohesive.injekt.injectLazy
private val applicationDirs by DI.global.instance<ApplicationDirs>()
private val applicationDirs: ApplicationDirs by injectLazy()
class ImageMutation {
data class ClearCachedImagesInput(

View File

@@ -7,9 +7,6 @@ import org.jetbrains.exposed.sql.deleteWhere
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.graphql.asDataFetcherResult
import suwayomi.tachidesk.graphql.types.MangaMetaType
import suwayomi.tachidesk.graphql.types.MangaType
@@ -20,6 +17,7 @@ import suwayomi.tachidesk.manga.model.table.MangaMetaTable
import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.manga.model.table.toDataClass
import suwayomi.tachidesk.server.JavalinSetup.future
import uy.kohesive.injekt.injectLazy
import java.time.Instant
import java.util.concurrent.CompletableFuture
@@ -29,7 +27,7 @@ import java.util.concurrent.CompletableFuture
* - Delete read/all downloaded chapters
*/
class MangaMutation {
private val updater by DI.global.instance<IUpdater>()
private val updater: IUpdater by injectLazy()
data class UpdateMangaPatch(
val inLibrary: Boolean? = null,

View File

@@ -5,9 +5,6 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withTimeout
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.graphql.asDataFetcherResult
import suwayomi.tachidesk.graphql.types.UpdateStatus
import suwayomi.tachidesk.manga.impl.Category
@@ -15,11 +12,12 @@ import suwayomi.tachidesk.manga.impl.update.IUpdater
import suwayomi.tachidesk.manga.model.table.CategoryTable
import suwayomi.tachidesk.manga.model.table.toDataClass
import suwayomi.tachidesk.server.JavalinSetup.future
import uy.kohesive.injekt.injectLazy
import java.util.concurrent.CompletableFuture
import kotlin.time.Duration.Companion.seconds
class UpdateMutation {
private val updater by DI.global.instance<IUpdater>()
private val updater: IUpdater by injectLazy()
data class UpdateLibraryMangaInput(
val clientMutationId: String? = null,

View File

@@ -1,16 +1,14 @@
package suwayomi.tachidesk.graphql.queries
import kotlinx.coroutines.flow.first
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.graphql.types.UpdateStatus
import suwayomi.tachidesk.manga.impl.update.IUpdater
import suwayomi.tachidesk.server.JavalinSetup.future
import uy.kohesive.injekt.injectLazy
import java.util.concurrent.CompletableFuture
class UpdateQuery {
private val updater by DI.global.instance<IUpdater>()
private val updater: IUpdater by injectLazy()
fun updateStatus(): CompletableFuture<UpdateStatus> = future { UpdateStatus(updater.status.first()) }

View File

@@ -9,14 +9,12 @@ package suwayomi.tachidesk.graphql.subscriptions
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.graphql.types.UpdateStatus
import suwayomi.tachidesk.manga.impl.update.IUpdater
import uy.kohesive.injekt.injectLazy
class UpdateSubscription {
private val updater by DI.global.instance<IUpdater>()
private val updater: IUpdater by injectLazy()
fun updateStatusChanged(): Flow<UpdateStatus> =
updater.status.map { updateStatus ->

View File

@@ -9,20 +9,17 @@ package suwayomi.tachidesk.manga.controller
import io.javalin.http.HttpCode
import io.javalin.websocket.WsConfig
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.download.DownloadManager
import suwayomi.tachidesk.manga.impl.download.DownloadManager.EnqueueInput
import suwayomi.tachidesk.server.JavalinSetup.future
import suwayomi.tachidesk.server.util.handler
import suwayomi.tachidesk.server.util.pathParam
import suwayomi.tachidesk.server.util.withOperation
import uy.kohesive.injekt.injectLazy
object DownloadController {
private val json by DI.global.instance<Json>()
private val json: Json by injectLazy()
/** Download queue stats */
fun downloadsWS(ws: WsConfig) {

View File

@@ -9,9 +9,6 @@ package suwayomi.tachidesk.manga.controller
import io.javalin.http.HttpCode
import kotlinx.serialization.json.Json
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.CategoryManga
import suwayomi.tachidesk.manga.impl.Chapter
import suwayomi.tachidesk.manga.impl.Library
@@ -27,10 +24,11 @@ import suwayomi.tachidesk.server.util.handler
import suwayomi.tachidesk.server.util.pathParam
import suwayomi.tachidesk.server.util.queryParam
import suwayomi.tachidesk.server.util.withOperation
import uy.kohesive.injekt.injectLazy
import kotlin.time.Duration.Companion.days
object MangaController {
private val json by DI.global.instance<Json>()
private val json: Json by injectLazy()
val retrieve =
handler(

View File

@@ -8,11 +8,7 @@ package suwayomi.tachidesk.manga.controller
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import io.javalin.http.HttpCode
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.MangaList
import suwayomi.tachidesk.manga.impl.Search
import suwayomi.tachidesk.manga.impl.Search.FilterChange
@@ -26,6 +22,7 @@ import suwayomi.tachidesk.server.util.handler
import suwayomi.tachidesk.server.util.pathParam
import suwayomi.tachidesk.server.util.queryParam
import suwayomi.tachidesk.server.util.withOperation
import uy.kohesive.injekt.injectLazy
object SourceController {
/** list of sources */
@@ -167,7 +164,7 @@ object SourceController {
},
)
private val json by DI.global.instance<Json>()
private val json: Json by injectLazy()
/** change filters of source with id `sourceId` */
val setFilters =

View File

@@ -10,9 +10,6 @@ package suwayomi.tachidesk.manga.controller
import io.javalin.http.HttpCode
import kotlinx.serialization.json.Json
import mu.KotlinLogging
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.track.Track
import suwayomi.tachidesk.manga.model.dataclass.TrackerDataClass
import suwayomi.tachidesk.server.JavalinSetup.future
@@ -20,10 +17,11 @@ import suwayomi.tachidesk.server.util.handler
import suwayomi.tachidesk.server.util.pathParam
import suwayomi.tachidesk.server.util.queryParam
import suwayomi.tachidesk.server.util.withOperation
import uy.kohesive.injekt.injectLazy
import kotlin.time.Duration.Companion.days
object TrackController {
private val json by DI.global.instance<Json>()
private val json: Json by injectLazy()
private val logger = KotlinLogging.logger {}
val list =

View File

@@ -3,9 +3,6 @@ package suwayomi.tachidesk.manga.controller
import io.javalin.http.HttpCode
import io.javalin.websocket.WsConfig
import mu.KotlinLogging
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.Category
import suwayomi.tachidesk.manga.impl.Chapter
import suwayomi.tachidesk.manga.impl.update.IUpdater
@@ -18,6 +15,8 @@ import suwayomi.tachidesk.server.util.formParam
import suwayomi.tachidesk.server.util.handler
import suwayomi.tachidesk.server.util.pathParam
import suwayomi.tachidesk.server.util.withOperation
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -67,7 +66,7 @@ object UpdateController {
}
},
behaviorOf = { ctx, categoryId ->
val updater by DI.global.instance<IUpdater>()
val updater = Injekt.get<IUpdater>()
if (categoryId == null) {
logger.info { "Adding Library to Update Queue" }
updater.addCategoriesToUpdateQueue(
@@ -116,7 +115,7 @@ object UpdateController {
}
},
behaviorOf = { ctx ->
val updater by DI.global.instance<IUpdater>()
val updater = Injekt.get<IUpdater>()
ctx.json(updater.statusDeprecated.value)
},
withResults = {
@@ -133,7 +132,7 @@ object UpdateController {
}
},
behaviorOf = { ctx ->
val updater by DI.global.instance<IUpdater>()
val updater = Injekt.get<IUpdater>()
logger.info { "Resetting Updater" }
ctx.future(
future {

View File

@@ -27,9 +27,6 @@ import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.MangaList.proxyThumbnailUrl
import suwayomi.tachidesk.manga.impl.Source.getSource
import suwayomi.tachidesk.manga.impl.download.fileProvider.impl.MissingThumbnailException
@@ -265,7 +262,7 @@ object Manga {
}[MangaTable.thumbnail_url]
}
private val applicationDirs by DI.global.instance<ApplicationDirs>()
private val applicationDirs: ApplicationDirs by injectLazy()
private val network: NetworkHelper by injectLazy()
private suspend fun fetchHttpSourceMangaThumbnail(

View File

@@ -12,12 +12,10 @@ import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import io.javalin.plugin.json.JsonMapper
import kotlinx.serialization.Serializable
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.MangaList.processEntries
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub
import suwayomi.tachidesk.manga.model.dataclass.PagedMangaListDataClass
import uy.kohesive.injekt.injectLazy
object Search {
suspend fun sourceSearch(
@@ -157,7 +155,7 @@ object Search {
return updateFilterList(filterList, changes)
}
private val jsonMapper by DI.global.instance<JsonMapper>()
private val jsonMapper: JsonMapper by injectLazy()
@Serializable
data class FilterChange(

View File

@@ -19,9 +19,6 @@ import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIconUrl
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrNull
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub
@@ -31,6 +28,7 @@ import suwayomi.tachidesk.manga.model.table.ExtensionTable
import suwayomi.tachidesk.manga.model.table.SourceMetaTable
import suwayomi.tachidesk.manga.model.table.SourceTable
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import xyz.nulldev.androidcompat.androidimpl.CustomContext
object Source {
@@ -77,7 +75,7 @@ object Source {
}
}
private val context by DI.global.instance<CustomContext>()
private val context: CustomContext by injectLazy()
/**
* (2021-11) Clients should support these types for extensions to work properly
@@ -125,7 +123,7 @@ object Source {
val value: String,
)
private val jsonMapper by DI.global.instance<JsonMapper>()
private val jsonMapper: JsonMapper by injectLazy()
fun setSourcePreference(
sourceId: Long,

View File

@@ -25,9 +25,6 @@ import org.jetbrains.exposed.sql.SortOrder
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.CategoryManga
import suwayomi.tachidesk.manga.impl.backup.BackupFlags
import suwayomi.tachidesk.manga.impl.backup.proto.models.Backup
@@ -48,6 +45,7 @@ import suwayomi.tachidesk.server.serverConfig
import suwayomi.tachidesk.util.HAScheduler
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.io.InputStream
import java.util.concurrent.TimeUnit
@@ -55,7 +53,7 @@ import kotlin.time.Duration.Companion.days
object ProtoBackupExport : ProtoBackupBase() {
private val logger = KotlinLogging.logger { }
private val applicationDirs by DI.global.instance<ApplicationDirs>()
private val applicationDirs: ApplicationDirs by injectLazy()
private var backupSchedulerJobId: String = ""
private const val LAST_AUTOMATED_BACKUP_KEY = "lastAutomatedBackup"
private val preferences = Injekt.get<Application>().getSharedPreferences("server_util", Context.MODE_PRIVATE)

View File

@@ -6,9 +6,6 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
import org.apache.commons.compress.archivers.zip.ZipFile
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.download.fileProvider.ChaptersFilesProvider
import suwayomi.tachidesk.manga.impl.download.fileProvider.FileType
import suwayomi.tachidesk.manga.impl.util.getChapterCachePath
@@ -16,10 +13,11 @@ import suwayomi.tachidesk.manga.impl.util.getChapterCbzPath
import suwayomi.tachidesk.manga.impl.util.getMangaDownloadDir
import suwayomi.tachidesk.manga.impl.util.storage.FileDeletionHelper
import suwayomi.tachidesk.server.ApplicationDirs
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.io.InputStream
private val applicationDirs by DI.global.instance<ApplicationDirs>()
private val applicationDirs: ApplicationDirs by injectLazy()
class ArchiveProvider(
mangaId: Int,

View File

@@ -1,18 +1,16 @@
package suwayomi.tachidesk.manga.impl.download.fileProvider.impl
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.download.fileProvider.ChaptersFilesProvider
import suwayomi.tachidesk.manga.impl.download.fileProvider.FileType.RegularFile
import suwayomi.tachidesk.manga.impl.util.getChapterCachePath
import suwayomi.tachidesk.manga.impl.util.getChapterDownloadPath
import suwayomi.tachidesk.manga.impl.util.storage.FileDeletionHelper
import suwayomi.tachidesk.server.ApplicationDirs
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.io.FileInputStream
private val applicationDirs by DI.global.instance<ApplicationDirs>()
private val applicationDirs: ApplicationDirs by injectLazy()
/*
* Provides downloaded files when pages were downloaded into folders

View File

@@ -1,8 +1,5 @@
package suwayomi.tachidesk.manga.impl.download.fileProvider.impl
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.Manga
import suwayomi.tachidesk.manga.impl.download.fileProvider.DownloadedFilesProvider
import suwayomi.tachidesk.manga.impl.download.fileProvider.FileDownload0Args
@@ -11,12 +8,13 @@ import suwayomi.tachidesk.manga.impl.util.getThumbnailDownloadPath
import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse
import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse.getCachedImageResponse
import suwayomi.tachidesk.server.ApplicationDirs
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.io.InputStream
class MissingThumbnailException : Exception("No thumbnail found")
private val applicationDirs by DI.global.instance<ApplicationDirs>()
private val applicationDirs: ApplicationDirs by injectLazy()
class ThumbnailFileProvider(
val mangaId: Int,

View File

@@ -24,9 +24,6 @@ import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.extension.ExtensionsList.extensionTableAsDataClass
import suwayomi.tachidesk.manga.impl.extension.github.ExtensionGithubApi
import suwayomi.tachidesk.manga.impl.util.PackageTools
@@ -54,7 +51,7 @@ import java.util.zip.ZipOutputStream
object Extension {
private val logger = KotlinLogging.logger {}
private val applicationDirs by DI.global.instance<ApplicationDirs>()
private val applicationDirs: ApplicationDirs by injectLazy()
suspend fun installExtension(pkgName: String): Int {
logger.debug("Installing $pkgName")

View File

@@ -9,14 +9,12 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import mu.KotlinLogging
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import uy.kohesive.injekt.injectLazy
object UpdaterSocket : Websocket<UpdateStatus>() {
private val logger = KotlinLogging.logger {}
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
private val updater by DI.global.instance<IUpdater>()
private val updater: IUpdater by injectLazy()
private var job: Job? = null
override fun notifyClient(

View File

@@ -10,17 +10,15 @@ package suwayomi.tachidesk.manga.impl.util
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource
import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.server.ApplicationDirs
import uy.kohesive.injekt.injectLazy
import xyz.nulldev.androidcompat.util.SafePath
import java.io.File
private val applicationDirs by DI.global.instance<ApplicationDirs>()
private val applicationDirs: ApplicationDirs by injectLazy()
private fun getMangaDir(mangaId: Int): String {
val mangaEntry = getMangaEntry(mangaId)

View File

@@ -17,12 +17,10 @@ import eu.kanade.tachiyomi.util.lang.Hash
import mu.KotlinLogging
import net.dongliu.apk.parser.ApkFile
import net.dongliu.apk.parser.ApkParsers
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import org.w3c.dom.Element
import org.w3c.dom.Node
import suwayomi.tachidesk.server.ApplicationDirs
import uy.kohesive.injekt.injectLazy
import xyz.nulldev.androidcompat.pm.InstalledPackage.Companion.toList
import xyz.nulldev.androidcompat.pm.toPackageInfo
import java.io.File
@@ -34,7 +32,7 @@ import javax.xml.parsers.DocumentBuilderFactory
object PackageTools {
private val logger = KotlinLogging.logger {}
private val applicationDirs by DI.global.instance<ApplicationDirs>()
private val applicationDirs: ApplicationDirs by injectLazy()
const val EXTENSION_FEATURE = "tachiyomi.extension"
const val METADATA_SOURCE_CLASS = "tachiyomi.extension.class"

View File

@@ -14,20 +14,18 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import mu.KotlinLogging
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.util.PackageTools.loadExtensionSources
import suwayomi.tachidesk.manga.model.table.ExtensionTable
import suwayomi.tachidesk.manga.model.table.SourceTable
import suwayomi.tachidesk.server.ApplicationDirs
import uy.kohesive.injekt.injectLazy
import java.util.concurrent.ConcurrentHashMap
object GetCatalogueSource {
private val logger = KotlinLogging.logger { }
private val sourceCache = ConcurrentHashMap<Long, CatalogueSource>()
private val applicationDirs by DI.global.instance<ApplicationDirs>()
private val applicationDirs: ApplicationDirs by injectLazy()
private fun getCatalogueSource(sourceId: Long): CatalogueSource? {
val cachedResult: CatalogueSource? = sourceCache[sourceId]

View File

@@ -24,14 +24,12 @@ import kotlinx.coroutines.runBlocking
import mu.KotlinLogging
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.ServerConnector
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.global.GlobalAPI
import suwayomi.tachidesk.graphql.GraphQL
import suwayomi.tachidesk.manga.MangaAPI
import suwayomi.tachidesk.server.util.Browser
import suwayomi.tachidesk.server.util.WebInterfaceManager
import uy.kohesive.injekt.injectLazy
import java.io.IOException
import java.lang.IllegalArgumentException
import java.util.concurrent.CompletableFuture
@@ -41,7 +39,7 @@ import kotlin.concurrent.thread
object JavalinSetup {
private val logger = KotlinLogging.logger {}
private val applicationDirs by DI.global.instance<ApplicationDirs>()
private val applicationDirs: ApplicationDirs by injectLazy()
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

View File

@@ -10,24 +10,19 @@ package suwayomi.tachidesk.server
import ch.qos.logback.classic.Level
import com.typesafe.config.ConfigRenderOptions
import eu.kanade.tachiyomi.App
import eu.kanade.tachiyomi.createAppModule
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.local.LocalSource
import io.javalin.plugin.json.JavalinJackson
import io.javalin.plugin.json.JsonMapper
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.serialization.json.Json
import kotlinx.serialization.protobuf.ProtoBuf
import mu.KotlinLogging
import nl.adaptivity.xmlutil.XmlDeclMode
import nl.adaptivity.xmlutil.core.XmlVersion
import nl.adaptivity.xmlutil.serialization.XML
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.kodein.di.DI
import org.kodein.di.bind
import org.kodein.di.conf.global
import org.kodein.di.instance
import org.kodein.di.singleton
import org.koin.core.context.startKoin
import org.koin.core.module.Module
import org.koin.dsl.module
import suwayomi.tachidesk.manga.impl.backup.proto.ProtoBackupExport
import suwayomi.tachidesk.manga.impl.download.DownloadManager
import suwayomi.tachidesk.manga.impl.update.IUpdater
@@ -37,12 +32,15 @@ import suwayomi.tachidesk.server.database.databaseUp
import suwayomi.tachidesk.server.generated.BuildConfig
import suwayomi.tachidesk.server.util.AppMutex.handleAppMutex
import suwayomi.tachidesk.server.util.SystemTray
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import xyz.nulldev.androidcompat.AndroidCompat
import xyz.nulldev.androidcompat.AndroidCompatInitializer
import xyz.nulldev.androidcompat.androidCompatModule
import xyz.nulldev.ts.config.ApplicationRootDir
import xyz.nulldev.ts.config.BASE_LOGGER_NAME
import xyz.nulldev.ts.config.ConfigKodeinModule
import xyz.nulldev.ts.config.GlobalConfigManager
import xyz.nulldev.ts.config.configManagerModule
import xyz.nulldev.ts.config.initLoggerConfig
import xyz.nulldev.ts.config.setLogLevelFor
import xyz.nulldev.ts.config.updateFileAppender
@@ -85,6 +83,13 @@ fun setupLogLevelUpdating(
}, ignoreInitialValue = false)
}
fun serverModule(applicationDirs: ApplicationDirs): Module =
module {
single { applicationDirs }
single<IUpdater> { Updater() }
single<JsonMapper> { JavalinJackson() }
}
fun applicationSetup() {
Thread.setDefaultUncaughtExceptionHandler { _, throwable ->
KotlinLogging.logger { }.error(throwable) { "unhandled exception" }
@@ -136,37 +141,6 @@ fun applicationSetup() {
.replace(Regex("(\"basicAuth(?:Username|Password)\"\\s:\\s)(?!\"\")\".*\""), "$1\"******\"")
}
DI.global.addImport(
DI.Module("Server") {
bind<ApplicationDirs>() with singleton { applicationDirs }
bind<IUpdater>() with singleton { Updater() }
bind<JsonMapper>() with singleton { JavalinJackson() }
bind<Json>() with
singleton {
Json {
ignoreUnknownKeys = true
explicitNulls = false
}
}
bind<XML>() with
singleton {
XML {
defaultPolicy {
ignoreUnknownChildren()
}
autoPolymorphic = true
xmlDeclMode = XmlDeclMode.Charset
indent = 2
xmlVersion = XmlVersion.XML10
}
}
bind<ProtoBuf>() with
singleton {
ProtoBuf
}
},
)
logger.debug("Data Root directory is set to: ${applicationDirs.dataRoot}")
// Migrate Directories from old versions
@@ -186,15 +160,27 @@ fun applicationSetup() {
File(it).mkdirs()
}
// initialize Koin modules
val app = App()
startKoin {
modules(
createAppModule(app),
androidCompatModule(),
configManagerModule(),
serverModule(applicationDirs),
)
}
// Make sure only one instance of the app is running
handleAppMutex()
// Load config API
DI.global.addImport(ConfigKodeinModule().create())
// Load Android compatibility dependencies
AndroidCompatInitializer().init()
// start app
androidCompat.startApp(App())
androidCompat.startApp(app)
// Initialize NetworkHelper early
Injekt.get<NetworkHelper>()
// create or update conf file if doesn't exist
try {
@@ -317,7 +303,7 @@ fun applicationSetup() {
Security.addProvider(BouncyCastleProvider())
// start automated global updates
val updater by DI.global.instance<IUpdater>()
val updater = Injekt.get<IUpdater>()
(updater as Updater).scheduleUpdateTask()
// start automated backups

View File

@@ -12,15 +12,14 @@ import de.neonew.exposed.migrations.runMigrations
import mu.KotlinLogging
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.DatabaseConfig
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.server.ApplicationDirs
import suwayomi.tachidesk.server.ServerConfig
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
object DBManager {
val db by lazy {
val applicationDirs by DI.global.instance<ApplicationDirs>()
val applicationDirs = Injekt.get<ApplicationDirs>()
Database.connect(
"jdbc:h2:${applicationDirs.dataRoot}/database",
"org.h2.Driver",

View File

@@ -11,14 +11,12 @@ import io.javalin.plugin.json.JsonMapper
import mu.KotlinLogging
import okhttp3.OkHttpClient
import okhttp3.Request.Builder
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.global.impl.AboutDataClass
import suwayomi.tachidesk.server.serverConfig
import suwayomi.tachidesk.server.util.Browser.openInBrowser
import suwayomi.tachidesk.server.util.ExitCode.MutexCheckFailedAnotherAppRunning
import suwayomi.tachidesk.server.util.ExitCode.MutexCheckFailedTachideskRunning
import uy.kohesive.injekt.injectLazy
import java.io.IOException
import java.util.concurrent.TimeUnit
@@ -35,7 +33,7 @@ object AppMutex {
private val appIP = if (serverConfig.ip.value == "0.0.0.0") "127.0.0.1" else serverConfig.ip.value
private val jsonMapper by DI.global.instance<JsonMapper>()
private val jsonMapper: JsonMapper by injectLazy()
private fun checkAppMutex(): AppMutexState {
val client =

View File

@@ -35,9 +35,6 @@ import kotlinx.serialization.json.jsonPrimitive
import mu.KLogger
import mu.KotlinLogging
import net.lingala.zip4j.ZipFile
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.graphql.types.AboutWebUI
import suwayomi.tachidesk.graphql.types.UpdateState
import suwayomi.tachidesk.graphql.types.UpdateState.DOWNLOADING
@@ -64,7 +61,7 @@ import java.util.Date
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.seconds
private val applicationDirs by DI.global.instance<ApplicationDirs>()
private val applicationDirs: ApplicationDirs by injectLazy()
private val tmpDir = System.getProperty("java.io.tmpdir")
private fun ByteArray.toHex(): String = joinToString(separator = "") { eachByte -> "%02x".format(eachByte) }

View File

@@ -1,87 +1,88 @@
package suwayomi.tachidesk.manga.controller
import io.javalin.http.Context
import io.javalin.http.HttpCode
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.runBlocking
import org.jetbrains.exposed.sql.insertAndGetId
import org.jetbrains.exposed.sql.transactions.transaction
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.Category
import suwayomi.tachidesk.manga.impl.CategoryManga
import suwayomi.tachidesk.manga.impl.update.IUpdater
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
import suwayomi.tachidesk.manga.model.table.CategoryTable
import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.test.ApplicationTest
import suwayomi.tachidesk.test.clearTables
// import io.javalin.http.Context
// import io.javalin.http.HttpCode
// import io.mockk.every
// import io.mockk.mockk
// import io.mockk.verify
// import kotlinx.coroutines.runBlocking
// import org.jetbrains.exposed.sql.insertAndGetId
// import org.jetbrains.exposed.sql.transactions.transaction
// import org.junit.jupiter.api.AfterEach
// import org.junit.jupiter.api.Assertions.assertEquals
// import org.junit.jupiter.api.Test
// import suwayomi.tachidesk.manga.impl.Category
// import suwayomi.tachidesk.manga.impl.CategoryManga
// import suwayomi.tachidesk.manga.impl.update.IUpdater
// import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
// import suwayomi.tachidesk.manga.model.table.CategoryTable
// import suwayomi.tachidesk.manga.model.table.MangaTable
// import suwayomi.tachidesk.test.ApplicationTest
// import suwayomi.tachidesk.test.clearTables
// import uy.kohesive.injekt.Injekt
// import uy.kohesive.injekt.api.get
//
internal class UpdateControllerTest : ApplicationTest() {
private val ctx = mockk<Context>(relaxed = true)
@Test
fun `POST non existent Category Id should give error`() {
every { ctx.formParam("category") } returns "1"
UpdateController.categoryUpdate(ctx)
verify { ctx.status(HttpCode.BAD_REQUEST) }
val updater by DI.global.instance<IUpdater>()
assertEquals(0, updater.status.value.numberOfJobs)
}
@Test
fun `POST existent Category Id should give success`() {
Category.createCategory("foo")
createLibraryManga("bar")
CategoryManga.addMangaToCategory(1, 1)
every { ctx.formParam("category") } returns "1"
UpdateController.categoryUpdate(ctx)
verify { ctx.status(HttpCode.OK) }
val updater by DI.global.instance<IUpdater>()
assertEquals(1, updater.status.value.numberOfJobs)
}
@Test
fun `POST null or empty category should update library`() {
val fooCatId = Category.createCategory("foo")
val fooMangaId = createLibraryManga("foo")
CategoryManga.addMangaToCategory(fooMangaId, fooCatId)
val barCatId = Category.createCategory("bar")
val barMangaId = createLibraryManga("bar")
CategoryManga.addMangaToCategory(barMangaId, barCatId)
createLibraryManga("mangaInDefault")
every { ctx.formParam("category") } returns null
UpdateController.categoryUpdate(ctx)
verify { ctx.status(HttpCode.OK) }
val updater by DI.global.instance<IUpdater>()
assertEquals(3, updater.status.value.numberOfJobs)
}
private fun createLibraryManga(_title: String): Int =
transaction {
MangaTable
.insertAndGetId {
it[title] = _title
it[url] = _title
it[sourceReference] = 1
it[inLibrary] = true
}.value
}
@AfterEach
internal fun tearDown() {
clearTables(
CategoryMangaTable,
MangaTable,
CategoryTable,
)
val updater by DI.global.instance<IUpdater>()
runBlocking { updater.reset() }
}
// private val ctx = mockk<Context>(relaxed = true)
//
// @Test
// fun `POST non existent Category Id should give error`() {
// every { ctx.formParam("category") } returns "1"
// UpdateController.categoryUpdate(ctx)
// verify { ctx.status(HttpCode.BAD_REQUEST) }
// val updater by DI.global.instance<IUpdater>()
// assertEquals(0, updater.status.value.numberOfJobs)
// }
//
// @Test
// fun `POST existent Category Id should give success`() {
// Category.createCategory("foo")
// createLibraryManga("bar")
// CategoryManga.addMangaToCategory(1, 1)
// every { ctx.formParam("category") } returns "1"
// UpdateController.categoryUpdate(ctx)
// verify { ctx.status(HttpCode.OK) }
// val updater by DI.global.instance<IUpdater>()
// assertEquals(1, updater.status.value.numberOfJobs)
// }
//
// @Test
// fun `POST null or empty category should update library`() {
// val fooCatId = Category.createCategory("foo")
// val fooMangaId = createLibraryManga("foo")
// CategoryManga.addMangaToCategory(fooMangaId, fooCatId)
// val barCatId = Category.createCategory("bar")
// val barMangaId = createLibraryManga("bar")
// CategoryManga.addMangaToCategory(barMangaId, barCatId)
// createLibraryManga("mangaInDefault")
// every { ctx.formParam("category") } returns null
// UpdateController.categoryUpdate(ctx)
// verify { ctx.status(HttpCode.OK) }
// val updater by DI.global.instance<IUpdater>()
// assertEquals(3, updater.status.value.numberOfJobs)
// }
//
// private fun createLibraryManga(_title: String): Int =
// transaction {
// MangaTable
// .insertAndGetId {
// it[title] = _title
// it[url] = _title
// it[sourceReference] = 1
// it[inLibrary] = true
// }.value
// }
//
// @AfterEach
// internal fun tearDown() {
// clearTables(
// CategoryMangaTable,
// MangaTable,
// CategoryTable,
// )
// val updater = Injekt.get<IUpdater>()
// runBlocking { updater.reset() }
// }
}

View File

@@ -206,6 +206,13 @@ class FilterListTest : ApplicationTest() {
JavalinJackson().toJsonString(filterList)
}
fun setFilter(
sourceId: Long,
filterChange: FilterChange,
) {
setFilter(sourceId, listOf(filterChange))
}
@Test
fun `Header and Separator should not change`() {
val source = registerSource(FilterListSource::class)
@@ -347,6 +354,7 @@ class FilterListTest : ApplicationTest() {
}
@AfterAll
@JvmStatic
fun teardown() {
(0 until sourceCount).forEach { unregisterCatalogueSource(it) }
}

View File

@@ -1,31 +1,54 @@
package suwayomi.tachidesk.manga.impl.update
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import java.util.concurrent.CopyOnWriteArrayList
class TestUpdater : IUpdater {
private val updateQueue = CopyOnWriteArrayList<UpdateJob>()
private var isRunning = false
private val _status = MutableStateFlow(UpdateStatus())
override val status: StateFlow<UpdateStatus> = _status.asStateFlow()
// private val updateQueue = CopyOnWriteArrayList<UpdateJob>()
// private var isRunning = false
// private val _status = MutableStateFlow(UpdateStatus())
// override val status: StateFlow<UpdateStatus> = _status.asStateFlow()
//
// override fun addMangasToQueue(mangas: List<MangaDataClass>) {
// mangas.forEach { updateQueue.add(UpdateJob(it)) }
// isRunning = true
// updateStatus()
// }
//
// override fun reset() {
// updateQueue.clear()
// isRunning = false
// updateStatus()
// }
//
// private fun updateStatus() {
// _status.update { UpdateStatus(updateQueue.toList(), isRunning) }
// }
override fun getLastUpdateTimestamp(): Long {
TODO("Not yet implemented")
}
override fun addCategoriesToUpdateQueue(
categories: List<CategoryDataClass>,
clear: Boolean?,
forceAll: Boolean,
) {
TODO("Not yet implemented")
}
override fun addMangasToQueue(mangas: List<MangaDataClass>) {
mangas.forEach { updateQueue.add(UpdateJob(it)) }
isRunning = true
updateStatus()
TODO("Not yet implemented")
}
override val status: Flow<UpdateStatus>
get() = TODO("Not yet implemented")
override val statusDeprecated: StateFlow<UpdateStatus>
get() = TODO("Not yet implemented")
override fun reset() {
updateQueue.clear()
isRunning = false
updateStatus()
}
private fun updateStatus() {
_status.update { UpdateStatus(updateQueue.toList(), isRunning) }
TODO("Not yet implemented")
}
}

View File

@@ -8,30 +8,29 @@ package suwayomi.tachidesk.test
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import eu.kanade.tachiyomi.App
import eu.kanade.tachiyomi.createAppModule
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.local.LocalSource
import io.javalin.plugin.json.JavalinJackson
import io.javalin.plugin.json.JsonMapper
import mu.KotlinLogging
import org.jetbrains.exposed.sql.Database
import org.junit.jupiter.api.BeforeAll
import org.kodein.di.DI
import org.kodein.di.bind
import org.kodein.di.conf.global
import org.kodein.di.singleton
import suwayomi.tachidesk.manga.impl.update.IUpdater
import suwayomi.tachidesk.manga.impl.update.TestUpdater
import org.koin.core.context.startKoin
import suwayomi.tachidesk.server.ApplicationDirs
import suwayomi.tachidesk.server.JavalinSetup
import suwayomi.tachidesk.server.ServerConfig
import suwayomi.tachidesk.server.androidCompat
import suwayomi.tachidesk.server.database.databaseUp
import suwayomi.tachidesk.server.serverConfig
import suwayomi.tachidesk.server.util.AppMutex
import suwayomi.tachidesk.server.serverModule
import suwayomi.tachidesk.server.util.AppMutex.handleAppMutex
import suwayomi.tachidesk.server.util.SystemTray
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import xyz.nulldev.androidcompat.AndroidCompatInitializer
import xyz.nulldev.androidcompat.androidCompatModule
import xyz.nulldev.ts.config.CONFIG_PREFIX
import xyz.nulldev.ts.config.ConfigKodeinModule
import xyz.nulldev.ts.config.GlobalConfigManager
import xyz.nulldev.ts.config.configManagerModule
import java.io.File
import java.util.Locale
@@ -59,14 +58,6 @@ open class ApplicationTest {
// Application dirs
val applicationDirs = ApplicationDirs()
DI.global.addImport(
DI.Module("Server") {
bind<ApplicationDirs>() with singleton { applicationDirs }
bind<JsonMapper>() with singleton { JavalinJackson() }
bind<IUpdater>() with singleton { TestUpdater() }
},
)
logger.debug("Data Root directory is set to: ${applicationDirs.dataRoot}")
// make dirs we need
@@ -86,15 +77,27 @@ open class ApplicationTest {
ServerConfig.register { GlobalConfigManager.config },
)
// Make sure only one instance of the app is running
AppMutex.handleAppMutex()
// initialize Koin modules
val app = App()
startKoin {
modules(
createAppModule(app),
androidCompatModule(),
configManagerModule(),
serverModule(applicationDirs),
)
}
// Make sure only one instance of the app is running
handleAppMutex()
// Load config API
DI.global.addImport(ConfigKodeinModule().create())
// Load Android compatibility dependencies
AndroidCompatInitializer().init()
// start app
androidCompat.startApp(App())
androidCompat.startApp(app)
// Initialize NetworkHelper early
Injekt.get<NetworkHelper>()
// create conf file if doesn't exist
try {