mirror of
https://github.com/Suwayomi/Tachidesk.git
synced 2025-12-10 06:42:07 +01:00
Switch to a new Ktlint Formatter (#705)
* Switch to new Ktlint plugin * Add ktlintCheck to PR builds * Run formatter * Put ktlint version in libs toml * Fix lint * Use Zip4Java from libs.toml
This commit is contained in:
11
.editorconfig
Normal file
11
.editorconfig
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[*.{kt,kts}]
|
||||||
|
indent_size=4
|
||||||
|
insert_final_newline=true
|
||||||
|
ij_kotlin_allow_trailing_comma=true
|
||||||
|
ij_kotlin_allow_trailing_comma_on_call_site=true
|
||||||
|
ij_kotlin_name_count_to_use_star_import=2147483647
|
||||||
|
ij_kotlin_name_count_to_use_star_import_for_members=2147483647
|
||||||
|
|
||||||
|
ktlint_standard_discouraged-comment-location=disabled
|
||||||
|
ktlint_standard_if-else-wrapping=disabled
|
||||||
|
ktlint_standard_no-consecutive-comments=disabled
|
||||||
2
.github/workflows/build_pull_request.yml
vendored
2
.github/workflows/build_pull_request.yml
vendored
@@ -49,5 +49,5 @@ jobs:
|
|||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
build-root-directory: master
|
build-root-directory: master
|
||||||
arguments: :server:shadowJar --stacktrace
|
arguments: ktlintCheck :server:shadowJar --stacktrace
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
@Suppress("DSL_SCOPE_VIOLATION")
|
|
||||||
plugins {
|
plugins {
|
||||||
id(libs.plugins.kotlin.jvm.get().pluginId)
|
id(libs.plugins.kotlin.jvm.get().pluginId)
|
||||||
id(libs.plugins.kotlin.serialization.get().pluginId)
|
id(libs.plugins.kotlin.serialization.get().pluginId)
|
||||||
id(libs.plugins.kotlinter.get().pluginId)
|
id(libs.plugins.ktlint.get().pluginId)
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ val ApplicationRootDir: String
|
|||||||
get(): String {
|
get(): String {
|
||||||
return System.getProperty(
|
return System.getProperty(
|
||||||
"$CONFIG_PREFIX.server.rootDir",
|
"$CONFIG_PREFIX.server.rootDir",
|
||||||
AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null)
|
AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import org.kodein.di.bind
|
|||||||
import org.kodein.di.singleton
|
import org.kodein.di.singleton
|
||||||
|
|
||||||
class ConfigKodeinModule {
|
class ConfigKodeinModule {
|
||||||
fun create() = DI.Module("ConfigManager") {
|
fun create() =
|
||||||
|
DI.Module("ConfigManager") {
|
||||||
// Config module
|
// Config module
|
||||||
bind<ConfigManager>() with singleton { GlobalConfigManager }
|
bind<ConfigManager>() with singleton { GlobalConfigManager }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,14 +63,16 @@ open class ConfigManager {
|
|||||||
val baseConfig =
|
val baseConfig =
|
||||||
ConfigFactory.parseMap(
|
ConfigFactory.parseMap(
|
||||||
mapOf(
|
mapOf(
|
||||||
"androidcompat.rootDir" to "$ApplicationRootDir/android-compat" // override AndroidCompat's rootDir
|
// override AndroidCompat's rootDir
|
||||||
)
|
"androidcompat.rootDir" to "$ApplicationRootDir/android-compat",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Load user config
|
// Load user config
|
||||||
val userConfig = getUserConfig()
|
val userConfig = getUserConfig()
|
||||||
|
|
||||||
val config = ConfigFactory.empty()
|
val config =
|
||||||
|
ConfigFactory.empty()
|
||||||
.withFallback(baseConfig)
|
.withFallback(baseConfig)
|
||||||
.withFallback(userConfig)
|
.withFallback(userConfig)
|
||||||
.withFallback(compatConfig)
|
.withFallback(compatConfig)
|
||||||
@@ -95,14 +97,20 @@ open class ConfigManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateUserConfigFile(path: String, value: ConfigValue) {
|
private fun updateUserConfigFile(
|
||||||
|
path: String,
|
||||||
|
value: ConfigValue,
|
||||||
|
) {
|
||||||
val userConfigDoc = ConfigDocumentFactory.parseFile(userConfigFile)
|
val userConfigDoc = ConfigDocumentFactory.parseFile(userConfigFile)
|
||||||
val updatedConfigDoc = userConfigDoc.withValue(path, value)
|
val updatedConfigDoc = userConfigDoc.withValue(path, value)
|
||||||
val newFileContent = updatedConfigDoc.render()
|
val newFileContent = updatedConfigDoc.render()
|
||||||
userConfigFile.writeText(newFileContent)
|
userConfigFile.writeText(newFileContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateValue(path: String, value: Any) {
|
suspend fun updateValue(
|
||||||
|
path: String,
|
||||||
|
value: Any,
|
||||||
|
) {
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
val configValue = ConfigValueFactory.fromAnyRef(value)
|
val configValue = ConfigValueFactory.fromAnyRef(value)
|
||||||
|
|
||||||
@@ -140,10 +148,16 @@ open class ConfigManager {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug { "user config is out of date, updating... (missingSettings= $hasMissingSettings, outdatedSettings= $hasOutdatedSettings" }
|
logger.debug {
|
||||||
|
"user config is out of date, updating... (missingSettings= $hasMissingSettings, outdatedSettings= $hasOutdatedSettings"
|
||||||
|
}
|
||||||
|
|
||||||
var newUserConfigDoc: ConfigDocument = resetUserConfig(false)
|
var newUserConfigDoc: ConfigDocument = resetUserConfig(false)
|
||||||
userConfig.entrySet().filter { serverConfig.hasPath(it.key) }.forEach { newUserConfigDoc = newUserConfigDoc.withValue(it.key, it.value) }
|
userConfig.entrySet().filter {
|
||||||
|
serverConfig.hasPath(
|
||||||
|
it.key,
|
||||||
|
)
|
||||||
|
}.forEach { newUserConfigDoc = newUserConfigDoc.withValue(it.key, it.value) }
|
||||||
|
|
||||||
userConfigFile.writeText(newUserConfigDoc.render())
|
userConfigFile.writeText(newUserConfigDoc.render())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,12 +26,16 @@ abstract class SystemPropertyOverridableConfigModule(getConfig: () -> Config, mo
|
|||||||
|
|
||||||
/** Defines a config property that is overridable with jvm `-D` commandline arguments prefixed with [CONFIG_PREFIX] */
|
/** Defines a config property that is overridable with jvm `-D` commandline arguments prefixed with [CONFIG_PREFIX] */
|
||||||
class SystemPropertyOverrideDelegate(val getConfig: () -> Config, val moduleName: String) {
|
class SystemPropertyOverrideDelegate(val getConfig: () -> Config, val moduleName: String) {
|
||||||
inline operator fun <R, reified T> getValue(thisRef: R, property: KProperty<*>): T {
|
inline operator fun <R, reified T> getValue(
|
||||||
|
thisRef: R,
|
||||||
|
property: KProperty<*>,
|
||||||
|
): T {
|
||||||
val configValue: T = getConfig().getValue(thisRef, property)
|
val configValue: T = getConfig().getValue(thisRef, property)
|
||||||
|
|
||||||
val combined = System.getProperty(
|
val combined =
|
||||||
|
System.getProperty(
|
||||||
"$CONFIG_PREFIX.$moduleName.${property.name}",
|
"$CONFIG_PREFIX.$moduleName.${property.name}",
|
||||||
configValue.toString()
|
configValue.toString(),
|
||||||
)
|
)
|
||||||
|
|
||||||
return when (T::class.simpleName) {
|
return when (T::class.simpleName) {
|
||||||
|
|||||||
@@ -19,23 +19,29 @@ import mu.KotlinLogging
|
|||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
private fun createRollingFileAppender(logContext: LoggerContext, logDirPath: String): RollingFileAppender<ILoggingEvent> {
|
private fun createRollingFileAppender(
|
||||||
|
logContext: LoggerContext,
|
||||||
|
logDirPath: String,
|
||||||
|
): RollingFileAppender<ILoggingEvent> {
|
||||||
val logFilename = "application"
|
val logFilename = "application"
|
||||||
|
|
||||||
val logEncoder = PatternLayoutEncoder().apply {
|
val logEncoder =
|
||||||
|
PatternLayoutEncoder().apply {
|
||||||
pattern = "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n"
|
pattern = "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n"
|
||||||
context = logContext
|
context = logContext
|
||||||
start()
|
start()
|
||||||
}
|
}
|
||||||
|
|
||||||
val appender = RollingFileAppender<ILoggingEvent>().apply {
|
val appender =
|
||||||
|
RollingFileAppender<ILoggingEvent>().apply {
|
||||||
name = "FILE"
|
name = "FILE"
|
||||||
context = logContext
|
context = logContext
|
||||||
encoder = logEncoder
|
encoder = logEncoder
|
||||||
file = "$logDirPath/$logFilename.log"
|
file = "$logDirPath/$logFilename.log"
|
||||||
}
|
}
|
||||||
|
|
||||||
val rollingPolicy = SizeAndTimeBasedRollingPolicy<ILoggingEvent>().apply {
|
val rollingPolicy =
|
||||||
|
SizeAndTimeBasedRollingPolicy<ILoggingEvent>().apply {
|
||||||
context = logContext
|
context = logContext
|
||||||
setParent(appender)
|
setParent(appender)
|
||||||
fileNamePattern = "$logDirPath/${logFilename}_%d{yyyy-MM-dd}_%i.log.gz"
|
fileNamePattern = "$logDirPath/${logFilename}_%d{yyyy-MM-dd}_%i.log.gz"
|
||||||
@@ -72,8 +78,12 @@ fun initLoggerConfig(appRootPath: String) {
|
|||||||
|
|
||||||
const val BASE_LOGGER_NAME = "_BaseLogger"
|
const val BASE_LOGGER_NAME = "_BaseLogger"
|
||||||
|
|
||||||
fun setLogLevelFor(name: String, level: Level) {
|
fun setLogLevelFor(
|
||||||
val logger = if (name == BASE_LOGGER_NAME) {
|
name: String,
|
||||||
|
level: Level,
|
||||||
|
) {
|
||||||
|
val logger =
|
||||||
|
if (name == BASE_LOGGER_NAME) {
|
||||||
getBaseLogger()
|
getBaseLogger()
|
||||||
} else {
|
} else {
|
||||||
getLogger(name)
|
getLogger(name)
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ package xyz.nulldev.ts.config.util
|
|||||||
|
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
|
|
||||||
operator fun Config.get(key: String) = getString(key)
|
operator fun Config.get(key: String) =
|
||||||
|
getString(key)
|
||||||
?: throw IllegalStateException("Could not find value for config entry: $key!")
|
?: throw IllegalStateException("Could not find value for config entry: $key!")
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
@Suppress("DSL_SCOPE_VIOLATION")
|
|
||||||
plugins {
|
plugins {
|
||||||
id(libs.plugins.kotlin.jvm.get().pluginId)
|
id(libs.plugins.kotlin.jvm.get().pluginId)
|
||||||
id(libs.plugins.kotlin.serialization.get().pluginId)
|
id(libs.plugins.kotlin.serialization.get().pluginId)
|
||||||
id(libs.plugins.kotlinter.get().pluginId)
|
id(libs.plugins.ktlint.get().pluginId)
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class PreferenceManager {
|
|||||||
fun getDefaultSharedPreferences(context: Context) =
|
fun getDefaultSharedPreferences(context: Context) =
|
||||||
context.getSharedPreferences(
|
context.getSharedPreferences(
|
||||||
context.applicationInfo.packageName,
|
context.applicationInfo.packageName,
|
||||||
Context.MODE_PRIVATE
|
Context.MODE_PRIVATE,
|
||||||
)!!
|
)!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import org.kodein.di.instance
|
|||||||
import xyz.nulldev.androidcompat.androidimpl.CustomContext
|
import xyz.nulldev.androidcompat.androidimpl.CustomContext
|
||||||
|
|
||||||
class AndroidCompat {
|
class AndroidCompat {
|
||||||
|
|
||||||
val context: CustomContext by DI.global.instance()
|
val context: CustomContext by DI.global.instance()
|
||||||
|
|
||||||
fun startApp(application: Application) {
|
fun startApp(application: Application) {
|
||||||
|
|||||||
@@ -18,10 +18,13 @@ class AndroidCompatInitializer {
|
|||||||
GlobalConfigManager.registerModules(
|
GlobalConfigManager.registerModules(
|
||||||
FilesConfigModule.register(GlobalConfigManager.config),
|
FilesConfigModule.register(GlobalConfigManager.config),
|
||||||
ApplicationInfoConfigModule.register(GlobalConfigManager.config),
|
ApplicationInfoConfigModule.register(GlobalConfigManager.config),
|
||||||
SystemConfigModule.register(GlobalConfigManager.config)
|
SystemConfigModule.register(GlobalConfigManager.config),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Set some properties extensions use
|
// Set some properties extensions use
|
||||||
System.setProperty("http.agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
|
System.setProperty(
|
||||||
|
"http.agent",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ import xyz.nulldev.androidcompat.service.ServiceSupport
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
class AndroidCompatModule {
|
class AndroidCompatModule {
|
||||||
fun create() = DI.Module("AndroidCompat") {
|
fun create() =
|
||||||
|
DI.Module("AndroidCompat") {
|
||||||
bind<AndroidFiles>() with singleton { AndroidFiles() }
|
bind<AndroidFiles>() with singleton { AndroidFiles() }
|
||||||
|
|
||||||
bind<ApplicationInfoImpl>() with singleton { ApplicationInfoImpl() }
|
bind<ApplicationInfoImpl>() with singleton { ApplicationInfoImpl() }
|
||||||
@@ -31,7 +32,8 @@ class AndroidCompatModule {
|
|||||||
|
|
||||||
// Context
|
// Context
|
||||||
bind<CustomContext>() with singleton { CustomContext() }
|
bind<CustomContext>() with singleton { CustomContext() }
|
||||||
bind<Context>() with singleton {
|
bind<Context>() with
|
||||||
|
singleton {
|
||||||
val context: Context by DI.global.instance<CustomContext>()
|
val context: Context by DI.global.instance<CustomContext>()
|
||||||
context
|
context
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ class ApplicationInfoConfigModule(getConfig: () -> Config) : ConfigModule(getCon
|
|||||||
val debug: Boolean by getConfig()
|
val debug: Boolean by getConfig()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun register(config: Config) =
|
fun register(config: Config) = ApplicationInfoConfigModule { config.getConfig("android.app") }
|
||||||
ApplicationInfoConfigModule { config.getConfig("android.app") }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ class FilesConfigModule(getConfig: () -> Config) : ConfigModule(getConfig) {
|
|||||||
val packageDir: String by getConfig()
|
val packageDir: String by getConfig()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun register(config: Config) =
|
fun register(config: Config) = FilesConfigModule { config.getConfig("android.files") }
|
||||||
FilesConfigModule { config.getConfig("android.files") }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,16 @@ class SystemConfigModule(val getConfig: () -> Config) : ConfigModule(getConfig)
|
|||||||
val propertyPrefix = "properties."
|
val propertyPrefix = "properties."
|
||||||
|
|
||||||
fun getStringProperty(property: String) = getConfig().getString("$propertyPrefix$property")!!
|
fun getStringProperty(property: String) = getConfig().getString("$propertyPrefix$property")!!
|
||||||
|
|
||||||
fun getIntProperty(property: String) = getConfig().getInt("$propertyPrefix$property")
|
fun getIntProperty(property: String) = getConfig().getInt("$propertyPrefix$property")
|
||||||
|
|
||||||
fun getLongProperty(property: String) = getConfig().getLong("$propertyPrefix$property")
|
fun getLongProperty(property: String) = getConfig().getLong("$propertyPrefix$property")
|
||||||
|
|
||||||
fun getBooleanProperty(property: String) = getConfig().getBoolean("$propertyPrefix$property")
|
fun getBooleanProperty(property: String) = getConfig().getBoolean("$propertyPrefix$property")
|
||||||
|
|
||||||
fun hasProperty(property: String) = getConfig().hasPath("$propertyPrefix$property")
|
fun hasProperty(property: String) = getConfig().hasPath("$propertyPrefix$property")
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun register(config: Config) =
|
fun register(config: Config) = SystemConfigModule { config.getConfig("android.system") }
|
||||||
SystemConfigModule { config.getConfig("android.system") }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import java.util.Calendar
|
|||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
||||||
|
|
||||||
private val cachedContent = mutableListOf<ResultSetEntry>()
|
private val cachedContent = mutableListOf<ResultSetEntry>()
|
||||||
private val columnCache = mutableMapOf<String, Int>()
|
private val columnCache = mutableMapOf<String, Int>()
|
||||||
private var lastReturnWasNull = false
|
private var lastReturnWasNull = false
|
||||||
@@ -29,7 +28,8 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
|
|
||||||
val parentMetadata = parent.metaData
|
val parentMetadata = parent.metaData
|
||||||
val columnCount = parentMetadata.columnCount
|
val columnCount = parentMetadata.columnCount
|
||||||
val columnLabels = (1..columnCount).map {
|
val columnLabels =
|
||||||
|
(1..columnCount).map {
|
||||||
parentMetadata.getColumnLabel(it)
|
parentMetadata.getColumnLabel(it)
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
|
|
||||||
@@ -43,7 +43,8 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
|
|
||||||
// Fill cache
|
// Fill cache
|
||||||
while (parent.next()) {
|
while (parent.next()) {
|
||||||
cachedContent += ResultSetEntry().apply {
|
cachedContent +=
|
||||||
|
ResultSetEntry().apply {
|
||||||
for (i in 1..columnCount)
|
for (i in 1..columnCount)
|
||||||
data += parent.getObject(i)
|
data += parent.getObject(i)
|
||||||
}
|
}
|
||||||
@@ -92,67 +93,121 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
return obj(columnLabel) as NClob
|
return obj(columnLabel) as NClob
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNString(columnIndex: Int, nString: String?) {
|
override fun updateNString(
|
||||||
|
columnIndex: Int,
|
||||||
|
nString: String?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNString(columnLabel: String?, nString: String?) {
|
override fun updateNString(
|
||||||
|
columnLabel: String?,
|
||||||
|
nString: String?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBinaryStream(columnIndex: Int, x: InputStream?, length: Int) {
|
override fun updateBinaryStream(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: InputStream?,
|
||||||
|
length: Int,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBinaryStream(columnLabel: String?, x: InputStream?, length: Int) {
|
override fun updateBinaryStream(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: InputStream?,
|
||||||
|
length: Int,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBinaryStream(columnIndex: Int, x: InputStream?, length: Long) {
|
override fun updateBinaryStream(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: InputStream?,
|
||||||
|
length: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBinaryStream(columnLabel: String?, x: InputStream?, length: Long) {
|
override fun updateBinaryStream(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: InputStream?,
|
||||||
|
length: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBinaryStream(columnIndex: Int, x: InputStream?) {
|
override fun updateBinaryStream(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: InputStream?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBinaryStream(columnLabel: String?, x: InputStream?) {
|
override fun updateBinaryStream(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: InputStream?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateTimestamp(columnIndex: Int, x: Timestamp?) {
|
override fun updateTimestamp(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Timestamp?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateTimestamp(columnLabel: String?, x: Timestamp?) {
|
override fun updateTimestamp(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Timestamp?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNCharacterStream(columnIndex: Int, x: Reader?, length: Long) {
|
override fun updateNCharacterStream(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Reader?,
|
||||||
|
length: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNCharacterStream(columnLabel: String?, reader: Reader?, length: Long) {
|
override fun updateNCharacterStream(
|
||||||
|
columnLabel: String?,
|
||||||
|
reader: Reader?,
|
||||||
|
length: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNCharacterStream(columnIndex: Int, x: Reader?) {
|
override fun updateNCharacterStream(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Reader?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNCharacterStream(columnLabel: String?, reader: Reader?) {
|
override fun updateNCharacterStream(
|
||||||
|
columnLabel: String?,
|
||||||
|
reader: Reader?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateInt(columnIndex: Int, x: Int) {
|
override fun updateInt(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Int,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateInt(columnLabel: String?, x: Int) {
|
override fun updateInt(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Int,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,12 +225,18 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDate(columnIndex: Int, cal: Calendar?): Date {
|
override fun getDate(
|
||||||
|
columnIndex: Int,
|
||||||
|
cal: Calendar?,
|
||||||
|
): Date {
|
||||||
// TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDate(columnLabel: String?, cal: Calendar?): Date {
|
override fun getDate(
|
||||||
|
columnLabel: String?,
|
||||||
|
cal: Calendar?,
|
||||||
|
): Date {
|
||||||
// TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
@@ -185,11 +246,17 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateFloat(columnIndex: Int, x: Float) {
|
override fun updateFloat(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Float,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateFloat(columnLabel: String?, x: Float) {
|
override fun updateFloat(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Float,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,12 +272,18 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
return cursor - 1 < resultSetLength
|
return cursor - 1 < resultSetLength
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getBigDecimal(columnIndex: Int, scale: Int): BigDecimal {
|
override fun getBigDecimal(
|
||||||
|
columnIndex: Int,
|
||||||
|
scale: Int,
|
||||||
|
): BigDecimal {
|
||||||
// TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getBigDecimal(columnLabel: String?, scale: Int): BigDecimal {
|
override fun getBigDecimal(
|
||||||
|
columnLabel: String?,
|
||||||
|
scale: Int,
|
||||||
|
): BigDecimal {
|
||||||
// TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
@@ -223,11 +296,17 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
return obj(columnLabel) as BigDecimal
|
return obj(columnLabel) as BigDecimal
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBytes(columnIndex: Int, x: ByteArray?) {
|
override fun updateBytes(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: ByteArray?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBytes(columnLabel: String?, x: ByteArray?) {
|
override fun updateBytes(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: ByteArray?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,12 +328,18 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTime(columnIndex: Int, cal: Calendar?): Time {
|
override fun getTime(
|
||||||
|
columnIndex: Int,
|
||||||
|
cal: Calendar?,
|
||||||
|
): Time {
|
||||||
// TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTime(columnLabel: String?, cal: Calendar?): Time {
|
override fun getTime(
|
||||||
|
columnLabel: String?,
|
||||||
|
cal: Calendar?,
|
||||||
|
): Time {
|
||||||
// TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
@@ -328,27 +413,49 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
return cursorValid()
|
return cursorValid()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateAsciiStream(columnIndex: Int, x: InputStream?, length: Int) {
|
override fun updateAsciiStream(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: InputStream?,
|
||||||
|
length: Int,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateAsciiStream(columnLabel: String?, x: InputStream?, length: Int) {
|
override fun updateAsciiStream(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: InputStream?,
|
||||||
|
length: Int,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateAsciiStream(columnIndex: Int, x: InputStream?, length: Long) {
|
override fun updateAsciiStream(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: InputStream?,
|
||||||
|
length: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateAsciiStream(columnLabel: String?, x: InputStream?, length: Long) {
|
override fun updateAsciiStream(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: InputStream?,
|
||||||
|
length: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateAsciiStream(columnIndex: Int, x: InputStream?) {
|
override fun updateAsciiStream(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: InputStream?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateAsciiStream(columnLabel: String?, x: InputStream?) {
|
override fun updateAsciiStream(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: InputStream?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,61 +467,107 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
return obj(columnLabel) as URL
|
return obj(columnLabel) as URL
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateShort(columnIndex: Int, x: Short) {
|
override fun updateShort(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Short,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateShort(columnLabel: String?, x: Short) {
|
override fun updateShort(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Short,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getType() = ResultSet.TYPE_SCROLL_INSENSITIVE
|
override fun getType() = ResultSet.TYPE_SCROLL_INSENSITIVE
|
||||||
|
|
||||||
override fun updateNClob(columnIndex: Int, nClob: NClob?) {
|
override fun updateNClob(
|
||||||
|
columnIndex: Int,
|
||||||
|
nClob: NClob?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNClob(columnLabel: String?, nClob: NClob?) {
|
override fun updateNClob(
|
||||||
|
columnLabel: String?,
|
||||||
|
nClob: NClob?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNClob(columnIndex: Int, reader: Reader?, length: Long) {
|
override fun updateNClob(
|
||||||
|
columnIndex: Int,
|
||||||
|
reader: Reader?,
|
||||||
|
length: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNClob(columnLabel: String?, reader: Reader?, length: Long) {
|
override fun updateNClob(
|
||||||
|
columnLabel: String?,
|
||||||
|
reader: Reader?,
|
||||||
|
length: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNClob(columnIndex: Int, reader: Reader?) {
|
override fun updateNClob(
|
||||||
|
columnIndex: Int,
|
||||||
|
reader: Reader?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNClob(columnLabel: String?, reader: Reader?) {
|
override fun updateNClob(
|
||||||
|
columnLabel: String?,
|
||||||
|
reader: Reader?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateRef(columnIndex: Int, x: Ref?) {
|
override fun updateRef(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Ref?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateRef(columnLabel: String?, x: Ref?) {
|
override fun updateRef(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Ref?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateObject(columnIndex: Int, x: Any?, scaleOrLength: Int) {
|
override fun updateObject(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Any?,
|
||||||
|
scaleOrLength: Int,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateObject(columnIndex: Int, x: Any?) {
|
override fun updateObject(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Any?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateObject(columnLabel: String?, x: Any?, scaleOrLength: Int) {
|
override fun updateObject(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Any?,
|
||||||
|
scaleOrLength: Int,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateObject(columnLabel: String?, x: Any?) {
|
override fun updateObject(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Any?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,11 +575,17 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
internalMove(resultSetLength + 1)
|
internalMove(resultSetLength + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateLong(columnIndex: Int, x: Long) {
|
override fun updateLong(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateLong(columnLabel: String?, x: Long) {
|
override fun updateLong(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,27 +599,47 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateClob(columnIndex: Int, x: Clob?) {
|
override fun updateClob(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Clob?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateClob(columnLabel: String?, x: Clob?) {
|
override fun updateClob(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Clob?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateClob(columnIndex: Int, reader: Reader?, length: Long) {
|
override fun updateClob(
|
||||||
|
columnIndex: Int,
|
||||||
|
reader: Reader?,
|
||||||
|
length: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateClob(columnLabel: String?, reader: Reader?, length: Long) {
|
override fun updateClob(
|
||||||
|
columnLabel: String?,
|
||||||
|
reader: Reader?,
|
||||||
|
length: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateClob(columnIndex: Int, reader: Reader?) {
|
override fun updateClob(
|
||||||
|
columnIndex: Int,
|
||||||
|
reader: Reader?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateClob(columnLabel: String?, reader: Reader?) {
|
override fun updateClob(
|
||||||
|
columnLabel: String?,
|
||||||
|
reader: Reader?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,19 +659,31 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
return obj(columnLabel) as String?
|
return obj(columnLabel) as String?
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateSQLXML(columnIndex: Int, xmlObject: SQLXML?) {
|
override fun updateSQLXML(
|
||||||
|
columnIndex: Int,
|
||||||
|
xmlObject: SQLXML?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateSQLXML(columnLabel: String?, xmlObject: SQLXML?) {
|
override fun updateSQLXML(
|
||||||
|
columnLabel: String?,
|
||||||
|
xmlObject: SQLXML?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateDate(columnIndex: Int, x: Date?) {
|
override fun updateDate(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Date?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateDate(columnLabel: String?, x: Date?) {
|
override fun updateDate(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Date?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,21 +695,33 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
return obj(columnLabel)
|
return obj(columnLabel)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getObject(columnIndex: Int, map: MutableMap<String, Class<*>>?): Any {
|
override fun getObject(
|
||||||
|
columnIndex: Int,
|
||||||
|
map: MutableMap<String, Class<*>>?,
|
||||||
|
): Any {
|
||||||
// TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getObject(columnLabel: String?, map: MutableMap<String, Class<*>>?): Any {
|
override fun getObject(
|
||||||
|
columnLabel: String?,
|
||||||
|
map: MutableMap<String, Class<*>>?,
|
||||||
|
): Any {
|
||||||
// TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T : Any?> getObject(columnIndex: Int, type: Class<T>?): T {
|
override fun <T : Any?> getObject(
|
||||||
|
columnIndex: Int,
|
||||||
|
type: Class<T>?,
|
||||||
|
): T {
|
||||||
return obj(columnIndex) as T
|
return obj(columnIndex) as T
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T : Any?> getObject(columnLabel: String?, type: Class<T>?): T {
|
override fun <T : Any?> getObject(
|
||||||
|
columnLabel: String?,
|
||||||
|
type: Class<T>?,
|
||||||
|
): T {
|
||||||
return obj(columnLabel) as T
|
return obj(columnLabel) as T
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,11 +730,17 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
return cursorValid()
|
return cursorValid()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateDouble(columnIndex: Int, x: Double) {
|
override fun updateDouble(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Double,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateDouble(columnLabel: String?, x: Double) {
|
override fun updateDouble(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Double,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,35 +774,61 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBlob(columnIndex: Int, x: Blob?) {
|
override fun updateBlob(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Blob?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBlob(columnLabel: String?, x: Blob?) {
|
override fun updateBlob(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Blob?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBlob(columnIndex: Int, inputStream: InputStream?, length: Long) {
|
override fun updateBlob(
|
||||||
|
columnIndex: Int,
|
||||||
|
inputStream: InputStream?,
|
||||||
|
length: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBlob(columnLabel: String?, inputStream: InputStream?, length: Long) {
|
override fun updateBlob(
|
||||||
|
columnLabel: String?,
|
||||||
|
inputStream: InputStream?,
|
||||||
|
length: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBlob(columnIndex: Int, inputStream: InputStream?) {
|
override fun updateBlob(
|
||||||
|
columnIndex: Int,
|
||||||
|
inputStream: InputStream?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBlob(columnLabel: String?, inputStream: InputStream?) {
|
override fun updateBlob(
|
||||||
|
columnLabel: String?,
|
||||||
|
inputStream: InputStream?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateByte(columnIndex: Int, x: Byte) {
|
override fun updateByte(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Byte,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateByte(columnLabel: String?, x: Byte) {
|
override fun updateByte(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Byte,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,11 +862,17 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateString(columnIndex: Int, x: String?) {
|
override fun updateString(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: String?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateString(columnLabel: String?, x: String?) {
|
override fun updateString(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: String?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -651,11 +892,17 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
return cursor - 1 < resultSetLength
|
return cursor - 1 < resultSetLength
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBoolean(columnIndex: Int, x: Boolean) {
|
override fun updateBoolean(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Boolean,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBoolean(columnLabel: String?, x: Boolean) {
|
override fun updateBoolean(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Boolean,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -665,11 +912,17 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
|
|
||||||
override fun rowUpdated() = false
|
override fun rowUpdated() = false
|
||||||
|
|
||||||
override fun updateBigDecimal(columnIndex: Int, x: BigDecimal?) {
|
override fun updateBigDecimal(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: BigDecimal?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateBigDecimal(columnLabel: String?, x: BigDecimal?) {
|
override fun updateBigDecimal(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: BigDecimal?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,11 +942,17 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
return getBinaryStream(columnLabel)
|
return getBinaryStream(columnLabel)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateTime(columnIndex: Int, x: Time?) {
|
override fun updateTime(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Time?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateTime(columnLabel: String?, x: Time?) {
|
override fun updateTime(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Time?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,12 +966,18 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTimestamp(columnIndex: Int, cal: Calendar?): Timestamp {
|
override fun getTimestamp(
|
||||||
|
columnIndex: Int,
|
||||||
|
cal: Calendar?,
|
||||||
|
): Timestamp {
|
||||||
// TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTimestamp(columnLabel: String?, cal: Calendar?): Timestamp {
|
override fun getTimestamp(
|
||||||
|
columnLabel: String?,
|
||||||
|
cal: Calendar?,
|
||||||
|
): Timestamp {
|
||||||
// TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
@@ -729,11 +994,17 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
|
|
||||||
override fun getConcurrency() = ResultSet.CONCUR_READ_ONLY
|
override fun getConcurrency() = ResultSet.CONCUR_READ_ONLY
|
||||||
|
|
||||||
override fun updateRowId(columnIndex: Int, x: RowId?) {
|
override fun updateRowId(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: RowId?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateRowId(columnLabel: String?, x: RowId?) {
|
override fun updateRowId(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: RowId?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -745,11 +1016,17 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
return getBinaryStream(columnLabel).reader()
|
return getBinaryStream(columnLabel).reader()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateArray(columnIndex: Int, x: Array?) {
|
override fun updateArray(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Array?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateArray(columnLabel: String?, x: Array?) {
|
override fun updateArray(
|
||||||
|
columnLabel: String?,
|
||||||
|
x: Array?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -814,9 +1091,13 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
override fun getMetaData(): ResultSetMetaData {
|
override fun getMetaData(): ResultSetMetaData {
|
||||||
return object : ResultSetMetaData by parentMetadata {
|
return object : ResultSetMetaData by parentMetadata {
|
||||||
override fun isReadOnly(column: Int) = true
|
override fun isReadOnly(column: Int) = true
|
||||||
|
|
||||||
override fun isWritable(column: Int) = false
|
override fun isWritable(column: Int) = false
|
||||||
|
|
||||||
override fun isDefinitelyWritable(column: Int) = false
|
override fun isDefinitelyWritable(column: Int) = false
|
||||||
|
|
||||||
override fun getColumnCount() = this@ScrollableResultSet.columnCount
|
override fun getColumnCount() = this@ScrollableResultSet.columnCount
|
||||||
|
|
||||||
override fun getColumnLabel(column: Int): String {
|
override fun getColumnLabel(column: Int): String {
|
||||||
return columnLabels[column - 1]
|
return columnLabels[column - 1]
|
||||||
}
|
}
|
||||||
@@ -831,27 +1112,49 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
return (obj(columnLabel) as ByteArray).inputStream()
|
return (obj(columnLabel) as ByteArray).inputStream()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateCharacterStream(columnIndex: Int, x: Reader?, length: Int) {
|
override fun updateCharacterStream(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Reader?,
|
||||||
|
length: Int,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateCharacterStream(columnLabel: String?, reader: Reader?, length: Int) {
|
override fun updateCharacterStream(
|
||||||
|
columnLabel: String?,
|
||||||
|
reader: Reader?,
|
||||||
|
length: Int,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateCharacterStream(columnIndex: Int, x: Reader?, length: Long) {
|
override fun updateCharacterStream(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Reader?,
|
||||||
|
length: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateCharacterStream(columnLabel: String?, reader: Reader?, length: Long) {
|
override fun updateCharacterStream(
|
||||||
|
columnLabel: String?,
|
||||||
|
reader: Reader?,
|
||||||
|
length: Long,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateCharacterStream(columnIndex: Int, x: Reader?) {
|
override fun updateCharacterStream(
|
||||||
|
columnIndex: Int,
|
||||||
|
x: Reader?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateCharacterStream(columnLabel: String?, reader: Reader?) {
|
override fun updateCharacterStream(
|
||||||
|
columnLabel: String?,
|
||||||
|
reader: Reader?,
|
||||||
|
) {
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,10 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
|
|||||||
return preferences.keys.associateWith { preferences.getStringOrNull(it) }.toMutableMap()
|
return preferences.keys.associateWith { preferences.getStringOrNull(it) }.toMutableMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getString(key: String, defValue: String?): String? {
|
override fun getString(
|
||||||
|
key: String,
|
||||||
|
defValue: String?,
|
||||||
|
): String? {
|
||||||
return if (defValue != null) {
|
return if (defValue != null) {
|
||||||
preferences.getString(key, defValue)
|
preferences.getString(key, defValue)
|
||||||
} else {
|
} else {
|
||||||
@@ -39,7 +42,10 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getStringSet(key: String, defValues: Set<String>?): Set<String>? {
|
override fun getStringSet(
|
||||||
|
key: String,
|
||||||
|
defValues: Set<String>?,
|
||||||
|
): Set<String>? {
|
||||||
try {
|
try {
|
||||||
return if (defValues != null) {
|
return if (defValues != null) {
|
||||||
preferences.decodeValue(SetSerializer(String.serializer()), key, defValues)
|
preferences.decodeValue(SetSerializer(String.serializer()), key, defValues)
|
||||||
@@ -51,19 +57,31 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getInt(key: String, defValue: Int): Int {
|
override fun getInt(
|
||||||
|
key: String,
|
||||||
|
defValue: Int,
|
||||||
|
): Int {
|
||||||
return preferences.getInt(key, defValue)
|
return preferences.getInt(key, defValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLong(key: String, defValue: Long): Long {
|
override fun getLong(
|
||||||
|
key: String,
|
||||||
|
defValue: Long,
|
||||||
|
): Long {
|
||||||
return preferences.getLong(key, defValue)
|
return preferences.getLong(key, defValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFloat(key: String, defValue: Float): Float {
|
override fun getFloat(
|
||||||
|
key: String,
|
||||||
|
defValue: Float,
|
||||||
|
): Float {
|
||||||
return preferences.getFloat(key, defValue)
|
return preferences.getFloat(key, defValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getBoolean(key: String, defValue: Boolean): Boolean {
|
override fun getBoolean(
|
||||||
|
key: String,
|
||||||
|
defValue: Boolean,
|
||||||
|
): Boolean {
|
||||||
return preferences.getBoolean(key, defValue)
|
return preferences.getBoolean(key, defValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,11 +98,15 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
|
|||||||
|
|
||||||
private sealed class Action {
|
private sealed class Action {
|
||||||
data class Add(val key: String, val value: Any) : Action()
|
data class Add(val key: String, val value: Any) : Action()
|
||||||
|
|
||||||
data class Remove(val key: String) : Action()
|
data class Remove(val key: String) : Action()
|
||||||
object Clear : Action()
|
object Clear : Action()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun putString(key: String, value: String?): SharedPreferences.Editor {
|
override fun putString(
|
||||||
|
key: String,
|
||||||
|
value: String?,
|
||||||
|
): SharedPreferences.Editor {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
actions += Action.Add(key, value)
|
actions += Action.Add(key, value)
|
||||||
} else {
|
} else {
|
||||||
@@ -95,7 +117,7 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
|
|||||||
|
|
||||||
override fun putStringSet(
|
override fun putStringSet(
|
||||||
key: String,
|
key: String,
|
||||||
values: MutableSet<String>?
|
values: MutableSet<String>?,
|
||||||
): SharedPreferences.Editor {
|
): SharedPreferences.Editor {
|
||||||
if (values != null) {
|
if (values != null) {
|
||||||
actions += Action.Add(key, values)
|
actions += Action.Add(key, values)
|
||||||
@@ -105,22 +127,34 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun putInt(key: String, value: Int): SharedPreferences.Editor {
|
override fun putInt(
|
||||||
|
key: String,
|
||||||
|
value: Int,
|
||||||
|
): SharedPreferences.Editor {
|
||||||
actions += Action.Add(key, value)
|
actions += Action.Add(key, value)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun putLong(key: String, value: Long): SharedPreferences.Editor {
|
override fun putLong(
|
||||||
|
key: String,
|
||||||
|
value: Long,
|
||||||
|
): SharedPreferences.Editor {
|
||||||
actions += Action.Add(key, value)
|
actions += Action.Add(key, value)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun putFloat(key: String, value: Float): SharedPreferences.Editor {
|
override fun putFloat(
|
||||||
|
key: String,
|
||||||
|
value: Float,
|
||||||
|
): SharedPreferences.Editor {
|
||||||
actions += Action.Add(key, value)
|
actions += Action.Add(key, value)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun putBoolean(key: String, value: Boolean): SharedPreferences.Editor {
|
override fun putBoolean(
|
||||||
|
key: String,
|
||||||
|
value: Boolean,
|
||||||
|
): SharedPreferences.Editor {
|
||||||
actions += Action.Add(key, value)
|
actions += Action.Add(key, value)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
@@ -148,7 +182,8 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
|
|||||||
actions.forEach {
|
actions.forEach {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
when (it) {
|
when (it) {
|
||||||
is Action.Add -> when (val value = it.value) {
|
is Action.Add ->
|
||||||
|
when (val value = it.value) {
|
||||||
is Set<*> -> preferences.encodeValue(SetSerializer(String.serializer()), it.key, value as Set<String>)
|
is Set<*> -> preferences.encodeValue(SetSerializer(String.serializer()), it.key, value as Set<String>)
|
||||||
is String -> preferences.putString(it.key, value)
|
is String -> preferences.putString(it.key, value)
|
||||||
is Int -> preferences.putInt(it.key, value)
|
is Int -> preferences.putInt(it.key, value)
|
||||||
@@ -178,7 +213,8 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun registerOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
|
override fun registerOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
|
||||||
val javaListener = PreferenceChangeListener {
|
val javaListener =
|
||||||
|
PreferenceChangeListener {
|
||||||
listener.onSharedPreferenceChanged(this, it.key)
|
listener.onSharedPreferenceChanged(this, it.key)
|
||||||
}
|
}
|
||||||
listeners[listener] = javaListener
|
listeners[listener] = javaListener
|
||||||
|
|||||||
@@ -20,15 +20,18 @@ data class InstalledPackage(val root: File) {
|
|||||||
val icon = File(root, "icon.png")
|
val icon = File(root, "icon.png")
|
||||||
|
|
||||||
val info: PackageInfo
|
val info: PackageInfo
|
||||||
get() = ApkParsers.getMetaInfo(apk).toPackageInfo(apk).also {
|
get() =
|
||||||
|
ApkParsers.getMetaInfo(apk).toPackageInfo(apk).also {
|
||||||
val parsed = ApkFile(apk)
|
val parsed = ApkFile(apk)
|
||||||
val dbFactory = DocumentBuilderFactory.newInstance()
|
val dbFactory = DocumentBuilderFactory.newInstance()
|
||||||
val dBuilder = dbFactory.newDocumentBuilder()
|
val dBuilder = dbFactory.newDocumentBuilder()
|
||||||
val doc = parsed.manifestXml.byteInputStream().use {
|
val doc =
|
||||||
|
parsed.manifestXml.byteInputStream().use {
|
||||||
dBuilder.parse(it)
|
dBuilder.parse(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
it.applicationInfo.metaData = Bundle().apply {
|
it.applicationInfo.metaData =
|
||||||
|
Bundle().apply {
|
||||||
val appTag = doc.getElementsByTagName("application").item(0)
|
val appTag = doc.getElementsByTagName("application").item(0)
|
||||||
|
|
||||||
appTag?.childNodes?.toList()?.filter {
|
appTag?.childNodes?.toList()?.filter {
|
||||||
@@ -40,20 +43,22 @@ data class InstalledPackage(val root: File) {
|
|||||||
}?.map {
|
}?.map {
|
||||||
putString(
|
putString(
|
||||||
it.attributes.getNamedItem("android:name").nodeValue,
|
it.attributes.getNamedItem("android:name").nodeValue,
|
||||||
it.attributes.getNamedItem("android:value").nodeValue
|
it.attributes.getNamedItem("android:value").nodeValue,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
it.signatures = (
|
it.signatures =
|
||||||
|
(
|
||||||
parsed.apkSingers.flatMap { it.certificateMetas }
|
parsed.apkSingers.flatMap { it.certificateMetas }
|
||||||
/*+ parsed.apkV2Singers.flatMap { it.certificateMetas }*/
|
// + parsed.apkV2Singers.flatMap { it.certificateMetas }
|
||||||
) // Blocked by: https://github.com/hsiafan/apk-parser/issues/72
|
) // Blocked by: https://github.com/hsiafan/apk-parser/issues/72
|
||||||
.map { Signature(it.data) }.toTypedArray()
|
.map { Signature(it.data) }.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun verify(): Boolean {
|
fun verify(): Boolean {
|
||||||
val res = ApkVerifier.Builder(apk)
|
val res =
|
||||||
|
ApkVerifier.Builder(apk)
|
||||||
.build()
|
.build()
|
||||||
.verify()
|
.verify()
|
||||||
|
|
||||||
@@ -64,7 +69,8 @@ data class InstalledPackage(val root: File) {
|
|||||||
try {
|
try {
|
||||||
val icons = ApkFile(apk).allIcons
|
val icons = ApkFile(apk).allIcons
|
||||||
|
|
||||||
val read = icons.filter { it.isFile }.map {
|
val read =
|
||||||
|
icons.filter { it.isFile }.map {
|
||||||
it.data.inputStream().use {
|
it.data.inputStream().use {
|
||||||
ImageIO.read(it)
|
ImageIO.read(it)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,10 @@ class PackageController {
|
|||||||
return File(androidFiles.packagesDir, pn)
|
return File(androidFiles.packagesDir, pn)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun installPackage(apk: File, allowReinstall: Boolean) {
|
fun installPackage(
|
||||||
|
apk: File,
|
||||||
|
allowReinstall: Boolean,
|
||||||
|
) {
|
||||||
val root = findRoot(apk)
|
val root = findRoot(apk)
|
||||||
|
|
||||||
if (root.exists()) {
|
if (root.exists()) {
|
||||||
|
|||||||
@@ -12,13 +12,15 @@ fun ApkMeta.toPackageInfo(apk: File): PackageInfo {
|
|||||||
it.versionCode = versionCode.toInt()
|
it.versionCode = versionCode.toInt()
|
||||||
it.versionName = versionName
|
it.versionName = versionName
|
||||||
|
|
||||||
it.reqFeatures = usesFeatures.map {
|
it.reqFeatures =
|
||||||
|
usesFeatures.map {
|
||||||
FeatureInfo().apply {
|
FeatureInfo().apply {
|
||||||
name = it.name
|
name = it.name
|
||||||
}
|
}
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
|
|
||||||
it.applicationInfo = ApplicationInfo().apply {
|
it.applicationInfo =
|
||||||
|
ApplicationInfo().apply {
|
||||||
packageName = it.packageName
|
packageName = it.packageName
|
||||||
nonLocalizedLabel = label
|
nonLocalizedLabel = label
|
||||||
sourceDir = apk.absolutePath
|
sourceDir = apk.absolutePath
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ class ServiceSupport {
|
|||||||
|
|
||||||
private val logger = KotlinLogging.logger {}
|
private val logger = KotlinLogging.logger {}
|
||||||
|
|
||||||
fun startService(@Suppress("UNUSED_PARAMETER") context: Context, intent: Intent) {
|
fun startService(
|
||||||
|
@Suppress("UNUSED_PARAMETER") context: Context,
|
||||||
|
intent: Intent,
|
||||||
|
) {
|
||||||
val name = intentToClassName(intent)
|
val name = intentToClassName(intent)
|
||||||
|
|
||||||
logger.debug { "Starting service: $name" }
|
logger.debug { "Starting service: $name" }
|
||||||
@@ -35,7 +38,10 @@ class ServiceSupport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopService(@Suppress("UNUSED_PARAMETER") context: Context, intent: Intent) {
|
fun stopService(
|
||||||
|
@Suppress("UNUSED_PARAMETER") context: Context,
|
||||||
|
intent: Intent,
|
||||||
|
) {
|
||||||
val name = intentToClassName(intent)
|
val name = intentToClassName(intent)
|
||||||
stopService(name)
|
stopService(name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ object KodeinGlobalHelper {
|
|||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun <T : Any> instance(type: Class<T>, kodein: DI? = null): T {
|
fun <T : Any> instance(
|
||||||
|
type: Class<T>,
|
||||||
|
kodein: DI? = null,
|
||||||
|
): T {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
AndroidFiles::class.java -> {
|
AndroidFiles::class.java -> {
|
||||||
val instance: AndroidFiles by (kodein ?: kodein()).instance()
|
val instance: AndroidFiles by (kodein ?: kodein()).instance()
|
||||||
|
|||||||
@@ -24,5 +24,7 @@ import java.net.URI
|
|||||||
* Utilites to convert between Java and Android Uris.
|
* Utilites to convert between Java and Android Uris.
|
||||||
*/
|
*/
|
||||||
fun Uri.java() = URI(this.toString())
|
fun Uri.java() = URI(this.toString())
|
||||||
|
|
||||||
fun Uri.file() = File(this.path)
|
fun Uri.file() = File(this.path)
|
||||||
|
|
||||||
fun URI.android() = Uri.parse(this.toString())!!
|
fun URI.android() = Uri.parse(this.toString())!!
|
||||||
|
|||||||
@@ -22,7 +22,10 @@ class CookieManagerImpl : CookieManager() {
|
|||||||
return acceptCookie
|
return acceptCookie
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setAcceptThirdPartyCookies(webview: WebView?, accept: Boolean) {
|
override fun setAcceptThirdPartyCookies(
|
||||||
|
webview: WebView?,
|
||||||
|
accept: Boolean,
|
||||||
|
) {
|
||||||
acceptThirdPartyCookies = accept
|
acceptThirdPartyCookies = accept
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,8 +33,12 @@ class CookieManagerImpl : CookieManager() {
|
|||||||
return acceptThirdPartyCookies
|
return acceptThirdPartyCookies
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setCookie(url: String, value: String?) {
|
override fun setCookie(
|
||||||
val uri = if (url.startsWith("http")) {
|
url: String,
|
||||||
|
value: String?,
|
||||||
|
) {
|
||||||
|
val uri =
|
||||||
|
if (url.startsWith("http")) {
|
||||||
URI(url)
|
URI(url)
|
||||||
} else {
|
} else {
|
||||||
URI("http://$url")
|
URI("http://$url")
|
||||||
@@ -42,13 +49,18 @@ class CookieManagerImpl : CookieManager() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setCookie(url: String, value: String?, callback: ValueCallback<Boolean>?) {
|
override fun setCookie(
|
||||||
|
url: String,
|
||||||
|
value: String?,
|
||||||
|
callback: ValueCallback<Boolean>?,
|
||||||
|
) {
|
||||||
setCookie(url, value)
|
setCookie(url, value)
|
||||||
callback?.onReceiveValue(true)
|
callback?.onReceiveValue(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCookie(url: String): String {
|
override fun getCookie(url: String): String {
|
||||||
val uri = if (url.startsWith("http")) {
|
val uri =
|
||||||
|
if (url.startsWith("http")) {
|
||||||
URI(url)
|
URI(url)
|
||||||
} else {
|
} else {
|
||||||
URI("http://$url")
|
URI("http://$url")
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
|
||||||
import org.jmailen.gradle.kotlinter.tasks.FormatTask
|
import org.jlleitschuh.gradle.ktlint.KtlintExtension
|
||||||
import org.jmailen.gradle.kotlinter.tasks.LintTask
|
import org.jlleitschuh.gradle.ktlint.KtlintPlugin
|
||||||
|
|
||||||
@Suppress("DSL_SCOPE_VIOLATION")
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.kotlin.jvm)
|
alias(libs.plugins.kotlin.jvm)
|
||||||
alias(libs.plugins.kotlin.serialization)
|
alias(libs.plugins.kotlin.serialization)
|
||||||
alias(libs.plugins.kotlinter)
|
alias(libs.plugins.ktlint)
|
||||||
alias(libs.plugins.buildconfig) apply false
|
alias(libs.plugins.buildconfig) apply false
|
||||||
alias(libs.plugins.download)
|
alias(libs.plugins.download)
|
||||||
}
|
}
|
||||||
@@ -32,24 +31,25 @@ subprojects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugins.withType<KtlintPlugin> {
|
||||||
|
extensions.configure<KtlintExtension>("ktlint") {
|
||||||
|
version.set(libs.versions.ktlint.get())
|
||||||
|
filter {
|
||||||
|
exclude("**/generated/**")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
withType<KotlinJvmCompile> {
|
withType<KotlinJvmCompile> {
|
||||||
dependsOn("formatKotlin")
|
dependsOn("ktlintFormat")
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||||
|
|
||||||
freeCompilerArgs += listOf(
|
freeCompilerArgs += listOf(
|
||||||
"-Xcontext-receivers"
|
"-Xcontext-receivers",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
withType<LintTask> {
|
|
||||||
source(files("src/kotlin"))
|
|
||||||
}
|
|
||||||
|
|
||||||
withType<FormatTask> {
|
|
||||||
source(files("src/kotlin"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,5 +7,5 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("net.lingala.zip4j:zip4j:2.9.0")
|
implementation(libs.zip4j)
|
||||||
}
|
}
|
||||||
9
buildSrc/settings.gradle.kts
Normal file
9
buildSrc/settings.gradle.kts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
rootProject.name = "buildSrc"
|
||||||
|
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
versionCatalogs {
|
||||||
|
create("libs") {
|
||||||
|
from(files("../gradle/libs.versions.toml"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ twelvemonkeys = "3.9.4"
|
|||||||
playwright = "1.28.0"
|
playwright = "1.28.0"
|
||||||
graphqlkotlin = "6.5.6"
|
graphqlkotlin = "6.5.6"
|
||||||
xmlserialization = "0.86.2"
|
xmlserialization = "0.86.2"
|
||||||
|
ktlint = "1.0.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
# Kotlin
|
# Kotlin
|
||||||
@@ -150,7 +151,7 @@ kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin"}
|
|||||||
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin"}
|
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin"}
|
||||||
|
|
||||||
# Linter
|
# Linter
|
||||||
kotlinter = { id = "org.jmailen.kotlinter", version = "3.12.0"}
|
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "11.6.0"}
|
||||||
|
|
||||||
# Build config
|
# Build config
|
||||||
buildconfig = { id = "com.github.gmazzo.buildconfig", version = "3.1.0"}
|
buildconfig = { id = "com.github.gmazzo.buildconfig", version = "3.1.0"}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import de.undercouch.gradle.tasks.download.Download
|
import de.undercouch.gradle.tasks.download.Download
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
@Suppress("DSL_SCOPE_VIOLATION")
|
|
||||||
plugins {
|
plugins {
|
||||||
id(libs.plugins.kotlin.jvm.get().pluginId)
|
id(libs.plugins.kotlin.jvm.get().pluginId)
|
||||||
id(libs.plugins.kotlin.serialization.get().pluginId)
|
id(libs.plugins.kotlin.serialization.get().pluginId)
|
||||||
id(libs.plugins.kotlinter.get().pluginId)
|
id(libs.plugins.ktlint.get().pluginId)
|
||||||
application
|
application
|
||||||
alias(libs.plugins.shadowjar)
|
alias(libs.plugins.shadowjar)
|
||||||
id(libs.plugins.buildconfig.get().pluginId)
|
id(libs.plugins.buildconfig.get().pluginId)
|
||||||
@@ -82,8 +81,9 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
applicationDefaultJvmArgs = listOf(
|
applicationDefaultJvmArgs =
|
||||||
"-Djunrar.extractor.thread-keep-alive-seconds=30"
|
listOf(
|
||||||
|
"-Djunrar.extractor.thread-keep-alive-seconds=30",
|
||||||
)
|
)
|
||||||
mainClass.set(MainClass)
|
mainClass.set(MainClass)
|
||||||
}
|
}
|
||||||
@@ -98,7 +98,7 @@ sourceSets {
|
|||||||
|
|
||||||
buildConfig {
|
buildConfig {
|
||||||
className("BuildConfig")
|
className("BuildConfig")
|
||||||
packageName("suwayomi.tachidesk.server")
|
packageName("suwayomi.tachidesk.server.generated")
|
||||||
|
|
||||||
useKotlinOutput()
|
useKotlinOutput()
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ tasks {
|
|||||||
"Implementation-Title" to rootProject.name,
|
"Implementation-Title" to rootProject.name,
|
||||||
"Implementation-Vendor" to "The Suwayomi Project",
|
"Implementation-Vendor" to "The Suwayomi Project",
|
||||||
"Specification-Version" to tachideskVersion,
|
"Specification-Version" to tachideskVersion,
|
||||||
"Implementation-Version" to tachideskRevision
|
"Implementation-Version" to tachideskRevision,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
archiveBaseName.set(rootProject.name)
|
archiveBaseName.set(rootProject.name)
|
||||||
@@ -151,14 +151,14 @@ tasks {
|
|||||||
src("https://github.com/Suwayomi/Tachidesk-WebUI-preview/releases/download/$webUIRevisionTag/Tachidesk-WebUI-$webUIRevisionTag.zip")
|
src("https://github.com/Suwayomi/Tachidesk-WebUI-preview/releases/download/$webUIRevisionTag/Tachidesk-WebUI-$webUIRevisionTag.zip")
|
||||||
dest("src/main/resources/WebUI.zip")
|
dest("src/main/resources/WebUI.zip")
|
||||||
|
|
||||||
|
|
||||||
fun shouldOverwrite(): Boolean {
|
fun shouldOverwrite(): Boolean {
|
||||||
val zipPath = project.projectDir.absolutePath + "/src/main/resources/WebUI.zip"
|
val zipPath = project.projectDir.absolutePath + "/src/main/resources/WebUI.zip"
|
||||||
val zipFile = net.lingala.zip4j.ZipFile(zipPath)
|
val zipFile = net.lingala.zip4j.ZipFile(zipPath)
|
||||||
|
|
||||||
var shouldOverwrite = true
|
var shouldOverwrite = true
|
||||||
if (zipFile.isValidZipFile) {
|
if (zipFile.isValidZipFile) {
|
||||||
val zipRevision = zipFile.getInputStream(zipFile.getFileHeader("revision")).bufferedReader().use {
|
val zipRevision =
|
||||||
|
zipFile.getInputStream(zipFile.getFileHeader("revision")).bufferedReader().use {
|
||||||
it.readText().trim()
|
it.readText().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,10 +177,11 @@ tasks {
|
|||||||
group = "application"
|
group = "application"
|
||||||
finalizedBy(run)
|
finalizedBy(run)
|
||||||
doFirst {
|
doFirst {
|
||||||
application.applicationDefaultJvmArgs = listOf(
|
application.applicationDefaultJvmArgs =
|
||||||
|
listOf(
|
||||||
"-Dsuwayomi.tachidesk.config.server.webUIInterface=electron",
|
"-Dsuwayomi.tachidesk.config.server.webUIInterface=electron",
|
||||||
// Change this to the installed electron application
|
// Change this to the installed electron application
|
||||||
"-Dsuwayomi.tachidesk.config.server.electronPath=/usr/bin/electron"
|
"-Dsuwayomi.tachidesk.config.server.electronPath=/usr/bin/electron",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,15 +9,11 @@ package eu.kanade.tachiyomi
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
// import android.content.res.Configuration
|
|
||||||
// import android.support.multidex.MultiDex
|
|
||||||
// import timber.log.Timber
|
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.InjektScope
|
import uy.kohesive.injekt.api.InjektScope
|
||||||
import uy.kohesive.injekt.registry.default.DefaultRegistrar
|
import uy.kohesive.injekt.registry.default.DefaultRegistrar
|
||||||
|
|
||||||
open class App : Application() {
|
open class App : Application() {
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
Injekt = InjektScope(DefaultRegistrar())
|
Injekt = InjektScope(DefaultRegistrar())
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package eu.kanade.tachiyomi
|
package eu.kanade.tachiyomi
|
||||||
|
|
||||||
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil
|
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil
|
||||||
|
import suwayomi.tachidesk.server.generated.BuildConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by extensions.
|
* Used by extensions.
|
||||||
@@ -14,14 +15,14 @@ object AppInfo {
|
|||||||
*
|
*
|
||||||
* @since extension-lib 1.3
|
* @since extension-lib 1.3
|
||||||
*/
|
*/
|
||||||
fun getVersionCode() = suwayomi.tachidesk.server.BuildConfig.REVISION.substring(1).toInt()
|
fun getVersionCode() = BuildConfig.REVISION.substring(1).toInt()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* should be something like "0.13.1"
|
* should be something like "0.13.1"
|
||||||
*
|
*
|
||||||
* @since extension-lib 1.3
|
* @since extension-lib 1.3
|
||||||
*/
|
*/
|
||||||
fun getVersionName() = suwayomi.tachidesk.server.BuildConfig.VERSION.substring(1)
|
fun getVersionName() = BuildConfig.VERSION.substring(1)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of supported image MIME types by the reader.
|
* A list of supported image MIME types by the reader.
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import uy.kohesive.injekt.api.addSingletonFactory
|
|||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
class AppModule(val app: Application) : InjektModule {
|
class AppModule(val app: Application) : InjektModule {
|
||||||
|
|
||||||
override fun InjektRegistrar.registerInjectables() {
|
override fun InjektRegistrar.registerInjectables() {
|
||||||
addSingleton(app)
|
addSingleton(app)
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import kotlinx.coroutines.withContext
|
|||||||
* Util for evaluating JavaScript in sources.
|
* Util for evaluating JavaScript in sources.
|
||||||
*/
|
*/
|
||||||
class JavaScriptEngine(context: Context) {
|
class JavaScriptEngine(context: Context) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluate arbitrary JavaScript code and get the result as a primitive type
|
* Evaluate arbitrary JavaScript code and get the result as a primitive type
|
||||||
* (e.g., String, Int).
|
* (e.g., String, Int).
|
||||||
@@ -19,7 +18,8 @@ class JavaScriptEngine(context: Context) {
|
|||||||
* @return Result of JavaScript code as a primitive type.
|
* @return Result of JavaScript code as a primitive type.
|
||||||
*/
|
*/
|
||||||
@Suppress("UNUSED", "UNCHECKED_CAST")
|
@Suppress("UNUSED", "UNCHECKED_CAST")
|
||||||
suspend fun <T> evaluate(script: String): T = withContext(Dispatchers.IO) {
|
suspend fun <T> evaluate(script: String): T =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
QuickJs.create().use {
|
QuickJs.create().use {
|
||||||
it.evaluate(script) as T
|
it.evaluate(script) as T
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ class MemoryCookieJar : CookieJar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
override fun saveFromResponse(
|
||||||
|
url: HttpUrl,
|
||||||
|
cookies: List<Cookie>,
|
||||||
|
) {
|
||||||
val cookiesToAdd = cookies.map { WrappedCookie.wrap(it) }
|
val cookiesToAdd = cookies.map { WrappedCookie.wrap(it) }
|
||||||
|
|
||||||
cache.removeAll(cookiesToAdd)
|
cache.removeAll(cookiesToAdd)
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ import java.util.concurrent.TimeUnit
|
|||||||
|
|
||||||
@Suppress("UNUSED_PARAMETER")
|
@Suppress("UNUSED_PARAMETER")
|
||||||
class NetworkHelper(context: Context) {
|
class NetworkHelper(context: Context) {
|
||||||
|
// private val preferences: PreferencesHelper by injectLazy()
|
||||||
// private val preferences: PreferencesHelper by injectLazy()
|
|
||||||
|
|
||||||
// private val cacheDir = File(context.cacheDir, "network_cache")
|
// private val cacheDir = File(context.cacheDir, "network_cache")
|
||||||
|
|
||||||
@@ -36,29 +35,34 @@ class NetworkHelper(context: Context) {
|
|||||||
|
|
||||||
// Tachidesk -->
|
// Tachidesk -->
|
||||||
val cookieStore = PersistentCookieStore(context)
|
val cookieStore = PersistentCookieStore(context)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
CookieHandler.setDefault(
|
CookieHandler.setDefault(
|
||||||
CookieManager(cookieStore, CookiePolicy.ACCEPT_ALL)
|
CookieManager(cookieStore, CookiePolicy.ACCEPT_ALL),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Tachidesk <--
|
// Tachidesk <--
|
||||||
|
|
||||||
private val baseClientBuilder: OkHttpClient.Builder
|
private val baseClientBuilder: OkHttpClient.Builder
|
||||||
get() {
|
get() {
|
||||||
val builder = OkHttpClient.Builder()
|
val builder =
|
||||||
|
OkHttpClient.Builder()
|
||||||
.cookieJar(PersistentCookieJar(cookieStore))
|
.cookieJar(PersistentCookieJar(cookieStore))
|
||||||
.connectTimeout(30, TimeUnit.SECONDS)
|
.connectTimeout(30, TimeUnit.SECONDS)
|
||||||
.readTimeout(30, TimeUnit.SECONDS)
|
.readTimeout(30, TimeUnit.SECONDS)
|
||||||
.callTimeout(2, TimeUnit.MINUTES)
|
.callTimeout(2, TimeUnit.MINUTES)
|
||||||
.addInterceptor(UserAgentInterceptor())
|
.addInterceptor(UserAgentInterceptor())
|
||||||
|
|
||||||
val httpLoggingInterceptor = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
|
val httpLoggingInterceptor =
|
||||||
|
HttpLoggingInterceptor(
|
||||||
|
object : HttpLoggingInterceptor.Logger {
|
||||||
val logger = KotlinLogging.logger { }
|
val logger = KotlinLogging.logger { }
|
||||||
|
|
||||||
override fun log(message: String) {
|
override fun log(message: String) {
|
||||||
logger.debug { message }
|
logger.debug { message }
|
||||||
}
|
}
|
||||||
}).apply {
|
},
|
||||||
|
).apply {
|
||||||
level = HttpLoggingInterceptor.Level.BASIC
|
level = HttpLoggingInterceptor.Level.BASIC
|
||||||
}
|
}
|
||||||
builder.addInterceptor(httpLoggingInterceptor)
|
builder.addInterceptor(httpLoggingInterceptor)
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ fun Call.asObservable(): Observable<Response> {
|
|||||||
val call = clone()
|
val call = clone()
|
||||||
|
|
||||||
// Wrap the call in a helper which handles both unsubscription and backpressure.
|
// Wrap the call in a helper which handles both unsubscription and backpressure.
|
||||||
val requestArbiter = object : AtomicBoolean(), Producer, Subscription {
|
val requestArbiter =
|
||||||
|
object : AtomicBoolean(), Producer, Subscription {
|
||||||
override fun request(n: Long) {
|
override fun request(n: Long) {
|
||||||
if (n == 0L || !compareAndSet(false, true)) return
|
if (n == 0L || !compareAndSet(false, true)) return
|
||||||
|
|
||||||
@@ -72,13 +73,19 @@ private suspend fun Call.await(callStack: Array<StackTraceElement>): Response {
|
|||||||
return suspendCancellableCoroutine { continuation ->
|
return suspendCancellableCoroutine { continuation ->
|
||||||
val callback =
|
val callback =
|
||||||
object : Callback {
|
object : Callback {
|
||||||
override fun onResponse(call: Call, response: Response) {
|
override fun onResponse(
|
||||||
|
call: Call,
|
||||||
|
response: Response,
|
||||||
|
) {
|
||||||
continuation.resume(response) {
|
continuation.resume(response) {
|
||||||
response.body.close()
|
response.body.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(call: Call, e: IOException) {
|
override fun onFailure(
|
||||||
|
call: Call,
|
||||||
|
e: IOException,
|
||||||
|
) {
|
||||||
// Don't bother with resuming the continuation if it is already cancelled.
|
// Don't bother with resuming the continuation if it is already cancelled.
|
||||||
if (continuation.isCancelled) return
|
if (continuation.isCancelled) return
|
||||||
val exception = IOException(e.message, e).apply { stackTrace = callStack }
|
val exception = IOException(e.message, e).apply { stackTrace = callStack }
|
||||||
@@ -116,8 +123,12 @@ suspend fun Call.awaitSuccess(): Response {
|
|||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
fun OkHttpClient.newCachelessCallWithProgress(request: Request, listener: ProgressListener): Call {
|
fun OkHttpClient.newCachelessCallWithProgress(
|
||||||
val progressClient = newBuilder()
|
request: Request,
|
||||||
|
listener: ProgressListener,
|
||||||
|
): Call {
|
||||||
|
val progressClient =
|
||||||
|
newBuilder()
|
||||||
.cache(null)
|
.cache(null)
|
||||||
.addNetworkInterceptor { chain ->
|
.addNetworkInterceptor { chain ->
|
||||||
val originalResponse = chain.proceed(chain.request())
|
val originalResponse = chain.proceed(chain.request())
|
||||||
@@ -139,7 +150,7 @@ context(Json)
|
|||||||
@OptIn(ExperimentalSerializationApi::class)
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
fun <T> decodeFromJsonResponse(
|
fun <T> decodeFromJsonResponse(
|
||||||
deserializer: DeserializationStrategy<T>,
|
deserializer: DeserializationStrategy<T>,
|
||||||
response: Response
|
response: Response,
|
||||||
): T {
|
): T {
|
||||||
return response.body.source().use {
|
return response.body.source().use {
|
||||||
decodeFromBufferedSource(deserializer, it)
|
decodeFromBufferedSource(deserializer, it)
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import okhttp3.HttpUrl
|
|||||||
|
|
||||||
// from TachiWeb-Server
|
// from TachiWeb-Server
|
||||||
class PersistentCookieJar(private val store: PersistentCookieStore) : CookieJar {
|
class PersistentCookieJar(private val store: PersistentCookieStore) : CookieJar {
|
||||||
|
override fun saveFromResponse(
|
||||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
url: HttpUrl,
|
||||||
|
cookies: List<Cookie>,
|
||||||
|
) {
|
||||||
store.addAll(url, cookies)
|
store.addAll(url, cookies)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
|
|
||||||
// from TachiWeb-Server
|
// from TachiWeb-Server
|
||||||
class PersistentCookieStore(context: Context) : CookieStore {
|
class PersistentCookieStore(context: Context) : CookieStore {
|
||||||
|
|
||||||
private val cookieMap = ConcurrentHashMap<String, List<Cookie>>()
|
private val cookieMap = ConcurrentHashMap<String, List<Cookie>>()
|
||||||
private val prefs = context.getSharedPreferences("cookie_store", Context.MODE_PRIVATE)
|
private val prefs = context.getSharedPreferences("cookie_store", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
@@ -28,7 +27,8 @@ class PersistentCookieStore(context: Context) : CookieStore {
|
|||||||
if (cookies != null) {
|
if (cookies != null) {
|
||||||
try {
|
try {
|
||||||
val url = "http://$key".toHttpUrlOrNull() ?: continue
|
val url = "http://$key".toHttpUrlOrNull() ?: continue
|
||||||
val nonExpiredCookies = cookies.mapNotNull { Cookie.parse(url, it) }
|
val nonExpiredCookies =
|
||||||
|
cookies.mapNotNull { Cookie.parse(url, it) }
|
||||||
.filter { !it.hasExpired() }
|
.filter { !it.hasExpired() }
|
||||||
cookieMap.put(key, nonExpiredCookies)
|
cookieMap.put(key, nonExpiredCookies)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -38,7 +38,10 @@ class PersistentCookieStore(context: Context) : CookieStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addAll(url: HttpUrl, cookies: List<Cookie>) {
|
fun addAll(
|
||||||
|
url: HttpUrl,
|
||||||
|
cookies: List<Cookie>,
|
||||||
|
) {
|
||||||
lock.withLock {
|
lock.withLock {
|
||||||
val uri = url.toUri()
|
val uri = url.toUri()
|
||||||
|
|
||||||
@@ -75,13 +78,17 @@ class PersistentCookieStore(context: Context) : CookieStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun get(uri: URI): List<HttpCookie> = get(uri.host).map {
|
override fun get(uri: URI): List<HttpCookie> =
|
||||||
|
get(uri.host).map {
|
||||||
it.toHttpCookie()
|
it.toHttpCookie()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun get(url: HttpUrl) = get(url.toUri().host)
|
fun get(url: HttpUrl) = get(url.toUri().host)
|
||||||
|
|
||||||
override fun add(uri: URI?, cookie: HttpCookie) {
|
override fun add(
|
||||||
|
uri: URI?,
|
||||||
|
cookie: HttpCookie,
|
||||||
|
) {
|
||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
val uri = uri ?: URI("http://" + cookie.domain.removePrefix("."))
|
val uri = uri ?: URI("http://" + cookie.domain.removePrefix("."))
|
||||||
lock.withLock {
|
lock.withLock {
|
||||||
@@ -105,12 +112,16 @@ class PersistentCookieStore(context: Context) : CookieStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun remove(uri: URI?, cookie: HttpCookie): Boolean {
|
override fun remove(
|
||||||
|
uri: URI?,
|
||||||
|
cookie: HttpCookie,
|
||||||
|
): Boolean {
|
||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
val uri = uri ?: URI("http://" + cookie.domain.removePrefix("."))
|
val uri = uri ?: URI("http://" + cookie.domain.removePrefix("."))
|
||||||
return lock.withLock {
|
return lock.withLock {
|
||||||
val cookies = cookieMap[uri.host].orEmpty()
|
val cookies = cookieMap[uri.host].orEmpty()
|
||||||
val index = cookies.indexOfFirst {
|
val index =
|
||||||
|
cookies.indexOfFirst {
|
||||||
it.name == cookie.name &&
|
it.name == cookie.name &&
|
||||||
it.path == cookie.path
|
it.path == cookie.path
|
||||||
}
|
}
|
||||||
@@ -132,7 +143,8 @@ class PersistentCookieStore(context: Context) : CookieStore {
|
|||||||
|
|
||||||
private fun saveToDisk(uri: URI) {
|
private fun saveToDisk(uri: URI) {
|
||||||
// Get cookies to be stored in disk
|
// Get cookies to be stored in disk
|
||||||
val newValues = cookieMap[uri.host]
|
val newValues =
|
||||||
|
cookieMap[uri.host]
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.filter { it.persistent && !it.hasExpired() }
|
.filter { it.persistent && !it.hasExpired() }
|
||||||
@@ -144,7 +156,8 @@ class PersistentCookieStore(context: Context) : CookieStore {
|
|||||||
|
|
||||||
private fun Cookie.hasExpired() = System.currentTimeMillis() >= expiresAt
|
private fun Cookie.hasExpired() = System.currentTimeMillis() >= expiresAt
|
||||||
|
|
||||||
private fun HttpCookie.toCookie(uri: URI) = Cookie.Builder()
|
private fun HttpCookie.toCookie(uri: URI) =
|
||||||
|
Cookie.Builder()
|
||||||
.name(name)
|
.name(name)
|
||||||
.value(value)
|
.value(value)
|
||||||
.domain(uri.host)
|
.domain(uri.host)
|
||||||
@@ -178,7 +191,8 @@ class PersistentCookieStore(context: Context) : CookieStore {
|
|||||||
domain = it.domain
|
domain = it.domain
|
||||||
path = it.path
|
path = it.path
|
||||||
secure = it.secure
|
secure = it.secure
|
||||||
maxAge = if (it.persistent) {
|
maxAge =
|
||||||
|
if (it.persistent) {
|
||||||
-1
|
-1
|
||||||
} else {
|
} else {
|
||||||
(it.expiresAt.milliseconds - System.currentTimeMillis().milliseconds).inWholeSeconds
|
(it.expiresAt.milliseconds - System.currentTimeMillis().milliseconds).inWholeSeconds
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package eu.kanade.tachiyomi.network
|
package eu.kanade.tachiyomi.network
|
||||||
|
|
||||||
interface ProgressListener {
|
interface ProgressListener {
|
||||||
fun update(bytesRead: Long, contentLength: Long, done: Boolean)
|
fun update(
|
||||||
|
bytesRead: Long,
|
||||||
|
contentLength: Long,
|
||||||
|
done: Boolean,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import okio.buffer
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class ProgressResponseBody(private val responseBody: ResponseBody, private val progressListener: ProgressListener) : ResponseBody() {
|
class ProgressResponseBody(private val responseBody: ResponseBody, private val progressListener: ProgressListener) : ResponseBody() {
|
||||||
|
|
||||||
private val bufferedSource: BufferedSource by lazy {
|
private val bufferedSource: BufferedSource by lazy {
|
||||||
source(responseBody.source()).buffer()
|
source(responseBody.source()).buffer()
|
||||||
}
|
}
|
||||||
@@ -32,7 +31,10 @@ class ProgressResponseBody(private val responseBody: ResponseBody, private val p
|
|||||||
var totalBytesRead = 0L
|
var totalBytesRead = 0L
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override fun read(sink: Buffer, byteCount: Long): Long {
|
override fun read(
|
||||||
|
sink: Buffer,
|
||||||
|
byteCount: Long,
|
||||||
|
): Long {
|
||||||
val bytesRead = super.read(sink, byteCount)
|
val bytesRead = super.read(sink, byteCount)
|
||||||
// read() returns the number of bytes read, or -1 if this source is exhausted.
|
// read() returns the number of bytes read, or -1 if this source is exhausted.
|
||||||
totalBytesRead += if (bytesRead != -1L) bytesRead else 0
|
totalBytesRead += if (bytesRead != -1L) bytesRead else 0
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@file:Suppress("ktlint:standard:function-naming")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.network
|
package eu.kanade.tachiyomi.network
|
||||||
|
|
||||||
import okhttp3.CacheControl
|
import okhttp3.CacheControl
|
||||||
@@ -15,7 +17,7 @@ private val DEFAULT_BODY: RequestBody = FormBody.Builder().build()
|
|||||||
fun GET(
|
fun GET(
|
||||||
url: String,
|
url: String,
|
||||||
headers: Headers = DEFAULT_HEADERS,
|
headers: Headers = DEFAULT_HEADERS,
|
||||||
cache: CacheControl = DEFAULT_CACHE_CONTROL
|
cache: CacheControl = DEFAULT_CACHE_CONTROL,
|
||||||
): Request {
|
): Request {
|
||||||
return Request.Builder()
|
return Request.Builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
@@ -30,7 +32,7 @@ fun GET(
|
|||||||
fun GET(
|
fun GET(
|
||||||
url: HttpUrl,
|
url: HttpUrl,
|
||||||
headers: Headers = DEFAULT_HEADERS,
|
headers: Headers = DEFAULT_HEADERS,
|
||||||
cache: CacheControl = DEFAULT_CACHE_CONTROL
|
cache: CacheControl = DEFAULT_CACHE_CONTROL,
|
||||||
): Request {
|
): Request {
|
||||||
return Request.Builder()
|
return Request.Builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
@@ -43,7 +45,7 @@ fun POST(
|
|||||||
url: String,
|
url: String,
|
||||||
headers: Headers = DEFAULT_HEADERS,
|
headers: Headers = DEFAULT_HEADERS,
|
||||||
body: RequestBody = DEFAULT_BODY,
|
body: RequestBody = DEFAULT_BODY,
|
||||||
cache: CacheControl = DEFAULT_CACHE_CONTROL
|
cache: CacheControl = DEFAULT_CACHE_CONTROL,
|
||||||
): Request {
|
): Request {
|
||||||
return Request.Builder()
|
return Request.Builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
|
|||||||
@@ -81,7 +81,8 @@ object CFClearance {
|
|||||||
|
|
||||||
logger.debug { "resolveWithWebView($url)" }
|
logger.debug { "resolveWithWebView($url)" }
|
||||||
|
|
||||||
val cookies = Playwright.create().use { playwright ->
|
val cookies =
|
||||||
|
Playwright.create().use { playwright ->
|
||||||
playwright.chromium().launch(
|
playwright.chromium().launch(
|
||||||
LaunchOptions()
|
LaunchOptions()
|
||||||
.setHeadless(false)
|
.setHeadless(false)
|
||||||
@@ -89,7 +90,7 @@ object CFClearance {
|
|||||||
if (serverConfig.socksProxyEnabled.value) {
|
if (serverConfig.socksProxyEnabled.value) {
|
||||||
setProxy("socks5://${serverConfig.socksProxyHost.value}:${serverConfig.socksProxyPort.value}")
|
setProxy("socks5://${serverConfig.socksProxyHost.value}:${serverConfig.socksProxyPort.value}")
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
).use { browser ->
|
).use { browser ->
|
||||||
val userAgent = originalRequest.header("User-Agent")
|
val userAgent = originalRequest.header("User-Agent")
|
||||||
if (userAgent != null) {
|
if (userAgent != null) {
|
||||||
@@ -105,25 +106,29 @@ object CFClearance {
|
|||||||
// Copy cookies to cookie store
|
// Copy cookies to cookie store
|
||||||
cookies.groupBy { it.domain }.forEach { (domain, cookies) ->
|
cookies.groupBy { it.domain }.forEach { (domain, cookies) ->
|
||||||
network.cookieStore.addAll(
|
network.cookieStore.addAll(
|
||||||
url = HttpUrl.Builder()
|
url =
|
||||||
|
HttpUrl.Builder()
|
||||||
.scheme("http")
|
.scheme("http")
|
||||||
.host(domain)
|
.host(domain)
|
||||||
.build(),
|
.build(),
|
||||||
cookies = cookies
|
cookies = cookies,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Merge new and existing cookies for this request
|
// Merge new and existing cookies for this request
|
||||||
// Find the cookies that we need to merge into this request
|
// Find the cookies that we need to merge into this request
|
||||||
val convertedForThisRequest = cookies.filter {
|
val convertedForThisRequest =
|
||||||
|
cookies.filter {
|
||||||
it.matches(originalRequest.url)
|
it.matches(originalRequest.url)
|
||||||
}
|
}
|
||||||
// Extract cookies from current request
|
// Extract cookies from current request
|
||||||
val existingCookies = Cookie.parseAll(
|
val existingCookies =
|
||||||
|
Cookie.parseAll(
|
||||||
originalRequest.url,
|
originalRequest.url,
|
||||||
originalRequest.headers
|
originalRequest.headers,
|
||||||
)
|
)
|
||||||
// Filter out existing values of cookies that we are about to merge in
|
// Filter out existing values of cookies that we are about to merge in
|
||||||
val filteredExisting = existingCookies.filter { existing ->
|
val filteredExisting =
|
||||||
|
existingCookies.filter { existing ->
|
||||||
convertedForThisRequest.none { converted -> converted.name == existing.name }
|
convertedForThisRequest.none { converted -> converted.name == existing.name }
|
||||||
}
|
}
|
||||||
logger.trace { "Existing cookies" }
|
logger.trace { "Existing cookies" }
|
||||||
@@ -143,7 +148,7 @@ object CFClearance {
|
|||||||
Playwright.create().use { playwright ->
|
Playwright.create().use { playwright ->
|
||||||
playwright.chromium().launch(
|
playwright.chromium().launch(
|
||||||
LaunchOptions()
|
LaunchOptions()
|
||||||
.setHeadless(true)
|
.setHeadless(true),
|
||||||
).use { browser ->
|
).use { browser ->
|
||||||
browser.newPage().use { page ->
|
browser.newPage().use { page ->
|
||||||
val userAgent = page.evaluate("() => {return navigator.userAgent}") as String
|
val userAgent = page.evaluate("() => {return navigator.userAgent}") as String
|
||||||
@@ -158,7 +163,10 @@ object CFClearance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCookies(page: Page, url: String): List<Cookie> {
|
private fun getCookies(
|
||||||
|
page: Page,
|
||||||
|
url: String,
|
||||||
|
): List<Cookie> {
|
||||||
applyStealthInitScripts(page)
|
applyStealthInitScripts(page)
|
||||||
page.navigate(url)
|
page.navigate(url)
|
||||||
val challengeResolved = waitForChallengeResolve(page)
|
val challengeResolved = waitForChallengeResolve(page)
|
||||||
@@ -198,7 +206,7 @@ object CFClearance {
|
|||||||
ServerConfig::class.java.getResource("/cloudflare-js/navigator.permissions.js")!!.readText(),
|
ServerConfig::class.java.getResource("/cloudflare-js/navigator.permissions.js")!!.readText(),
|
||||||
ServerConfig::class.java.getResource("/cloudflare-js/navigator.webdriver.js")!!.readText(),
|
ServerConfig::class.java.getResource("/cloudflare-js/navigator.webdriver.js")!!.readText(),
|
||||||
ServerConfig::class.java.getResource("/cloudflare-js/chrome.runtime.js")!!.readText(),
|
ServerConfig::class.java.getResource("/cloudflare-js/chrome.runtime.js")!!.readText(),
|
||||||
ServerConfig::class.java.getResource("/cloudflare-js/chrome.plugin.js")!!.readText()
|
ServerConfig::class.java.getResource("/cloudflare-js/chrome.plugin.js")!!.readText(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +223,8 @@ object CFClearance {
|
|||||||
val timeoutSeconds = 120
|
val timeoutSeconds = 120
|
||||||
repeat(timeoutSeconds) {
|
repeat(timeoutSeconds) {
|
||||||
page.waitForTimeout(1.seconds.toDouble(DurationUnit.MILLISECONDS))
|
page.waitForTimeout(1.seconds.toDouble(DurationUnit.MILLISECONDS))
|
||||||
val success = try {
|
val success =
|
||||||
|
try {
|
||||||
page.querySelector("#challenge-form") == null
|
page.querySelector("#challenge-form") == null
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.debug(e) { "query Error" }
|
logger.debug(e) { "query Error" }
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import kotlin.time.toDurationUnit
|
|||||||
fun OkHttpClient.Builder.rateLimit(
|
fun OkHttpClient.Builder.rateLimit(
|
||||||
permits: Int,
|
permits: Int,
|
||||||
period: Long = 1,
|
period: Long = 1,
|
||||||
unit: TimeUnit = TimeUnit.SECONDS
|
unit: TimeUnit = TimeUnit.SECONDS,
|
||||||
) = addInterceptor(RateLimitInterceptor(null, permits, period.toDuration(unit.toDurationUnit())))
|
) = addInterceptor(RateLimitInterceptor(null, permits, period.toDuration(unit.toDurationUnit())))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,17 +50,18 @@ fun OkHttpClient.Builder.rateLimit(
|
|||||||
* @param permits [Int] Number of requests allowed within a period of units.
|
* @param permits [Int] Number of requests allowed within a period of units.
|
||||||
* @param period [Duration] The limiting duration. Defaults to 1.seconds.
|
* @param period [Duration] The limiting duration. Defaults to 1.seconds.
|
||||||
*/
|
*/
|
||||||
fun OkHttpClient.Builder.rateLimit(permits: Int, period: Duration = 1.seconds) =
|
fun OkHttpClient.Builder.rateLimit(
|
||||||
addInterceptor(RateLimitInterceptor(null, permits, period))
|
permits: Int,
|
||||||
|
period: Duration = 1.seconds,
|
||||||
|
) = addInterceptor(RateLimitInterceptor(null, permits, period))
|
||||||
|
|
||||||
/** We can probably accept domains or wildcards by comparing with [endsWith], etc. */
|
/** We can probably accept domains or wildcards by comparing with [endsWith], etc. */
|
||||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||||
internal class RateLimitInterceptor(
|
internal class RateLimitInterceptor(
|
||||||
private val host: String?,
|
private val host: String?,
|
||||||
private val permits: Int,
|
private val permits: Int,
|
||||||
period: Duration
|
period: Duration,
|
||||||
) : Interceptor {
|
) : Interceptor {
|
||||||
|
|
||||||
private val requestQueue = ArrayDeque<Long>(permits)
|
private val requestQueue = ArrayDeque<Long>(permits)
|
||||||
private val rateLimitMillis = period.inWholeMilliseconds
|
private val rateLimitMillis = period.inWholeMilliseconds
|
||||||
private val fairLock = Semaphore(1, true)
|
private val fairLock = Semaphore(1, true)
|
||||||
@@ -98,7 +99,8 @@ internal class RateLimitInterceptor(
|
|||||||
} else if (hasRemovedExpired) {
|
} else if (hasRemovedExpired) {
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
try { // wait for the first entry to expire, or notified by cached response
|
try {
|
||||||
|
// wait for the first entry to expire, or notified by cached response
|
||||||
(requestQueue as Object).wait(requestQueue.first - periodStart)
|
(requestQueue as Object).wait(requestQueue.first - periodStart)
|
||||||
} catch (_: InterruptedException) {
|
} catch (_: InterruptedException) {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ fun OkHttpClient.Builder.rateLimitHost(
|
|||||||
httpUrl: HttpUrl,
|
httpUrl: HttpUrl,
|
||||||
permits: Int,
|
permits: Int,
|
||||||
period: Long = 1,
|
period: Long = 1,
|
||||||
unit: TimeUnit = TimeUnit.SECONDS
|
unit: TimeUnit = TimeUnit.SECONDS,
|
||||||
) = addInterceptor(RateLimitInterceptor(httpUrl.host, permits, period.toDuration(unit.toDurationUnit())))
|
) = addInterceptor(RateLimitInterceptor(httpUrl.host, permits, period.toDuration(unit.toDurationUnit())))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,7 +49,7 @@ fun OkHttpClient.Builder.rateLimitHost(
|
|||||||
fun OkHttpClient.Builder.rateLimitHost(
|
fun OkHttpClient.Builder.rateLimitHost(
|
||||||
httpUrl: HttpUrl,
|
httpUrl: HttpUrl,
|
||||||
permits: Int,
|
permits: Int,
|
||||||
period: Duration = 1.seconds
|
period: Duration = 1.seconds,
|
||||||
): OkHttpClient.Builder = addInterceptor(RateLimitInterceptor(httpUrl.host, permits, period))
|
): OkHttpClient.Builder = addInterceptor(RateLimitInterceptor(httpUrl.host, permits, period))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,5 +69,5 @@ fun OkHttpClient.Builder.rateLimitHost(
|
|||||||
fun OkHttpClient.Builder.rateLimitHost(
|
fun OkHttpClient.Builder.rateLimitHost(
|
||||||
url: String,
|
url: String,
|
||||||
permits: Int,
|
permits: Int,
|
||||||
period: Duration = 1.seconds
|
period: Duration = 1.seconds,
|
||||||
): OkHttpClient.Builder = addInterceptor(RateLimitInterceptor(url.toHttpUrlOrNull()?.host, permits, period))
|
): OkHttpClient.Builder = addInterceptor(RateLimitInterceptor(url.toHttpUrlOrNull()?.host, permits, period))
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ class UserAgentInterceptor : Interceptor {
|
|||||||
val originalRequest = chain.request()
|
val originalRequest = chain.request()
|
||||||
|
|
||||||
return if (originalRequest.header("User-Agent").isNullOrEmpty()) {
|
return if (originalRequest.header("User-Agent").isNullOrEmpty()) {
|
||||||
val newRequest = originalRequest
|
val newRequest =
|
||||||
|
originalRequest
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
.removeHeader("User-Agent")
|
.removeHeader("User-Agent")
|
||||||
.addHeader("User-Agent", HttpSource.DEFAULT_USER_AGENT)
|
.addHeader("User-Agent", HttpSource.DEFAULT_USER_AGENT)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import rx.Observable
|
|||||||
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
|
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
|
||||||
|
|
||||||
interface CatalogueSource : Source {
|
interface CatalogueSource : Source {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ISO 639-1 compliant language code (two letters in lower case).
|
* An ISO 639-1 compliant language code (two letters in lower case).
|
||||||
*/
|
*/
|
||||||
@@ -37,7 +36,11 @@ interface CatalogueSource : Source {
|
|||||||
* @param filters the list of filters to apply.
|
* @param filters the list of filters to apply.
|
||||||
*/
|
*/
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
suspend fun getSearchManga(
|
||||||
|
page: Int,
|
||||||
|
query: String,
|
||||||
|
filters: FilterList,
|
||||||
|
): MangasPage {
|
||||||
return fetchSearchManga(page, query, filters).awaitSingle()
|
return fetchSearchManga(page, query, filters).awaitSingle()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,22 +62,23 @@ interface CatalogueSource : Source {
|
|||||||
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
"Use the non-RxJava API instead",
|
"Use the non-RxJava API instead",
|
||||||
ReplaceWith("getPopularManga")
|
ReplaceWith("getPopularManga"),
|
||||||
)
|
)
|
||||||
fun fetchPopularManga(page: Int): Observable<MangasPage> =
|
fun fetchPopularManga(page: Int): Observable<MangasPage> = throw IllegalStateException("Not used")
|
||||||
throw IllegalStateException("Not used")
|
|
||||||
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
"Use the non-RxJava API instead",
|
"Use the non-RxJava API instead",
|
||||||
ReplaceWith("getSearchManga")
|
ReplaceWith("getSearchManga"),
|
||||||
)
|
)
|
||||||
fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> =
|
fun fetchSearchManga(
|
||||||
throw IllegalStateException("Not used")
|
page: Int,
|
||||||
|
query: String,
|
||||||
|
filters: FilterList,
|
||||||
|
): Observable<MangasPage> = throw IllegalStateException("Not used")
|
||||||
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
"Use the non-RxJava API instead",
|
"Use the non-RxJava API instead",
|
||||||
ReplaceWith("getLatestUpdates")
|
ReplaceWith("getLatestUpdates"),
|
||||||
)
|
)
|
||||||
fun fetchLatestUpdates(page: Int): Observable<MangasPage> =
|
fun fetchLatestUpdates(page: Int): Observable<MangasPage> = throw IllegalStateException("Not used")
|
||||||
throw IllegalStateException("Not used")
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,12 @@ import uy.kohesive.injekt.Injekt
|
|||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
interface ConfigurableSource : Source {
|
interface ConfigurableSource : Source {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets instance of [SharedPreferences] scoped to the specific source.
|
* Gets instance of [SharedPreferences] scoped to the specific source.
|
||||||
*
|
*
|
||||||
* @since extensions-lib 1.5
|
* @since extensions-lib 1.5
|
||||||
*/
|
*/
|
||||||
fun getSourcePreferences(): SharedPreferences =
|
fun getSourcePreferences(): SharedPreferences = Injekt.get<Application>().getSharedPreferences(preferenceKey(), Context.MODE_PRIVATE)
|
||||||
Injekt.get<Application>().getSharedPreferences(preferenceKey(), Context.MODE_PRIVATE)
|
|
||||||
|
|
||||||
fun setupPreferenceScreen(screen: PreferenceScreen)
|
fun setupPreferenceScreen(screen: PreferenceScreen)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
|
|||||||
* A basic interface for creating a source. It could be an online source, a local source, etc...
|
* A basic interface for creating a source. It could be an online source, a local source, etc...
|
||||||
*/
|
*/
|
||||||
interface Source {
|
interface Source {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Id for the source. Must be unique.
|
* Id for the source. Must be unique.
|
||||||
*/
|
*/
|
||||||
@@ -60,19 +59,19 @@ interface Source {
|
|||||||
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
"Use the non-RxJava API instead",
|
"Use the non-RxJava API instead",
|
||||||
ReplaceWith("getMangaDetails")
|
ReplaceWith("getMangaDetails"),
|
||||||
)
|
)
|
||||||
fun fetchMangaDetails(manga: SManga): Observable<SManga> = throw IllegalStateException("Not used")
|
fun fetchMangaDetails(manga: SManga): Observable<SManga> = throw IllegalStateException("Not used")
|
||||||
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
"Use the non-RxJava API instead",
|
"Use the non-RxJava API instead",
|
||||||
ReplaceWith("getChapterList")
|
ReplaceWith("getChapterList"),
|
||||||
)
|
)
|
||||||
fun fetchChapterList(manga: SManga): Observable<List<SChapter>> = throw IllegalStateException("Not used")
|
fun fetchChapterList(manga: SManga): Observable<List<SChapter>> = throw IllegalStateException("Not used")
|
||||||
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
"Use the non-RxJava API instead",
|
"Use the non-RxJava API instead",
|
||||||
ReplaceWith("getPageList")
|
ReplaceWith("getPageList"),
|
||||||
)
|
)
|
||||||
fun fetchPageList(chapter: SChapter): Observable<List<Page>> = Observable.empty()
|
fun fetchPageList(chapter: SChapter): Observable<List<Page>> = Observable.empty()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@file:Suppress("ktlint:standard:property-naming")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.source.local
|
package eu.kanade.tachiyomi.source.local
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
@@ -55,9 +57,8 @@ import com.github.junrar.Archive as JunrarArchive
|
|||||||
|
|
||||||
class LocalSource(
|
class LocalSource(
|
||||||
private val fileSystem: LocalSourceFileSystem,
|
private val fileSystem: LocalSourceFileSystem,
|
||||||
private val coverManager: LocalCoverManager
|
private val coverManager: LocalCoverManager,
|
||||||
) : CatalogueSource, UnmeteredSource {
|
) : CatalogueSource, UnmeteredSource {
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
private val xml: XML by injectLazy()
|
private val xml: XML by injectLazy()
|
||||||
|
|
||||||
@@ -79,10 +80,15 @@ class LocalSource(
|
|||||||
|
|
||||||
override suspend fun getLatestUpdates(page: Int) = getSearchManga(page, "", LATEST_FILTERS)
|
override suspend fun getLatestUpdates(page: Int) = getSearchManga(page, "", LATEST_FILTERS)
|
||||||
|
|
||||||
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
override suspend fun getSearchManga(
|
||||||
|
page: Int,
|
||||||
|
query: String,
|
||||||
|
filters: FilterList,
|
||||||
|
): MangasPage {
|
||||||
val baseDirsFiles = fileSystem.getFilesInBaseDirectories()
|
val baseDirsFiles = fileSystem.getFilesInBaseDirectories()
|
||||||
val lastModifiedLimit by lazy { if (filters === LATEST_FILTERS) System.currentTimeMillis() - LATEST_THRESHOLD else 0L }
|
val lastModifiedLimit by lazy { if (filters === LATEST_FILTERS) System.currentTimeMillis() - LATEST_THRESHOLD else 0L }
|
||||||
var mangaDirs = baseDirsFiles
|
var mangaDirs =
|
||||||
|
baseDirsFiles
|
||||||
// Filter out files that are hidden and is not a folder
|
// Filter out files that are hidden and is not a folder
|
||||||
.filter { it.isDirectory && !it.name.startsWith('.') }
|
.filter { it.isDirectory && !it.name.startsWith('.') }
|
||||||
.distinctBy { it.name }
|
.distinctBy { it.name }
|
||||||
@@ -97,14 +103,16 @@ class LocalSource(
|
|||||||
filters.forEach { filter ->
|
filters.forEach { filter ->
|
||||||
when (filter) {
|
when (filter) {
|
||||||
is OrderBy.Popular -> {
|
is OrderBy.Popular -> {
|
||||||
mangaDirs = if (filter.state!!.ascending) {
|
mangaDirs =
|
||||||
|
if (filter.state!!.ascending) {
|
||||||
mangaDirs.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name })
|
mangaDirs.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name })
|
||||||
} else {
|
} else {
|
||||||
mangaDirs.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name })
|
mangaDirs.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is OrderBy.Latest -> {
|
is OrderBy.Latest -> {
|
||||||
mangaDirs = if (filter.state!!.ascending) {
|
mangaDirs =
|
||||||
|
if (filter.state!!.ascending) {
|
||||||
mangaDirs.sortedBy(File::lastModified)
|
mangaDirs.sortedBy(File::lastModified)
|
||||||
} else {
|
} else {
|
||||||
mangaDirs.sortedByDescending(File::lastModified)
|
mangaDirs.sortedByDescending(File::lastModified)
|
||||||
@@ -112,13 +120,14 @@ class LocalSource(
|
|||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
/* Do nothing */
|
// Do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform mangaDirs to list of SManga
|
// Transform mangaDirs to list of SManga
|
||||||
val mangas = mangaDirs.map { mangaDir ->
|
val mangas =
|
||||||
|
mangaDirs.map { mangaDir ->
|
||||||
SManga.create().apply {
|
SManga.create().apply {
|
||||||
title = mangaDir.name
|
title = mangaDir.name
|
||||||
url = mangaDir.name
|
url = mangaDir.name
|
||||||
@@ -156,7 +165,8 @@ class LocalSource(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Manga details related
|
// Manga details related
|
||||||
override suspend fun getMangaDetails(manga: SManga): SManga = withContext(Dispatchers.IO) {
|
override suspend fun getMangaDetails(manga: SManga): SManga =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
coverManager.find(manga.url)?.let {
|
coverManager.find(manga.url)?.let {
|
||||||
manga.thumbnail_url = it.absolutePath
|
manga.thumbnail_url = it.absolutePath
|
||||||
}
|
}
|
||||||
@@ -165,11 +175,14 @@ class LocalSource(
|
|||||||
try {
|
try {
|
||||||
val mangaDirFiles = fileSystem.getFilesInMangaDirectory(manga.url).toList()
|
val mangaDirFiles = fileSystem.getFilesInMangaDirectory(manga.url).toList()
|
||||||
|
|
||||||
val comicInfoFile = mangaDirFiles
|
val comicInfoFile =
|
||||||
|
mangaDirFiles
|
||||||
.firstOrNull { it.name == COMIC_INFO_FILE }
|
.firstOrNull { it.name == COMIC_INFO_FILE }
|
||||||
val noXmlFile = mangaDirFiles
|
val noXmlFile =
|
||||||
|
mangaDirFiles
|
||||||
.firstOrNull { it.name == ".noxml" }
|
.firstOrNull { it.name == ".noxml" }
|
||||||
val legacyJsonDetailsFile = mangaDirFiles
|
val legacyJsonDetailsFile =
|
||||||
|
mangaDirFiles
|
||||||
.firstOrNull { it.extension == "json" }
|
.firstOrNull { it.extension == "json" }
|
||||||
|
|
||||||
when {
|
when {
|
||||||
@@ -193,7 +206,8 @@ class LocalSource(
|
|||||||
|
|
||||||
// Copy ComicInfo.xml from chapter archive to top level if found
|
// Copy ComicInfo.xml from chapter archive to top level if found
|
||||||
noXmlFile == null -> {
|
noXmlFile == null -> {
|
||||||
val chapterArchives = mangaDirFiles
|
val chapterArchives =
|
||||||
|
mangaDirFiles
|
||||||
.filter(Archive::isSupported)
|
.filter(Archive::isSupported)
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
@@ -216,7 +230,10 @@ class LocalSource(
|
|||||||
return@withContext manga
|
return@withContext manga
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun copyComicInfoFileFromArchive(chapterArchives: List<File>, folderPath: String?): File? {
|
private fun copyComicInfoFileFromArchive(
|
||||||
|
chapterArchives: List<File>,
|
||||||
|
folderPath: String?,
|
||||||
|
): File? {
|
||||||
for (chapter in chapterArchives) {
|
for (chapter in chapterArchives) {
|
||||||
when (Format.valueOf(chapter)) {
|
when (Format.valueOf(chapter)) {
|
||||||
is Format.Zip -> {
|
is Format.Zip -> {
|
||||||
@@ -243,7 +260,10 @@ class LocalSource(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun copyComicInfoFile(comicInfoFileStream: InputStream, folderPath: String?): File {
|
private fun copyComicInfoFile(
|
||||||
|
comicInfoFileStream: InputStream,
|
||||||
|
folderPath: String?,
|
||||||
|
): File {
|
||||||
return File("$folderPath/$COMIC_INFO_FILE").apply {
|
return File("$folderPath/$COMIC_INFO_FILE").apply {
|
||||||
outputStream().use { outputStream ->
|
outputStream().use { outputStream ->
|
||||||
comicInfoFileStream.use { it.copyTo(outputStream) }
|
comicInfoFileStream.use { it.copyTo(outputStream) }
|
||||||
@@ -251,8 +271,12 @@ class LocalSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setMangaDetailsFromComicInfoFile(stream: InputStream, manga: SManga) {
|
private fun setMangaDetailsFromComicInfoFile(
|
||||||
val comicInfo = KtXmlReader(stream, StandardCharsets.UTF_8.name()).use {
|
stream: InputStream,
|
||||||
|
manga: SManga,
|
||||||
|
) {
|
||||||
|
val comicInfo =
|
||||||
|
KtXmlReader(stream, StandardCharsets.UTF_8.name()).use {
|
||||||
xml.decodeFromReader<ComicInfo>(it)
|
xml.decodeFromReader<ComicInfo>(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,13 +291,15 @@ class LocalSource(
|
|||||||
.map { chapterFile ->
|
.map { chapterFile ->
|
||||||
SChapter.create().apply {
|
SChapter.create().apply {
|
||||||
url = "${manga.url}/${chapterFile.name}"
|
url = "${manga.url}/${chapterFile.name}"
|
||||||
name = if (chapterFile.isDirectory) {
|
name =
|
||||||
|
if (chapterFile.isDirectory) {
|
||||||
chapterFile.name
|
chapterFile.name
|
||||||
} else {
|
} else {
|
||||||
chapterFile.nameWithoutExtension
|
chapterFile.nameWithoutExtension
|
||||||
}
|
}
|
||||||
date_upload = chapterFile.lastModified()
|
date_upload = chapterFile.lastModified()
|
||||||
chapter_number = ChapterRecognition
|
chapter_number =
|
||||||
|
ChapterRecognition
|
||||||
.parseChapterNumber(manga.title, this.name, this.chapter_number.toDouble())
|
.parseChapterNumber(manga.title, this.name, this.chapter_number.toDouble())
|
||||||
.toFloat()
|
.toFloat()
|
||||||
|
|
||||||
@@ -305,7 +331,7 @@ class LocalSource(
|
|||||||
.mapIndexed { index, page ->
|
.mapIndexed { index, page ->
|
||||||
Page(
|
Page(
|
||||||
index,
|
index,
|
||||||
imageUrl = applicationDirs.localMangaRoot + "/" + chapter.url + "/" + page.name
|
imageUrl = applicationDirs.localMangaRoot + "/" + chapter.url + "/" + page.name,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,11 +373,15 @@ class LocalSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCover(chapter: SChapter, manga: SManga): File? {
|
private fun updateCover(
|
||||||
|
chapter: SChapter,
|
||||||
|
manga: SManga,
|
||||||
|
): File? {
|
||||||
return try {
|
return try {
|
||||||
when (val format = getFormat(chapter)) {
|
when (val format = getFormat(chapter)) {
|
||||||
is Format.Directory -> {
|
is Format.Directory -> {
|
||||||
val entry = format.file.listFiles()
|
val entry =
|
||||||
|
format.file.listFiles()
|
||||||
?.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
?.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||||
?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
|
?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
|
||||||
|
|
||||||
@@ -359,7 +389,8 @@ class LocalSource(
|
|||||||
}
|
}
|
||||||
is Format.Zip -> {
|
is Format.Zip -> {
|
||||||
ZipFile(format.file).use { zip ->
|
ZipFile(format.file).use { zip ->
|
||||||
val entry = zip.entries.toList()
|
val entry =
|
||||||
|
zip.entries.toList()
|
||||||
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||||
.find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
|
.find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
|
||||||
|
|
||||||
@@ -368,7 +399,8 @@ class LocalSource(
|
|||||||
}
|
}
|
||||||
is Format.Rar -> {
|
is Format.Rar -> {
|
||||||
JunrarArchive(format.file).use { archive ->
|
JunrarArchive(format.file).use { archive ->
|
||||||
val entry = archive.fileHeaders
|
val entry =
|
||||||
|
archive.fileHeaders
|
||||||
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
|
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
|
||||||
.find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } }
|
.find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } }
|
||||||
|
|
||||||
@@ -377,7 +409,8 @@ class LocalSource(
|
|||||||
}
|
}
|
||||||
is Format.Epub -> {
|
is Format.Epub -> {
|
||||||
EpubFile(format.file).use { epub ->
|
EpubFile(format.file).use { epub ->
|
||||||
val entry = epub.getImagesFromPages()
|
val entry =
|
||||||
|
epub.getImagesFromPages()
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
?.let { epub.getEntry(it) }
|
?.let { epub.getEntry(it) }
|
||||||
|
|
||||||
@@ -412,7 +445,8 @@ class LocalSource(
|
|||||||
|
|
||||||
if (sourceRecord == null) {
|
if (sourceRecord == null) {
|
||||||
// must do this to avoid database integrity errors
|
// must do this to avoid database integrity errors
|
||||||
val extensionId = ExtensionTable.insertAndGetId {
|
val extensionId =
|
||||||
|
ExtensionTable.insertAndGetId {
|
||||||
it[apkName] = "localSource"
|
it[apkName] = "localSource"
|
||||||
it[name] = EXTENSION_NAME
|
it[name] = EXTENSION_NAME
|
||||||
it[pkgName] = LocalSource::class.java.`package`.name
|
it[pkgName] = LocalSource::class.java.`package`.name
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import eu.kanade.tachiyomi.source.model.Filter
|
|||||||
sealed class OrderBy(selection: Selection) : Filter.Sort(
|
sealed class OrderBy(selection: Selection) : Filter.Sort(
|
||||||
"Order by",
|
"Order by",
|
||||||
arrayOf("Title", "Date"),
|
arrayOf("Title", "Date"),
|
||||||
selection
|
selection,
|
||||||
) {
|
) {
|
||||||
class Popular() : OrderBy(Selection(0, true))
|
class Popular() : OrderBy(Selection(0, true))
|
||||||
|
|
||||||
class Latest() : OrderBy(Selection(1, false))
|
class Latest() : OrderBy(Selection(1, false))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,8 @@ import java.io.InputStream
|
|||||||
private const val DEFAULT_COVER_NAME = "cover.jpg"
|
private const val DEFAULT_COVER_NAME = "cover.jpg"
|
||||||
|
|
||||||
class LocalCoverManager(
|
class LocalCoverManager(
|
||||||
private val fileSystem: LocalSourceFileSystem
|
private val fileSystem: LocalSourceFileSystem,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun find(mangaUrl: String): File? {
|
fun find(mangaUrl: String): File? {
|
||||||
return fileSystem.getFilesInMangaDirectory(mangaUrl)
|
return fileSystem.getFilesInMangaDirectory(mangaUrl)
|
||||||
// Get all file whose names start with 'cover'
|
// Get all file whose names start with 'cover'
|
||||||
@@ -24,7 +23,7 @@ class LocalCoverManager(
|
|||||||
|
|
||||||
fun update(
|
fun update(
|
||||||
manga: SManga,
|
manga: SManga,
|
||||||
inputStream: InputStream
|
inputStream: InputStream,
|
||||||
): File? {
|
): File? {
|
||||||
val directory = fileSystem.getMangaDirectory(manga.url)
|
val directory = fileSystem.getMangaDirectory(manga.url)
|
||||||
if (directory == null) {
|
if (directory == null) {
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package eu.kanade.tachiyomi.source.local.io
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
object Archive {
|
object Archive {
|
||||||
|
|
||||||
private val SUPPORTED_ARCHIVE_TYPES = listOf("zip", "cbz", "rar", "cbr", "epub")
|
private val SUPPORTED_ARCHIVE_TYPES = listOf("zip", "cbz", "rar", "cbr", "epub")
|
||||||
|
|
||||||
fun isSupported(file: File): Boolean = with(file) {
|
fun isSupported(file: File): Boolean =
|
||||||
|
with(file) {
|
||||||
return extension.lowercase() in SUPPORTED_ARCHIVE_TYPES
|
return extension.lowercase() in SUPPORTED_ARCHIVE_TYPES
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,18 @@ import java.io.File
|
|||||||
|
|
||||||
sealed interface Format {
|
sealed interface Format {
|
||||||
data class Directory(val file: File) : Format
|
data class Directory(val file: File) : Format
|
||||||
|
|
||||||
data class Zip(val file: File) : Format
|
data class Zip(val file: File) : Format
|
||||||
|
|
||||||
data class Rar(val file: File) : Format
|
data class Rar(val file: File) : Format
|
||||||
|
|
||||||
data class Epub(val file: File) : Format
|
data class Epub(val file: File) : Format
|
||||||
|
|
||||||
class UnknownFormatException : Exception()
|
class UnknownFormatException : Exception()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
fun valueOf(file: File) =
|
||||||
fun valueOf(file: File) = with(file) {
|
with(file) {
|
||||||
when {
|
when {
|
||||||
isDirectory -> Directory(this)
|
isDirectory -> Directory(this)
|
||||||
extension.equals("zip", true) || extension.equals("cbz", true) -> Zip(this)
|
extension.equals("zip", true) || extension.equals("cbz", true) -> Zip(this)
|
||||||
|
|||||||
@@ -4,9 +4,8 @@ import suwayomi.tachidesk.server.ApplicationDirs
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class LocalSourceFileSystem(
|
class LocalSourceFileSystem(
|
||||||
private val applicationDirs: ApplicationDirs
|
private val applicationDirs: ApplicationDirs,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun getBaseDirectories(): Sequence<File> {
|
fun getBaseDirectories(): Sequence<File> {
|
||||||
return sequenceOf(File(applicationDirs.localMangaRoot))
|
return sequenceOf(File(applicationDirs.localMangaRoot))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import java.io.File
|
|||||||
* Loader used to load a chapter from a .epub file.
|
* Loader used to load a chapter from a .epub file.
|
||||||
*/
|
*/
|
||||||
class EpubPageLoader(file: File) : PageLoader {
|
class EpubPageLoader(file: File) : PageLoader {
|
||||||
|
|
||||||
private val epub = EpubFile(file)
|
private val epub = EpubFile(file)
|
||||||
|
|
||||||
override suspend fun getPages(): List<ReaderPage> {
|
override suspend fun getPages(): List<ReaderPage> {
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import java.io.PipedOutputStream
|
|||||||
* Loader used to load a chapter from a .rar or .cbr file.
|
* Loader used to load a chapter from a .rar or .cbr file.
|
||||||
*/
|
*/
|
||||||
class RarPageLoader(file: File) : PageLoader {
|
class RarPageLoader(file: File) : PageLoader {
|
||||||
|
|
||||||
private val rar = Archive(file)
|
private val rar = Archive(file)
|
||||||
|
|
||||||
override suspend fun getPages(): List<ReaderPage> {
|
override suspend fun getPages(): List<ReaderPage> {
|
||||||
@@ -35,7 +34,10 @@ class RarPageLoader(file: File) : PageLoader {
|
|||||||
/**
|
/**
|
||||||
* Returns an input stream for the given [header].
|
* Returns an input stream for the given [header].
|
||||||
*/
|
*/
|
||||||
private fun getStream(rar: Archive, header: FileHeader): InputStream {
|
private fun getStream(
|
||||||
|
rar: Archive,
|
||||||
|
header: FileHeader,
|
||||||
|
): InputStream {
|
||||||
val pipeIn = PipedInputStream()
|
val pipeIn = PipedInputStream()
|
||||||
val pipeOut = PipedOutputStream(pipeIn)
|
val pipeOut = PipedOutputStream(pipeIn)
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ class ReaderPage(
|
|||||||
index: Int,
|
index: Int,
|
||||||
url: String = "",
|
url: String = "",
|
||||||
imageUrl: String? = null,
|
imageUrl: String? = null,
|
||||||
var stream: (() -> InputStream)? = null
|
var stream: (() -> InputStream)? = null,
|
||||||
) : Page(index, url, imageUrl, null)
|
) : Page(index, url, imageUrl, null)
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import java.io.File
|
|||||||
* Loader used to load a chapter from a .zip or .cbz file.
|
* Loader used to load a chapter from a .zip or .cbz file.
|
||||||
*/
|
*/
|
||||||
class ZipPageLoader(file: File) : PageLoader {
|
class ZipPageLoader(file: File) : PageLoader {
|
||||||
|
|
||||||
private val zip = ZipFile(file)
|
private val zip = ZipFile(file)
|
||||||
|
|
||||||
override suspend fun getPages(): List<ReaderPage> {
|
override suspend fun getPages(): List<ReaderPage> {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ fun SManga.copyFromComicInfo(comicInfo: ComicInfo) {
|
|||||||
listOfNotNull(
|
listOfNotNull(
|
||||||
comicInfo.genre?.value,
|
comicInfo.genre?.value,
|
||||||
comicInfo.tags?.value,
|
comicInfo.tags?.value,
|
||||||
comicInfo.categories?.value
|
comicInfo.categories?.value,
|
||||||
)
|
)
|
||||||
.distinct()
|
.distinct()
|
||||||
.joinToString(", ") { it.trim() }
|
.joinToString(", ") { it.trim() }
|
||||||
@@ -28,7 +28,7 @@ fun SManga.copyFromComicInfo(comicInfo: ComicInfo) {
|
|||||||
comicInfo.inker?.value,
|
comicInfo.inker?.value,
|
||||||
comicInfo.colorist?.value,
|
comicInfo.colorist?.value,
|
||||||
comicInfo.letterer?.value,
|
comicInfo.letterer?.value,
|
||||||
comicInfo.coverArtist?.value
|
comicInfo.coverArtist?.value,
|
||||||
)
|
)
|
||||||
.flatMap { it.split(", ") }
|
.flatMap { it.split(", ") }
|
||||||
.distinct()
|
.distinct()
|
||||||
@@ -57,7 +57,7 @@ data class ComicInfo(
|
|||||||
val tags: Tags?,
|
val tags: Tags?,
|
||||||
val web: Web?,
|
val web: Web?,
|
||||||
val publishingStatus: PublishingStatusTachiyomi?,
|
val publishingStatus: PublishingStatusTachiyomi?,
|
||||||
val categories: CategoriesTachiyomi?
|
val categories: CategoriesTachiyomi?,
|
||||||
) {
|
) {
|
||||||
@Suppress("UNUSED")
|
@Suppress("UNUSED")
|
||||||
@XmlElement(false)
|
@XmlElement(false)
|
||||||
@@ -71,73 +71,105 @@ data class ComicInfo(
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("Title", "", "")
|
@XmlSerialName("Title", "", "")
|
||||||
data class Title(@XmlValue(true) val value: String = "")
|
data class Title(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("Series", "", "")
|
@XmlSerialName("Series", "", "")
|
||||||
data class Series(@XmlValue(true) val value: String = "")
|
data class Series(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("Number", "", "")
|
@XmlSerialName("Number", "", "")
|
||||||
data class Number(@XmlValue(true) val value: String = "")
|
data class Number(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("Summary", "", "")
|
@XmlSerialName("Summary", "", "")
|
||||||
data class Summary(@XmlValue(true) val value: String = "")
|
data class Summary(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("Writer", "", "")
|
@XmlSerialName("Writer", "", "")
|
||||||
data class Writer(@XmlValue(true) val value: String = "")
|
data class Writer(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("Penciller", "", "")
|
@XmlSerialName("Penciller", "", "")
|
||||||
data class Penciller(@XmlValue(true) val value: String = "")
|
data class Penciller(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("Inker", "", "")
|
@XmlSerialName("Inker", "", "")
|
||||||
data class Inker(@XmlValue(true) val value: String = "")
|
data class Inker(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("Colorist", "", "")
|
@XmlSerialName("Colorist", "", "")
|
||||||
data class Colorist(@XmlValue(true) val value: String = "")
|
data class Colorist(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("Letterer", "", "")
|
@XmlSerialName("Letterer", "", "")
|
||||||
data class Letterer(@XmlValue(true) val value: String = "")
|
data class Letterer(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("CoverArtist", "", "")
|
@XmlSerialName("CoverArtist", "", "")
|
||||||
data class CoverArtist(@XmlValue(true) val value: String = "")
|
data class CoverArtist(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("Translator", "", "")
|
@XmlSerialName("Translator", "", "")
|
||||||
data class Translator(@XmlValue(true) val value: String = "")
|
data class Translator(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("Genre", "", "")
|
@XmlSerialName("Genre", "", "")
|
||||||
data class Genre(@XmlValue(true) val value: String = "")
|
data class Genre(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("Tags", "", "")
|
@XmlSerialName("Tags", "", "")
|
||||||
data class Tags(@XmlValue(true) val value: String = "")
|
data class Tags(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("Web", "", "")
|
@XmlSerialName("Web", "", "")
|
||||||
data class Web(@XmlValue(true) val value: String = "")
|
data class Web(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
// The spec doesn't have a good field for this
|
// The spec doesn't have a good field for this
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("PublishingStatusTachiyomi", "http://www.w3.org/2001/XMLSchema", "ty")
|
@XmlSerialName("PublishingStatusTachiyomi", "http://www.w3.org/2001/XMLSchema", "ty")
|
||||||
data class PublishingStatusTachiyomi(@XmlValue(true) val value: String = "")
|
data class PublishingStatusTachiyomi(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@XmlSerialName("Categories", "http://www.w3.org/2001/XMLSchema", "ty")
|
@XmlSerialName("Categories", "http://www.w3.org/2001/XMLSchema", "ty")
|
||||||
data class CategoriesTachiyomi(@XmlValue(true) val value: String = "")
|
data class CategoriesTachiyomi(
|
||||||
|
@XmlValue(true) val value: String = "",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ComicInfoPublishingStatus(
|
enum class ComicInfoPublishingStatus(
|
||||||
val comicInfoValue: String,
|
val comicInfoValue: String,
|
||||||
val sMangaModelValue: Int
|
val sMangaModelValue: Int,
|
||||||
) {
|
) {
|
||||||
ONGOING("Ongoing", SManga.ONGOING),
|
ONGOING("Ongoing", SManga.ONGOING),
|
||||||
COMPLETED("Completed", SManga.COMPLETED),
|
COMPLETED("Completed", SManga.COMPLETED),
|
||||||
@@ -145,7 +177,7 @@ enum class ComicInfoPublishingStatus(
|
|||||||
PUBLISHING_FINISHED("Publishing finished", SManga.PUBLISHING_FINISHED),
|
PUBLISHING_FINISHED("Publishing finished", SManga.PUBLISHING_FINISHED),
|
||||||
CANCELLED("Cancelled", SManga.CANCELLED),
|
CANCELLED("Cancelled", SManga.CANCELLED),
|
||||||
ON_HIATUS("On hiatus", SManga.ON_HIATUS),
|
ON_HIATUS("On hiatus", SManga.ON_HIATUS),
|
||||||
UNKNOWN("Unknown", SManga.UNKNOWN)
|
UNKNOWN("Unknown", SManga.UNKNOWN),
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ class MangaDetails(
|
|||||||
val artist: String? = null,
|
val artist: String? = null,
|
||||||
val description: String? = null,
|
val description: String? = null,
|
||||||
val genre: List<String>? = null,
|
val genre: List<String>? = null,
|
||||||
val status: Int? = null
|
val status: Int? = null,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,15 +4,22 @@ package eu.kanade.tachiyomi.source.model
|
|||||||
// sealed class Filter<T>(val name: String, var state: T) {
|
// sealed class Filter<T>(val name: String, var state: T) {
|
||||||
open class Filter<T>(val name: String, var state: T) {
|
open class Filter<T>(val name: String, var state: T) {
|
||||||
open class Header(name: String) : Filter<Any>(name, 0)
|
open class Header(name: String) : Filter<Any>(name, 0)
|
||||||
|
|
||||||
open class Separator(name: String = "") : Filter<Any>(name, 0)
|
open class Separator(name: String = "") : Filter<Any>(name, 0)
|
||||||
|
|
||||||
abstract class Select<V>(name: String, val values: Array<V>, state: Int = 0) : Filter<Int>(name, state) {
|
abstract class Select<V>(name: String, val values: Array<V>, state: Int = 0) : Filter<Int>(name, state) {
|
||||||
val displayValues get() = values.map { it.toString() }
|
val displayValues get() = values.map { it.toString() }
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class Text(name: String, state: String = "") : Filter<String>(name, state)
|
abstract class Text(name: String, state: String = "") : Filter<String>(name, state)
|
||||||
|
|
||||||
abstract class CheckBox(name: String, state: Boolean = false) : Filter<Boolean>(name, state)
|
abstract class CheckBox(name: String, state: Boolean = false) : Filter<Boolean>(name, state)
|
||||||
|
|
||||||
abstract class TriState(name: String, state: Int = STATE_IGNORE) : Filter<Int>(name, state) {
|
abstract class TriState(name: String, state: Int = STATE_IGNORE) : Filter<Int>(name, state) {
|
||||||
fun isIgnored() = state == STATE_IGNORE
|
fun isIgnored() = state == STATE_IGNORE
|
||||||
|
|
||||||
fun isIncluded() = state == STATE_INCLUDE
|
fun isIncluded() = state == STATE_INCLUDE
|
||||||
|
|
||||||
fun isExcluded() = state == STATE_EXCLUDE
|
fun isExcluded() = state == STATE_EXCLUDE
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package eu.kanade.tachiyomi.source.model
|
package eu.kanade.tachiyomi.source.model
|
||||||
|
|
||||||
data class FilterList(val list: List<Filter<*>>) : List<Filter<*>> by list {
|
data class FilterList(val list: List<Filter<*>>) : List<Filter<*>> by list {
|
||||||
|
|
||||||
constructor(vararg fs: Filter<*>) : this(if (fs.isNotEmpty()) fs.asList() else emptyList())
|
constructor(vararg fs: Filter<*>) : this(if (fs.isNotEmpty()) fs.asList() else emptyList())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,19 @@ open class Page(
|
|||||||
val index: Int,
|
val index: Int,
|
||||||
val url: String = "",
|
val url: String = "",
|
||||||
var imageUrl: String? = null,
|
var imageUrl: String? = null,
|
||||||
@Transient var uri: Uri? = null // Deprecated but can't be deleted due to extensions
|
// Deprecated but can't be deleted due to extensions
|
||||||
|
@Transient var uri: Uri? = null,
|
||||||
) : ProgressListener {
|
) : ProgressListener {
|
||||||
|
|
||||||
private val _progress = MutableStateFlow(0)
|
private val _progress = MutableStateFlow(0)
|
||||||
val progress = _progress.asStateFlow()
|
val progress = _progress.asStateFlow()
|
||||||
|
|
||||||
override fun update(bytesRead: Long, contentLength: Long, done: Boolean) {
|
override fun update(
|
||||||
_progress.value = if (contentLength > 0) {
|
bytesRead: Long,
|
||||||
|
contentLength: Long,
|
||||||
|
done: Boolean,
|
||||||
|
) {
|
||||||
|
_progress.value =
|
||||||
|
if (contentLength > 0) {
|
||||||
(100 * bytesRead / contentLength).toInt()
|
(100 * bytesRead / contentLength).toInt()
|
||||||
} else {
|
} else {
|
||||||
-1
|
-1
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
@file:Suppress("ktlint:standard:property-naming")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.source.model
|
package eu.kanade.tachiyomi.source.model
|
||||||
|
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
interface SChapter : Serializable {
|
interface SChapter : Serializable {
|
||||||
|
|
||||||
var url: String
|
var url: String
|
||||||
|
|
||||||
var name: String
|
var name: String
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
@file:Suppress("ktlint:standard:property-naming")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.source.model
|
package eu.kanade.tachiyomi.source.model
|
||||||
|
|
||||||
class SChapterImpl : SChapter {
|
class SChapterImpl : SChapter {
|
||||||
|
|
||||||
override lateinit var url: String
|
override lateinit var url: String
|
||||||
|
|
||||||
override lateinit var name: String
|
override lateinit var name: String
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
@file:Suppress("ktlint:standard:property-naming")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.source.model
|
package eu.kanade.tachiyomi.source.model
|
||||||
|
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
interface SManga : Serializable {
|
interface SManga : Serializable {
|
||||||
|
|
||||||
var url: String
|
var url: String
|
||||||
|
|
||||||
var title: String
|
var title: String
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
@file:Suppress("ktlint:standard:property-naming")
|
||||||
|
|
||||||
package eu.kanade.tachiyomi.source.model
|
package eu.kanade.tachiyomi.source.model
|
||||||
|
|
||||||
class SMangaImpl : SManga {
|
class SMangaImpl : SManga {
|
||||||
|
|
||||||
override lateinit var url: String
|
override lateinit var url: String
|
||||||
|
|
||||||
override lateinit var title: String
|
override lateinit var title: String
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ package eu.kanade.tachiyomi.source.model
|
|||||||
|
|
||||||
enum class UpdateStrategy {
|
enum class UpdateStrategy {
|
||||||
ALWAYS_UPDATE,
|
ALWAYS_UPDATE,
|
||||||
ONLY_FETCH_ONCE
|
ONLY_FETCH_ONCE,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import okhttp3.Response
|
|||||||
import rx.Observable
|
import rx.Observable
|
||||||
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
|
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
// import uy.kohesive.injekt.injectLazy
|
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URISyntaxException
|
import java.net.URISyntaxException
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
@@ -28,7 +27,6 @@ import java.security.MessageDigest
|
|||||||
* A simple implementation for sources from a website.
|
* A simple implementation for sources from a website.
|
||||||
*/
|
*/
|
||||||
abstract class HttpSource : CatalogueSource {
|
abstract class HttpSource : CatalogueSource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Network service.
|
* Network service.
|
||||||
*/
|
*/
|
||||||
@@ -91,7 +89,11 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
* @param versionId [Int] the version ID of the source
|
* @param versionId [Int] the version ID of the source
|
||||||
* @return a unique ID for the source
|
* @return a unique ID for the source
|
||||||
*/
|
*/
|
||||||
protected fun generateId(name: String, lang: String, versionId: Int): Long {
|
protected fun generateId(
|
||||||
|
name: String,
|
||||||
|
lang: String,
|
||||||
|
versionId: Int,
|
||||||
|
): Long {
|
||||||
val key = "${name.lowercase()}/$lang/$versionId"
|
val key = "${name.lowercase()}/$lang/$versionId"
|
||||||
val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray())
|
val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray())
|
||||||
return (0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) }.reduce(Long::or) and Long.MAX_VALUE
|
return (0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) }.reduce(Long::or) and Long.MAX_VALUE
|
||||||
@@ -100,7 +102,8 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
/**
|
/**
|
||||||
* Headers builder for requests. Implementations can override this method for custom headers.
|
* Headers builder for requests. Implementations can override this method for custom headers.
|
||||||
*/
|
*/
|
||||||
protected open fun headersBuilder() = Headers.Builder().apply {
|
protected open fun headersBuilder() =
|
||||||
|
Headers.Builder().apply {
|
||||||
add("User-Agent", DEFAULT_USER_AGENT)
|
add("User-Agent", DEFAULT_USER_AGENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +150,11 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
* @param filters the list of filters to apply.
|
* @param filters the list of filters to apply.
|
||||||
*/
|
*/
|
||||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getSearchManga"))
|
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getSearchManga"))
|
||||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
override fun fetchSearchManga(
|
||||||
|
page: Int,
|
||||||
|
query: String,
|
||||||
|
filters: FilterList,
|
||||||
|
): Observable<MangasPage> {
|
||||||
return client.newCall(searchMangaRequest(page, query, filters))
|
return client.newCall(searchMangaRequest(page, query, filters))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
.map { response ->
|
.map { response ->
|
||||||
@@ -162,7 +169,11 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
* @param query the search query.
|
* @param query the search query.
|
||||||
* @param filters the list of filters to apply.
|
* @param filters the list of filters to apply.
|
||||||
*/
|
*/
|
||||||
protected abstract fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request
|
protected abstract fun searchMangaRequest(
|
||||||
|
page: Int,
|
||||||
|
query: String,
|
||||||
|
filters: FilterList,
|
||||||
|
): Request
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the response from the site and returns a [MangasPage] object.
|
* Parses the response from the site and returns a [MangasPage] object.
|
||||||
@@ -450,7 +461,10 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
* @param chapter the chapter to be added.
|
* @param chapter the chapter to be added.
|
||||||
* @param manga the manga of the chapter.
|
* @param manga the manga of the chapter.
|
||||||
*/
|
*/
|
||||||
open fun prepareNewChapter(chapter: SChapter, manga: SManga) {}
|
open fun prepareNewChapter(
|
||||||
|
chapter: SChapter,
|
||||||
|
manga: SManga,
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the list of filters for the source.
|
* Returns the list of filters for the source.
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import org.jsoup.nodes.Element
|
|||||||
* A simple implementation for sources from a website using Jsoup, an HTML parser.
|
* A simple implementation for sources from a website using Jsoup, an HTML parser.
|
||||||
*/
|
*/
|
||||||
abstract class ParsedHttpSource : HttpSource() {
|
abstract class ParsedHttpSource : HttpSource() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the response from the site and returns a [MangasPage] object.
|
* Parses the response from the site and returns a [MangasPage] object.
|
||||||
*
|
*
|
||||||
@@ -22,11 +21,13 @@ abstract class ParsedHttpSource : HttpSource() {
|
|||||||
override fun popularMangaParse(response: Response): MangasPage {
|
override fun popularMangaParse(response: Response): MangasPage {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
|
|
||||||
val mangas = document.select(popularMangaSelector()).map { element ->
|
val mangas =
|
||||||
|
document.select(popularMangaSelector()).map { element ->
|
||||||
popularMangaFromElement(element)
|
popularMangaFromElement(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
val hasNextPage = popularMangaNextPageSelector()?.let { selector ->
|
val hasNextPage =
|
||||||
|
popularMangaNextPageSelector()?.let { selector ->
|
||||||
document.select(selector).first()
|
document.select(selector).first()
|
||||||
} != null
|
} != null
|
||||||
|
|
||||||
@@ -60,11 +61,13 @@ abstract class ParsedHttpSource : HttpSource() {
|
|||||||
override fun searchMangaParse(response: Response): MangasPage {
|
override fun searchMangaParse(response: Response): MangasPage {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
|
|
||||||
val mangas = document.select(searchMangaSelector()).map { element ->
|
val mangas =
|
||||||
|
document.select(searchMangaSelector()).map { element ->
|
||||||
searchMangaFromElement(element)
|
searchMangaFromElement(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
val hasNextPage = searchMangaNextPageSelector()?.let { selector ->
|
val hasNextPage =
|
||||||
|
searchMangaNextPageSelector()?.let { selector ->
|
||||||
document.select(selector).first()
|
document.select(selector).first()
|
||||||
} != null
|
} != null
|
||||||
|
|
||||||
@@ -98,11 +101,13 @@ abstract class ParsedHttpSource : HttpSource() {
|
|||||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
|
|
||||||
val mangas = document.select(latestUpdatesSelector()).map { element ->
|
val mangas =
|
||||||
|
document.select(latestUpdatesSelector()).map { element ->
|
||||||
latestUpdatesFromElement(element)
|
latestUpdatesFromElement(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
val hasNextPage = latestUpdatesNextPageSelector()?.let { selector ->
|
val hasNextPage =
|
||||||
|
latestUpdatesNextPageSelector()?.let { selector ->
|
||||||
document.select(selector).first()
|
document.select(selector).first()
|
||||||
} != null
|
} != null
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import eu.kanade.tachiyomi.source.model.SManga
|
|||||||
*/
|
*/
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
interface ResolvableSource : Source {
|
interface ResolvableSource : Source {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this source may potentially handle the given URI.
|
* Whether this source may potentially handle the given URI.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -5,11 +5,17 @@ import org.jsoup.Jsoup
|
|||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
|
|
||||||
fun Element.selectText(css: String, defaultValue: String? = null): String? {
|
fun Element.selectText(
|
||||||
|
css: String,
|
||||||
|
defaultValue: String? = null,
|
||||||
|
): String? {
|
||||||
return select(css).first()?.text() ?: defaultValue
|
return select(css).first()?.text() ?: defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Element.selectInt(css: String, defaultValue: Int = 0): Int {
|
fun Element.selectInt(
|
||||||
|
css: String,
|
||||||
|
defaultValue: Int = 0,
|
||||||
|
): Int {
|
||||||
return select(css).first()?.text()?.toInt() ?: defaultValue
|
return select(css).first()?.text()?.toInt() ?: defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ package eu.kanade.tachiyomi.util.chapter
|
|||||||
* -R> = regex conversion.
|
* -R> = regex conversion.
|
||||||
*/
|
*/
|
||||||
object ChapterRecognition {
|
object ChapterRecognition {
|
||||||
|
|
||||||
private const val NUMBER_PATTERN = """([0-9]+)(\.[0-9]+)?(\.?[a-z]+)?"""
|
private const val NUMBER_PATTERN = """([0-9]+)(\.[0-9]+)?(\.?[a-z]+)?"""
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,7 +29,11 @@ object ChapterRecognition {
|
|||||||
*/
|
*/
|
||||||
private val unwantedWhiteSpace = Regex("""\s(?=extra|special|omake)""")
|
private val unwantedWhiteSpace = Regex("""\s(?=extra|special|omake)""")
|
||||||
|
|
||||||
fun parseChapterNumber(mangaTitle: String, chapterName: String, chapterNumber: Double? = null): Double {
|
fun parseChapterNumber(
|
||||||
|
mangaTitle: String,
|
||||||
|
chapterName: String,
|
||||||
|
chapterNumber: Double? = null,
|
||||||
|
): Double {
|
||||||
// If chapter number is known return.
|
// If chapter number is known return.
|
||||||
if (chapterNumber != null && (chapterNumber == -2.0 || chapterNumber > -1.0)) {
|
if (chapterNumber != null && (chapterNumber == -2.0 || chapterNumber > -1.0)) {
|
||||||
return chapterNumber
|
return chapterNumber
|
||||||
@@ -81,7 +84,10 @@ object ChapterRecognition {
|
|||||||
* @param alpha alpha value of regex
|
* @param alpha alpha value of regex
|
||||||
* @return decimal/alpha float value
|
* @return decimal/alpha float value
|
||||||
*/
|
*/
|
||||||
private fun checkForDecimal(decimal: String?, alpha: String?): Double {
|
private fun checkForDecimal(
|
||||||
|
decimal: String?,
|
||||||
|
alpha: String?,
|
||||||
|
): Double {
|
||||||
if (!decimal.isNullOrEmpty()) {
|
if (!decimal.isNullOrEmpty()) {
|
||||||
return decimal.toDouble()
|
return decimal.toDouble()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package eu.kanade.tachiyomi.util.lang
|
|||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
|
||||||
object Hash {
|
object Hash {
|
||||||
|
private val chars =
|
||||||
private val chars = charArrayOf(
|
charArrayOf(
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
'a', 'b', 'c', 'd', 'e', 'f'
|
'a', 'b', 'c', 'd', 'e', 'f',
|
||||||
)
|
)
|
||||||
|
|
||||||
private val MD5 get() = MessageDigest.getInstance("MD5")
|
private val MD5 get() = MessageDigest.getInstance("MD5")
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ import kotlin.math.floor
|
|||||||
* Replaces the given string to have at most [count] characters using [replacement] at its end.
|
* Replaces the given string to have at most [count] characters using [replacement] at its end.
|
||||||
* If [replacement] is longer than [count] an exception will be thrown when `length > count`.
|
* If [replacement] is longer than [count] an exception will be thrown when `length > count`.
|
||||||
*/
|
*/
|
||||||
fun String.chop(count: Int, replacement: String = "…"): String {
|
fun String.chop(
|
||||||
|
count: Int,
|
||||||
|
replacement: String = "…",
|
||||||
|
): String {
|
||||||
return if (length > count) {
|
return if (length > count) {
|
||||||
take(count - replacement.length) + replacement
|
take(count - replacement.length) + replacement
|
||||||
} else {
|
} else {
|
||||||
@@ -19,7 +22,10 @@ fun String.chop(count: Int, replacement: String = "…"): String {
|
|||||||
* Replaces the given string to have at most [count] characters using [replacement] near the center.
|
* Replaces the given string to have at most [count] characters using [replacement] near the center.
|
||||||
* If [replacement] is longer than [count] an exception will be thrown when `length > count`.
|
* If [replacement] is longer than [count] an exception will be thrown when `length > count`.
|
||||||
*/
|
*/
|
||||||
fun String.truncateCenter(count: Int, replacement: String = "..."): String {
|
fun String.truncateCenter(
|
||||||
|
count: Int,
|
||||||
|
replacement: String = "...",
|
||||||
|
): String {
|
||||||
if (length <= count) {
|
if (length <= count) {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import java.io.InputStream
|
|||||||
* Wrapper over ZipFile to load files in epub format.
|
* Wrapper over ZipFile to load files in epub format.
|
||||||
*/
|
*/
|
||||||
class EpubFile(file: File) : Closeable {
|
class EpubFile(file: File) : Closeable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zip file of this epub.
|
* Zip file of this epub.
|
||||||
*/
|
*/
|
||||||
@@ -81,7 +80,8 @@ class EpubFile(file: File) : Closeable {
|
|||||||
* Returns all the pages from the epub.
|
* Returns all the pages from the epub.
|
||||||
*/
|
*/
|
||||||
fun getPagesFromDocument(document: Document): List<String> {
|
fun getPagesFromDocument(document: Document): List<String> {
|
||||||
val pages = document.select("manifest > item")
|
val pages =
|
||||||
|
document.select("manifest > item")
|
||||||
.filter { node -> "application/xhtml+xml" == node.attr("media-type") }
|
.filter { node -> "application/xhtml+xml" == node.attr("media-type") }
|
||||||
.associateBy { it.attr("id") }
|
.associateBy { it.attr("id") }
|
||||||
|
|
||||||
@@ -92,7 +92,10 @@ class EpubFile(file: File) : Closeable {
|
|||||||
/**
|
/**
|
||||||
* Returns all the images contained in every page from the epub.
|
* Returns all the images contained in every page from the epub.
|
||||||
*/
|
*/
|
||||||
private fun getImagesFromPages(pages: List<String>, packageHref: String): List<String> {
|
private fun getImagesFromPages(
|
||||||
|
pages: List<String>,
|
||||||
|
packageHref: String,
|
||||||
|
): List<String> {
|
||||||
val result = mutableListOf<String>()
|
val result = mutableListOf<String>()
|
||||||
val basePath = getParentDirectory(packageHref)
|
val basePath = getParentDirectory(packageHref)
|
||||||
pages.forEach { page ->
|
pages.forEach { page ->
|
||||||
@@ -128,7 +131,10 @@ class EpubFile(file: File) : Closeable {
|
|||||||
/**
|
/**
|
||||||
* Resolves a zip path from base and relative components and a path separator.
|
* Resolves a zip path from base and relative components and a path separator.
|
||||||
*/
|
*/
|
||||||
private fun resolveZipPath(basePath: String, relativePath: String): String {
|
private fun resolveZipPath(
|
||||||
|
basePath: String,
|
||||||
|
relativePath: String,
|
||||||
|
): String {
|
||||||
if (relativePath.startsWith(pathSeparator)) {
|
if (relativePath.startsWith(pathSeparator)) {
|
||||||
// Path is absolute, so return as-is.
|
// Path is absolute, so return as-is.
|
||||||
return relativePath
|
return relativePath
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ import suwayomi.tachidesk.server.util.withOperation
|
|||||||
|
|
||||||
object GlobalMetaController {
|
object GlobalMetaController {
|
||||||
/** used to modify a category's meta parameters */
|
/** used to modify a category's meta parameters */
|
||||||
val getMeta = handler(
|
val getMeta =
|
||||||
|
handler(
|
||||||
documentWith = {
|
documentWith = {
|
||||||
withOperation {
|
withOperation {
|
||||||
summary("Server level meta mapping")
|
summary("Server level meta mapping")
|
||||||
@@ -28,11 +29,12 @@ object GlobalMetaController {
|
|||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpCode.OK)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
/** used to modify global meta parameters */
|
/** used to modify global meta parameters */
|
||||||
val modifyMeta = handler(
|
val modifyMeta =
|
||||||
|
handler(
|
||||||
formParam<String>("key"),
|
formParam<String>("key"),
|
||||||
formParam<String>("value"),
|
formParam<String>("value"),
|
||||||
documentWith = {
|
documentWith = {
|
||||||
@@ -48,6 +50,6 @@ object GlobalMetaController {
|
|||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpCode.OK)
|
||||||
httpCode(HttpCode.NOT_FOUND)
|
httpCode(HttpCode.NOT_FOUND)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ import suwayomi.tachidesk.server.util.withOperation
|
|||||||
/** Settings Page/Screen */
|
/** Settings Page/Screen */
|
||||||
object SettingsController {
|
object SettingsController {
|
||||||
/** returns some static info about the current app build */
|
/** returns some static info about the current app build */
|
||||||
val about = handler(
|
val about =
|
||||||
|
handler(
|
||||||
documentWith = {
|
documentWith = {
|
||||||
withOperation {
|
withOperation {
|
||||||
summary("About Tachidesk")
|
summary("About Tachidesk")
|
||||||
@@ -31,11 +32,12 @@ object SettingsController {
|
|||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<AboutDataClass>(HttpCode.OK)
|
json<AboutDataClass>(HttpCode.OK)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
/** check for app updates */
|
/** check for app updates */
|
||||||
val checkUpdate = handler(
|
val checkUpdate =
|
||||||
|
handler(
|
||||||
documentWith = {
|
documentWith = {
|
||||||
withOperation {
|
withOperation {
|
||||||
summary("Tachidesk update check")
|
summary("Tachidesk update check")
|
||||||
@@ -44,11 +46,11 @@ object SettingsController {
|
|||||||
},
|
},
|
||||||
behaviorOf = { ctx ->
|
behaviorOf = { ctx ->
|
||||||
ctx.future(
|
ctx.future(
|
||||||
future { AppUpdate.checkUpdate() }
|
future { AppUpdate.checkUpdate() },
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
json<Array<UpdateDataClass>>(HttpCode.OK)
|
json<Array<UpdateDataClass>>(HttpCode.OK)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package suwayomi.tachidesk.global.impl
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import suwayomi.tachidesk.server.BuildConfig
|
import suwayomi.tachidesk.server.generated.BuildConfig
|
||||||
|
|
||||||
data class AboutDataClass(
|
data class AboutDataClass(
|
||||||
val name: String,
|
val name: String,
|
||||||
@@ -16,7 +16,7 @@ data class AboutDataClass(
|
|||||||
val buildType: String,
|
val buildType: String,
|
||||||
val buildTime: Long,
|
val buildTime: Long,
|
||||||
val github: String,
|
val github: String,
|
||||||
val discord: String
|
val discord: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
object About {
|
object About {
|
||||||
@@ -28,7 +28,7 @@ object About {
|
|||||||
BuildConfig.BUILD_TYPE,
|
BuildConfig.BUILD_TYPE,
|
||||||
BuildConfig.BUILD_TIME,
|
BuildConfig.BUILD_TIME,
|
||||||
BuildConfig.GITHUB,
|
BuildConfig.GITHUB,
|
||||||
BuildConfig.DISCORD
|
BuildConfig.DISCORD,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ data class UpdateDataClass(
|
|||||||
/** [channel] mirrors [suwayomi.tachidesk.server.BuildConfig.BUILD_TYPE] */
|
/** [channel] mirrors [suwayomi.tachidesk.server.BuildConfig.BUILD_TYPE] */
|
||||||
val channel: String,
|
val channel: String,
|
||||||
val tag: String,
|
val tag: String,
|
||||||
val url: String
|
val url: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
object AppUpdate {
|
object AppUpdate {
|
||||||
@@ -30,29 +30,31 @@ object AppUpdate {
|
|||||||
private val network: NetworkHelper by injectLazy()
|
private val network: NetworkHelper by injectLazy()
|
||||||
|
|
||||||
suspend fun checkUpdate(): List<UpdateDataClass> {
|
suspend fun checkUpdate(): List<UpdateDataClass> {
|
||||||
val stableJson = json.parseToJsonElement(
|
val stableJson =
|
||||||
|
json.parseToJsonElement(
|
||||||
network.client.newCall(
|
network.client.newCall(
|
||||||
GET(LATEST_STABLE_CHANNEL_URL)
|
GET(LATEST_STABLE_CHANNEL_URL),
|
||||||
).await().body.string()
|
).await().body.string(),
|
||||||
).jsonObject
|
).jsonObject
|
||||||
|
|
||||||
val previewJson = json.parseToJsonElement(
|
val previewJson =
|
||||||
|
json.parseToJsonElement(
|
||||||
network.client.newCall(
|
network.client.newCall(
|
||||||
GET(LATEST_PREVIEW_CHANNEL_URL)
|
GET(LATEST_PREVIEW_CHANNEL_URL),
|
||||||
).await().body.string()
|
).await().body.string(),
|
||||||
).jsonObject
|
).jsonObject
|
||||||
|
|
||||||
return listOf(
|
return listOf(
|
||||||
UpdateDataClass(
|
UpdateDataClass(
|
||||||
"Stable",
|
"Stable",
|
||||||
stableJson["tag_name"]!!.jsonPrimitive.content,
|
stableJson["tag_name"]!!.jsonPrimitive.content,
|
||||||
stableJson["html_url"]!!.jsonPrimitive.content
|
stableJson["html_url"]!!.jsonPrimitive.content,
|
||||||
),
|
),
|
||||||
UpdateDataClass(
|
UpdateDataClass(
|
||||||
"Preview",
|
"Preview",
|
||||||
previewJson["tag_name"]!!.jsonPrimitive.content,
|
previewJson["tag_name"]!!.jsonPrimitive.content,
|
||||||
previewJson["html_url"]!!.jsonPrimitive.content
|
previewJson["html_url"]!!.jsonPrimitive.content,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,13 @@ import suwayomi.tachidesk.global.model.table.GlobalMetaTable
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
object GlobalMeta {
|
object GlobalMeta {
|
||||||
fun modifyMeta(key: String, value: String) {
|
fun modifyMeta(
|
||||||
|
key: String,
|
||||||
|
value: String,
|
||||||
|
) {
|
||||||
|
transaction {
|
||||||
|
val meta =
|
||||||
transaction {
|
transaction {
|
||||||
val meta = transaction {
|
|
||||||
GlobalMetaTable.select { GlobalMetaTable.key eq key }
|
GlobalMetaTable.select { GlobalMetaTable.key eq key }
|
||||||
}.firstOrNull()
|
}.firstOrNull()
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ object GraphQLController {
|
|||||||
ctx.future(
|
ctx.future(
|
||||||
future {
|
future {
|
||||||
server.execute(ctx)
|
server.execute(ctx)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,11 +23,14 @@ import suwayomi.tachidesk.server.JavalinSetup.future
|
|||||||
|
|
||||||
class CategoryDataLoader : KotlinDataLoader<Int, CategoryType> {
|
class CategoryDataLoader : KotlinDataLoader<Int, CategoryType> {
|
||||||
override val dataLoaderName = "CategoryDataLoader"
|
override val dataLoaderName = "CategoryDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<Int, CategoryType> = DataLoaderFactory.newDataLoader { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<Int, CategoryType> =
|
||||||
|
DataLoaderFactory.newDataLoader { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val categories = CategoryTable.select { CategoryTable.id inList ids }
|
val categories =
|
||||||
|
CategoryTable.select { CategoryTable.id inList ids }
|
||||||
.map { CategoryType(it) }
|
.map { CategoryType(it) }
|
||||||
.associateBy { it.id }
|
.associateBy { it.id }
|
||||||
ids.map { categories[it] }
|
ids.map { categories[it] }
|
||||||
@@ -38,7 +41,9 @@ class CategoryDataLoader : KotlinDataLoader<Int, CategoryType> {
|
|||||||
|
|
||||||
class CategoryForIdsDataLoader : KotlinDataLoader<List<Int>, CategoryNodeList> {
|
class CategoryForIdsDataLoader : KotlinDataLoader<List<Int>, CategoryNodeList> {
|
||||||
override val dataLoaderName = "CategoryForIdsDataLoader"
|
override val dataLoaderName = "CategoryForIdsDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<List<Int>, CategoryNodeList> = DataLoaderFactory.newDataLoader { categoryIds ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<List<Int>, CategoryNodeList> =
|
||||||
|
DataLoaderFactory.newDataLoader { categoryIds ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
@@ -54,11 +59,14 @@ class CategoryForIdsDataLoader : KotlinDataLoader<List<Int>, CategoryNodeList> {
|
|||||||
|
|
||||||
class CategoriesForMangaDataLoader : KotlinDataLoader<Int, CategoryNodeList> {
|
class CategoriesForMangaDataLoader : KotlinDataLoader<Int, CategoryNodeList> {
|
||||||
override val dataLoaderName = "CategoriesForMangaDataLoader"
|
override val dataLoaderName = "CategoriesForMangaDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<Int, CategoryNodeList> = DataLoaderFactory.newDataLoader<Int, CategoryNodeList> { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<Int, CategoryNodeList> =
|
||||||
|
DataLoaderFactory.newDataLoader<Int, CategoryNodeList> { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val itemsByRef = CategoryMangaTable.innerJoin(CategoryTable)
|
val itemsByRef =
|
||||||
|
CategoryMangaTable.innerJoin(CategoryTable)
|
||||||
.select { CategoryMangaTable.manga inList ids }
|
.select { CategoryMangaTable.manga inList ids }
|
||||||
.map { Pair(it[CategoryMangaTable.manga].value, CategoryType(it)) }
|
.map { Pair(it[CategoryMangaTable.manga].value, CategoryType(it)) }
|
||||||
.groupBy { it.first }
|
.groupBy { it.first }
|
||||||
|
|||||||
@@ -25,11 +25,14 @@ import suwayomi.tachidesk.server.JavalinSetup.future
|
|||||||
|
|
||||||
class ChapterDataLoader : KotlinDataLoader<Int, ChapterType?> {
|
class ChapterDataLoader : KotlinDataLoader<Int, ChapterType?> {
|
||||||
override val dataLoaderName = "ChapterDataLoader"
|
override val dataLoaderName = "ChapterDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<Int, ChapterType?> = DataLoaderFactory.newDataLoader<Int, ChapterType> { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<Int, ChapterType?> =
|
||||||
|
DataLoaderFactory.newDataLoader<Int, ChapterType> { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val chapters = ChapterTable.select { ChapterTable.id inList ids }
|
val chapters =
|
||||||
|
ChapterTable.select { ChapterTable.id inList ids }
|
||||||
.map { ChapterType(it) }
|
.map { ChapterType(it) }
|
||||||
.associateBy { it.id }
|
.associateBy { it.id }
|
||||||
ids.map { chapters[it] }
|
ids.map { chapters[it] }
|
||||||
@@ -40,11 +43,14 @@ class ChapterDataLoader : KotlinDataLoader<Int, ChapterType?> {
|
|||||||
|
|
||||||
class ChaptersForMangaDataLoader : KotlinDataLoader<Int, ChapterNodeList> {
|
class ChaptersForMangaDataLoader : KotlinDataLoader<Int, ChapterNodeList> {
|
||||||
override val dataLoaderName = "ChaptersForMangaDataLoader"
|
override val dataLoaderName = "ChaptersForMangaDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<Int, ChapterNodeList> = DataLoaderFactory.newDataLoader<Int, ChapterNodeList> { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<Int, ChapterNodeList> =
|
||||||
|
DataLoaderFactory.newDataLoader<Int, ChapterNodeList> { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val chaptersByMangaId = ChapterTable.select { ChapterTable.manga inList ids }
|
val chaptersByMangaId =
|
||||||
|
ChapterTable.select { ChapterTable.manga inList ids }
|
||||||
.map { ChapterType(it) }
|
.map { ChapterType(it) }
|
||||||
.groupBy { it.mangaId }
|
.groupBy { it.mangaId }
|
||||||
ids.map { (chaptersByMangaId[it] ?: emptyList()).toNodeList() }
|
ids.map { (chaptersByMangaId[it] ?: emptyList()).toNodeList() }
|
||||||
@@ -55,7 +61,9 @@ class ChaptersForMangaDataLoader : KotlinDataLoader<Int, ChapterNodeList> {
|
|||||||
|
|
||||||
class DownloadedChapterCountForMangaDataLoader : KotlinDataLoader<Int, Int> {
|
class DownloadedChapterCountForMangaDataLoader : KotlinDataLoader<Int, Int> {
|
||||||
override val dataLoaderName = "DownloadedChapterCountForMangaDataLoader"
|
override val dataLoaderName = "DownloadedChapterCountForMangaDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<Int, Int> = DataLoaderFactory.newDataLoader<Int, Int> { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<Int, Int> =
|
||||||
|
DataLoaderFactory.newDataLoader<Int, Int> { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
@@ -73,7 +81,9 @@ class DownloadedChapterCountForMangaDataLoader : KotlinDataLoader<Int, Int> {
|
|||||||
|
|
||||||
class UnreadChapterCountForMangaDataLoader : KotlinDataLoader<Int, Int> {
|
class UnreadChapterCountForMangaDataLoader : KotlinDataLoader<Int, Int> {
|
||||||
override val dataLoaderName = "UnreadChapterCountForMangaDataLoader"
|
override val dataLoaderName = "UnreadChapterCountForMangaDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<Int, Int> = DataLoaderFactory.newDataLoader<Int, Int> { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<Int, Int> =
|
||||||
|
DataLoaderFactory.newDataLoader<Int, Int> { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
@@ -91,11 +101,14 @@ class UnreadChapterCountForMangaDataLoader : KotlinDataLoader<Int, Int> {
|
|||||||
|
|
||||||
class LastReadChapterForMangaDataLoader : KotlinDataLoader<Int, ChapterType?> {
|
class LastReadChapterForMangaDataLoader : KotlinDataLoader<Int, ChapterType?> {
|
||||||
override val dataLoaderName = "LastReadChapterForMangaDataLoader"
|
override val dataLoaderName = "LastReadChapterForMangaDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<Int, ChapterType?> = DataLoaderFactory.newDataLoader<Int, ChapterType?> { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<Int, ChapterType?> =
|
||||||
|
DataLoaderFactory.newDataLoader<Int, ChapterType?> { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val lastReadChaptersByMangaId = ChapterTable
|
val lastReadChaptersByMangaId =
|
||||||
|
ChapterTable
|
||||||
.select { (ChapterTable.manga inList ids) and (ChapterTable.isRead eq true) }
|
.select { (ChapterTable.manga inList ids) and (ChapterTable.isRead eq true) }
|
||||||
.orderBy(ChapterTable.sourceOrder to SortOrder.DESC)
|
.orderBy(ChapterTable.sourceOrder to SortOrder.DESC)
|
||||||
.groupBy { it[ChapterTable.manga].value }
|
.groupBy { it[ChapterTable.manga].value }
|
||||||
|
|||||||
@@ -21,11 +21,14 @@ import suwayomi.tachidesk.server.JavalinSetup.future
|
|||||||
|
|
||||||
class ExtensionDataLoader : KotlinDataLoader<String, ExtensionType?> {
|
class ExtensionDataLoader : KotlinDataLoader<String, ExtensionType?> {
|
||||||
override val dataLoaderName = "ExtensionDataLoader"
|
override val dataLoaderName = "ExtensionDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<String, ExtensionType?> = DataLoaderFactory.newDataLoader { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<String, ExtensionType?> =
|
||||||
|
DataLoaderFactory.newDataLoader { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val extensions = ExtensionTable.select { ExtensionTable.pkgName inList ids }
|
val extensions =
|
||||||
|
ExtensionTable.select { ExtensionTable.pkgName inList ids }
|
||||||
.map { ExtensionType(it) }
|
.map { ExtensionType(it) }
|
||||||
.associateBy { it.pkgName }
|
.associateBy { it.pkgName }
|
||||||
ids.map { extensions[it] }
|
ids.map { extensions[it] }
|
||||||
@@ -36,16 +39,20 @@ class ExtensionDataLoader : KotlinDataLoader<String, ExtensionType?> {
|
|||||||
|
|
||||||
class ExtensionForSourceDataLoader : KotlinDataLoader<Long, ExtensionType?> {
|
class ExtensionForSourceDataLoader : KotlinDataLoader<Long, ExtensionType?> {
|
||||||
override val dataLoaderName = "ExtensionForSourceDataLoader"
|
override val dataLoaderName = "ExtensionForSourceDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<Long, ExtensionType?> = DataLoaderFactory.newDataLoader { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<Long, ExtensionType?> =
|
||||||
|
DataLoaderFactory.newDataLoader { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val extensions = ExtensionTable.innerJoin(SourceTable)
|
val extensions =
|
||||||
|
ExtensionTable.innerJoin(SourceTable)
|
||||||
.select { SourceTable.id inList ids }
|
.select { SourceTable.id inList ids }
|
||||||
.toList()
|
.toList()
|
||||||
.map { Triple(it[SourceTable.id].value, it[ExtensionTable.pkgName], it) }
|
.map { Triple(it[SourceTable.id].value, it[ExtensionTable.pkgName], it) }
|
||||||
.let { triples ->
|
.let { triples ->
|
||||||
val sources = buildMap {
|
val sources =
|
||||||
|
buildMap {
|
||||||
triples.forEach {
|
triples.forEach {
|
||||||
if (!containsKey(it.second)) {
|
if (!containsKey(it.second)) {
|
||||||
put(it.second, ExtensionType(it.third))
|
put(it.second, ExtensionType(it.third))
|
||||||
|
|||||||
@@ -24,11 +24,14 @@ import suwayomi.tachidesk.server.JavalinSetup.future
|
|||||||
|
|
||||||
class MangaDataLoader : KotlinDataLoader<Int, MangaType?> {
|
class MangaDataLoader : KotlinDataLoader<Int, MangaType?> {
|
||||||
override val dataLoaderName = "MangaDataLoader"
|
override val dataLoaderName = "MangaDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<Int, MangaType?> = DataLoaderFactory.newDataLoader { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<Int, MangaType?> =
|
||||||
|
DataLoaderFactory.newDataLoader { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val manga = MangaTable.select { MangaTable.id inList ids }
|
val manga =
|
||||||
|
MangaTable.select { MangaTable.id inList ids }
|
||||||
.map { MangaType(it) }
|
.map { MangaType(it) }
|
||||||
.associateBy { it.id }
|
.associateBy { it.id }
|
||||||
ids.map { manga[it] }
|
ids.map { manga[it] }
|
||||||
@@ -39,11 +42,14 @@ class MangaDataLoader : KotlinDataLoader<Int, MangaType?> {
|
|||||||
|
|
||||||
class MangaForCategoryDataLoader : KotlinDataLoader<Int, MangaNodeList> {
|
class MangaForCategoryDataLoader : KotlinDataLoader<Int, MangaNodeList> {
|
||||||
override val dataLoaderName = "MangaForCategoryDataLoader"
|
override val dataLoaderName = "MangaForCategoryDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<Int, MangaNodeList> = DataLoaderFactory.newDataLoader<Int, MangaNodeList> { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<Int, MangaNodeList> =
|
||||||
|
DataLoaderFactory.newDataLoader<Int, MangaNodeList> { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val itemsByRef = if (ids.contains(0)) {
|
val itemsByRef =
|
||||||
|
if (ids.contains(0)) {
|
||||||
MangaTable
|
MangaTable
|
||||||
.leftJoin(CategoryMangaTable)
|
.leftJoin(CategoryMangaTable)
|
||||||
.select { MangaTable.inLibrary eq true }
|
.select { MangaTable.inLibrary eq true }
|
||||||
@@ -54,7 +60,8 @@ class MangaForCategoryDataLoader : KotlinDataLoader<Int, MangaNodeList> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
emptyMap()
|
emptyMap()
|
||||||
} + CategoryMangaTable.innerJoin(MangaTable)
|
} +
|
||||||
|
CategoryMangaTable.innerJoin(MangaTable)
|
||||||
.select { CategoryMangaTable.category inList ids }
|
.select { CategoryMangaTable.category inList ids }
|
||||||
.map { Pair(it[CategoryMangaTable.category].value, MangaType(it)) }
|
.map { Pair(it[CategoryMangaTable.category].value, MangaType(it)) }
|
||||||
.groupBy { it.first }
|
.groupBy { it.first }
|
||||||
@@ -68,11 +75,14 @@ class MangaForCategoryDataLoader : KotlinDataLoader<Int, MangaNodeList> {
|
|||||||
|
|
||||||
class MangaForSourceDataLoader : KotlinDataLoader<Long, MangaNodeList> {
|
class MangaForSourceDataLoader : KotlinDataLoader<Long, MangaNodeList> {
|
||||||
override val dataLoaderName = "MangaForSourceDataLoader"
|
override val dataLoaderName = "MangaForSourceDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<Long, MangaNodeList> = DataLoaderFactory.newDataLoader<Long, MangaNodeList> { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<Long, MangaNodeList> =
|
||||||
|
DataLoaderFactory.newDataLoader<Long, MangaNodeList> { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val mangaBySourceId = MangaTable.select { MangaTable.sourceReference inList ids }
|
val mangaBySourceId =
|
||||||
|
MangaTable.select { MangaTable.sourceReference inList ids }
|
||||||
.map { MangaType(it) }
|
.map { MangaType(it) }
|
||||||
.groupBy { it.sourceId }
|
.groupBy { it.sourceId }
|
||||||
ids.map { (mangaBySourceId[it] ?: emptyList()).toNodeList() }
|
ids.map { (mangaBySourceId[it] ?: emptyList()).toNodeList() }
|
||||||
@@ -83,12 +93,15 @@ class MangaForSourceDataLoader : KotlinDataLoader<Long, MangaNodeList> {
|
|||||||
|
|
||||||
class MangaForIdsDataLoader : KotlinDataLoader<List<Int>, MangaNodeList> {
|
class MangaForIdsDataLoader : KotlinDataLoader<List<Int>, MangaNodeList> {
|
||||||
override val dataLoaderName = "MangaForIdsDataLoader"
|
override val dataLoaderName = "MangaForIdsDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<List<Int>, MangaNodeList> = DataLoaderFactory.newDataLoader { mangaIds ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<List<Int>, MangaNodeList> =
|
||||||
|
DataLoaderFactory.newDataLoader { mangaIds ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val ids = mangaIds.flatten().distinct()
|
val ids = mangaIds.flatten().distinct()
|
||||||
val manga = MangaTable.select { MangaTable.id inList ids }
|
val manga =
|
||||||
|
MangaTable.select { MangaTable.id inList ids }
|
||||||
.map { MangaType(it) }
|
.map { MangaType(it) }
|
||||||
mangaIds.map { mangaIds ->
|
mangaIds.map { mangaIds ->
|
||||||
manga.filter { it.id in mangaIds }.toNodeList()
|
manga.filter { it.id in mangaIds }.toNodeList()
|
||||||
|
|||||||
@@ -19,11 +19,14 @@ import suwayomi.tachidesk.server.JavalinSetup.future
|
|||||||
|
|
||||||
class GlobalMetaDataLoader : KotlinDataLoader<String, GlobalMetaType?> {
|
class GlobalMetaDataLoader : KotlinDataLoader<String, GlobalMetaType?> {
|
||||||
override val dataLoaderName = "GlobalMetaDataLoader"
|
override val dataLoaderName = "GlobalMetaDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<String, GlobalMetaType?> = DataLoaderFactory.newDataLoader<String, GlobalMetaType?> { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<String, GlobalMetaType?> =
|
||||||
|
DataLoaderFactory.newDataLoader<String, GlobalMetaType?> { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val metasByRefId = GlobalMetaTable.select { GlobalMetaTable.key inList ids }
|
val metasByRefId =
|
||||||
|
GlobalMetaTable.select { GlobalMetaTable.key inList ids }
|
||||||
.map { GlobalMetaType(it) }
|
.map { GlobalMetaType(it) }
|
||||||
.associateBy { it.key }
|
.associateBy { it.key }
|
||||||
ids.map { metasByRefId[it] }
|
ids.map { metasByRefId[it] }
|
||||||
@@ -34,11 +37,14 @@ class GlobalMetaDataLoader : KotlinDataLoader<String, GlobalMetaType?> {
|
|||||||
|
|
||||||
class ChapterMetaDataLoader : KotlinDataLoader<Int, List<ChapterMetaType>> {
|
class ChapterMetaDataLoader : KotlinDataLoader<Int, List<ChapterMetaType>> {
|
||||||
override val dataLoaderName = "ChapterMetaDataLoader"
|
override val dataLoaderName = "ChapterMetaDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<Int, List<ChapterMetaType>> = DataLoaderFactory.newDataLoader<Int, List<ChapterMetaType>> { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<Int, List<ChapterMetaType>> =
|
||||||
|
DataLoaderFactory.newDataLoader<Int, List<ChapterMetaType>> { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val metasByRefId = ChapterMetaTable.select { ChapterMetaTable.ref inList ids }
|
val metasByRefId =
|
||||||
|
ChapterMetaTable.select { ChapterMetaTable.ref inList ids }
|
||||||
.map { ChapterMetaType(it) }
|
.map { ChapterMetaType(it) }
|
||||||
.groupBy { it.chapterId }
|
.groupBy { it.chapterId }
|
||||||
ids.map { metasByRefId[it].orEmpty() }
|
ids.map { metasByRefId[it].orEmpty() }
|
||||||
@@ -49,11 +55,14 @@ class ChapterMetaDataLoader : KotlinDataLoader<Int, List<ChapterMetaType>> {
|
|||||||
|
|
||||||
class MangaMetaDataLoader : KotlinDataLoader<Int, List<MangaMetaType>> {
|
class MangaMetaDataLoader : KotlinDataLoader<Int, List<MangaMetaType>> {
|
||||||
override val dataLoaderName = "MangaMetaDataLoader"
|
override val dataLoaderName = "MangaMetaDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<Int, List<MangaMetaType>> = DataLoaderFactory.newDataLoader<Int, List<MangaMetaType>> { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<Int, List<MangaMetaType>> =
|
||||||
|
DataLoaderFactory.newDataLoader<Int, List<MangaMetaType>> { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val metasByRefId = MangaMetaTable.select { MangaMetaTable.ref inList ids }
|
val metasByRefId =
|
||||||
|
MangaMetaTable.select { MangaMetaTable.ref inList ids }
|
||||||
.map { MangaMetaType(it) }
|
.map { MangaMetaType(it) }
|
||||||
.groupBy { it.mangaId }
|
.groupBy { it.mangaId }
|
||||||
ids.map { metasByRefId[it].orEmpty() }
|
ids.map { metasByRefId[it].orEmpty() }
|
||||||
@@ -64,11 +73,14 @@ class MangaMetaDataLoader : KotlinDataLoader<Int, List<MangaMetaType>> {
|
|||||||
|
|
||||||
class CategoryMetaDataLoader : KotlinDataLoader<Int, List<CategoryMetaType>> {
|
class CategoryMetaDataLoader : KotlinDataLoader<Int, List<CategoryMetaType>> {
|
||||||
override val dataLoaderName = "CategoryMetaDataLoader"
|
override val dataLoaderName = "CategoryMetaDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<Int, List<CategoryMetaType>> = DataLoaderFactory.newDataLoader<Int, List<CategoryMetaType>> { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<Int, List<CategoryMetaType>> =
|
||||||
|
DataLoaderFactory.newDataLoader<Int, List<CategoryMetaType>> { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val metasByRefId = CategoryMetaTable.select { CategoryMetaTable.ref inList ids }
|
val metasByRefId =
|
||||||
|
CategoryMetaTable.select { CategoryMetaTable.ref inList ids }
|
||||||
.map { CategoryMetaType(it) }
|
.map { CategoryMetaType(it) }
|
||||||
.groupBy { it.categoryId }
|
.groupBy { it.categoryId }
|
||||||
ids.map { metasByRefId[it].orEmpty() }
|
ids.map { metasByRefId[it].orEmpty() }
|
||||||
|
|||||||
@@ -23,11 +23,14 @@ import suwayomi.tachidesk.server.JavalinSetup.future
|
|||||||
|
|
||||||
class SourceDataLoader : KotlinDataLoader<Long, SourceType?> {
|
class SourceDataLoader : KotlinDataLoader<Long, SourceType?> {
|
||||||
override val dataLoaderName = "SourceDataLoader"
|
override val dataLoaderName = "SourceDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<Long, SourceType?> = DataLoaderFactory.newDataLoader { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<Long, SourceType?> =
|
||||||
|
DataLoaderFactory.newDataLoader { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val source = SourceTable.select { SourceTable.id inList ids }
|
val source =
|
||||||
|
SourceTable.select { SourceTable.id inList ids }
|
||||||
.mapNotNull { SourceType(it) }
|
.mapNotNull { SourceType(it) }
|
||||||
.associateBy { it.id }
|
.associateBy { it.id }
|
||||||
ids.map { source[it] }
|
ids.map { source[it] }
|
||||||
@@ -38,12 +41,15 @@ class SourceDataLoader : KotlinDataLoader<Long, SourceType?> {
|
|||||||
|
|
||||||
class SourcesForExtensionDataLoader : KotlinDataLoader<String, SourceNodeList> {
|
class SourcesForExtensionDataLoader : KotlinDataLoader<String, SourceNodeList> {
|
||||||
override val dataLoaderName = "SourcesForExtensionDataLoader"
|
override val dataLoaderName = "SourcesForExtensionDataLoader"
|
||||||
override fun getDataLoader(): DataLoader<String, SourceNodeList> = DataLoaderFactory.newDataLoader { ids ->
|
|
||||||
|
override fun getDataLoader(): DataLoader<String, SourceNodeList> =
|
||||||
|
DataLoaderFactory.newDataLoader { ids ->
|
||||||
future {
|
future {
|
||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
|
|
||||||
val sourcesByExtensionPkg = SourceTable.innerJoin(ExtensionTable)
|
val sourcesByExtensionPkg =
|
||||||
|
SourceTable.innerJoin(ExtensionTable)
|
||||||
.select { ExtensionTable.pkgName inList ids }
|
.select { ExtensionTable.pkgName inList ids }
|
||||||
.map { Pair(it[ExtensionTable.pkgName], SourceType(it)) }
|
.map { Pair(it[ExtensionTable.pkgName], SourceType(it)) }
|
||||||
.groupBy { it.first }
|
.groupBy { it.first }
|
||||||
|
|||||||
@@ -20,17 +20,16 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
class BackupMutation {
|
class BackupMutation {
|
||||||
data class RestoreBackupInput(
|
data class RestoreBackupInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val backup: UploadedFile
|
val backup: UploadedFile,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class RestoreBackupPayload(
|
data class RestoreBackupPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val status: BackupRestoreStatus
|
val status: BackupRestoreStatus,
|
||||||
)
|
)
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
fun restoreBackup(
|
fun restoreBackup(input: RestoreBackupInput): CompletableFuture<RestoreBackupPayload> {
|
||||||
input: RestoreBackupInput
|
|
||||||
): CompletableFuture<RestoreBackupPayload> {
|
|
||||||
val (clientMutationId, backup) = input
|
val (clientMutationId, backup) = input
|
||||||
|
|
||||||
return future {
|
return future {
|
||||||
@@ -38,7 +37,8 @@ class BackupMutation {
|
|||||||
ProtoBackupImport.performRestore(backup.content)
|
ProtoBackupImport.performRestore(backup.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
val status = withTimeout(10.seconds) {
|
val status =
|
||||||
|
withTimeout(10.seconds) {
|
||||||
ProtoBackupImport.backupRestoreState.first {
|
ProtoBackupImport.backupRestoreState.first {
|
||||||
it != ProtoBackupImport.BackupRestoreState.Idle
|
it != ProtoBackupImport.BackupRestoreState.Idle
|
||||||
}.toStatus()
|
}.toStatus()
|
||||||
@@ -51,32 +51,33 @@ class BackupMutation {
|
|||||||
data class CreateBackupInput(
|
data class CreateBackupInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val includeChapters: Boolean? = null,
|
val includeChapters: Boolean? = null,
|
||||||
val includeCategories: Boolean? = null
|
val includeCategories: Boolean? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class CreateBackupPayload(
|
data class CreateBackupPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val url: String
|
val url: String,
|
||||||
)
|
)
|
||||||
fun createBackup(
|
|
||||||
input: CreateBackupInput? = null
|
fun createBackup(input: CreateBackupInput? = null): CreateBackupPayload {
|
||||||
): CreateBackupPayload {
|
|
||||||
val filename = ProtoBackupExport.getBackupFilename()
|
val filename = ProtoBackupExport.getBackupFilename()
|
||||||
|
|
||||||
val backup = ProtoBackupExport.createBackup(
|
val backup =
|
||||||
|
ProtoBackupExport.createBackup(
|
||||||
BackupFlags(
|
BackupFlags(
|
||||||
includeManga = true,
|
includeManga = true,
|
||||||
includeCategories = input?.includeCategories ?: true,
|
includeCategories = input?.includeCategories ?: true,
|
||||||
includeChapters = input?.includeChapters ?: true,
|
includeChapters = input?.includeChapters ?: true,
|
||||||
includeTracking = true,
|
includeTracking = true,
|
||||||
includeHistory = true
|
includeHistory = true,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
TemporaryFileStorage.saveFile(filename, backup)
|
TemporaryFileStorage.saveFile(filename, backup)
|
||||||
|
|
||||||
return CreateBackupPayload(
|
return CreateBackupPayload(
|
||||||
clientMutationId = input?.clientMutationId,
|
clientMutationId = input?.clientMutationId,
|
||||||
url = "/api/graphql/files/backup/$filename"
|
url = "/api/graphql/files/backup/$filename",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,15 +27,15 @@ import suwayomi.tachidesk.manga.model.table.MangaTable
|
|||||||
class CategoryMutation {
|
class CategoryMutation {
|
||||||
data class SetCategoryMetaInput(
|
data class SetCategoryMetaInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val meta: CategoryMetaType
|
val meta: CategoryMetaType,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class SetCategoryMetaPayload(
|
data class SetCategoryMetaPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val meta: CategoryMetaType
|
val meta: CategoryMetaType,
|
||||||
)
|
)
|
||||||
fun setCategoryMeta(
|
|
||||||
input: SetCategoryMetaInput
|
fun setCategoryMeta(input: SetCategoryMetaInput): SetCategoryMetaPayload {
|
||||||
): SetCategoryMetaPayload {
|
|
||||||
val (clientMutationId, meta) = input
|
val (clientMutationId, meta) = input
|
||||||
|
|
||||||
Category.modifyMeta(meta.categoryId, meta.key, meta.value)
|
Category.modifyMeta(meta.categoryId, meta.key, meta.value)
|
||||||
@@ -46,25 +46,28 @@ class CategoryMutation {
|
|||||||
data class DeleteCategoryMetaInput(
|
data class DeleteCategoryMetaInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val categoryId: Int,
|
val categoryId: Int,
|
||||||
val key: String
|
val key: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DeleteCategoryMetaPayload(
|
data class DeleteCategoryMetaPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val meta: CategoryMetaType?,
|
val meta: CategoryMetaType?,
|
||||||
val category: CategoryType
|
val category: CategoryType,
|
||||||
)
|
)
|
||||||
fun deleteCategoryMeta(
|
|
||||||
input: DeleteCategoryMetaInput
|
fun deleteCategoryMeta(input: DeleteCategoryMetaInput): DeleteCategoryMetaPayload {
|
||||||
): DeleteCategoryMetaPayload {
|
|
||||||
val (clientMutationId, categoryId, key) = input
|
val (clientMutationId, categoryId, key) = input
|
||||||
|
|
||||||
val (meta, category) = transaction {
|
val (meta, category) =
|
||||||
val meta = CategoryMetaTable.select { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) }
|
transaction {
|
||||||
|
val meta =
|
||||||
|
CategoryMetaTable.select { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) }
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
|
|
||||||
CategoryMetaTable.deleteWhere { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) }
|
CategoryMetaTable.deleteWhere { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) }
|
||||||
|
|
||||||
val category = transaction {
|
val category =
|
||||||
|
transaction {
|
||||||
CategoryType(CategoryTable.select { CategoryTable.id eq categoryId }.first())
|
CategoryType(CategoryTable.select { CategoryTable.id eq categoryId }.first())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,30 +84,35 @@ class CategoryMutation {
|
|||||||
data class UpdateCategoryPatch(
|
data class UpdateCategoryPatch(
|
||||||
val name: String? = null,
|
val name: String? = null,
|
||||||
val default: Boolean? = null,
|
val default: Boolean? = null,
|
||||||
val includeInUpdate: IncludeInUpdate? = null
|
val includeInUpdate: IncludeInUpdate? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateCategoryPayload(
|
data class UpdateCategoryPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val category: CategoryType
|
val category: CategoryType,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateCategoryInput(
|
data class UpdateCategoryInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val patch: UpdateCategoryPatch
|
val patch: UpdateCategoryPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateCategoriesPayload(
|
data class UpdateCategoriesPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val categories: List<CategoryType>
|
val categories: List<CategoryType>,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateCategoriesInput(
|
data class UpdateCategoriesInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val ids: List<Int>,
|
val ids: List<Int>,
|
||||||
val patch: UpdateCategoryPatch
|
val patch: UpdateCategoryPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun updateCategories(ids: List<Int>, patch: UpdateCategoryPatch) {
|
private fun updateCategories(
|
||||||
|
ids: List<Int>,
|
||||||
|
patch: UpdateCategoryPatch,
|
||||||
|
) {
|
||||||
transaction {
|
transaction {
|
||||||
if (patch.name != null) {
|
if (patch.name != null) {
|
||||||
CategoryTable.update({ CategoryTable.id inList ids }) { update ->
|
CategoryTable.update({ CategoryTable.id inList ids }) { update ->
|
||||||
@@ -135,13 +143,14 @@ class CategoryMutation {
|
|||||||
|
|
||||||
updateCategories(listOf(id), patch)
|
updateCategories(listOf(id), patch)
|
||||||
|
|
||||||
val category = transaction {
|
val category =
|
||||||
|
transaction {
|
||||||
CategoryType(CategoryTable.select { CategoryTable.id eq id }.first())
|
CategoryType(CategoryTable.select { CategoryTable.id eq id }.first())
|
||||||
}
|
}
|
||||||
|
|
||||||
return UpdateCategoryPayload(
|
return UpdateCategoryPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
category = category
|
category = category,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,24 +159,26 @@ class CategoryMutation {
|
|||||||
|
|
||||||
updateCategories(ids, patch)
|
updateCategories(ids, patch)
|
||||||
|
|
||||||
val categories = transaction {
|
val categories =
|
||||||
|
transaction {
|
||||||
CategoryTable.select { CategoryTable.id inList ids }.map { CategoryType(it) }
|
CategoryTable.select { CategoryTable.id inList ids }.map { CategoryType(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return UpdateCategoriesPayload(
|
return UpdateCategoriesPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
categories = categories
|
categories = categories,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class UpdateCategoryOrderPayload(
|
data class UpdateCategoryOrderPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val categories: List<CategoryType>
|
val categories: List<CategoryType>,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateCategoryOrderInput(
|
data class UpdateCategoryOrderInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val position: Int
|
val position: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun updateCategoryOrder(input: UpdateCategoryOrderInput): UpdateCategoryOrderPayload {
|
fun updateCategoryOrder(input: UpdateCategoryOrderInput): UpdateCategoryOrderPayload {
|
||||||
@@ -177,7 +188,8 @@ class CategoryMutation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
transaction {
|
transaction {
|
||||||
val currentOrder = CategoryTable
|
val currentOrder =
|
||||||
|
CategoryTable
|
||||||
.select { CategoryTable.id eq categoryId }
|
.select { CategoryTable.id eq categoryId }
|
||||||
.first()[CategoryTable.order]
|
.first()[CategoryTable.order]
|
||||||
|
|
||||||
@@ -200,13 +212,14 @@ class CategoryMutation {
|
|||||||
|
|
||||||
Category.normalizeCategories()
|
Category.normalizeCategories()
|
||||||
|
|
||||||
val categories = transaction {
|
val categories =
|
||||||
|
transaction {
|
||||||
CategoryTable.selectAll().orderBy(CategoryTable.order).map { CategoryType(it) }
|
CategoryTable.selectAll().orderBy(CategoryTable.order).map { CategoryType(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return UpdateCategoryOrderPayload(
|
return UpdateCategoryOrderPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
categories = categories
|
categories = categories,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,15 +228,15 @@ class CategoryMutation {
|
|||||||
val name: String,
|
val name: String,
|
||||||
val order: Int? = null,
|
val order: Int? = null,
|
||||||
val default: Boolean? = null,
|
val default: Boolean? = null,
|
||||||
val includeInUpdate: IncludeInUpdate? = null
|
val includeInUpdate: IncludeInUpdate? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class CreateCategoryPayload(
|
data class CreateCategoryPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val category: CategoryType
|
val category: CategoryType,
|
||||||
)
|
)
|
||||||
fun createCategory(
|
|
||||||
input: CreateCategoryInput
|
fun createCategory(input: CreateCategoryInput): CreateCategoryPayload {
|
||||||
): CreateCategoryPayload {
|
|
||||||
val (clientMutationId, name, order, default, includeInUpdate) = input
|
val (clientMutationId, name, order, default, includeInUpdate) = input
|
||||||
transaction {
|
transaction {
|
||||||
require(CategoryTable.select { CategoryTable.name eq input.name }.isEmpty()) {
|
require(CategoryTable.select { CategoryTable.name eq input.name }.isEmpty()) {
|
||||||
@@ -239,14 +252,16 @@ class CategoryMutation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val category = transaction {
|
val category =
|
||||||
|
transaction {
|
||||||
if (order != null) {
|
if (order != null) {
|
||||||
CategoryTable.update({ CategoryTable.order greaterEq order }) {
|
CategoryTable.update({ CategoryTable.order greaterEq order }) {
|
||||||
it[CategoryTable.order] = CategoryTable.order + 1
|
it[CategoryTable.order] = CategoryTable.order + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val id = CategoryTable.insertAndGetId {
|
val id =
|
||||||
|
CategoryTable.insertAndGetId {
|
||||||
it[CategoryTable.name] = input.name
|
it[CategoryTable.name] = input.name
|
||||||
it[CategoryTable.order] = order ?: Int.MAX_VALUE
|
it[CategoryTable.order] = order ?: Int.MAX_VALUE
|
||||||
if (default != null) {
|
if (default != null) {
|
||||||
@@ -267,30 +282,33 @@ class CategoryMutation {
|
|||||||
|
|
||||||
data class DeleteCategoryInput(
|
data class DeleteCategoryInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val categoryId: Int
|
val categoryId: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DeleteCategoryPayload(
|
data class DeleteCategoryPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val category: CategoryType?,
|
val category: CategoryType?,
|
||||||
val mangas: List<MangaType>
|
val mangas: List<MangaType>,
|
||||||
)
|
)
|
||||||
fun deleteCategory(
|
|
||||||
input: DeleteCategoryInput
|
fun deleteCategory(input: DeleteCategoryInput): DeleteCategoryPayload {
|
||||||
): DeleteCategoryPayload {
|
|
||||||
val (clientMutationId, categoryId) = input
|
val (clientMutationId, categoryId) = input
|
||||||
if (categoryId == 0) { // Don't delete default category
|
if (categoryId == 0) { // Don't delete default category
|
||||||
return DeleteCategoryPayload(
|
return DeleteCategoryPayload(
|
||||||
clientMutationId,
|
clientMutationId,
|
||||||
null,
|
null,
|
||||||
emptyList()
|
emptyList(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val (category, mangas) = transaction {
|
val (category, mangas) =
|
||||||
val category = CategoryTable.select { CategoryTable.id eq categoryId }
|
transaction {
|
||||||
|
val category =
|
||||||
|
CategoryTable.select { CategoryTable.id eq categoryId }
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
|
|
||||||
val mangas = transaction {
|
val mangas =
|
||||||
|
transaction {
|
||||||
MangaTable.innerJoin(CategoryMangaTable)
|
MangaTable.innerJoin(CategoryMangaTable)
|
||||||
.select { CategoryMangaTable.category eq categoryId }
|
.select { CategoryMangaTable.category eq categoryId }
|
||||||
.map { MangaType(it) }
|
.map { MangaType(it) }
|
||||||
@@ -313,30 +331,35 @@ class CategoryMutation {
|
|||||||
data class UpdateMangaCategoriesPatch(
|
data class UpdateMangaCategoriesPatch(
|
||||||
val clearCategories: Boolean? = null,
|
val clearCategories: Boolean? = null,
|
||||||
val addToCategories: List<Int>? = null,
|
val addToCategories: List<Int>? = null,
|
||||||
val removeFromCategories: List<Int>? = null
|
val removeFromCategories: List<Int>? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateMangaCategoriesPayload(
|
data class UpdateMangaCategoriesPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val manga: MangaType
|
val manga: MangaType,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateMangaCategoriesInput(
|
data class UpdateMangaCategoriesInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val patch: UpdateMangaCategoriesPatch
|
val patch: UpdateMangaCategoriesPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateMangasCategoriesPayload(
|
data class UpdateMangasCategoriesPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val mangas: List<MangaType>
|
val mangas: List<MangaType>,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateMangasCategoriesInput(
|
data class UpdateMangasCategoriesInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val ids: List<Int>,
|
val ids: List<Int>,
|
||||||
val patch: UpdateMangaCategoriesPatch
|
val patch: UpdateMangaCategoriesPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun updateMangas(ids: List<Int>, patch: UpdateMangaCategoriesPatch) {
|
private fun updateMangas(
|
||||||
|
ids: List<Int>,
|
||||||
|
patch: UpdateMangaCategoriesPatch,
|
||||||
|
) {
|
||||||
transaction {
|
transaction {
|
||||||
if (patch.clearCategories == true) {
|
if (patch.clearCategories == true) {
|
||||||
CategoryMangaTable.deleteWhere { CategoryMangaTable.manga inList ids }
|
CategoryMangaTable.deleteWhere { CategoryMangaTable.manga inList ids }
|
||||||
@@ -346,10 +369,12 @@ class CategoryMutation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!patch.addToCategories.isNullOrEmpty()) {
|
if (!patch.addToCategories.isNullOrEmpty()) {
|
||||||
val newCategories = buildList {
|
val newCategories =
|
||||||
|
buildList {
|
||||||
ids.forEach { mangaId ->
|
ids.forEach { mangaId ->
|
||||||
patch.addToCategories.forEach { categoryId ->
|
patch.addToCategories.forEach { categoryId ->
|
||||||
val existingMapping = CategoryMangaTable.select {
|
val existingMapping =
|
||||||
|
CategoryMangaTable.select {
|
||||||
(CategoryMangaTable.manga eq mangaId) and (CategoryMangaTable.category eq categoryId)
|
(CategoryMangaTable.manga eq mangaId) and (CategoryMangaTable.category eq categoryId)
|
||||||
}.isNotEmpty()
|
}.isNotEmpty()
|
||||||
|
|
||||||
@@ -373,13 +398,14 @@ class CategoryMutation {
|
|||||||
|
|
||||||
updateMangas(listOf(id), patch)
|
updateMangas(listOf(id), patch)
|
||||||
|
|
||||||
val manga = transaction {
|
val manga =
|
||||||
|
transaction {
|
||||||
MangaType(MangaTable.select { MangaTable.id eq id }.first())
|
MangaType(MangaTable.select { MangaTable.id eq id }.first())
|
||||||
}
|
}
|
||||||
|
|
||||||
return UpdateMangaCategoriesPayload(
|
return UpdateMangaCategoriesPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
manga = manga
|
manga = manga,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,13 +414,14 @@ class CategoryMutation {
|
|||||||
|
|
||||||
updateMangas(ids, patch)
|
updateMangas(ids, patch)
|
||||||
|
|
||||||
val mangas = transaction {
|
val mangas =
|
||||||
|
transaction {
|
||||||
MangaTable.select { MangaTable.id inList ids }.map { MangaType(it) }
|
MangaTable.select { MangaTable.id inList ids }.map { MangaType(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return UpdateMangasCategoriesPayload(
|
return UpdateMangasCategoriesPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
mangas = mangas
|
mangas = mangas,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,30 +25,35 @@ class ChapterMutation {
|
|||||||
data class UpdateChapterPatch(
|
data class UpdateChapterPatch(
|
||||||
val isBookmarked: Boolean? = null,
|
val isBookmarked: Boolean? = null,
|
||||||
val isRead: Boolean? = null,
|
val isRead: Boolean? = null,
|
||||||
val lastPageRead: Int? = null
|
val lastPageRead: Int? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateChapterPayload(
|
data class UpdateChapterPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val chapter: ChapterType
|
val chapter: ChapterType,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateChapterInput(
|
data class UpdateChapterInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val patch: UpdateChapterPatch
|
val patch: UpdateChapterPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateChaptersPayload(
|
data class UpdateChaptersPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val chapters: List<ChapterType>
|
val chapters: List<ChapterType>,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateChaptersInput(
|
data class UpdateChaptersInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val ids: List<Int>,
|
val ids: List<Int>,
|
||||||
val patch: UpdateChapterPatch
|
val patch: UpdateChapterPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun updateChapters(ids: List<Int>, patch: UpdateChapterPatch) {
|
private fun updateChapters(
|
||||||
|
ids: List<Int>,
|
||||||
|
patch: UpdateChapterPatch,
|
||||||
|
) {
|
||||||
transaction {
|
transaction {
|
||||||
if (patch.isRead != null || patch.isBookmarked != null || patch.lastPageRead != null) {
|
if (patch.isRead != null || patch.isBookmarked != null || patch.lastPageRead != null) {
|
||||||
val now = Instant.now().epochSecond
|
val now = Instant.now().epochSecond
|
||||||
@@ -68,58 +73,56 @@ class ChapterMutation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateChapter(
|
fun updateChapter(input: UpdateChapterInput): UpdateChapterPayload {
|
||||||
input: UpdateChapterInput
|
|
||||||
): UpdateChapterPayload {
|
|
||||||
val (clientMutationId, id, patch) = input
|
val (clientMutationId, id, patch) = input
|
||||||
|
|
||||||
updateChapters(listOf(id), patch)
|
updateChapters(listOf(id), patch)
|
||||||
|
|
||||||
val chapter = transaction {
|
val chapter =
|
||||||
|
transaction {
|
||||||
ChapterType(ChapterTable.select { ChapterTable.id eq id }.first())
|
ChapterType(ChapterTable.select { ChapterTable.id eq id }.first())
|
||||||
}
|
}
|
||||||
|
|
||||||
return UpdateChapterPayload(
|
return UpdateChapterPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
chapter = chapter
|
chapter = chapter,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateChapters(
|
fun updateChapters(input: UpdateChaptersInput): UpdateChaptersPayload {
|
||||||
input: UpdateChaptersInput
|
|
||||||
): UpdateChaptersPayload {
|
|
||||||
val (clientMutationId, ids, patch) = input
|
val (clientMutationId, ids, patch) = input
|
||||||
|
|
||||||
updateChapters(ids, patch)
|
updateChapters(ids, patch)
|
||||||
|
|
||||||
val chapters = transaction {
|
val chapters =
|
||||||
|
transaction {
|
||||||
ChapterTable.select { ChapterTable.id inList ids }.map { ChapterType(it) }
|
ChapterTable.select { ChapterTable.id inList ids }.map { ChapterType(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return UpdateChaptersPayload(
|
return UpdateChaptersPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
chapters = chapters
|
chapters = chapters,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class FetchChaptersInput(
|
data class FetchChaptersInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val mangaId: Int
|
val mangaId: Int,
|
||||||
)
|
|
||||||
data class FetchChaptersPayload(
|
|
||||||
val clientMutationId: String?,
|
|
||||||
val chapters: List<ChapterType>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun fetchChapters(
|
data class FetchChaptersPayload(
|
||||||
input: FetchChaptersInput
|
val clientMutationId: String?,
|
||||||
): CompletableFuture<FetchChaptersPayload> {
|
val chapters: List<ChapterType>,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun fetchChapters(input: FetchChaptersInput): CompletableFuture<FetchChaptersPayload> {
|
||||||
val (clientMutationId, mangaId) = input
|
val (clientMutationId, mangaId) = input
|
||||||
|
|
||||||
return future {
|
return future {
|
||||||
Chapter.fetchChapterList(mangaId)
|
Chapter.fetchChapterList(mangaId)
|
||||||
}.thenApply {
|
}.thenApply {
|
||||||
val chapters = transaction {
|
val chapters =
|
||||||
|
transaction {
|
||||||
ChapterTable.select { ChapterTable.manga eq mangaId }
|
ChapterTable.select { ChapterTable.manga eq mangaId }
|
||||||
.orderBy(ChapterTable.sourceOrder)
|
.orderBy(ChapterTable.sourceOrder)
|
||||||
.map { ChapterType(it) }
|
.map { ChapterType(it) }
|
||||||
@@ -127,22 +130,22 @@ class ChapterMutation {
|
|||||||
|
|
||||||
FetchChaptersPayload(
|
FetchChaptersPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
chapters = chapters
|
chapters = chapters,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class SetChapterMetaInput(
|
data class SetChapterMetaInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val meta: ChapterMetaType
|
val meta: ChapterMetaType,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class SetChapterMetaPayload(
|
data class SetChapterMetaPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val meta: ChapterMetaType
|
val meta: ChapterMetaType,
|
||||||
)
|
)
|
||||||
fun setChapterMeta(
|
|
||||||
input: SetChapterMetaInput
|
fun setChapterMeta(input: SetChapterMetaInput): SetChapterMetaPayload {
|
||||||
): SetChapterMetaPayload {
|
|
||||||
val (clientMutationId, meta) = input
|
val (clientMutationId, meta) = input
|
||||||
|
|
||||||
Chapter.modifyChapterMeta(meta.chapterId, meta.key, meta.value)
|
Chapter.modifyChapterMeta(meta.chapterId, meta.key, meta.value)
|
||||||
@@ -153,25 +156,28 @@ class ChapterMutation {
|
|||||||
data class DeleteChapterMetaInput(
|
data class DeleteChapterMetaInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val chapterId: Int,
|
val chapterId: Int,
|
||||||
val key: String
|
val key: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DeleteChapterMetaPayload(
|
data class DeleteChapterMetaPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val meta: ChapterMetaType?,
|
val meta: ChapterMetaType?,
|
||||||
val chapter: ChapterType
|
val chapter: ChapterType,
|
||||||
)
|
)
|
||||||
fun deleteChapterMeta(
|
|
||||||
input: DeleteChapterMetaInput
|
fun deleteChapterMeta(input: DeleteChapterMetaInput): DeleteChapterMetaPayload {
|
||||||
): DeleteChapterMetaPayload {
|
|
||||||
val (clientMutationId, chapterId, key) = input
|
val (clientMutationId, chapterId, key) = input
|
||||||
|
|
||||||
val (meta, chapter) = transaction {
|
val (meta, chapter) =
|
||||||
val meta = ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
|
transaction {
|
||||||
|
val meta =
|
||||||
|
ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
|
|
||||||
ChapterMetaTable.deleteWhere { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
|
ChapterMetaTable.deleteWhere { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
|
||||||
|
|
||||||
val chapter = transaction {
|
val chapter =
|
||||||
|
transaction {
|
||||||
ChapterType(ChapterTable.select { ChapterTable.id eq chapterId }.first())
|
ChapterType(ChapterTable.select { ChapterTable.id eq chapterId }.first())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,16 +193,16 @@ class ChapterMutation {
|
|||||||
|
|
||||||
data class FetchChapterPagesInput(
|
data class FetchChapterPagesInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val chapterId: Int
|
val chapterId: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class FetchChapterPagesPayload(
|
data class FetchChapterPagesPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val pages: List<String>,
|
val pages: List<String>,
|
||||||
val chapter: ChapterType
|
val chapter: ChapterType,
|
||||||
)
|
)
|
||||||
fun fetchChapterPages(
|
|
||||||
input: FetchChapterPagesInput
|
fun fetchChapterPages(input: FetchChapterPagesInput): CompletableFuture<FetchChapterPagesPayload> {
|
||||||
): CompletableFuture<FetchChapterPagesPayload> {
|
|
||||||
val (clientMutationId, chapterId) = input
|
val (clientMutationId, chapterId) = input
|
||||||
|
|
||||||
return future {
|
return future {
|
||||||
@@ -204,10 +210,11 @@ class ChapterMutation {
|
|||||||
}.thenApply { chapter ->
|
}.thenApply { chapter ->
|
||||||
FetchChapterPagesPayload(
|
FetchChapterPagesPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
pages = List(chapter.pageCount) { index ->
|
pages =
|
||||||
|
List(chapter.pageCount) { index ->
|
||||||
"/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}/page/$index"
|
"/api/v1/manga/${chapter.mangaId}/chapter/${chapter.index}/page/$index"
|
||||||
},
|
},
|
||||||
chapter = ChapterType(chapter)
|
chapter = ChapterType(chapter),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ import java.util.concurrent.CompletableFuture
|
|||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
class DownloadMutation {
|
class DownloadMutation {
|
||||||
|
|
||||||
data class DeleteDownloadedChaptersInput(
|
data class DeleteDownloadedChaptersInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val ids: List<Int>
|
val ids: List<Int>,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DeleteDownloadedChaptersPayload(
|
data class DeleteDownloadedChaptersPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val chapters: List<ChapterType>
|
val chapters: List<ChapterType>,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun deleteDownloadedChapters(input: DeleteDownloadedChaptersInput): DeleteDownloadedChaptersPayload {
|
fun deleteDownloadedChapters(input: DeleteDownloadedChaptersInput): DeleteDownloadedChaptersPayload {
|
||||||
@@ -33,20 +33,22 @@ class DownloadMutation {
|
|||||||
|
|
||||||
return DeleteDownloadedChaptersPayload(
|
return DeleteDownloadedChaptersPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
chapters = transaction {
|
chapters =
|
||||||
|
transaction {
|
||||||
ChapterTable.select { ChapterTable.id inList chapters }
|
ChapterTable.select { ChapterTable.id inList chapters }
|
||||||
.map { ChapterType(it) }
|
.map { ChapterType(it) }
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class DeleteDownloadedChapterInput(
|
data class DeleteDownloadedChapterInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val id: Int
|
val id: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DeleteDownloadedChapterPayload(
|
data class DeleteDownloadedChapterPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val chapters: ChapterType
|
val chapters: ChapterType,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun deleteDownloadedChapter(input: DeleteDownloadedChapterInput): DeleteDownloadedChapterPayload {
|
fun deleteDownloadedChapter(input: DeleteDownloadedChapterInput): DeleteDownloadedChapterPayload {
|
||||||
@@ -56,24 +58,24 @@ class DownloadMutation {
|
|||||||
|
|
||||||
return DeleteDownloadedChapterPayload(
|
return DeleteDownloadedChapterPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
chapters = transaction {
|
chapters =
|
||||||
|
transaction {
|
||||||
ChapterType(ChapterTable.select { ChapterTable.id eq chapter }.first())
|
ChapterType(ChapterTable.select { ChapterTable.id eq chapter }.first())
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class EnqueueChapterDownloadsInput(
|
data class EnqueueChapterDownloadsInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val ids: List<Int>
|
val ids: List<Int>,
|
||||||
)
|
|
||||||
data class EnqueueChapterDownloadsPayload(
|
|
||||||
val clientMutationId: String?,
|
|
||||||
val downloadStatus: DownloadStatus
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun enqueueChapterDownloads(
|
data class EnqueueChapterDownloadsPayload(
|
||||||
input: EnqueueChapterDownloadsInput
|
val clientMutationId: String?,
|
||||||
): CompletableFuture<EnqueueChapterDownloadsPayload> {
|
val downloadStatus: DownloadStatus,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun enqueueChapterDownloads(input: EnqueueChapterDownloadsInput): CompletableFuture<EnqueueChapterDownloadsPayload> {
|
||||||
val (clientMutationId, chapters) = input
|
val (clientMutationId, chapters) = input
|
||||||
|
|
||||||
DownloadManager.enqueue(DownloadManager.EnqueueInput(chapters))
|
DownloadManager.enqueue(DownloadManager.EnqueueInput(chapters))
|
||||||
@@ -81,25 +83,25 @@ class DownloadMutation {
|
|||||||
return future {
|
return future {
|
||||||
EnqueueChapterDownloadsPayload(
|
EnqueueChapterDownloadsPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
downloadStatus = withTimeout(30.seconds) {
|
downloadStatus =
|
||||||
|
withTimeout(30.seconds) {
|
||||||
DownloadStatus(DownloadManager.status.first { it.queue.any { it.chapter.id in chapters } })
|
DownloadStatus(DownloadManager.status.first { it.queue.any { it.chapter.id in chapters } })
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class EnqueueChapterDownloadInput(
|
data class EnqueueChapterDownloadInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val id: Int
|
val id: Int,
|
||||||
)
|
|
||||||
data class EnqueueChapterDownloadPayload(
|
|
||||||
val clientMutationId: String?,
|
|
||||||
val downloadStatus: DownloadStatus
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun enqueueChapterDownload(
|
data class EnqueueChapterDownloadPayload(
|
||||||
input: EnqueueChapterDownloadInput
|
val clientMutationId: String?,
|
||||||
): CompletableFuture<EnqueueChapterDownloadPayload> {
|
val downloadStatus: DownloadStatus,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun enqueueChapterDownload(input: EnqueueChapterDownloadInput): CompletableFuture<EnqueueChapterDownloadPayload> {
|
||||||
val (clientMutationId, chapter) = input
|
val (clientMutationId, chapter) = input
|
||||||
|
|
||||||
DownloadManager.enqueue(DownloadManager.EnqueueInput(listOf(chapter)))
|
DownloadManager.enqueue(DownloadManager.EnqueueInput(listOf(chapter)))
|
||||||
@@ -107,25 +109,25 @@ class DownloadMutation {
|
|||||||
return future {
|
return future {
|
||||||
EnqueueChapterDownloadPayload(
|
EnqueueChapterDownloadPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
downloadStatus = withTimeout(30.seconds) {
|
downloadStatus =
|
||||||
|
withTimeout(30.seconds) {
|
||||||
DownloadStatus(DownloadManager.status.first { it.queue.any { it.chapter.id == chapter } })
|
DownloadStatus(DownloadManager.status.first { it.queue.any { it.chapter.id == chapter } })
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class DequeueChapterDownloadsInput(
|
data class DequeueChapterDownloadsInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val ids: List<Int>
|
val ids: List<Int>,
|
||||||
)
|
|
||||||
data class DequeueChapterDownloadsPayload(
|
|
||||||
val clientMutationId: String?,
|
|
||||||
val downloadStatus: DownloadStatus
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun dequeueChapterDownloads(
|
data class DequeueChapterDownloadsPayload(
|
||||||
input: DequeueChapterDownloadsInput
|
val clientMutationId: String?,
|
||||||
): CompletableFuture<DequeueChapterDownloadsPayload> {
|
val downloadStatus: DownloadStatus,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun dequeueChapterDownloads(input: DequeueChapterDownloadsInput): CompletableFuture<DequeueChapterDownloadsPayload> {
|
||||||
val (clientMutationId, chapters) = input
|
val (clientMutationId, chapters) = input
|
||||||
|
|
||||||
DownloadManager.dequeue(DownloadManager.EnqueueInput(chapters))
|
DownloadManager.dequeue(DownloadManager.EnqueueInput(chapters))
|
||||||
@@ -133,25 +135,25 @@ class DownloadMutation {
|
|||||||
return future {
|
return future {
|
||||||
DequeueChapterDownloadsPayload(
|
DequeueChapterDownloadsPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
downloadStatus = withTimeout(30.seconds) {
|
downloadStatus =
|
||||||
|
withTimeout(30.seconds) {
|
||||||
DownloadStatus(DownloadManager.status.first { it.queue.none { it.chapter.id in chapters } })
|
DownloadStatus(DownloadManager.status.first { it.queue.none { it.chapter.id in chapters } })
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class DequeueChapterDownloadInput(
|
data class DequeueChapterDownloadInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val id: Int
|
val id: Int,
|
||||||
)
|
|
||||||
data class DequeueChapterDownloadPayload(
|
|
||||||
val clientMutationId: String?,
|
|
||||||
val downloadStatus: DownloadStatus
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun dequeueChapterDownload(
|
data class DequeueChapterDownloadPayload(
|
||||||
input: DequeueChapterDownloadInput
|
val clientMutationId: String?,
|
||||||
): CompletableFuture<DequeueChapterDownloadPayload> {
|
val downloadStatus: DownloadStatus,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun dequeueChapterDownload(input: DequeueChapterDownloadInput): CompletableFuture<DequeueChapterDownloadPayload> {
|
||||||
val (clientMutationId, chapter) = input
|
val (clientMutationId, chapter) = input
|
||||||
|
|
||||||
DownloadManager.dequeue(DownloadManager.EnqueueInput(listOf(chapter)))
|
DownloadManager.dequeue(DownloadManager.EnqueueInput(listOf(chapter)))
|
||||||
@@ -159,19 +161,21 @@ class DownloadMutation {
|
|||||||
return future {
|
return future {
|
||||||
DequeueChapterDownloadPayload(
|
DequeueChapterDownloadPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
downloadStatus = withTimeout(30.seconds) {
|
downloadStatus =
|
||||||
|
withTimeout(30.seconds) {
|
||||||
DownloadStatus(DownloadManager.status.first { it.queue.none { it.chapter.id == chapter } })
|
DownloadStatus(DownloadManager.status.first { it.queue.none { it.chapter.id == chapter } })
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class StartDownloaderInput(
|
data class StartDownloaderInput(
|
||||||
val clientMutationId: String? = null
|
val clientMutationId: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class StartDownloaderPayload(
|
data class StartDownloaderPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val downloadStatus: DownloadStatus
|
val downloadStatus: DownloadStatus,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun startDownloader(input: StartDownloaderInput): CompletableFuture<StartDownloaderPayload> {
|
fun startDownloader(input: StartDownloaderInput): CompletableFuture<StartDownloaderPayload> {
|
||||||
@@ -180,21 +184,23 @@ class DownloadMutation {
|
|||||||
return future {
|
return future {
|
||||||
StartDownloaderPayload(
|
StartDownloaderPayload(
|
||||||
input.clientMutationId,
|
input.clientMutationId,
|
||||||
downloadStatus = withTimeout(30.seconds) {
|
downloadStatus =
|
||||||
|
withTimeout(30.seconds) {
|
||||||
DownloadStatus(
|
DownloadStatus(
|
||||||
DownloadManager.status.first { it.status == Status.Started }
|
DownloadManager.status.first { it.status == Status.Started },
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class StopDownloaderInput(
|
data class StopDownloaderInput(
|
||||||
val clientMutationId: String? = null
|
val clientMutationId: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class StopDownloaderPayload(
|
data class StopDownloaderPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val downloadStatus: DownloadStatus
|
val downloadStatus: DownloadStatus,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun stopDownloader(input: StopDownloaderInput): CompletableFuture<StopDownloaderPayload> {
|
fun stopDownloader(input: StopDownloaderInput): CompletableFuture<StopDownloaderPayload> {
|
||||||
@@ -202,21 +208,23 @@ class DownloadMutation {
|
|||||||
DownloadManager.stop()
|
DownloadManager.stop()
|
||||||
StopDownloaderPayload(
|
StopDownloaderPayload(
|
||||||
input.clientMutationId,
|
input.clientMutationId,
|
||||||
downloadStatus = withTimeout(30.seconds) {
|
downloadStatus =
|
||||||
|
withTimeout(30.seconds) {
|
||||||
DownloadStatus(
|
DownloadStatus(
|
||||||
DownloadManager.status.first { it.status == Status.Stopped }
|
DownloadManager.status.first { it.status == Status.Stopped },
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ClearDownloaderInput(
|
data class ClearDownloaderInput(
|
||||||
val clientMutationId: String? = null
|
val clientMutationId: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ClearDownloaderPayload(
|
data class ClearDownloaderPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val downloadStatus: DownloadStatus
|
val downloadStatus: DownloadStatus,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun clearDownloader(input: ClearDownloaderInput): CompletableFuture<ClearDownloaderPayload> {
|
fun clearDownloader(input: ClearDownloaderInput): CompletableFuture<ClearDownloaderPayload> {
|
||||||
@@ -224,11 +232,12 @@ class DownloadMutation {
|
|||||||
DownloadManager.clear()
|
DownloadManager.clear()
|
||||||
ClearDownloaderPayload(
|
ClearDownloaderPayload(
|
||||||
input.clientMutationId,
|
input.clientMutationId,
|
||||||
downloadStatus = withTimeout(30.seconds) {
|
downloadStatus =
|
||||||
|
withTimeout(30.seconds) {
|
||||||
DownloadStatus(
|
DownloadStatus(
|
||||||
DownloadManager.status.first { it.status == Status.Stopped && it.queue.isEmpty() }
|
DownloadManager.status.first { it.status == Status.Stopped && it.queue.isEmpty() },
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,11 +245,12 @@ class DownloadMutation {
|
|||||||
data class ReorderChapterDownloadInput(
|
data class ReorderChapterDownloadInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val chapterId: Int,
|
val chapterId: Int,
|
||||||
val to: Int
|
val to: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ReorderChapterDownloadPayload(
|
data class ReorderChapterDownloadPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val downloadStatus: DownloadStatus
|
val downloadStatus: DownloadStatus,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun reorderChapterDownload(input: ReorderChapterDownloadInput): CompletableFuture<ReorderChapterDownloadPayload> {
|
fun reorderChapterDownload(input: ReorderChapterDownloadInput): CompletableFuture<ReorderChapterDownloadPayload> {
|
||||||
@@ -250,11 +260,12 @@ class DownloadMutation {
|
|||||||
return future {
|
return future {
|
||||||
ReorderChapterDownloadPayload(
|
ReorderChapterDownloadPayload(
|
||||||
clientMutationId,
|
clientMutationId,
|
||||||
downloadStatus = withTimeout(30.seconds) {
|
downloadStatus =
|
||||||
|
withTimeout(30.seconds) {
|
||||||
DownloadStatus(
|
DownloadStatus(
|
||||||
DownloadManager.status.first { it.queue.indexOfFirst { it.chapter.id == chapter } <= to }
|
DownloadManager.status.first { it.queue.indexOfFirst { it.chapter.id == chapter } <= to },
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,7 +273,7 @@ class DownloadMutation {
|
|||||||
data class DownloadAheadInput(
|
data class DownloadAheadInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val mangaIds: List<Int> = emptyList(),
|
val mangaIds: List<Int> = emptyList(),
|
||||||
val latestReadChapterIds: List<Int>? = null
|
val latestReadChapterIds: List<Int>? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DownloadAheadPayload(val clientMutationId: String?)
|
data class DownloadAheadPayload(val clientMutationId: String?)
|
||||||
|
|||||||
@@ -15,31 +15,37 @@ class ExtensionMutation {
|
|||||||
data class UpdateExtensionPatch(
|
data class UpdateExtensionPatch(
|
||||||
val install: Boolean? = null,
|
val install: Boolean? = null,
|
||||||
val update: Boolean? = null,
|
val update: Boolean? = null,
|
||||||
val uninstall: Boolean? = null
|
val uninstall: Boolean? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateExtensionPayload(
|
data class UpdateExtensionPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val extension: ExtensionType
|
val extension: ExtensionType,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateExtensionInput(
|
data class UpdateExtensionInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val id: String,
|
val id: String,
|
||||||
val patch: UpdateExtensionPatch
|
val patch: UpdateExtensionPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateExtensionsPayload(
|
data class UpdateExtensionsPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val extensions: List<ExtensionType>
|
val extensions: List<ExtensionType>,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateExtensionsInput(
|
data class UpdateExtensionsInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val ids: List<String>,
|
val ids: List<String>,
|
||||||
val patch: UpdateExtensionPatch
|
val patch: UpdateExtensionPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
private suspend fun updateExtensions(ids: List<String>, patch: UpdateExtensionPatch) {
|
private suspend fun updateExtensions(
|
||||||
val extensions = transaction {
|
ids: List<String>,
|
||||||
|
patch: UpdateExtensionPatch,
|
||||||
|
) {
|
||||||
|
val extensions =
|
||||||
|
transaction {
|
||||||
ExtensionTable.select { ExtensionTable.pkgName inList ids }
|
ExtensionTable.select { ExtensionTable.pkgName inList ids }
|
||||||
.map { ExtensionType(it) }
|
.map { ExtensionType(it) }
|
||||||
}
|
}
|
||||||
@@ -69,13 +75,14 @@ class ExtensionMutation {
|
|||||||
return future {
|
return future {
|
||||||
updateExtensions(listOf(id), patch)
|
updateExtensions(listOf(id), patch)
|
||||||
}.thenApply {
|
}.thenApply {
|
||||||
val extension = transaction {
|
val extension =
|
||||||
|
transaction {
|
||||||
ExtensionType(ExtensionTable.select { ExtensionTable.pkgName eq id }.first())
|
ExtensionType(ExtensionTable.select { ExtensionTable.pkgName eq id }.first())
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateExtensionPayload(
|
UpdateExtensionPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
extension = extension
|
extension = extension,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,54 +93,55 @@ class ExtensionMutation {
|
|||||||
return future {
|
return future {
|
||||||
updateExtensions(ids, patch)
|
updateExtensions(ids, patch)
|
||||||
}.thenApply {
|
}.thenApply {
|
||||||
val extensions = transaction {
|
val extensions =
|
||||||
|
transaction {
|
||||||
ExtensionTable.select { ExtensionTable.pkgName inList ids }
|
ExtensionTable.select { ExtensionTable.pkgName inList ids }
|
||||||
.map { ExtensionType(it) }
|
.map { ExtensionType(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateExtensionsPayload(
|
UpdateExtensionsPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
extensions = extensions
|
extensions = extensions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class FetchExtensionsInput(
|
data class FetchExtensionsInput(
|
||||||
val clientMutationId: String? = null
|
val clientMutationId: String? = null,
|
||||||
)
|
|
||||||
data class FetchExtensionsPayload(
|
|
||||||
val clientMutationId: String?,
|
|
||||||
val extensions: List<ExtensionType>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun fetchExtensions(
|
data class FetchExtensionsPayload(
|
||||||
input: FetchExtensionsInput
|
val clientMutationId: String?,
|
||||||
): CompletableFuture<FetchExtensionsPayload> {
|
val extensions: List<ExtensionType>,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun fetchExtensions(input: FetchExtensionsInput): CompletableFuture<FetchExtensionsPayload> {
|
||||||
val (clientMutationId) = input
|
val (clientMutationId) = input
|
||||||
|
|
||||||
return future {
|
return future {
|
||||||
ExtensionsList.fetchExtensions()
|
ExtensionsList.fetchExtensions()
|
||||||
}.thenApply {
|
}.thenApply {
|
||||||
val extensions = transaction {
|
val extensions =
|
||||||
|
transaction {
|
||||||
ExtensionTable.select { ExtensionTable.name neq LocalSource.EXTENSION_NAME }
|
ExtensionTable.select { ExtensionTable.name neq LocalSource.EXTENSION_NAME }
|
||||||
.map { ExtensionType(it) }
|
.map { ExtensionType(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
FetchExtensionsPayload(
|
FetchExtensionsPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
extensions = extensions
|
extensions = extensions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class InstallExternalExtensionInput(
|
data class InstallExternalExtensionInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val extensionFile: UploadedFile
|
val extensionFile: UploadedFile,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class InstallExternalExtensionPayload(
|
data class InstallExternalExtensionPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val extension: ExtensionType
|
val extension: ExtensionType,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun installExternalExtension(input: InstallExternalExtensionInput): CompletableFuture<InstallExternalExtensionPayload> {
|
fun installExternalExtension(input: InstallExternalExtensionInput): CompletableFuture<InstallExternalExtensionPayload> {
|
||||||
@@ -146,7 +154,7 @@ class ExtensionMutation {
|
|||||||
|
|
||||||
InstallExternalExtensionPayload(
|
InstallExternalExtensionPayload(
|
||||||
clientMutationId,
|
clientMutationId,
|
||||||
extension = ExtensionType(dbExtension)
|
extension = ExtensionType(dbExtension),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
|
|
||||||
class InfoMutation {
|
class InfoMutation {
|
||||||
data class WebUIUpdateInput(
|
data class WebUIUpdateInput(
|
||||||
val clientMutationId: String? = null
|
val clientMutationId: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class WebUIUpdatePayload(
|
data class WebUIUpdatePayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val updateStatus: WebUIUpdateStatus
|
val updateStatus: WebUIUpdateStatus,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun updateWebUI(input: WebUIUpdateInput): CompletableFuture<WebUIUpdatePayload> {
|
fun updateWebUI(input: WebUIUpdateInput): CompletableFuture<WebUIUpdatePayload> {
|
||||||
@@ -35,14 +35,15 @@ class InfoMutation {
|
|||||||
return@withTimeout WebUIUpdatePayload(
|
return@withTimeout WebUIUpdatePayload(
|
||||||
input.clientMutationId,
|
input.clientMutationId,
|
||||||
WebUIUpdateStatus(
|
WebUIUpdateStatus(
|
||||||
info = WebUIUpdateInfo(
|
info =
|
||||||
|
WebUIUpdateInfo(
|
||||||
channel = serverConfig.webUIChannel.value,
|
channel = serverConfig.webUIChannel.value,
|
||||||
tag = version,
|
tag = version,
|
||||||
updateAvailable
|
updateAvailable,
|
||||||
),
|
),
|
||||||
state = STOPPED,
|
state = STOPPED,
|
||||||
progress = 0
|
progress = 0,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -53,7 +54,7 @@ class InfoMutation {
|
|||||||
|
|
||||||
WebUIUpdatePayload(
|
WebUIUpdatePayload(
|
||||||
input.clientMutationId,
|
input.clientMutationId,
|
||||||
updateStatus = WebInterfaceManager.status.first { it.state == DOWNLOADING }
|
updateStatus = WebInterfaceManager.status.first { it.state == DOWNLOADING },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,30 +22,35 @@ import java.util.concurrent.CompletableFuture
|
|||||||
*/
|
*/
|
||||||
class MangaMutation {
|
class MangaMutation {
|
||||||
data class UpdateMangaPatch(
|
data class UpdateMangaPatch(
|
||||||
val inLibrary: Boolean? = null
|
val inLibrary: Boolean? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateMangaPayload(
|
data class UpdateMangaPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val manga: MangaType
|
val manga: MangaType,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateMangaInput(
|
data class UpdateMangaInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val patch: UpdateMangaPatch
|
val patch: UpdateMangaPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateMangasPayload(
|
data class UpdateMangasPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val mangas: List<MangaType>
|
val mangas: List<MangaType>,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateMangasInput(
|
data class UpdateMangasInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val ids: List<Int>,
|
val ids: List<Int>,
|
||||||
val patch: UpdateMangaPatch
|
val patch: UpdateMangaPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
private suspend fun updateMangas(ids: List<Int>, patch: UpdateMangaPatch) {
|
private suspend fun updateMangas(
|
||||||
|
ids: List<Int>,
|
||||||
|
patch: UpdateMangaPatch,
|
||||||
|
) {
|
||||||
transaction {
|
transaction {
|
||||||
if (patch.inLibrary != null) {
|
if (patch.inLibrary != null) {
|
||||||
MangaTable.update({ MangaTable.id inList ids }) { update ->
|
MangaTable.update({ MangaTable.id inList ids }) { update ->
|
||||||
@@ -69,13 +74,14 @@ class MangaMutation {
|
|||||||
return future {
|
return future {
|
||||||
updateMangas(listOf(id), patch)
|
updateMangas(listOf(id), patch)
|
||||||
}.thenApply {
|
}.thenApply {
|
||||||
val manga = transaction {
|
val manga =
|
||||||
|
transaction {
|
||||||
MangaType(MangaTable.select { MangaTable.id eq id }.first())
|
MangaType(MangaTable.select { MangaTable.id eq id }.first())
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateMangaPayload(
|
UpdateMangaPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
manga = manga
|
manga = manga,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,55 +92,56 @@ class MangaMutation {
|
|||||||
return future {
|
return future {
|
||||||
updateMangas(ids, patch)
|
updateMangas(ids, patch)
|
||||||
}.thenApply {
|
}.thenApply {
|
||||||
val mangas = transaction {
|
val mangas =
|
||||||
|
transaction {
|
||||||
MangaTable.select { MangaTable.id inList ids }.map { MangaType(it) }
|
MangaTable.select { MangaTable.id inList ids }.map { MangaType(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateMangasPayload(
|
UpdateMangasPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
mangas = mangas
|
mangas = mangas,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class FetchMangaInput(
|
data class FetchMangaInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val id: Int
|
val id: Int,
|
||||||
)
|
|
||||||
data class FetchMangaPayload(
|
|
||||||
val clientMutationId: String?,
|
|
||||||
val manga: MangaType
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun fetchManga(
|
data class FetchMangaPayload(
|
||||||
input: FetchMangaInput
|
val clientMutationId: String?,
|
||||||
): CompletableFuture<FetchMangaPayload> {
|
val manga: MangaType,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun fetchManga(input: FetchMangaInput): CompletableFuture<FetchMangaPayload> {
|
||||||
val (clientMutationId, id) = input
|
val (clientMutationId, id) = input
|
||||||
|
|
||||||
return future {
|
return future {
|
||||||
Manga.fetchManga(id)
|
Manga.fetchManga(id)
|
||||||
}.thenApply {
|
}.thenApply {
|
||||||
val manga = transaction {
|
val manga =
|
||||||
|
transaction {
|
||||||
MangaTable.select { MangaTable.id eq id }.first()
|
MangaTable.select { MangaTable.id eq id }.first()
|
||||||
}
|
}
|
||||||
FetchMangaPayload(
|
FetchMangaPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
manga = MangaType(manga)
|
manga = MangaType(manga),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class SetMangaMetaInput(
|
data class SetMangaMetaInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val meta: MangaMetaType
|
val meta: MangaMetaType,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class SetMangaMetaPayload(
|
data class SetMangaMetaPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val meta: MangaMetaType
|
val meta: MangaMetaType,
|
||||||
)
|
)
|
||||||
fun setMangaMeta(
|
|
||||||
input: SetMangaMetaInput
|
fun setMangaMeta(input: SetMangaMetaInput): SetMangaMetaPayload {
|
||||||
): SetMangaMetaPayload {
|
|
||||||
val (clientMutationId, meta) = input
|
val (clientMutationId, meta) = input
|
||||||
|
|
||||||
Manga.modifyMangaMeta(meta.mangaId, meta.key, meta.value)
|
Manga.modifyMangaMeta(meta.mangaId, meta.key, meta.value)
|
||||||
@@ -145,25 +152,28 @@ class MangaMutation {
|
|||||||
data class DeleteMangaMetaInput(
|
data class DeleteMangaMetaInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val mangaId: Int,
|
val mangaId: Int,
|
||||||
val key: String
|
val key: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DeleteMangaMetaPayload(
|
data class DeleteMangaMetaPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val meta: MangaMetaType?,
|
val meta: MangaMetaType?,
|
||||||
val manga: MangaType
|
val manga: MangaType,
|
||||||
)
|
)
|
||||||
fun deleteMangaMeta(
|
|
||||||
input: DeleteMangaMetaInput
|
fun deleteMangaMeta(input: DeleteMangaMetaInput): DeleteMangaMetaPayload {
|
||||||
): DeleteMangaMetaPayload {
|
|
||||||
val (clientMutationId, mangaId, key) = input
|
val (clientMutationId, mangaId, key) = input
|
||||||
|
|
||||||
val (meta, manga) = transaction {
|
val (meta, manga) =
|
||||||
val meta = MangaMetaTable.select { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) }
|
transaction {
|
||||||
|
val meta =
|
||||||
|
MangaMetaTable.select { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) }
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
|
|
||||||
MangaMetaTable.deleteWhere { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) }
|
MangaMetaTable.deleteWhere { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) }
|
||||||
|
|
||||||
val manga = transaction {
|
val manga =
|
||||||
|
transaction {
|
||||||
MangaType(MangaTable.select { MangaTable.id eq mangaId }.first())
|
MangaType(MangaTable.select { MangaTable.id eq mangaId }.first())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,18 +9,17 @@ import suwayomi.tachidesk.global.model.table.GlobalMetaTable
|
|||||||
import suwayomi.tachidesk.graphql.types.GlobalMetaType
|
import suwayomi.tachidesk.graphql.types.GlobalMetaType
|
||||||
|
|
||||||
class MetaMutation {
|
class MetaMutation {
|
||||||
|
|
||||||
data class SetGlobalMetaInput(
|
data class SetGlobalMetaInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val meta: GlobalMetaType
|
val meta: GlobalMetaType,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class SetGlobalMetaPayload(
|
data class SetGlobalMetaPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val meta: GlobalMetaType
|
val meta: GlobalMetaType,
|
||||||
)
|
)
|
||||||
fun setGlobalMeta(
|
|
||||||
input: SetGlobalMetaInput
|
fun setGlobalMeta(input: SetGlobalMetaInput): SetGlobalMetaPayload {
|
||||||
): SetGlobalMetaPayload {
|
|
||||||
val (clientMutationId, meta) = input
|
val (clientMutationId, meta) = input
|
||||||
|
|
||||||
GlobalMeta.modifyMeta(meta.key, meta.value)
|
GlobalMeta.modifyMeta(meta.key, meta.value)
|
||||||
@@ -30,19 +29,21 @@ class MetaMutation {
|
|||||||
|
|
||||||
data class DeleteGlobalMetaInput(
|
data class DeleteGlobalMetaInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val key: String
|
val key: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DeleteGlobalMetaPayload(
|
data class DeleteGlobalMetaPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val meta: GlobalMetaType?
|
val meta: GlobalMetaType?,
|
||||||
)
|
)
|
||||||
fun deleteGlobalMeta(
|
|
||||||
input: DeleteGlobalMetaInput
|
fun deleteGlobalMeta(input: DeleteGlobalMetaInput): DeleteGlobalMetaPayload {
|
||||||
): DeleteGlobalMetaPayload {
|
|
||||||
val (clientMutationId, key) = input
|
val (clientMutationId, key) = input
|
||||||
|
|
||||||
val meta = transaction {
|
val meta =
|
||||||
val meta = GlobalMetaTable.select { GlobalMetaTable.key eq key }
|
transaction {
|
||||||
|
val meta =
|
||||||
|
GlobalMetaTable.select { GlobalMetaTable.key eq key }
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
|
|
||||||
GlobalMetaTable.deleteWhere { GlobalMetaTable.key eq key }
|
GlobalMetaTable.deleteWhere { GlobalMetaTable.key eq key }
|
||||||
|
|||||||
@@ -11,53 +11,107 @@ import xyz.nulldev.ts.config.GlobalConfigManager
|
|||||||
class SettingsMutation {
|
class SettingsMutation {
|
||||||
data class SetSettingsInput(
|
data class SetSettingsInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val settings: PartialSettingsType
|
val settings: PartialSettingsType,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class SetSettingsPayload(
|
data class SetSettingsPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val settings: SettingsType
|
val settings: SettingsType,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun updateSettings(settings: Settings) {
|
private fun updateSettings(settings: Settings) {
|
||||||
if (settings.ip != null) serverConfig.ip.value = settings.ip!!
|
if (settings.ip != null) serverConfig.ip.value = settings.ip!!
|
||||||
if (settings.port != null) serverConfig.port.value = settings.port!!
|
if (settings.port != null) serverConfig.port.value = settings.port!!
|
||||||
|
|
||||||
if (settings.socksProxyEnabled != null) serverConfig.socksProxyEnabled.value = settings.socksProxyEnabled!!
|
if (settings.socksProxyEnabled != null) {
|
||||||
if (settings.socksProxyHost != null) serverConfig.socksProxyHost.value = settings.socksProxyHost!!
|
serverConfig.socksProxyEnabled.value = settings.socksProxyEnabled!!
|
||||||
if (settings.socksProxyPort != null) serverConfig.socksProxyPort.value = settings.socksProxyPort!!
|
}
|
||||||
|
if (settings.socksProxyHost != null) {
|
||||||
|
serverConfig.socksProxyHost.value = settings.socksProxyHost!!
|
||||||
|
}
|
||||||
|
if (settings.socksProxyPort != null) {
|
||||||
|
serverConfig.socksProxyPort.value = settings.socksProxyPort!!
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.webUIFlavor != null) serverConfig.webUIFlavor.value = settings.webUIFlavor!!.uiName
|
if (settings.webUIFlavor != null) {
|
||||||
if (settings.initialOpenInBrowserEnabled != null) serverConfig.initialOpenInBrowserEnabled.value = settings.initialOpenInBrowserEnabled!!
|
serverConfig.webUIFlavor.value = settings.webUIFlavor!!.uiName
|
||||||
if (settings.webUIInterface != null) serverConfig.webUIInterface.value = settings.webUIInterface!!.name.lowercase()
|
}
|
||||||
if (settings.electronPath != null) serverConfig.electronPath.value = settings.electronPath!!
|
if (settings.initialOpenInBrowserEnabled != null) {
|
||||||
if (settings.webUIChannel != null) serverConfig.webUIChannel.value = settings.webUIChannel!!.name.lowercase()
|
serverConfig.initialOpenInBrowserEnabled.value = settings.initialOpenInBrowserEnabled!!
|
||||||
if (settings.webUIUpdateCheckInterval != null) serverConfig.webUIUpdateCheckInterval.value = settings.webUIUpdateCheckInterval!!
|
}
|
||||||
|
if (settings.webUIInterface != null) {
|
||||||
|
serverConfig.webUIInterface.value = settings.webUIInterface!!.name.lowercase()
|
||||||
|
}
|
||||||
|
if (settings.electronPath != null) {
|
||||||
|
serverConfig.electronPath.value = settings.electronPath!!
|
||||||
|
}
|
||||||
|
if (settings.webUIChannel != null) {
|
||||||
|
serverConfig.webUIChannel.value = settings.webUIChannel!!.name.lowercase()
|
||||||
|
}
|
||||||
|
if (settings.webUIUpdateCheckInterval != null) {
|
||||||
|
serverConfig.webUIUpdateCheckInterval.value = settings.webUIUpdateCheckInterval!!
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.downloadAsCbz != null) serverConfig.downloadAsCbz.value = settings.downloadAsCbz!!
|
if (settings.downloadAsCbz != null) {
|
||||||
if (settings.downloadsPath != null) serverConfig.downloadsPath.value = settings.downloadsPath!!
|
serverConfig.downloadAsCbz.value = settings.downloadAsCbz!!
|
||||||
if (settings.autoDownloadNewChapters != null) serverConfig.autoDownloadNewChapters.value = settings.autoDownloadNewChapters!!
|
}
|
||||||
|
if (settings.downloadsPath != null) {
|
||||||
|
serverConfig.downloadsPath.value = settings.downloadsPath!!
|
||||||
|
}
|
||||||
|
if (settings.autoDownloadNewChapters != null) {
|
||||||
|
serverConfig.autoDownloadNewChapters.value = settings.autoDownloadNewChapters!!
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.maxSourcesInParallel != null) serverConfig.maxSourcesInParallel.value = settings.maxSourcesInParallel!!
|
if (settings.maxSourcesInParallel != null) {
|
||||||
|
serverConfig.maxSourcesInParallel.value = settings.maxSourcesInParallel!!
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.excludeUnreadChapters != null) serverConfig.excludeUnreadChapters.value = settings.excludeUnreadChapters!!
|
if (settings.excludeUnreadChapters != null) {
|
||||||
if (settings.excludeNotStarted != null) serverConfig.excludeNotStarted.value = settings.excludeNotStarted!!
|
serverConfig.excludeUnreadChapters.value = settings.excludeUnreadChapters!!
|
||||||
if (settings.excludeCompleted != null) serverConfig.excludeCompleted.value = settings.excludeCompleted!!
|
}
|
||||||
if (settings.globalUpdateInterval != null) serverConfig.globalUpdateInterval.value = settings.globalUpdateInterval!!
|
if (settings.excludeNotStarted != null) {
|
||||||
|
serverConfig.excludeNotStarted.value = settings.excludeNotStarted!!
|
||||||
|
}
|
||||||
|
if (settings.excludeCompleted != null) {
|
||||||
|
serverConfig.excludeCompleted.value = settings.excludeCompleted!!
|
||||||
|
}
|
||||||
|
if (settings.globalUpdateInterval != null) {
|
||||||
|
serverConfig.globalUpdateInterval.value = settings.globalUpdateInterval!!
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.basicAuthEnabled != null) serverConfig.basicAuthEnabled.value = settings.basicAuthEnabled!!
|
if (settings.basicAuthEnabled != null) {
|
||||||
if (settings.basicAuthUsername != null) serverConfig.basicAuthUsername.value = settings.basicAuthUsername!!
|
serverConfig.basicAuthEnabled.value = settings.basicAuthEnabled!!
|
||||||
if (settings.basicAuthPassword != null) serverConfig.basicAuthPassword.value = settings.basicAuthPassword!!
|
}
|
||||||
|
if (settings.basicAuthUsername != null) {
|
||||||
|
serverConfig.basicAuthUsername.value = settings.basicAuthUsername!!
|
||||||
|
}
|
||||||
|
if (settings.basicAuthPassword != null) {
|
||||||
|
serverConfig.basicAuthPassword.value = settings.basicAuthPassword!!
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.debugLogsEnabled != null) serverConfig.debugLogsEnabled.value = settings.debugLogsEnabled!!
|
if (settings.debugLogsEnabled != null) {
|
||||||
if (settings.systemTrayEnabled != null) serverConfig.systemTrayEnabled.value = settings.systemTrayEnabled!!
|
serverConfig.debugLogsEnabled.value = settings.debugLogsEnabled!!
|
||||||
|
}
|
||||||
|
if (settings.systemTrayEnabled != null) {
|
||||||
|
serverConfig.systemTrayEnabled.value = settings.systemTrayEnabled!!
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.backupPath != null) serverConfig.backupPath.value = settings.backupPath!!
|
if (settings.backupPath != null) {
|
||||||
if (settings.backupTime != null) serverConfig.backupTime.value = settings.backupTime!!
|
serverConfig.backupPath.value = settings.backupPath!!
|
||||||
if (settings.backupInterval != null) serverConfig.backupInterval.value = settings.backupInterval!!
|
}
|
||||||
if (settings.backupTTL != null) serverConfig.backupTTL.value = settings.backupTTL!!
|
if (settings.backupTime != null) {
|
||||||
|
serverConfig.backupTime.value = settings.backupTime!!
|
||||||
|
}
|
||||||
|
if (settings.backupInterval != null) {
|
||||||
|
serverConfig.backupInterval.value = settings.backupInterval!!
|
||||||
|
}
|
||||||
|
if (settings.backupTTL != null) {
|
||||||
|
serverConfig.backupTTL.value = settings.backupTTL!!
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.localSourcePath != null) serverConfig.localSourcePath.value = settings.localSourcePath!!
|
if (settings.localSourcePath != null) {
|
||||||
|
serverConfig.localSourcePath.value = settings.localSourcePath!!
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSettings(input: SetSettingsInput): SetSettingsPayload {
|
fun setSettings(input: SetSettingsInput): SetSettingsPayload {
|
||||||
@@ -72,7 +126,7 @@ class SettingsMutation {
|
|||||||
|
|
||||||
data class ResetSettingsPayload(
|
data class ResetSettingsPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val settings: SettingsType
|
val settings: SettingsType,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun resetSettings(input: ResetSettingsInput): ResetSettingsPayload {
|
fun resetSettings(input: ResetSettingsInput): ResetSettingsPayload {
|
||||||
|
|||||||
@@ -20,39 +20,39 @@ import suwayomi.tachidesk.server.JavalinSetup.future
|
|||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
class SourceMutation {
|
class SourceMutation {
|
||||||
|
|
||||||
enum class FetchSourceMangaType {
|
enum class FetchSourceMangaType {
|
||||||
SEARCH,
|
SEARCH,
|
||||||
POPULAR,
|
POPULAR,
|
||||||
LATEST
|
LATEST,
|
||||||
}
|
}
|
||||||
|
|
||||||
data class FetchSourceMangaInput(
|
data class FetchSourceMangaInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val source: Long,
|
val source: Long,
|
||||||
val type: FetchSourceMangaType,
|
val type: FetchSourceMangaType,
|
||||||
val page: Int,
|
val page: Int,
|
||||||
val query: String? = null,
|
val query: String? = null,
|
||||||
val filters: List<FilterChange>? = null
|
val filters: List<FilterChange>? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class FetchSourceMangaPayload(
|
data class FetchSourceMangaPayload(
|
||||||
val clientMutationId: String?,
|
val clientMutationId: String?,
|
||||||
val mangas: List<MangaType>,
|
val mangas: List<MangaType>,
|
||||||
val hasNextPage: Boolean
|
val hasNextPage: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun fetchSourceManga(
|
fun fetchSourceManga(input: FetchSourceMangaInput): CompletableFuture<FetchSourceMangaPayload> {
|
||||||
input: FetchSourceMangaInput
|
|
||||||
): CompletableFuture<FetchSourceMangaPayload> {
|
|
||||||
val (clientMutationId, sourceId, type, page, query, filters) = input
|
val (clientMutationId, sourceId, type, page, query, filters) = input
|
||||||
|
|
||||||
return future {
|
return future {
|
||||||
val source = GetCatalogueSource.getCatalogueSourceOrNull(sourceId)!!
|
val source = GetCatalogueSource.getCatalogueSourceOrNull(sourceId)!!
|
||||||
val mangasPage = when (type) {
|
val mangasPage =
|
||||||
|
when (type) {
|
||||||
FetchSourceMangaType.SEARCH -> {
|
FetchSourceMangaType.SEARCH -> {
|
||||||
source.getSearchManga(
|
source.getSearchManga(
|
||||||
page = page,
|
page = page,
|
||||||
query = query.orEmpty(),
|
query = query.orEmpty(),
|
||||||
filters = updateFilterList(source, filters)
|
filters = updateFilterList(source, filters),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
FetchSourceMangaType.POPULAR -> {
|
FetchSourceMangaType.POPULAR -> {
|
||||||
@@ -66,7 +66,8 @@ class SourceMutation {
|
|||||||
|
|
||||||
val mangaIds = mangasPage.insertOrGet(sourceId)
|
val mangaIds = mangasPage.insertOrGet(sourceId)
|
||||||
|
|
||||||
val mangas = transaction {
|
val mangas =
|
||||||
|
transaction {
|
||||||
MangaTable.select { MangaTable.id inList mangaIds }
|
MangaTable.select { MangaTable.id inList mangaIds }
|
||||||
.map { MangaType(it) }
|
.map { MangaType(it) }
|
||||||
}.sortedBy {
|
}.sortedBy {
|
||||||
@@ -76,7 +77,7 @@ class SourceMutation {
|
|||||||
FetchSourceMangaPayload(
|
FetchSourceMangaPayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
mangas = mangas,
|
mangas = mangas,
|
||||||
hasNextPage = mangasPage.hasNextPage
|
hasNextPage = mangasPage.hasNextPage,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,21 +88,21 @@ class SourceMutation {
|
|||||||
val checkBoxState: Boolean? = null,
|
val checkBoxState: Boolean? = null,
|
||||||
val editTextState: String? = null,
|
val editTextState: String? = null,
|
||||||
val listState: String? = null,
|
val listState: String? = null,
|
||||||
val multiSelectState: List<String>? = null
|
val multiSelectState: List<String>? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateSourcePreferenceInput(
|
data class UpdateSourcePreferenceInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val source: Long,
|
val source: Long,
|
||||||
val change: SourcePreferenceChange
|
val change: SourcePreferenceChange,
|
||||||
)
|
|
||||||
data class UpdateSourcePreferencePayload(
|
|
||||||
val clientMutationId: String?,
|
|
||||||
val preferences: List<Preference>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun updateSourcePreference(
|
data class UpdateSourcePreferencePayload(
|
||||||
input: UpdateSourcePreferenceInput
|
val clientMutationId: String?,
|
||||||
): UpdateSourcePreferencePayload {
|
val preferences: List<Preference>,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun updateSourcePreference(input: UpdateSourcePreferenceInput): UpdateSourcePreferencePayload {
|
||||||
val (clientMutationId, sourceId, change) = input
|
val (clientMutationId, sourceId, change) = input
|
||||||
|
|
||||||
Source.setSourcePreference(sourceId, change.position, "") { preference ->
|
Source.setSourcePreference(sourceId, change.position, "") { preference ->
|
||||||
@@ -117,7 +118,7 @@ class SourceMutation {
|
|||||||
|
|
||||||
return UpdateSourcePreferencePayload(
|
return UpdateSourcePreferencePayload(
|
||||||
clientMutationId = clientMutationId,
|
clientMutationId = clientMutationId,
|
||||||
preferences = Source.getSourcePreferencesRaw(sourceId).map { preferenceOf(it) }
|
preferences = Source.getSourcePreferencesRaw(sourceId).map { preferenceOf(it) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user