mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-01 01:44:34 -05:00
Remote Image Processing (#1684)
* Update ServerConfig.kt * Update ConversionUtil.kt * Update Page.kt * Update ServerConfig.kt fixed deletions caused by ide * Update ServerConfig.kt * Update ServerConfig.kt * Cleanup * Post-processing terminology * More comments * Lint * Add known image mimes * Fix weird mime set/get * Implement different downloadConversions and serveConversions * Lint * Improve Post-Processing massivly * Fix thumbnail build * Use Array for headers * Actually fix headers * Actually fix headers 2 * Manually parse DownloadConversion * Cleanup parse * Fix write * Update TypeName * Optimize imports * Remove header type * Fix build --------- Co-authored-by: Syer10 <syer10@users.noreply.github.com>
This commit is contained in:
@@ -1,21 +1,42 @@
|
||||
package suwayomi.tachidesk.graphql.types
|
||||
|
||||
import kotlin.time.Duration
|
||||
|
||||
// These types belong to SettingsType.kt. However, since that file is auto-generated, these types need to be placed in
|
||||
// a "static" file.
|
||||
|
||||
data class DownloadConversion(
|
||||
class DownloadConversion(
|
||||
val target: String,
|
||||
val compressionLevel: Double? = null,
|
||||
val callTimeout: Duration? = null,
|
||||
val connectTimeout: Duration? = null,
|
||||
val headers: Map<String, String>? = null,
|
||||
)
|
||||
|
||||
interface SettingsDownloadConversion {
|
||||
val mimeType: String
|
||||
val target: String
|
||||
val compressionLevel: Double?
|
||||
val callTimeout: Duration?
|
||||
val connectTimeout: Duration?
|
||||
val headers: List<SettingsDownloadConversionHeader>?
|
||||
}
|
||||
|
||||
class SettingsDownloadConversionType(
|
||||
override val mimeType: String,
|
||||
override val target: String,
|
||||
override val compressionLevel: Double?,
|
||||
override val callTimeout: Duration?,
|
||||
override val connectTimeout: Duration?,
|
||||
override val headers: List<SettingsDownloadConversionHeaderType>?
|
||||
) : SettingsDownloadConversion
|
||||
|
||||
interface SettingsDownloadConversionHeader {
|
||||
val name: String
|
||||
val value: String
|
||||
}
|
||||
|
||||
class SettingsDownloadConversionHeaderType(
|
||||
override val name: String,
|
||||
override val value: String,
|
||||
) : SettingsDownloadConversionHeader
|
||||
|
||||
@@ -4,6 +4,8 @@ import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoNumber
|
||||
import suwayomi.tachidesk.graphql.types.SettingsDownloadConversion
|
||||
import suwayomi.tachidesk.graphql.types.SettingsDownloadConversionHeader
|
||||
import kotlin.time.Duration
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Serializable
|
||||
@@ -11,4 +13,15 @@ class BackupSettingsDownloadConversionType(
|
||||
@ProtoNumber(1) override val mimeType: String,
|
||||
@ProtoNumber(2) override val target: String,
|
||||
@ProtoNumber(3) override val compressionLevel: Double?,
|
||||
) : SettingsDownloadConversion
|
||||
@ProtoNumber(4) override val callTimeout: Duration?,
|
||||
@ProtoNumber(5) override val connectTimeout: Duration?,
|
||||
@ProtoNumber(6) override val headers: List<BackupSettingsDownloadConversionHeaderType>?
|
||||
) : SettingsDownloadConversion
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Serializable
|
||||
class BackupSettingsDownloadConversionHeaderType(
|
||||
@ProtoNumber(1) override val name: String,
|
||||
@ProtoNumber(2) override val value: String
|
||||
|
||||
): SettingsDownloadConversionHeader
|
||||
|
||||
@@ -33,11 +33,13 @@ import suwayomi.tachidesk.graphql.types.DownloadConversion
|
||||
import suwayomi.tachidesk.graphql.types.KoreaderSyncChecksumMethod
|
||||
import suwayomi.tachidesk.graphql.types.KoreaderSyncConflictStrategy
|
||||
import suwayomi.tachidesk.graphql.types.KoreaderSyncLegacyStrategy
|
||||
import suwayomi.tachidesk.graphql.types.SettingsDownloadConversionHeaderType
|
||||
import suwayomi.tachidesk.graphql.types.SettingsDownloadConversionType
|
||||
import suwayomi.tachidesk.graphql.types.WebUIChannel
|
||||
import suwayomi.tachidesk.graphql.types.WebUIFlavor
|
||||
import suwayomi.tachidesk.graphql.types.WebUIInterface
|
||||
import suwayomi.tachidesk.manga.impl.backup.BackupFlags
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.models.BackupSettingsDownloadConversionHeaderType
|
||||
import suwayomi.tachidesk.manga.impl.backup.proto.models.BackupSettingsDownloadConversionType
|
||||
import suwayomi.tachidesk.manga.impl.extension.repoMatchRegex
|
||||
import suwayomi.tachidesk.server.settings.BooleanSetting
|
||||
@@ -537,8 +539,8 @@ class ServerConfig(
|
||||
excludeFromBackup = true,
|
||||
)
|
||||
|
||||
val downloadConversions: MutableStateFlow<Map<String, DownloadConversion>> by MapSetting<String, DownloadConversion>(
|
||||
protoNumber = 57,
|
||||
fun createDownloadConversionsMap(protoNumber: Int, key: String) = MapSetting<String, DownloadConversion>(
|
||||
protoNumber = protoNumber,
|
||||
defaultValue = emptyMap(),
|
||||
group = SettingGroup.DOWNLOADER,
|
||||
typeInfo =
|
||||
@@ -559,6 +561,14 @@ class ServerConfig(
|
||||
it.key,
|
||||
it.value.target,
|
||||
it.value.compressionLevel,
|
||||
it.value.callTimeout,
|
||||
it.value.connectTimeout,
|
||||
it.value.headers?.map { header ->
|
||||
SettingsDownloadConversionHeaderType(
|
||||
header.key,
|
||||
header.value,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -571,6 +581,11 @@ class ServerConfig(
|
||||
DownloadConversion(
|
||||
target = it.target,
|
||||
compressionLevel = it.compressionLevel,
|
||||
callTimeout = it.callTimeout,
|
||||
connectTimeout = it.connectTimeout,
|
||||
headers = it.headers?.associate { header ->
|
||||
header.name to header.value
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -583,6 +598,14 @@ class ServerConfig(
|
||||
it.key,
|
||||
it.value.target,
|
||||
it.value.compressionLevel,
|
||||
it.value.callTimeout,
|
||||
it.value.connectTimeout,
|
||||
it.value.headers?.map { header ->
|
||||
BackupSettingsDownloadConversionHeaderType(
|
||||
header.key,
|
||||
header.value,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -590,12 +613,16 @@ class ServerConfig(
|
||||
description =
|
||||
"""
|
||||
map input mime type to conversion information, or "default" for others
|
||||
server.downloadConversions."image/webp" = {
|
||||
target = "image/jpeg" # image type to convert to
|
||||
server.$key."image/webp" = {
|
||||
target = "image/jpeg" # image type to convert to, can also be a url to an external server
|
||||
compressionLevel = 0.8 # quality in range [0,1], leave away to use default compression
|
||||
}
|
||||
""".trimIndent(),
|
||||
)
|
||||
val downloadConversions: MutableStateFlow<Map<String, DownloadConversion>> by createDownloadConversionsMap(
|
||||
protoNumber = 57,
|
||||
key = "downloadConversions"
|
||||
)
|
||||
|
||||
val jwtAudience: MutableStateFlow<String> by StringSetting(
|
||||
protoNumber = 58,
|
||||
@@ -669,6 +696,7 @@ class ServerConfig(
|
||||
typeInfo = SettingsRegistry.PartialTypeInfo(imports = listOf("suwayomi.tachidesk.graphql.types.KoreaderSyncChecksumMethod")),
|
||||
)
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@Deprecated("Use koreaderSyncStrategyForward and koreaderSyncStrategyBackward instead")
|
||||
val koreaderSyncStrategy: MutableStateFlow<KoreaderSyncLegacyStrategy> by MigratedConfigValue(
|
||||
protoNumber = 64,
|
||||
@@ -707,15 +735,11 @@ class ServerConfig(
|
||||
),
|
||||
readMigrated = {
|
||||
// This is a best-effort reverse mapping. It's not perfect but covers common cases.
|
||||
when {
|
||||
koreaderSyncStrategyForward.value == KoreaderSyncConflictStrategy.PROMPT &&
|
||||
koreaderSyncStrategyBackward.value == KoreaderSyncConflictStrategy.PROMPT -> KoreaderSyncLegacyStrategy.PROMPT
|
||||
koreaderSyncStrategyForward.value == KoreaderSyncConflictStrategy.KEEP_REMOTE &&
|
||||
koreaderSyncStrategyBackward.value == KoreaderSyncConflictStrategy.KEEP_LOCAL -> KoreaderSyncLegacyStrategy.SILENT
|
||||
koreaderSyncStrategyForward.value == KoreaderSyncConflictStrategy.KEEP_LOCAL &&
|
||||
koreaderSyncStrategyBackward.value == KoreaderSyncConflictStrategy.KEEP_LOCAL -> KoreaderSyncLegacyStrategy.SEND
|
||||
koreaderSyncStrategyForward.value == KoreaderSyncConflictStrategy.KEEP_REMOTE &&
|
||||
koreaderSyncStrategyBackward.value == KoreaderSyncConflictStrategy.KEEP_REMOTE -> KoreaderSyncLegacyStrategy.RECEIVE
|
||||
when (koreaderSyncStrategyForward.value) {
|
||||
KoreaderSyncConflictStrategy.PROMPT if koreaderSyncStrategyBackward.value == KoreaderSyncConflictStrategy.PROMPT -> KoreaderSyncLegacyStrategy.PROMPT
|
||||
KoreaderSyncConflictStrategy.KEEP_REMOTE if koreaderSyncStrategyBackward.value == KoreaderSyncConflictStrategy.KEEP_LOCAL -> KoreaderSyncLegacyStrategy.SILENT
|
||||
KoreaderSyncConflictStrategy.KEEP_LOCAL if koreaderSyncStrategyBackward.value == KoreaderSyncConflictStrategy.KEEP_LOCAL -> KoreaderSyncLegacyStrategy.SEND
|
||||
KoreaderSyncConflictStrategy.KEEP_REMOTE if koreaderSyncStrategyBackward.value == KoreaderSyncConflictStrategy.KEEP_REMOTE -> KoreaderSyncLegacyStrategy.RECEIVE
|
||||
else -> KoreaderSyncLegacyStrategy.DISABLED
|
||||
}
|
||||
},
|
||||
@@ -885,6 +909,11 @@ class ServerConfig(
|
||||
description = "Controls the MimeType that Suwayomi sends in OPDS entries for CBZ archives. Also affects global CBZ download. Modern follows recent IANA standard (2017), while LEGACY (deprecated mimetype for .cbz) and COMPATIBLE (deprecated mimetype for all comic archives) might be more compatible with older clients.",
|
||||
)
|
||||
|
||||
val serveConversions: MutableStateFlow<Map<String, DownloadConversion>> by createDownloadConversionsMap(
|
||||
protoNumber = 84,
|
||||
key = "serveConversions"
|
||||
)
|
||||
|
||||
|
||||
|
||||
/** ****************************************************************** **/
|
||||
|
||||
@@ -14,6 +14,7 @@ object ConfigTypeRegistration {
|
||||
|
||||
registerCustomType(MutableStateFlowType())
|
||||
registerCustomType(DurationType())
|
||||
registerCustomType(DownloadConversionType())
|
||||
|
||||
registered = true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package suwayomi.tachidesk.server.util
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigValue
|
||||
import io.github.config4k.ClassContainer
|
||||
import io.github.config4k.CustomType
|
||||
import io.github.config4k.extract
|
||||
import io.github.config4k.toConfig
|
||||
import suwayomi.tachidesk.graphql.types.DownloadConversion
|
||||
import kotlin.time.Duration
|
||||
|
||||
class DownloadConversionType : CustomType {
|
||||
override fun parse(
|
||||
clazz: ClassContainer,
|
||||
config: Config,
|
||||
name: String,
|
||||
): Any? {
|
||||
val target = config.extract<String>("$name.target")
|
||||
val compressionLevel = config.extract<Double?>("$name.compressionLevel")
|
||||
val callTimeout = config.extract<Duration?>("$name.callTimeout")
|
||||
val connectTimeout = config.extract<Duration?>("$name.connectTimeout")
|
||||
val headers = config.extract<Map<String, String>?>("$name.headers")
|
||||
|
||||
return DownloadConversion(
|
||||
target = target,
|
||||
compressionLevel = compressionLevel,
|
||||
callTimeout = callTimeout,
|
||||
connectTimeout = connectTimeout,
|
||||
headers = headers,
|
||||
)
|
||||
}
|
||||
|
||||
override fun testParse(clazz: ClassContainer): Boolean =
|
||||
clazz.mapperClass.qualifiedName == "suwayomi.tachidesk.graphql.types.DownloadConversion"
|
||||
|
||||
override fun testToConfig(obj: Any): Boolean = obj is DownloadConversion
|
||||
|
||||
override fun toConfig(
|
||||
obj: Any,
|
||||
name: String,
|
||||
): Config {
|
||||
val conversion = obj as DownloadConversion
|
||||
val builder = ConfigFactory.empty()
|
||||
|
||||
var config =
|
||||
builder
|
||||
.withValue("$name.target", conversion.target.asConfigValue())
|
||||
.withValueIfPresent("$name.compressionLevel", conversion.compressionLevel)
|
||||
.withValueIfPresent("$name.callTimeout", conversion.callTimeout?.toString())
|
||||
.withValueIfPresent("$name.connectTimeout", conversion.connectTimeout?.toString())
|
||||
|
||||
if (conversion.headers != null) {
|
||||
config =
|
||||
config
|
||||
.withValue("$name.headers", conversion.headers.asConfigValue())
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
private fun Config.withValueIfPresent(
|
||||
key: String,
|
||||
value: Any?,
|
||||
): Config =
|
||||
if (value != null) {
|
||||
withValue(key, value.asConfigValue())
|
||||
} else {
|
||||
this
|
||||
}
|
||||
|
||||
private fun Any.asConfigValue(): ConfigValue = toConfig("internal").getValue("internal")
|
||||
}
|
||||
Reference in New Issue
Block a user