[skip ci] Formatting

This commit is contained in:
Syer10
2024-09-03 21:37:18 -04:00
parent e968a2195a
commit 6c1fbfa63b
220 changed files with 2493 additions and 2519 deletions
+15 -3
View File
@@ -1,7 +1,19 @@
plugins {
id(libs.plugins.kotlin.jvm.get().pluginId)
id(libs.plugins.kotlin.serialization.get().pluginId)
id(libs.plugins.ktlint.get().pluginId)
id(
libs.plugins.kotlin.jvm
.get()
.pluginId,
)
id(
libs.plugins.kotlin.serialization
.get()
.pluginId,
)
id(
libs.plugins.ktlint
.get()
.pluginId,
)
}
dependencies {
@@ -47,11 +47,10 @@ open class ConfigManager {
@Suppress("UNCHECKED_CAST")
fun <T : ConfigModule> module(type: Class<T>): T = loadedModules[type] as T
private fun getUserConfig(): Config {
return userConfigFile.let {
private fun getUserConfig(): Config =
userConfigFile.let {
ConfigFactory.parseFile(it)
}
}
/**
* Load configs
@@ -72,7 +71,8 @@ open class ConfigManager {
val userConfig = getUserConfig()
val config =
ConfigFactory.empty()
ConfigFactory
.empty()
.withFallback(baseConfig)
.withFallback(userConfig)
.withFallback(compatConfig)
@@ -153,11 +153,13 @@ open class ConfigManager {
}
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())
}
@@ -17,17 +17,25 @@ import kotlin.reflect.KProperty
* Abstract config module.
*/
@Suppress("UNUSED_PARAMETER")
abstract class ConfigModule(getConfig: () -> Config)
abstract class ConfigModule(
getConfig: () -> Config,
)
/**
* Abstract jvm-commandline-argument-overridable config module.
*/
abstract class SystemPropertyOverridableConfigModule(getConfig: () -> Config, moduleName: String) : ConfigModule(getConfig) {
abstract class SystemPropertyOverridableConfigModule(
getConfig: () -> Config,
moduleName: String,
) : ConfigModule(getConfig) {
val overridableConfig = SystemPropertyOverrideDelegate(getConfig, moduleName)
}
/** 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<*>,
@@ -22,13 +22,12 @@ import org.slf4j.LoggerFactory
private fun fileSizeValueOfOrDefault(
fileSizeStr: String,
default: String,
): FileSize {
return try {
): FileSize =
try {
FileSize.valueOf(fileSizeStr)
} catch (e: IllegalArgumentException) {
FileSize.valueOf(default)
}
}
private const val FILE_APPENDER_NAME = "SuwayomiDefaultAppender"
@@ -73,9 +72,8 @@ private fun createRollingFileAppender(
return appender
}
private fun getBaseLogger(): ch.qos.logback.classic.Logger {
return (KotlinLogging.logger(Logger.ROOT_LOGGER_NAME).underlyingLogger as ch.qos.logback.classic.Logger)
}
private fun getBaseLogger(): ch.qos.logback.classic.Logger =
(KotlinLogging.logger(Logger.ROOT_LOGGER_NAME).underlyingLogger as ch.qos.logback.classic.Logger)
private fun getLogger(name: String): ch.qos.logback.classic.Logger {
val context = LoggerFactory.getILoggerFactory() as LoggerContext
+15 -3
View File
@@ -1,7 +1,19 @@
plugins {
id(libs.plugins.kotlin.jvm.get().pluginId)
id(libs.plugins.kotlin.serialization.get().pluginId)
id(libs.plugins.ktlint.get().pluginId)
id(
libs.plugins.kotlin.jvm
.get()
.pluginId,
)
id(
libs.plugins.kotlin.serialization
.get()
.pluginId,
)
id(
libs.plugins.ktlint
.get()
.pluginId,
)
}
dependencies {
@@ -8,7 +8,9 @@ import xyz.nulldev.ts.config.ConfigModule
* Application info config.
*/
class ApplicationInfoConfigModule(getConfig: () -> Config) : ConfigModule(getConfig) {
class ApplicationInfoConfigModule(
getConfig: () -> Config,
) : ConfigModule(getConfig) {
val packageName: String by getConfig()
val debug: Boolean by getConfig()
@@ -8,7 +8,9 @@ import xyz.nulldev.ts.config.ConfigModule
* Files configuration modules. Specifies where to store the Android files.
*/
class FilesConfigModule(getConfig: () -> Config) : ConfigModule(getConfig) {
class FilesConfigModule(
getConfig: () -> Config,
) : ConfigModule(getConfig) {
val dataDir: String by getConfig()
val filesDir: String by getConfig()
val noBackupFilesDir: String by getConfig()
@@ -4,7 +4,9 @@ import com.typesafe.config.Config
import io.github.config4k.getValue
import xyz.nulldev.ts.config.ConfigModule
class SystemConfigModule(val getConfig: () -> Config) : ConfigModule(getConfig) {
class SystemConfigModule(
val getConfig: () -> Config,
) : ConfigModule(getConfig) {
val isDebuggable: Boolean by getConfig()
val propertyPrefix = "properties."
@@ -19,7 +19,9 @@ import java.sql.Timestamp
import java.util.Calendar
@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 columnCache = mutableMapOf<String, Int>()
private var lastReturnWasNull = false
@@ -29,9 +31,10 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
val parentMetadata = parent.metaData
val columnCount = parentMetadata.columnCount
val columnLabels =
(1..columnCount).map {
parentMetadata.getColumnLabel(it)
}.toTypedArray()
(1..columnCount)
.map {
parentMetadata.getColumnLabel(it)
}.toTypedArray()
init {
val columnCount = columnCount
@@ -45,20 +48,17 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
while (parent.next()) {
cachedContent +=
ResultSetEntry().apply {
for (i in 1..columnCount)
for (i in 1..columnCount) {
data += parent.getObject(i)
}
}
resultSetLength++
}
}
private fun notImplemented(): Nothing {
throw UnsupportedOperationException("This class currently does not support this operation!")
}
private fun notImplemented(): Nothing = throw UnsupportedOperationException("This class currently does not support this operation!")
private fun cursorValid(): Boolean {
return isAfterLast || isBeforeFirst
}
private fun cursorValid(): Boolean = isAfterLast || isBeforeFirst
private fun internalMove(row: Int) {
if (cursor < 0) {
@@ -76,22 +76,16 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
return obj
}
private fun obj(column: String?): Any? {
return obj(cachedFindColumn(column))
}
private fun obj(column: String?): Any? = obj(cachedFindColumn(column))
private fun cachedFindColumn(column: String?) =
columnCache.getOrPut(column!!, {
findColumn(column)
})
override fun getNClob(columnIndex: Int): NClob {
return obj(columnIndex) as NClob
}
override fun getNClob(columnIndex: Int): NClob = obj(columnIndex) as NClob
override fun getNClob(columnLabel: String?): NClob {
return obj(columnLabel) as NClob
}
override fun getNClob(columnLabel: String?): NClob = obj(columnLabel) as NClob
override fun updateNString(
columnIndex: Int,
@@ -260,17 +254,11 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
notImplemented()
}
override fun getBoolean(columnIndex: Int): Boolean {
return obj(columnIndex) as Boolean
}
override fun getBoolean(columnIndex: Int): Boolean = obj(columnIndex) as Boolean
override fun getBoolean(columnLabel: String?): Boolean {
return obj(columnLabel) as Boolean
}
override fun getBoolean(columnLabel: String?): Boolean = obj(columnLabel) as Boolean
override fun isFirst(): Boolean {
return cursor - 1 < resultSetLength
}
override fun isFirst(): Boolean = cursor - 1 < resultSetLength
override fun getBigDecimal(
columnIndex: Int,
@@ -288,13 +276,9 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
notImplemented()
}
override fun getBigDecimal(columnIndex: Int): BigDecimal {
return obj(columnIndex) as BigDecimal
}
override fun getBigDecimal(columnIndex: Int): BigDecimal = obj(columnIndex) as BigDecimal
override fun getBigDecimal(columnLabel: String?): BigDecimal {
return obj(columnLabel) as BigDecimal
}
override fun getBigDecimal(columnLabel: String?): BigDecimal = obj(columnLabel) as BigDecimal
override fun updateBytes(
columnIndex: Int,
@@ -310,9 +294,7 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
notImplemented()
}
override fun isLast(): Boolean {
return cursor == resultSetLength
}
override fun isLast(): Boolean = cursor == resultSetLength
override fun insertRow() {
notImplemented()
@@ -351,9 +333,7 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
return cursorValid()
}
override fun isAfterLast(): Boolean {
return cursor > resultSetLength
}
override fun isAfterLast(): Boolean = cursor > resultSetLength
override fun relative(rows: Int): Boolean {
internalMove(cursor + rows)
@@ -365,8 +345,9 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
internalMove(row)
} else {
last()
for (i in 1..row)
for (i in 1..row) {
previous()
}
}
return cursorValid()
}
@@ -394,19 +375,13 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
return cursorValid()
}
override fun getFloat(columnIndex: Int): Float {
return obj(columnIndex) as Float
}
override fun getFloat(columnIndex: Int): Float = obj(columnIndex) as Float
override fun getFloat(columnLabel: String?): Float {
return obj(columnLabel) as Float
}
override fun getFloat(columnLabel: String?): Float = obj(columnLabel) as Float
override fun wasNull() = lastReturnWasNull
override fun getRow(): Int {
return cursor
}
override fun getRow(): Int = cursor
override fun first(): Boolean {
internalMove(1)
@@ -459,13 +434,9 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
notImplemented()
}
override fun getURL(columnIndex: Int): URL {
return obj(columnIndex) as URL
}
override fun getURL(columnIndex: Int): URL = obj(columnIndex) as URL
override fun getURL(columnLabel: String?): URL {
return obj(columnLabel) as URL
}
override fun getURL(columnLabel: String?): URL = obj(columnLabel) as URL
override fun updateShort(
columnIndex: Int,
@@ -643,21 +614,13 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
notImplemented()
}
override fun getByte(columnIndex: Int): Byte {
return obj(columnIndex) as Byte
}
override fun getByte(columnIndex: Int): Byte = obj(columnIndex) as Byte
override fun getByte(columnLabel: String?): Byte {
return obj(columnLabel) as Byte
}
override fun getByte(columnLabel: String?): Byte = obj(columnLabel) as Byte
override fun getString(columnIndex: Int): String? {
return obj(columnIndex) as String?
}
override fun getString(columnIndex: Int): String? = obj(columnIndex) as String?
override fun getString(columnLabel: String?): String? {
return obj(columnLabel) as String?
}
override fun getString(columnLabel: String?): String? = obj(columnLabel) as String?
override fun updateSQLXML(
columnIndex: Int,
@@ -687,13 +650,9 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
notImplemented()
}
override fun getObject(columnIndex: Int): Any? {
return obj(columnIndex)
}
override fun getObject(columnIndex: Int): Any? = obj(columnIndex)
override fun getObject(columnLabel: String?): Any? {
return obj(columnLabel)
}
override fun getObject(columnLabel: String?): Any? = obj(columnLabel)
override fun getObject(
columnIndex: Int,
@@ -714,16 +673,12 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
override fun <T : Any?> getObject(
columnIndex: Int,
type: Class<T>?,
): T {
return obj(columnIndex) as T
}
): T = obj(columnIndex) as T
override fun <T : Any?> getObject(
columnLabel: String?,
type: Class<T>?,
): T {
return obj(columnLabel) as T
}
): T = obj(columnLabel) as T
override fun previous(): Boolean {
internalMove(cursor - 1)
@@ -756,13 +711,9 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
}
}
override fun getLong(columnIndex: Int): Long {
return castToLong(obj(columnIndex))
}
override fun getLong(columnIndex: Int): Long = castToLong(obj(columnIndex))
override fun getLong(columnLabel: String?): Long {
return castToLong(obj(columnLabel))
}
override fun getLong(columnLabel: String?): Long = castToLong(obj(columnLabel))
override fun getClob(columnIndex: Int): Clob {
// TODO Maybe?
@@ -840,13 +791,9 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
notImplemented()
}
override fun getNString(columnIndex: Int): String {
return obj(columnIndex) as String
}
override fun getNString(columnIndex: Int): String = obj(columnIndex) as String
override fun getNString(columnLabel: String?): String {
return obj(columnLabel) as String
}
override fun getNString(columnLabel: String?): String = obj(columnLabel) as String
override fun getArray(columnIndex: Int): Array {
// TODO Maybe?
@@ -880,17 +827,11 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
notImplemented()
}
override fun getCharacterStream(columnIndex: Int): Reader {
return getNCharacterStream(columnIndex)
}
override fun getCharacterStream(columnIndex: Int): Reader = getNCharacterStream(columnIndex)
override fun getCharacterStream(columnLabel: String?): Reader {
return getNCharacterStream(columnLabel)
}
override fun getCharacterStream(columnLabel: String?): Reader = getNCharacterStream(columnLabel)
override fun isBeforeFirst(): Boolean {
return cursor - 1 < resultSetLength
}
override fun isBeforeFirst(): Boolean = cursor - 1 < resultSetLength
override fun updateBoolean(
columnIndex: Int,
@@ -926,21 +867,13 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
notImplemented()
}
override fun getShort(columnIndex: Int): Short {
return obj(columnIndex) as Short
}
override fun getShort(columnIndex: Int): Short = obj(columnIndex) as Short
override fun getShort(columnLabel: String?): Short {
return obj(columnLabel) as Short
}
override fun getShort(columnLabel: String?): Short = obj(columnLabel) as Short
override fun getAsciiStream(columnIndex: Int): InputStream {
return getBinaryStream(columnIndex)
}
override fun getAsciiStream(columnIndex: Int): InputStream = getBinaryStream(columnIndex)
override fun getAsciiStream(columnLabel: String?): InputStream {
return getBinaryStream(columnLabel)
}
override fun getAsciiStream(columnLabel: String?): InputStream = getBinaryStream(columnLabel)
override fun updateTime(
columnIndex: Int,
@@ -1008,13 +941,9 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
notImplemented()
}
override fun getNCharacterStream(columnIndex: Int): Reader {
return getBinaryStream(columnIndex).reader()
}
override fun getNCharacterStream(columnIndex: Int): Reader = getBinaryStream(columnIndex).reader()
override fun getNCharacterStream(columnLabel: String?): Reader {
return getBinaryStream(columnLabel).reader()
}
override fun getNCharacterStream(columnLabel: String?): Reader = getBinaryStream(columnLabel).reader()
override fun updateArray(
columnIndex: Int,
@@ -1030,45 +959,27 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
notImplemented()
}
override fun getBytes(columnIndex: Int): ByteArray {
return obj(columnIndex) as ByteArray
}
override fun getBytes(columnIndex: Int): ByteArray = obj(columnIndex) as ByteArray
override fun getBytes(columnLabel: String?): ByteArray {
return obj(columnLabel) as ByteArray
}
override fun getBytes(columnLabel: String?): ByteArray = obj(columnLabel) as ByteArray
override fun getDouble(columnIndex: Int): Double {
return obj(columnIndex) as Double
}
override fun getDouble(columnIndex: Int): Double = obj(columnIndex) as Double
override fun getDouble(columnLabel: String?): Double {
return obj(columnLabel) as Double
}
override fun getDouble(columnLabel: String?): Double = obj(columnLabel) as Double
override fun getUnicodeStream(columnIndex: Int): InputStream {
return getBinaryStream(columnIndex)
}
override fun getUnicodeStream(columnIndex: Int): InputStream = getBinaryStream(columnIndex)
override fun getUnicodeStream(columnLabel: String?): InputStream {
return getBinaryStream(columnLabel)
}
override fun getUnicodeStream(columnLabel: String?): InputStream = getBinaryStream(columnLabel)
override fun rowInserted() = false
private fun thisIsWrapperFor(iface: Class<*>?) = this.javaClass.isInstance(iface)
override fun isWrapperFor(iface: Class<*>?): Boolean {
return thisIsWrapperFor(iface) || parent.isWrapperFor(iface)
}
override fun isWrapperFor(iface: Class<*>?): Boolean = thisIsWrapperFor(iface) || parent.isWrapperFor(iface)
override fun getInt(columnIndex: Int): Int {
return obj(columnIndex) as Int
}
override fun getInt(columnIndex: Int): Int = obj(columnIndex) as Int
override fun getInt(columnLabel: String?): Int {
return obj(columnLabel) as Int
}
override fun getInt(columnLabel: String?): Int = obj(columnLabel) as Int
override fun updateNull(columnIndex: Int) {
notImplemented()
@@ -1088,8 +999,8 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
notImplemented()
}
override fun getMetaData(): ResultSetMetaData {
return object : ResultSetMetaData by parentMetadata {
override fun getMetaData(): ResultSetMetaData =
object : ResultSetMetaData by parentMetadata {
override fun isReadOnly(column: Int) = true
override fun isWritable(column: Int) = false
@@ -1098,19 +1009,12 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
override fun getColumnCount() = this@ScrollableResultSet.columnCount
override fun getColumnLabel(column: Int): String {
return columnLabels[column - 1]
}
override fun getColumnLabel(column: Int): String = columnLabels[column - 1]
}
}
override fun getBinaryStream(columnIndex: Int): InputStream {
return (obj(columnIndex) as ByteArray).inputStream()
}
override fun getBinaryStream(columnIndex: Int): InputStream = (obj(columnIndex) as ByteArray).inputStream()
override fun getBinaryStream(columnLabel: String?): InputStream {
return (obj(columnLabel) as ByteArray).inputStream()
}
override fun getBinaryStream(columnLabel: String?): InputStream = (obj(columnLabel) as ByteArray).inputStream()
override fun updateCharacterStream(
columnIndex: Int,
@@ -8,7 +8,10 @@ import org.kodein.di.instance
import xyz.nulldev.androidcompat.config.ApplicationInfoConfigModule
import xyz.nulldev.ts.config.ConfigManager
class ApplicationInfoImpl(override val di: DI = DI.global) : ApplicationInfo(), DIAware {
class ApplicationInfoImpl(
override val di: DI = DI.global,
) : ApplicationInfo(),
DIAware {
val configManager: ConfigManager by di.instance()
val appInfoConfig: ApplicationInfoConfigModule
@@ -8,7 +8,9 @@ import java.io.File
/**
* Android file constants.
*/
class AndroidFiles(val configManager: ConfigManager = GlobalConfigManager) {
class AndroidFiles(
val configManager: ConfigManager = GlobalConfigManager,
) {
val filesConfig: FilesConfigModule
get() = configManager.module()
@@ -30,9 +32,8 @@ class AndroidFiles(val configManager: ConfigManager = GlobalConfigManager) {
val packagesDir: File get() = registerFile(filesConfig.packageDir)
fun registerFile(file: String): File {
return File(file).apply {
fun registerFile(file: String): File =
File(file).apply {
mkdirs()
}
}
}
@@ -30,7 +30,9 @@ import kotlin.io.path.inputStream
import kotlin.io.path.outputStream
@OptIn(ExperimentalSerializationApi::class, ExperimentalSettingsApi::class)
class JavaSharedPreferences(key: String) : SharedPreferences {
class JavaSharedPreferences(
key: String,
) : SharedPreferences {
companion object {
private val logger = KotlinLogging.logger {}
}
@@ -72,20 +74,17 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
private val listeners = mutableMapOf<SharedPreferences.OnSharedPreferenceChangeListener, (String) -> Unit>()
// TODO: 2021-05-29 Need to find a way to get this working with all pref types
override fun getAll(): MutableMap<String, *> {
return preferences.keys.associateWith { preferences.getStringOrNull(it) }.toMutableMap()
}
override fun getAll(): MutableMap<String, *> = preferences.keys.associateWith { preferences.getStringOrNull(it) }.toMutableMap()
override fun getString(
key: String,
defValue: String?,
): String? {
return if (defValue != null) {
): String? =
if (defValue != null) {
preferences.getString(key, defValue)
} else {
preferences.getStringOrNull(key)
}
}
override fun getStringSet(
key: String,
@@ -105,50 +104,48 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
override fun getInt(
key: String,
defValue: Int,
): Int {
return preferences.getInt(key, defValue)
}
): Int = preferences.getInt(key, defValue)
override fun getLong(
key: String,
defValue: Long,
): Long {
return preferences.getLong(key, defValue)
}
): Long = preferences.getLong(key, defValue)
override fun getFloat(
key: String,
defValue: Float,
): Float {
return preferences.getFloat(key, defValue)
}
): Float = preferences.getFloat(key, defValue)
override fun getBoolean(
key: String,
defValue: Boolean,
): Boolean {
return preferences.getBoolean(key, defValue)
}
): Boolean = preferences.getBoolean(key, defValue)
override fun contains(key: String): Boolean {
return key in preferences.keys
}
override fun contains(key: String): Boolean = key in preferences.keys
override fun edit(): SharedPreferences.Editor {
return Editor(preferences) { key ->
override fun edit(): SharedPreferences.Editor =
Editor(preferences) { key ->
listeners.forEach { (_, listener) ->
listener(key)
}
}
}
class Editor(private val preferences: Settings, private val notify: (String) -> Unit) : SharedPreferences.Editor {
class Editor(
private val preferences: Settings,
private val notify: (String) -> Unit,
) : SharedPreferences.Editor {
private val actions = mutableListOf<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()
data object Clear : Action()
}
@@ -14,7 +14,9 @@ import java.io.File
import javax.imageio.ImageIO
import javax.xml.parsers.DocumentBuilderFactory
data class InstalledPackage(val root: File) {
data class InstalledPackage(
val root: File,
) {
val apk = File(root, "package.apk")
val jar = File(root, "translated.jar")
val icon = File(root, "icon.png")
@@ -34,18 +36,21 @@ data class InstalledPackage(val root: File) {
Bundle().apply {
val appTag = doc.getElementsByTagName("application").item(0)
appTag?.childNodes?.toList()?.filter {
it.nodeType == Node.ELEMENT_NODE
}?.map {
it as Element
}?.filter {
it.tagName == "meta-data"
}?.map {
putString(
it.attributes.getNamedItem("android:name").nodeValue,
it.attributes.getNamedItem("android:value").nodeValue,
)
}
appTag
?.childNodes
?.toList()
?.filter {
it.nodeType == Node.ELEMENT_NODE
}?.map {
it as Element
}?.filter {
it.tagName == "meta-data"
}?.map {
putString(
it.attributes.getNamedItem("android:name").nodeValue,
it.attributes.getNamedItem("android:value").nodeValue,
)
}
}
it.signatures =
@@ -53,12 +58,14 @@ data class InstalledPackage(val root: File) {
parsed.apkSingers.flatMap { it.certificateMetas }
// + parsed.apkV2Singers.flatMap { it.certificateMetas }
) // Blocked by: https://github.com/hsiafan/apk-parser/issues/72
.map { Signature(it.data) }.toTypedArray()
.map { Signature(it.data) }
.toTypedArray()
}
fun verify(): Boolean {
val res =
ApkVerifier.Builder(apk)
ApkVerifier
.Builder(apk)
.build()
.verify()
@@ -70,11 +77,14 @@ data class InstalledPackage(val root: File) {
val icons = ApkFile(apk).allIcons
val read =
icons.filter { it.isFile }.map {
it.data.inputStream().use {
ImageIO.read(it)
}
}.sortedByDescending { it.width * it.height }.firstOrNull() ?: return
icons
.filter { it.isFile }
.map {
it.data.inputStream().use {
ImageIO.read(it)
}
}.sortedByDescending { it.width * it.height }
.firstOrNull() ?: return
ImageIO.write(read, "png", icon)
} catch (e: Exception) {
@@ -94,8 +104,9 @@ data class InstalledPackage(val root: File) {
fun NodeList.toList(): List<Node> {
val out = mutableListOf<Node>()
for (i in 0 until length)
for (i in 0 until length) {
out += item(i)
}
return out
}
@@ -57,13 +57,15 @@ class PackageController {
}
}
fun listInstalled(): List<InstalledPackage> {
return androidFiles.packagesDir.listFiles().orEmpty().filter {
it.isDirectory
}.map {
InstalledPackage(it)
}
}
fun listInstalled(): List<InstalledPackage> =
androidFiles.packagesDir
.listFiles()
.orEmpty()
.filter {
it.isDirectory
}.map {
InstalledPackage(it)
}
fun deletePackage(pack: InstalledPackage) {
if (!pack.root.exists()) error("Package was never installed!")
@@ -6,18 +6,19 @@ import android.content.pm.PackageInfo
import net.dongliu.apk.parser.bean.ApkMeta
import java.io.File
fun ApkMeta.toPackageInfo(apk: File): PackageInfo {
return PackageInfo().also {
fun ApkMeta.toPackageInfo(apk: File): PackageInfo =
PackageInfo().also {
it.packageName = packageName
it.versionCode = versionCode.toInt()
it.versionName = versionName
it.reqFeatures =
usesFeatures.map {
FeatureInfo().apply {
name = it.name
}
}.toTypedArray()
usesFeatures
.map {
FeatureInfo().apply {
name = it.name
}
}.toTypedArray()
it.applicationInfo =
ApplicationInfo().apply {
@@ -26,4 +27,3 @@ fun ApkMeta.toPackageInfo(apk: File): PackageInfo {
sourceDir = apk.absolutePath
}
}
}
@@ -1,6 +1,8 @@
package xyz.nulldev.androidcompat.res
class DrawableResource(val location: String) : Resource {
class DrawableResource(
val location: String,
) : Resource {
override fun getType() = DrawableResource::class.java
override fun getValue() = javaClass.getResourceAsStream(location)
@@ -19,7 +19,9 @@ package xyz.nulldev.androidcompat.res
/**
* String resource.
*/
class StringResource(val string: String) : Resource {
class StringResource(
val string: String,
) : Resource {
override fun getValue() = string
override fun getType() = StringResource::class.java
@@ -29,8 +29,8 @@ object KodeinGlobalHelper {
fun <T : Any> instance(
type: Class<T>,
kodein: DI? = null,
): T {
return when (type) {
): T =
when (type) {
AndroidFiles::class.java -> {
val instance: AndroidFiles by (kodein ?: kodein()).instance()
instance as T
@@ -61,10 +61,7 @@ object KodeinGlobalHelper {
}
else -> throw IllegalArgumentException("Kodein instance not found")
}
}
@JvmStatic
fun <T : Any> instance(type: Class<T>): T {
return instance(type, null)
}
fun <T : Any> instance(type: Class<T>): T = instance(type, null)
}
@@ -18,9 +18,7 @@ class CookieManagerImpl : CookieManager() {
acceptCookie = accept
}
override fun acceptCookie(): Boolean {
return acceptCookie
}
override fun acceptCookie(): Boolean = acceptCookie
override fun setAcceptThirdPartyCookies(
webview: WebView?,
@@ -29,9 +27,7 @@ class CookieManagerImpl : CookieManager() {
acceptThirdPartyCookies = accept
}
override fun acceptThirdPartyCookies(webview: WebView?): Boolean {
return acceptThirdPartyCookies
}
override fun acceptThirdPartyCookies(webview: WebView?): Boolean = acceptThirdPartyCookies
override fun setCookie(
url: String,
@@ -65,7 +61,8 @@ class CookieManagerImpl : CookieManager() {
} else {
URI("http://$url")
}
return cookieHandler.cookieStore.get(uri)
return cookieHandler.cookieStore
.get(uri)
.joinToString("; ") { "${it.name}=${it.value}" }
}
@@ -87,15 +84,11 @@ class CookieManagerImpl : CookieManager() {
callback?.onReceiveValue(removedCookies)
}
override fun hasCookies(): Boolean {
return cookieHandler.cookieStore.cookies.isNotEmpty()
}
override fun hasCookies(): Boolean = cookieHandler.cookieStore.cookies.isNotEmpty()
override fun flush() {}
override fun allowFileSchemeCookiesImpl(): Boolean {
return allowFileSchemeCookies
}
override fun allowFileSchemeCookiesImpl(): Boolean = allowFileSchemeCookies
override fun setAcceptFileSchemeCookiesImpl(accept: Boolean) {
allowFileSchemeCookies = acceptCookie
+20 -4
View File
@@ -3,12 +3,28 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
import java.time.Instant
plugins {
id(libs.plugins.kotlin.jvm.get().pluginId)
id(libs.plugins.kotlin.serialization.get().pluginId)
id(libs.plugins.ktlint.get().pluginId)
id(
libs.plugins.kotlin.jvm
.get()
.pluginId,
)
id(
libs.plugins.kotlin.serialization
.get()
.pluginId,
)
id(
libs.plugins.ktlint
.get()
.pluginId,
)
application
alias(libs.plugins.shadowjar)
id(libs.plugins.buildconfig.get().pluginId)
id(
libs.plugins.buildconfig
.get()
.pluginId,
)
}
dependencies {
@@ -32,7 +32,9 @@ import uy.kohesive.injekt.api.addSingleton
import uy.kohesive.injekt.api.addSingletonFactory
import uy.kohesive.injekt.api.get
class AppModule(val app: Application) : InjektModule {
class AppModule(
val app: Application,
) : InjektModule {
override fun InjektRegistrar.registerInjectables() {
addSingleton(app)
@@ -49,7 +49,9 @@ class MemoryCookieJar : CookieJar {
}
}
class WrappedCookie private constructor(val cookie: Cookie) {
class WrappedCookie private constructor(
val cookie: Cookie,
) {
fun unwrap() = cookie
fun isExpired() = cookie.expiresAt < System.currentTimeMillis()
@@ -30,7 +30,9 @@ import java.net.CookieManager
import java.net.CookiePolicy
import java.util.concurrent.TimeUnit
class NetworkHelper(context: Context) {
class NetworkHelper(
context: Context,
) {
// private val preferences: PreferencesHelper by injectLazy()
// private val cacheDir = File(context.cacheDir, "network_cache")
@@ -53,9 +55,7 @@ class NetworkHelper(context: Context) {
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
)
fun defaultUserAgentProvider(): String {
return userAgent.value
}
fun defaultUserAgentProvider(): String = userAgent.value
init {
@OptIn(DelicateCoroutinesApi::class)
@@ -63,14 +63,14 @@ class NetworkHelper(context: Context) {
.drop(1)
.onEach {
GetCatalogueSource.unregisterAllCatalogueSources() // need to reset the headers
}
.launchIn(GlobalScope)
}.launchIn(GlobalScope)
}
private val baseClientBuilder: OkHttpClient.Builder
get() {
val builder =
OkHttpClient.Builder()
OkHttpClient
.Builder()
.cookieJar(PersistentCookieJar(cookieStore))
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
@@ -80,8 +80,7 @@ class NetworkHelper(context: Context) {
directory = File.createTempFile("tachidesk_network_cache", null),
maxSize = 5L * 1024 * 1024, // 5 MiB
),
)
.addInterceptor(UncaughtExceptionInterceptor())
).addInterceptor(UncaughtExceptionInterceptor())
.addInterceptor(UserAgentInterceptor(::defaultUserAgentProvider))
.addNetworkInterceptor(IgnoreGzipInterceptor())
.addNetworkInterceptor(BrotliInterceptor)
@@ -50,9 +50,7 @@ fun Call.asObservable(): Observable<Response> {
// call.cancel()
}
override fun isUnsubscribed(): Boolean {
return call.isCanceled()
}
override fun isUnsubscribed(): Boolean = call.isCanceled()
}
subscriber.add(requestArbiter)
@@ -60,15 +58,14 @@ fun Call.asObservable(): Observable<Response> {
}
}
fun Call.asObservableSuccess(): Observable<Response> {
return asObservable()
fun Call.asObservableSuccess(): Observable<Response> =
asObservable()
.doOnNext { response ->
if (!response.isSuccessful) {
response.close()
throw HttpException(response.code)
}
}
}
// Based on https://github.com/gildor/kotlin-coroutines-okhttp
@OptIn(ExperimentalCoroutinesApi::class)
@@ -135,29 +132,28 @@ fun OkHttpClient.newCachelessCallWithProgress(
.cache(null)
.addNetworkInterceptor { chain ->
val originalResponse = chain.proceed(chain.request())
originalResponse.newBuilder()
originalResponse
.newBuilder()
.body(ProgressResponseBody(originalResponse.body, listener))
.build()
}
.build()
}.build()
return progressClient.newCall(request)
}
context(Json)
inline fun <reified T> Response.parseAs(): T {
return decodeFromJsonResponse(serializer(), this)
}
inline fun <reified T> Response.parseAs(): T = decodeFromJsonResponse(serializer(), this)
context(Json)
@OptIn(ExperimentalSerializationApi::class)
fun <T> decodeFromJsonResponse(
deserializer: DeserializationStrategy<T>,
response: Response,
): T {
return response.body.source().use {
): T =
response.body.source().use {
decodeFromBufferedSource(deserializer, it)
}
}
class HttpException(val code: Int) : IllegalStateException("HTTP error $code")
class HttpException(
val code: Int,
) : IllegalStateException("HTTP error $code")
@@ -5,7 +5,9 @@ import okhttp3.CookieJar
import okhttp3.HttpUrl
// from TachiWeb-Server
class PersistentCookieJar(private val store: PersistentCookieStore) : CookieJar {
class PersistentCookieJar(
private val store: PersistentCookieStore,
) : CookieJar {
override fun saveFromResponse(
url: HttpUrl,
cookies: List<Cookie>,
@@ -13,7 +15,5 @@ class PersistentCookieJar(private val store: PersistentCookieStore) : CookieJar
store.addAll(url, cookies)
}
override fun loadForRequest(url: HttpUrl): List<Cookie> {
return store.get(url)
}
override fun loadForRequest(url: HttpUrl): List<Cookie> = store.get(url)
}
@@ -15,7 +15,9 @@ import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
// from TachiWeb-Server
class PersistentCookieStore(context: Context) : CookieStore {
class PersistentCookieStore(
context: Context,
) : CookieStore {
private val cookieMap = ConcurrentHashMap<String, List<Cookie>>()
private val prefs = context.getSharedPreferences("cookie_store", Context.MODE_PRIVATE)
@@ -23,7 +25,8 @@ class PersistentCookieStore(context: Context) : CookieStore {
init {
val domains =
prefs.all.keys.map { it.substringBeforeLast(".") }
prefs.all.keys
.map { it.substringBeforeLast(".") }
.toSet()
domains.forEach { domain ->
val cookies = prefs.getStringSet(domain, emptySet())
@@ -31,7 +34,8 @@ class PersistentCookieStore(context: Context) : CookieStore {
try {
val url = "http://$domain".toHttpUrlOrNull() ?: return@forEach
val nonExpiredCookies =
cookies.mapNotNull { Cookie.parse(url, it) }
cookies
.mapNotNull { Cookie.parse(url, it) }
.filter { !it.hasExpired() }
cookieMap[domain] = nonExpiredCookies
} catch (e: Exception) {
@@ -63,14 +67,13 @@ class PersistentCookieStore(context: Context) : CookieStore {
}
}
override fun removeAll(): Boolean {
return lock.withLock {
override fun removeAll(): Boolean =
lock.withLock {
val wasNotEmpty = cookieMap.isEmpty()
prefs.edit().clear().apply()
cookieMap.clear()
wasNotEmpty
}
}
fun remove(uri: URI) {
val url = uri.toURL()
@@ -87,9 +90,7 @@ class PersistentCookieStore(context: Context) : CookieStore {
}
}
fun get(url: HttpUrl): List<Cookie> {
return get(url.host)
}
fun get(url: HttpUrl): List<Cookie> = get(url.host)
override fun add(
uri: URI?,
@@ -105,19 +106,17 @@ class PersistentCookieStore(context: Context) : CookieStore {
}
}
override fun getCookies(): List<HttpCookie> {
return cookieMap.values.flatMap {
override fun getCookies(): List<HttpCookie> =
cookieMap.values.flatMap {
it.map {
it.toHttpCookie()
}
}
}
override fun getURIs(): List<URI> {
return cookieMap.keys().toList().map {
override fun getURIs(): List<URI> =
cookieMap.keys().toList().map {
URI("http://$it")
}
}
override fun remove(
uri: URI?,
@@ -145,9 +144,7 @@ class PersistentCookieStore(context: Context) : CookieStore {
}
}
private fun get(url: String): List<Cookie> {
return cookieMap[url].orEmpty().filter { !it.hasExpired() }
}
private fun get(url: String): List<Cookie> = cookieMap[url].orEmpty().filter { !it.hasExpired() }
private fun saveToDisk(url: URL) {
// Get cookies to be stored in disk
@@ -165,7 +162,8 @@ class PersistentCookieStore(context: Context) : CookieStore {
private fun Cookie.hasExpired() = System.currentTimeMillis() >= expiresAt
private fun HttpCookie.toCookie(uri: URI) =
Cookie.Builder()
Cookie
.Builder()
.name(name)
.value(value)
.domain(uri.toURL().host)
@@ -176,22 +174,19 @@ class PersistentCookieStore(context: Context) : CookieStore {
} else {
it.expiresAt(Long.MAX_VALUE)
}
}
.let {
}.let {
if (secure) {
it.secure()
} else {
it
}
}
.let {
}.let {
if (isHttpOnly) {
it.httpOnly()
} else {
it
}
}
.build()
}.build()
private fun Cookie.toHttpCookie(): HttpCookie {
val it = this
@@ -9,22 +9,19 @@ import okio.Source
import okio.buffer
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 {
source(responseBody.source()).buffer()
}
override fun contentType(): MediaType? {
return responseBody.contentType()
}
override fun contentType(): MediaType? = responseBody.contentType()
override fun contentLength(): Long {
return responseBody.contentLength()
}
override fun contentLength(): Long = responseBody.contentLength()
override fun source(): BufferedSource {
return bufferedSource
}
override fun source(): BufferedSource = bufferedSource
private fun source(source: Source): Source {
return object : ForwardingSource(source) {
@@ -18,13 +18,13 @@ fun GET(
url: String,
headers: Headers = DEFAULT_HEADERS,
cache: CacheControl = DEFAULT_CACHE_CONTROL,
): Request {
return Request.Builder()
): Request =
Request
.Builder()
.url(url)
.headers(headers)
.cacheControl(cache)
.build()
}
/**
* @since extensions-lib 1.4
@@ -33,52 +33,52 @@ fun GET(
url: HttpUrl,
headers: Headers = DEFAULT_HEADERS,
cache: CacheControl = DEFAULT_CACHE_CONTROL,
): Request {
return Request.Builder()
): Request =
Request
.Builder()
.url(url)
.headers(headers)
.cacheControl(cache)
.build()
}
fun POST(
url: String,
headers: Headers = DEFAULT_HEADERS,
body: RequestBody = DEFAULT_BODY,
cache: CacheControl = DEFAULT_CACHE_CONTROL,
): Request {
return Request.Builder()
): Request =
Request
.Builder()
.url(url)
.post(body)
.headers(headers)
.cacheControl(cache)
.build()
}
fun PUT(
url: String,
headers: Headers = DEFAULT_HEADERS,
body: RequestBody = DEFAULT_BODY,
cache: CacheControl = DEFAULT_CACHE_CONTROL,
): Request {
return Request.Builder()
): Request =
Request
.Builder()
.url(url)
.put(body)
.headers(headers)
.cacheControl(cache)
.build()
}
fun DELETE(
url: String,
headers: Headers = DEFAULT_HEADERS,
body: RequestBody = DEFAULT_BODY,
cache: CacheControl = DEFAULT_CACHE_CONTROL,
): Request {
return Request.Builder()
): Request =
Request
.Builder()
.url(url)
.delete(body)
.headers(headers)
.cacheControl(cache)
.build()
}
@@ -117,12 +117,12 @@ object CFClearance {
serverConfig.flareSolverrTimeout
.map { timeoutInt ->
val timeout = timeoutInt.seconds
network.client.newBuilder()
network.client
.newBuilder()
.callTimeout(timeout.plus(10.seconds).toJavaDuration())
.readTimeout(timeout.plus(5.seconds).toJavaDuration())
.build()
}
.stateIn(GlobalScope, SharingStarted.Eagerly, network.client)
}.stateIn(GlobalScope, SharingStarted.Eagerly, network.client)
}
private val json: Json by injectLazy()
private val jsonMediaType = "application/json".toMediaType()
@@ -190,26 +190,29 @@ object CFClearance {
return with(json) {
mutex.withLock {
client.value.newCall(
POST(
url = serverConfig.flareSolverrUrl.value.removeSuffix("/") + "/v1",
body =
Json.encodeToString(
FlareSolverRequest(
"request.get",
originalRequest.url.toString(),
session = serverConfig.flareSolverrSessionName.value,
sessionTtlMinutes = serverConfig.flareSolverrSessionTtl.value,
cookies =
network.cookieStore.get(originalRequest.url).map {
FlareSolverCookie(it.name, it.value)
},
returnOnlyCookies = onlyCookies,
maxTimeout = timeout.inWholeMilliseconds.toInt(),
),
).toRequestBody(jsonMediaType),
),
).awaitSuccess().parseAs<FlareSolverResponse>()
client.value
.newCall(
POST(
url = serverConfig.flareSolverrUrl.value.removeSuffix("/") + "/v1",
body =
Json
.encodeToString(
FlareSolverRequest(
"request.get",
originalRequest.url.toString(),
session = serverConfig.flareSolverrSessionName.value,
sessionTtlMinutes = serverConfig.flareSolverrSessionTtl.value,
cookies =
network.cookieStore.get(originalRequest.url).map {
FlareSolverCookie(it.name, it.value)
},
returnOnlyCookies = onlyCookies,
maxTimeout = timeout.inWholeMilliseconds.toInt(),
),
).toRequestBody(jsonMediaType),
),
).awaitSuccess()
.parseAs<FlareSolverResponse>()
}
}
}
@@ -224,7 +227,8 @@ object CFClearance {
val cookies =
flareSolverResponse.solution.cookies
.map { cookie ->
Cookie.Builder()
Cookie
.Builder()
.name(cookie.name)
.value(cookie.value)
.domain(cookie.domain.removePrefix("."))
@@ -233,13 +237,12 @@ object CFClearance {
if (cookie.httpOnly != null && cookie.httpOnly) it.httpOnly()
if (cookie.secure != null && cookie.secure) it.secure()
if (!cookie.path.isNullOrEmpty()) it.path(cookie.path)
}
.build()
}
.groupBy { it.domain }
}.build()
}.groupBy { it.domain }
.flatMap { (domain, cookies) ->
network.cookieStore.addAll(
HttpUrl.Builder()
HttpUrl
.Builder()
.scheme("http")
.host(domain.removePrefix("."))
.build(),
@@ -254,7 +257,8 @@ object CFClearance {
"${it.name}=${it.value}"
}
logger.trace { "Final cookies\n$finalCookies" }
return originalRequest.newBuilder()
return originalRequest
.newBuilder()
.header("Cookie", finalCookies)
.header("User-Agent", flareSolverResponse.solution.userAgent)
.build()
@@ -13,8 +13,8 @@ import java.io.IOException
* See https://square.github.io/okhttp/4.x/okhttp/okhttp3/-interceptor/
*/
class UncaughtExceptionInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
return try {
override fun intercept(chain: Interceptor.Chain): Response =
try {
chain.proceed(chain.request())
} catch (e: Exception) {
if (e is IOException) {
@@ -23,5 +23,4 @@ class UncaughtExceptionInterceptor : Interceptor {
throw IOException(e)
}
}
}
}
@@ -3,7 +3,9 @@ package eu.kanade.tachiyomi.network.interceptor
import okhttp3.Interceptor
import okhttp3.Response
class UserAgentInterceptor(private val userAgentProvider: () -> String) : Interceptor {
class UserAgentInterceptor(
private val userAgentProvider: () -> String,
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
@@ -23,9 +23,7 @@ interface CatalogueSource : Source {
* @param page the page number to retrieve.
*/
@Suppress("DEPRECATION")
suspend fun getPopularManga(page: Int): MangasPage {
return fetchPopularManga(page).awaitSingle()
}
suspend fun getPopularManga(page: Int): MangasPage = fetchPopularManga(page).awaitSingle()
/**
* Get a page with a list of manga.
@@ -40,9 +38,7 @@ interface CatalogueSource : Source {
page: Int,
query: String,
filters: FilterList,
): MangasPage {
return fetchSearchManga(page, query, filters).awaitSingle()
}
): MangasPage = fetchSearchManga(page, query, filters).awaitSingle()
/**
* Get a page with a list of latest manga updates.
@@ -51,9 +47,7 @@ interface CatalogueSource : Source {
* @param page the page number to retrieve.
*/
@Suppress("DEPRECATION")
suspend fun getLatestUpdates(page: Int): MangasPage {
return fetchLatestUpdates(page).awaitSingle()
}
suspend fun getLatestUpdates(page: Int): MangasPage = fetchLatestUpdates(page).awaitSingle()
/**
* Returns the list of filters for the source.
@@ -31,9 +31,7 @@ interface Source {
* @return the updated manga.
*/
@Suppress("DEPRECATION")
suspend fun getMangaDetails(manga: SManga): SManga {
return fetchMangaDetails(manga).awaitSingle()
}
suspend fun getMangaDetails(manga: SManga): SManga = fetchMangaDetails(manga).awaitSingle()
/**
* Get all the available chapters for a manga.
@@ -43,9 +41,7 @@ interface Source {
* @return the chapters for the manga.
*/
@Suppress("DEPRECATION")
suspend fun getChapterList(manga: SManga): List<SChapter> {
return fetchChapterList(manga).awaitSingle()
}
suspend fun getChapterList(manga: SManga): List<SChapter> = fetchChapterList(manga).awaitSingle()
/**
* Get the list of pages a chapter has. Pages should be returned
@@ -56,9 +52,7 @@ interface Source {
* @return the pages for the chapter.
*/
@Suppress("DEPRECATION")
suspend fun getPageList(chapter: SChapter): List<Page> {
return fetchPageList(chapter).awaitSingle()
}
suspend fun getPageList(chapter: SChapter): List<Page> = fetchPageList(chapter).awaitSingle()
@Deprecated(
"Use the non-RxJava API instead",
@@ -59,7 +59,8 @@ import com.github.junrar.Archive as JunrarArchive
class LocalSource(
private val fileSystem: LocalSourceFileSystem,
private val coverManager: LocalCoverManager,
) : CatalogueSource, UnmeteredSource {
) : CatalogueSource,
UnmeteredSource {
private val json: Json by injectLazy()
private val xml: XML by injectLazy()
@@ -93,7 +94,8 @@ class LocalSource(
// Filter out files that are hidden and is not a folder
.filter { it.isDirectory && !it.name.startsWith('.') }
.distinctBy { it.name }
.filter { // Filter by query or last modified
.filter {
// Filter by query or last modified
if (lastModifiedLimit == 0L) {
it.name.contains(query, ignoreCase = true)
} else {
@@ -134,7 +136,8 @@ class LocalSource(
url = mangaDir.name
// Try to find the cover
coverManager.find(mangaDir.name)
coverManager
.find(mangaDir.name)
?.takeIf(File::exists)
?.let { thumbnail_url = it.absolutePath }
}
@@ -238,7 +241,7 @@ class LocalSource(
for (chapter in chapterArchives) {
when (Format.valueOf(chapter)) {
is Format.Zip -> {
ZipFile(chapter).use { zip: ZipFile ->
ZipFile.builder().setFile(chapter).get().use { zip: ZipFile ->
zip.getEntry(COMIC_INFO_FILE)?.let { comicInfoFile ->
zip.getInputStream(comicInfoFile).buffered().use { stream ->
return copyComicInfoFile(stream, folderPath)
@@ -264,13 +267,12 @@ class LocalSource(
private fun copyComicInfoFile(
comicInfoFileStream: InputStream,
folderPath: String?,
): File {
return File("$folderPath/$COMIC_INFO_FILE").apply {
): File =
File("$folderPath/$COMIC_INFO_FILE").apply {
outputStream().use { outputStream ->
comicInfoFileStream.use { it.copyTo(outputStream) }
}
}
}
@OptIn(ExperimentalXmlUtilApi::class)
private fun setMangaDetailsFromComicInfoFile(
@@ -286,8 +288,9 @@ class LocalSource(
}
// Chapters
override suspend fun getChapterList(manga: SManga): List<SChapter> {
return fileSystem.getFilesInMangaDirectory(manga.url)
override suspend fun getChapterList(manga: SManga): List<SChapter> =
fileSystem
.getFilesInMangaDirectory(manga.url)
// Only keep supported formats
.filter { it.isDirectory || Archive.isSupported(it) }
.map { chapterFile ->
@@ -312,22 +315,21 @@ class LocalSource(
}
}
}
}
.sortedWith { c1, c2 ->
}.sortedWith { c1, c2 ->
val c = c2.chapter_number.compareTo(c1.chapter_number)
if (c == 0) c2.name.compareToCaseInsensitiveNaturalOrder(c1.name) else c
}
.toList()
}
}.toList()
// Filters
override fun getFilterList() = FilterList(OrderBy.Popular())
// TODO Fix Memory Leak
override suspend fun getPageList(chapter: SChapter): List<Page> {
return when (val format = getFormat(chapter)) {
override suspend fun getPageList(chapter: SChapter): List<Page> =
when (val format = getFormat(chapter)) {
is Format.Directory -> {
format.file.listFiles().orEmpty()
format.file
.listFiles()
.orEmpty()
.filter { !it.isDirectory && ImageUtil.isImage(it.name, it::inputStream) }
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
.mapIndexed { index, page ->
@@ -359,11 +361,11 @@ class LocalSource(
pages
}
}
}
fun getFormat(chapter: SChapter): Format {
try {
return fileSystem.getBaseDirectories()
return fileSystem
.getBaseDirectories()
.map { dir -> File(dir, chapter.url) }
.find { it.exists() }
?.let(Format.Companion::valueOf)
@@ -378,21 +380,23 @@ class LocalSource(
private fun updateCover(
chapter: SChapter,
manga: SManga,
): File? {
return try {
): File? =
try {
when (val format = getFormat(chapter)) {
is Format.Directory -> {
val entry =
format.file.listFiles()
format.file
.listFiles()
?.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
entry?.let { coverManager.update(manga, it.inputStream()) }
}
is Format.Zip -> {
ZipFile(format.file).use { zip ->
ZipFile.builder().setFile(format.file).get().use { zip ->
val entry =
zip.entries.toList()
zip.entries
.toList()
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
.find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
@@ -412,7 +416,8 @@ class LocalSource(
is Format.Epub -> {
EpubFile(format.file).use { epub ->
val entry =
epub.getImagesFromPages()
epub
.getImagesFromPages()
.firstOrNull()
?.let { epub.getEntry(it) }
@@ -424,7 +429,6 @@ class LocalSource(
logger.error(e) { "Error updating cover for ${manga.title}" }
null
}
}
companion object {
const val ID = 0L
@@ -2,12 +2,14 @@ package eu.kanade.tachiyomi.source.local.filter
import eu.kanade.tachiyomi.source.model.Filter
sealed class OrderBy(selection: Selection) : Filter.Sort(
"Order by",
arrayOf("Title", "Date"),
selection,
) {
class Popular() : OrderBy(Selection(0, true))
sealed class OrderBy(
selection: Selection,
) : Filter.Sort(
"Order by",
arrayOf("Title", "Date"),
selection,
) {
class Popular : OrderBy(Selection(0, true))
class Latest() : OrderBy(Selection(1, false))
class Latest : OrderBy(Selection(1, false))
}
@@ -11,15 +11,15 @@ private const val DEFAULT_COVER_NAME = "cover.jpg"
class LocalCoverManager(
private val fileSystem: LocalSourceFileSystem,
) {
fun find(mangaUrl: String): File? {
return fileSystem.getFilesInMangaDirectory(mangaUrl)
fun find(mangaUrl: String): File? =
fileSystem
.getFilesInMangaDirectory(mangaUrl)
// Get all file whose names start with 'cover'
.filter { it.isFile && it.nameWithoutExtension.equals("cover", ignoreCase = true) }
// Get the first actual image
.firstOrNull {
ImageUtil.isImage(it.name) { it.inputStream() }
}
}
fun update(
manga: SManga,
@@ -3,13 +3,21 @@ package eu.kanade.tachiyomi.source.local.io
import java.io.File
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()
@@ -6,27 +6,22 @@ import java.io.File
class LocalSourceFileSystem(
private val applicationDirs: ApplicationDirs,
) {
fun getBaseDirectories(): Sequence<File> {
return sequenceOf(File(applicationDirs.localMangaRoot))
}
fun getBaseDirectories(): Sequence<File> = sequenceOf(File(applicationDirs.localMangaRoot))
fun getFilesInBaseDirectories(): Sequence<File> {
return getBaseDirectories()
fun getFilesInBaseDirectories(): Sequence<File> =
getBaseDirectories()
// Get all the files inside all baseDir
.flatMap { it.listFiles().orEmpty().toList() }
}
fun getMangaDirectory(name: String): File? {
return getFilesInBaseDirectories()
fun getMangaDirectory(name: String): File? =
getFilesInBaseDirectories()
// Get the first mangaDir or null
.firstOrNull { it.isDirectory && it.name == name }
}
fun getFilesInMangaDirectory(name: String): Sequence<File> {
return getFilesInBaseDirectories()
fun getFilesInMangaDirectory(name: String): Sequence<File> =
getFilesInBaseDirectories()
// Filter out ones that are not related to the manga and is not a directory
.filter { it.isDirectory && it.name == name }
// Get all the files inside the filtered folders
.flatMap { it.listFiles().orEmpty().toList() }
}
}
@@ -6,18 +6,20 @@ import java.io.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)
override suspend fun getPages(): List<ReaderPage> {
return epub.getImagesFromPages()
override suspend fun getPages(): List<ReaderPage> =
epub
.getImagesFromPages()
.mapIndexed { i, path ->
val streamFn = { epub.getInputStream(epub.getEntry(path)!!) }
ReaderPage(i).apply {
stream = streamFn
}
}
}
override fun recycle() {
epub.close()
@@ -12,20 +12,21 @@ import java.io.PipedOutputStream
/**
* 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)
override suspend fun getPages(): List<ReaderPage> {
return rar.fileHeaders.asSequence()
override suspend fun getPages(): List<ReaderPage> =
rar.fileHeaders
.asSequence()
.filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { rar.getInputStream(it) } }
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
.mapIndexed { i, header ->
ReaderPage(i).apply {
stream = { getStream(rar, header) }
}
}
.toList()
}
}.toList()
override fun recycle() {
rar.close()
@@ -8,20 +8,21 @@ import java.io.File
/**
* Loader used to load a chapter from a .zip or .cbz file.
*/
class ZipPageLoader(file: File) : PageLoader {
private val zip = ZipFile(file)
class ZipPageLoader(
file: File,
) : PageLoader {
private val zip = ZipFile.builder().setFile(file).get()
override suspend fun getPages(): List<ReaderPage> {
return zip.entries.asSequence()
override suspend fun getPages(): List<ReaderPage> =
zip.entries
.asSequence()
.filter { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
.mapIndexed { i, entry ->
ReaderPage(i).apply {
stream = { zip.getInputStream(entry) }
}
}
.toList()
}
}.toList()
override fun recycle() {
zip.close()
@@ -17,8 +17,7 @@ fun SManga.copyFromComicInfo(comicInfo: ComicInfo) {
comicInfo.genre?.value,
comicInfo.tags?.value,
comicInfo.categories?.value,
)
.distinct()
).distinct()
.joinToString(", ") { it.trim() }
.takeIf { it.isNotEmpty() }
?.let { genre = it }
@@ -29,8 +28,7 @@ fun SManga.copyFromComicInfo(comicInfo: ComicInfo) {
comicInfo.colorist?.value,
comicInfo.letterer?.value,
comicInfo.coverArtist?.value,
)
.flatMap { it.split(", ") }
).flatMap { it.split(", ") }
.distinct()
.joinToString(", ") { it.trim() }
.takeIf { it.isNotEmpty() }
@@ -202,14 +200,12 @@ enum class ComicInfoPublishingStatus(
;
companion object {
fun toComicInfoValue(value: Long): String {
return entries.firstOrNull { it.sMangaModelValue == value.toInt() }?.comicInfoValue
fun toComicInfoValue(value: Long): String =
entries.firstOrNull { it.sMangaModelValue == value.toInt() }?.comicInfoValue
?: UNKNOWN.comicInfoValue
}
fun toSMangaValue(value: String?): Int {
return entries.firstOrNull { it.comicInfoValue == value }?.sMangaModelValue
fun toSMangaValue(value: String?): Int =
entries.firstOrNull { it.comicInfoValue == value }?.sMangaModelValue
?: UNKNOWN.sMangaModelValue
}
}
}
@@ -2,20 +2,40 @@ package eu.kanade.tachiyomi.source.model
// The class is originally sealed, Tachidesk adds new subclasses for serialization
// sealed 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 Filter<T>(
val name: String,
var state: T,
) {
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() }
}
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 isIncluded() = state == STATE_INCLUDE
@@ -29,11 +49,20 @@ open class Filter<T>(val name: String, var state: T) {
}
}
abstract class Group<V>(name: String, state: List<V>) : Filter<List<V>>(name, state)
abstract class Group<V>(
name: String,
state: List<V>,
) : Filter<List<V>>(name, state)
abstract class Sort(name: String, val values: Array<String>, state: Selection? = null) :
Filter<Sort.Selection?>(name, state) {
data class Selection(val index: Int, val ascending: Boolean)
abstract class Sort(
name: String,
val values: Array<String>,
state: Selection? = null,
) : Filter<Sort.Selection?>(name, state) {
data class Selection(
val index: Int,
val ascending: Boolean,
)
}
override fun equals(other: Any?): Boolean {
@@ -1,5 +1,7 @@
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())
}
@@ -1,3 +1,6 @@
package eu.kanade.tachiyomi.source.model
data class MangasPage(val mangas: List<SManga>, val hasNextPage: Boolean)
data class MangasPage(
val mangas: List<SManga>,
val hasNextPage: Boolean,
)
@@ -24,8 +24,6 @@ interface SChapter : Serializable {
}
companion object {
fun create(): SChapter {
return SChapterImpl()
}
fun create(): SChapter = SChapterImpl()
}
}
@@ -62,9 +62,7 @@ interface SManga : Serializable {
const val CANCELLED = 5
const val ON_HIATUS = 6
fun create(): SManga {
return SMangaImpl()
}
fun create(): SManga = SMangaImpl()
}
}
@@ -66,9 +66,7 @@ abstract class HttpSource : CatalogueSource {
open val client: OkHttpClient
get() = network.client
private fun generateId(): Long {
return generateId("${name.lowercase()}/$lang/$versionId")
}
private fun generateId(): Long = generateId("${name.lowercase()}/$lang/$versionId")
/**
* Generates a unique ID for the source based on the provided [name], [lang] and
@@ -121,13 +119,13 @@ abstract class HttpSource : CatalogueSource {
* @param page the page number to retrieve.
*/
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPopularManga"))
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
return client.newCall(popularMangaRequest(page))
override fun fetchPopularManga(page: Int): Observable<MangasPage> =
client
.newCall(popularMangaRequest(page))
.asObservableSuccess()
.map { response ->
popularMangaParse(response)
}
}
/**
* Returns the request for the popular manga given the page.
@@ -156,20 +154,19 @@ abstract class HttpSource : CatalogueSource {
page: Int,
query: String,
filters: FilterList,
): Observable<MangasPage> {
return Observable.defer {
try {
client.newCall(searchMangaRequest(page, query, filters)).asObservableSuccess()
} catch (e: NoClassDefFoundError) {
// RxJava doesn't handle Errors, which tends to happen during global searches
// if an old extension using non-existent classes is still around
throw RuntimeException(e)
}
}
.map { response ->
): Observable<MangasPage> =
Observable
.defer {
try {
client.newCall(searchMangaRequest(page, query, filters)).asObservableSuccess()
} catch (e: NoClassDefFoundError) {
// RxJava doesn't handle Errors, which tends to happen during global searches
// if an old extension using non-existent classes is still around
throw RuntimeException(e)
}
}.map { response ->
searchMangaParse(response)
}
}
/**
* Returns the request for the search manga given the page.
@@ -197,13 +194,13 @@ abstract class HttpSource : CatalogueSource {
* @param page the page number to retrieve.
*/
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getLatestUpdates"))
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
return client.newCall(latestUpdatesRequest(page))
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> =
client
.newCall(latestUpdatesRequest(page))
.asObservableSuccess()
.map { response ->
latestUpdatesParse(response)
}
}
/**
* Returns the request for latest manga given the page.
@@ -227,18 +224,16 @@ abstract class HttpSource : CatalogueSource {
* @return the updated manga.
*/
@Suppress("DEPRECATION")
override suspend fun getMangaDetails(manga: SManga): SManga {
return fetchMangaDetails(manga).awaitSingle()
}
override suspend fun getMangaDetails(manga: SManga): SManga = fetchMangaDetails(manga).awaitSingle()
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getMangaDetails"))
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(mangaDetailsRequest(manga))
override fun fetchMangaDetails(manga: SManga): Observable<SManga> =
client
.newCall(mangaDetailsRequest(manga))
.asObservableSuccess()
.map { response ->
mangaDetailsParse(response).apply { initialized = true }
}
}
/**
* Returns the request for the details of a manga. Override only if it's needed to change the
@@ -246,9 +241,7 @@ abstract class HttpSource : CatalogueSource {
*
* @param manga the manga to be updated.
*/
open fun mangaDetailsRequest(manga: SManga): Request {
return GET(baseUrl + manga.url, headers)
}
open fun mangaDetailsRequest(manga: SManga): Request = GET(baseUrl + manga.url, headers)
/**
* Parses the response from the site and returns the details of a manga.
@@ -275,9 +268,10 @@ abstract class HttpSource : CatalogueSource {
}
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getChapterList"))
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
return if (manga.status != SManga.LICENSED) {
client.newCall(chapterListRequest(manga))
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> =
if (manga.status != SManga.LICENSED) {
client
.newCall(chapterListRequest(manga))
.asObservableSuccess()
.map { response ->
chapterListParse(response)
@@ -285,7 +279,6 @@ abstract class HttpSource : CatalogueSource {
} else {
Observable.error(LicensedMangaChaptersException())
}
}
/**
* Returns the request for updating the chapter list. Override only if it's needed to override
@@ -293,9 +286,7 @@ abstract class HttpSource : CatalogueSource {
*
* @param manga the manga to look for chapters.
*/
protected open fun chapterListRequest(manga: SManga): Request {
return GET(baseUrl + manga.url, headers)
}
protected open fun chapterListRequest(manga: SManga): Request = GET(baseUrl + manga.url, headers)
/**
* Parses the response from the site and returns a list of chapters.
@@ -312,18 +303,16 @@ abstract class HttpSource : CatalogueSource {
* @return the pages for the chapter.
*/
@Suppress("DEPRECATION")
override suspend fun getPageList(chapter: SChapter): List<Page> {
return fetchPageList(chapter).awaitSingle()
}
override suspend fun getPageList(chapter: SChapter): List<Page> = fetchPageList(chapter).awaitSingle()
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPageList"))
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
return client.newCall(pageListRequest(chapter))
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> =
client
.newCall(pageListRequest(chapter))
.asObservableSuccess()
.map { response ->
pageListParse(response)
}
}
/**
* Returns the request for getting the page list. Override only if it's needed to override the
@@ -331,9 +320,7 @@ abstract class HttpSource : CatalogueSource {
*
* @param chapter the chapter whose page list has to be fetched.
*/
protected open fun pageListRequest(chapter: SChapter): Request {
return GET(baseUrl + chapter.url, headers)
}
protected open fun pageListRequest(chapter: SChapter): Request = GET(baseUrl + chapter.url, headers)
/**
* Parses the response from the site and returns a list of pages.
@@ -350,16 +337,14 @@ abstract class HttpSource : CatalogueSource {
* @param page the page whose source image has to be fetched.
*/
@Suppress("DEPRECATION")
open suspend fun getImageUrl(page: Page): String {
return fetchImageUrl(page).awaitSingle()
}
open suspend fun getImageUrl(page: Page): String = fetchImageUrl(page).awaitSingle()
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getImageUrl"))
open fun fetchImageUrl(page: Page): Observable<String> {
return client.newCall(imageUrlRequest(page))
open fun fetchImageUrl(page: Page): Observable<String> =
client
.newCall(imageUrlRequest(page))
.asObservableSuccess()
.map { imageUrlParse(it) }
}
/**
* Returns the request for getting the url to the source image. Override only if it's needed to
@@ -367,9 +352,7 @@ abstract class HttpSource : CatalogueSource {
*
* @param page the chapter whose page list has to be fetched
*/
protected open fun imageUrlRequest(page: Page): Request {
return GET(page.url, headers)
}
protected open fun imageUrlRequest(page: Page): Request = GET(page.url, headers)
/**
* Parses the response from the site and returns the absolute url to the source image.
@@ -385,10 +368,10 @@ abstract class HttpSource : CatalogueSource {
* @since extensions-lib 1.5
* @param page the page whose source image has to be downloaded.
*/
open suspend fun getImage(page: Page): Response {
return client.newCachelessCallWithProgress(imageRequest(page), page)
open suspend fun getImage(page: Page): Response =
client
.newCachelessCallWithProgress(imageRequest(page), page)
.awaitSuccess()
}
/**
* Returns the request for getting the source image. Override only if it's needed to override
@@ -396,9 +379,7 @@ abstract class HttpSource : CatalogueSource {
*
* @param page the chapter whose page list has to be fetched
*/
protected open fun imageRequest(page: Page): Request {
return GET(page.imageUrl!!, headers)
}
protected open fun imageRequest(page: Page): Request = GET(page.imageUrl!!, headers)
/**
* Assigns the url of the chapter without the scheme and domain. It saves some redundancy from
@@ -425,8 +406,8 @@ abstract class HttpSource : CatalogueSource {
*
* @param orig the full url.
*/
private fun getUrlWithoutDomain(orig: String): String {
return try {
private fun getUrlWithoutDomain(orig: String): String =
try {
val uri = URI(orig.replace(" ", "%20"))
var out = uri.path
if (uri.query != null) {
@@ -439,7 +420,6 @@ abstract class HttpSource : CatalogueSource {
} catch (e: URISyntaxException) {
orig
}
}
/**
* Returns the url of the provided manga
@@ -448,9 +428,7 @@ abstract class HttpSource : CatalogueSource {
* @param manga the manga
* @return url of the manga
*/
open fun getMangaUrl(manga: SManga): String {
return mangaDetailsRequest(manga).url.toString()
}
open fun getMangaUrl(manga: SManga): String = mangaDetailsRequest(manga).url.toString()
/**
* Returns the url of the provided chapter
@@ -459,9 +437,7 @@ abstract class HttpSource : CatalogueSource {
* @param chapter the chapter
* @return url of the chapter
*/
open fun getChapterUrl(chapter: SChapter): String {
return pageListRequest(chapter).url.toString()
}
open fun getChapterUrl(chapter: SChapter): String = pageListRequest(chapter).url.toString()
/**
* Called before inserting a new chapter into database. Use it if you need to override chapter
@@ -138,9 +138,7 @@ abstract class ParsedHttpSource : HttpSource() {
*
* @param response the response from the site.
*/
override fun mangaDetailsParse(response: Response): SManga {
return mangaDetailsParse(response.asJsoup())
}
override fun mangaDetailsParse(response: Response): SManga = mangaDetailsParse(response.asJsoup())
/**
* Returns the details of the manga from the given [document].
@@ -176,9 +174,7 @@ abstract class ParsedHttpSource : HttpSource() {
*
* @param response the response from the site.
*/
override fun pageListParse(response: Response): List<Page> {
return pageListParse(response.asJsoup())
}
override fun pageListParse(response: Response): List<Page> = pageListParse(response.asJsoup())
/**
* Returns a page list from the given document.
@@ -192,9 +188,7 @@ abstract class ParsedHttpSource : HttpSource() {
*
* @param response the response from the site.
*/
override fun imageUrlParse(response: Response): String {
return imageUrlParse(response.asJsoup())
}
override fun imageUrlParse(response: Response): String = imageUrlParse(response.asJsoup())
/**
* Returns the absolute url to the source image from the document.
@@ -8,25 +8,17 @@ import org.jsoup.nodes.Element
fun Element.selectText(
css: String,
defaultValue: String? = null,
): String? {
return select(css).first()?.text() ?: defaultValue
}
): String? = select(css).first()?.text() ?: defaultValue
fun Element.selectInt(
css: String,
defaultValue: Int = 0,
): Int {
return select(css).first()?.text()?.toInt() ?: defaultValue
}
): Int = select(css).first()?.text()?.toInt() ?: defaultValue
fun Element.attrOrText(css: String): String {
return if (css != "text") attr(css) else text()
}
fun Element.attrOrText(css: String): String = if (css != "text") attr(css) else text()
/**
* Returns a Jsoup document for this response.
* @param html the body of the response. Use only if the body was read before calling this method.
*/
fun Response.asJsoup(html: String? = null): Document {
return Jsoup.parse(html ?: body.string(), request.url.toString())
}
fun Response.asJsoup(html: String? = null): Document = Jsoup.parse(html ?: body.string(), request.url.toString())
@@ -68,15 +68,14 @@ object ChapterRecognition {
* @param match result of regex
* @return chapter number if found else null
*/
private fun getChapterNumberFromMatch(match: MatchResult): Double {
return match.let {
private fun getChapterNumberFromMatch(match: MatchResult): Double =
match.let {
val initial = it.groups[1]?.value?.toDouble()!!
val subChapterDecimal = it.groups[2]?.value
val subChapterAlpha = it.groups[3]?.value
val addition = checkForDecimal(subChapterDecimal, subChapterAlpha)
initial.plus(addition)
}
}
/**
* Check for decimal in received strings
@@ -1,11 +1,10 @@
package eu.kanade.tachiyomi.util.chapter
object ChapterSanitizer {
fun String.sanitize(title: String): String {
return trim()
fun String.sanitize(title: String): String =
trim()
.removePrefix(title)
.trim(*CHAPTER_TRIM_CHARS)
}
private val CHAPTER_TRIM_CHARS =
arrayOf(
@@ -5,29 +5,35 @@ import java.security.MessageDigest
object Hash {
private val chars =
charArrayOf(
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f',
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'a',
'b',
'c',
'd',
'e',
'f',
)
private val MD5 get() = MessageDigest.getInstance("MD5")
private val SHA256 get() = MessageDigest.getInstance("SHA-256")
fun sha256(bytes: ByteArray): String {
return encodeHex(SHA256.digest(bytes))
}
fun sha256(bytes: ByteArray): String = encodeHex(SHA256.digest(bytes))
fun sha256(string: String): String {
return sha256(string.toByteArray())
}
fun sha256(string: String): String = sha256(string.toByteArray())
fun md5(bytes: ByteArray): String {
return encodeHex(MD5.digest(bytes))
}
fun md5(bytes: ByteArray): String = encodeHex(MD5.digest(bytes))
fun md5(string: String): String {
return md5(string.toByteArray())
}
fun md5(string: String): String = md5(string.toByteArray())
private fun encodeHex(data: ByteArray): String {
val l = data.size
@@ -10,13 +10,12 @@ import kotlin.math.floor
fun String.chop(
count: Int,
replacement: String = "",
): String {
return if (length > count) {
): String =
if (length > count) {
take(count - replacement.length) + replacement
} else {
this
}
}
/**
* Replaces the given string to have at most [count] characters using [replacement] near the center.
@@ -46,9 +45,7 @@ fun String.compareToCaseInsensitiveNaturalOrder(other: String): Int {
/**
* Returns the size of the string as the number of bytes.
*/
fun String.byteSize(): Int {
return toByteArray(Charsets.UTF_8).size
}
fun String.byteSize(): Int = toByteArray(Charsets.UTF_8).size
/**
* Returns a string containing the first [n] bytes from this string, or the entire string if this
@@ -11,11 +11,13 @@ import java.io.InputStream
/**
* Wrapper over ZipFile to load files in epub format.
*/
class EpubFile(file: File) : Closeable {
class EpubFile(
file: File,
) : Closeable {
/**
* Zip file of this epub.
*/
private val zip = ZipFile(file)
private val zip = ZipFile.builder().setFile(file).get()
/**
* Path separator used by this epub.
@@ -32,16 +34,12 @@ class EpubFile(file: File) : Closeable {
/**
* Returns an input stream for reading the contents of the specified zip file entry.
*/
fun getInputStream(entry: ZipArchiveEntry): InputStream {
return zip.getInputStream(entry)
}
fun getInputStream(entry: ZipArchiveEntry): InputStream = zip.getInputStream(entry)
/**
* Returns the zip file entry for the specified name, or null if not found.
*/
fun getEntry(name: String): ZipArchiveEntry? {
return zip.getEntry(name)
}
fun getEntry(name: String): ZipArchiveEntry? = zip.getEntry(name)
/**
* Returns the path of all the images found in the epub file.
@@ -81,7 +79,8 @@ class EpubFile(file: File) : Closeable {
*/
fun getPagesFromDocument(document: Document): List<String> {
val pages =
document.select("manifest > item")
document
.select("manifest > item")
.filter { node -> "application/xhtml+xml" == node.attr("media-type") }
.associateBy { it.attr("id") }
@@ -20,8 +20,8 @@ data class AboutDataClass(
)
object About {
fun getAbout(): AboutDataClass {
return AboutDataClass(
fun getAbout(): AboutDataClass =
AboutDataClass(
BuildConfig.NAME,
BuildConfig.VERSION,
BuildConfig.REVISION,
@@ -30,5 +30,4 @@ object About {
BuildConfig.GITHUB,
BuildConfig.DISCORD,
)
}
}
@@ -31,18 +31,26 @@ object AppUpdate {
suspend fun checkUpdate(): List<UpdateDataClass> {
val stableJson =
json.parseToJsonElement(
network.client.newCall(
GET(LATEST_STABLE_CHANNEL_URL),
).await().body.string(),
).jsonObject
json
.parseToJsonElement(
network.client
.newCall(
GET(LATEST_STABLE_CHANNEL_URL),
).await()
.body
.string(),
).jsonObject
val previewJson =
json.parseToJsonElement(
network.client.newCall(
GET(LATEST_PREVIEW_CHANNEL_URL),
).await().body.string(),
).jsonObject
json
.parseToJsonElement(
network.client
.newCall(
GET(LATEST_PREVIEW_CHANNEL_URL),
).await()
.body
.string(),
).jsonObject
return listOf(
UpdateDataClass(
@@ -38,10 +38,10 @@ object GlobalMeta {
}
}
fun getMetaMap(): Map<String, String> {
return transaction {
GlobalMetaTable.selectAll()
fun getMetaMap(): Map<String, String> =
transaction {
GlobalMetaTable
.selectAll()
.associate { it[GlobalMetaTable.key] to it[GlobalMetaTable.value] }
}
}
}
@@ -14,12 +14,14 @@ inline fun <T> asDataFetcherResult(block: () -> T): DataFetcherResult<T?> {
if (result.isFailure) {
logger.error(result.exceptionOrNull()) { "asDataFetcherResult: failed due to" }
return DataFetcherResult.newResult<T?>()
return DataFetcherResult
.newResult<T?>()
.error(result.exceptionOrNull()?.toGraphQLError())
.build()
}
return DataFetcherResult.newResult<T?>()
return DataFetcherResult
.newResult<T?>()
.data(result.getOrNull())
.build()
}
@@ -10,21 +10,13 @@ class CustomCacheMap<K, V> : CacheMap<K, V> {
cache = HashMap()
}
override fun containsKey(key: K): Boolean {
return cache.containsKey(key)
}
override fun containsKey(key: K): Boolean = cache.containsKey(key)
override fun get(key: K): CompletableFuture<V> {
return cache[key]!!
}
override fun get(key: K): CompletableFuture<V> = cache[key]!!
fun getKeys(): Collection<K> {
return cache.keys.toSet()
}
fun getKeys(): Collection<K> = cache.keys.toSet()
override fun getAll(): Collection<CompletableFuture<V>> {
return cache.values
}
override fun getAll(): Collection<CompletableFuture<V>> = cache.values
override fun set(
key: K,
@@ -30,7 +30,8 @@ class CategoryDataLoader : KotlinDataLoader<Int, CategoryType> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val categories =
CategoryTable.select { CategoryTable.id inList ids }
CategoryTable
.select { CategoryTable.id inList ids }
.map { CategoryType(it) }
.associateBy { it.id }
ids.map { categories[it] }
@@ -66,7 +67,8 @@ class CategoriesForMangaDataLoader : KotlinDataLoader<Int, CategoryNodeList> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val itemsByRef =
CategoryMangaTable.innerJoin(CategoryTable)
CategoryMangaTable
.innerJoin(CategoryTable)
.select { CategoryMangaTable.manga inList ids }
.map { Pair(it[CategoryMangaTable.manga].value, CategoryType(it)) }
.groupBy { it.first }
@@ -32,7 +32,8 @@ class ChapterDataLoader : KotlinDataLoader<Int, ChapterType?> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val chapters =
ChapterTable.select { ChapterTable.id inList ids }
ChapterTable
.select { ChapterTable.id inList ids }
.map { ChapterType(it) }
.associateBy { it.id }
ids.map { chapters[it] }
@@ -50,7 +51,8 @@ class ChaptersForMangaDataLoader : KotlinDataLoader<Int, ChapterNodeList> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val chaptersByMangaId =
ChapterTable.select { ChapterTable.manga inList ids }
ChapterTable
.select { ChapterTable.manga inList ids }
.map { ChapterType(it) }
.groupBy { it.mangaId }
ids.map { (chaptersByMangaId[it] ?: emptyList()).toNodeList() }
@@ -128,7 +130,8 @@ class HasDuplicateChaptersForMangaDataLoader : KotlinDataLoader<Int, Boolean> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val duplicatedChapterCountByMangaId =
ChapterTable.slice(ChapterTable.manga, ChapterTable.chapter_number, ChapterTable.chapter_number.count())
ChapterTable
.slice(ChapterTable.manga, ChapterTable.chapter_number, ChapterTable.chapter_number.count())
.select { (ChapterTable.manga inList ids) and (ChapterTable.chapter_number greaterEq 0f) }
.groupBy(ChapterTable.manga, ChapterTable.chapter_number)
.having { ChapterTable.chapter_number.count() greater 1 }
@@ -28,7 +28,8 @@ class ExtensionDataLoader : KotlinDataLoader<String, ExtensionType?> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val extensions =
ExtensionTable.select { ExtensionTable.pkgName inList ids }
ExtensionTable
.select { ExtensionTable.pkgName inList ids }
.map { ExtensionType(it) }
.associateBy { it.pkgName }
ids.map { extensions[it] }
@@ -46,7 +47,8 @@ class ExtensionForSourceDataLoader : KotlinDataLoader<Long, ExtensionType?> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val extensions =
ExtensionTable.innerJoin(SourceTable)
ExtensionTable
.innerJoin(SourceTable)
.select { SourceTable.id inList ids }
.toList()
.map { Triple(it[SourceTable.id].value, it[ExtensionTable.pkgName], it) }
@@ -33,7 +33,8 @@ class MangaDataLoader : KotlinDataLoader<Int, MangaType?> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val manga =
MangaTable.select { MangaTable.id inList ids }
MangaTable
.select { MangaTable.id inList ids }
.map { MangaType(it) }
.associateBy { it.id }
ids.map { manga[it] }
@@ -63,7 +64,8 @@ class MangaForCategoryDataLoader : KotlinDataLoader<Int, MangaNodeList> {
} else {
emptyMap()
} +
CategoryMangaTable.innerJoin(MangaTable)
CategoryMangaTable
.innerJoin(MangaTable)
.select { CategoryMangaTable.category inList ids }
.map { Pair(it[CategoryMangaTable.category].value, MangaType(it)) }
.groupBy { it.first }
@@ -84,7 +86,8 @@ class MangaForSourceDataLoader : KotlinDataLoader<Long, MangaNodeList> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val mangaBySourceId =
MangaTable.select { MangaTable.sourceReference inList ids }
MangaTable
.select { MangaTable.sourceReference inList ids }
.map { MangaType(it) }
.groupBy { it.sourceId }
ids.map { (mangaBySourceId[it] ?: emptyList()).toNodeList() }
@@ -104,7 +107,8 @@ class MangaForIdsDataLoader : KotlinDataLoader<List<Int>, MangaNodeList> {
addLogger(Slf4jSqlDebugLogger)
val ids = mangaIds.flatten().distinct()
val manga =
MangaTable.select { MangaTable.id inList ids }
MangaTable
.select { MangaTable.id inList ids }
.map { MangaType(it) }
mangaIds.map { mangaIds ->
manga.filter { it.id in mangaIds }.toNodeList()
@@ -28,7 +28,8 @@ class GlobalMetaDataLoader : KotlinDataLoader<String, GlobalMetaType?> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val metasByRefId =
GlobalMetaTable.select { GlobalMetaTable.key inList ids }
GlobalMetaTable
.select { GlobalMetaTable.key inList ids }
.map { GlobalMetaType(it) }
.associateBy { it.key }
ids.map { metasByRefId[it] }
@@ -46,7 +47,8 @@ class ChapterMetaDataLoader : KotlinDataLoader<Int, List<ChapterMetaType>> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val metasByRefId =
ChapterMetaTable.select { ChapterMetaTable.ref inList ids }
ChapterMetaTable
.select { ChapterMetaTable.ref inList ids }
.map { ChapterMetaType(it) }
.groupBy { it.chapterId }
ids.map { metasByRefId[it].orEmpty() }
@@ -64,7 +66,8 @@ class MangaMetaDataLoader : KotlinDataLoader<Int, List<MangaMetaType>> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val metasByRefId =
MangaMetaTable.select { MangaMetaTable.ref inList ids }
MangaMetaTable
.select { MangaMetaTable.ref inList ids }
.map { MangaMetaType(it) }
.groupBy { it.mangaId }
ids.map { metasByRefId[it].orEmpty() }
@@ -82,7 +85,8 @@ class CategoryMetaDataLoader : KotlinDataLoader<Int, List<CategoryMetaType>> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val metasByRefId =
CategoryMetaTable.select { CategoryMetaTable.ref inList ids }
CategoryMetaTable
.select { CategoryMetaTable.ref inList ids }
.map { CategoryMetaType(it) }
.groupBy { it.categoryId }
ids.map { metasByRefId[it].orEmpty() }
@@ -100,7 +104,8 @@ class SourceMetaDataLoader : KotlinDataLoader<Long, List<SourceMetaType>> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val metasByRefId =
SourceMetaTable.select { SourceMetaTable.ref inList ids }
SourceMetaTable
.select { SourceMetaTable.ref inList ids }
.map { SourceMetaType(it) }
.groupBy { it.sourceId }
ids.map { metasByRefId[it].orEmpty() }
@@ -30,7 +30,8 @@ class SourceDataLoader : KotlinDataLoader<Long, SourceType?> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val source =
SourceTable.select { SourceTable.id inList ids }
SourceTable
.select { SourceTable.id inList ids }
.mapNotNull { SourceType(it) }
.associateBy { it.id }
ids.map { source[it] }
@@ -49,7 +50,8 @@ class SourcesForExtensionDataLoader : KotlinDataLoader<String, SourceNodeList> {
addLogger(Slf4jSqlDebugLogger)
val sourcesByExtensionPkg =
SourceTable.innerJoin(ExtensionTable)
SourceTable
.innerJoin(ExtensionTable)
.select { ExtensionTable.pkgName inList ids }
.map { Pair(it[ExtensionTable.pkgName], SourceType(it)) }
.groupBy { it.first }
@@ -89,7 +89,8 @@ class TrackRecordsForMangaIdDataLoader : KotlinDataLoader<Int, TrackRecordNodeLi
transaction {
addLogger(Slf4jSqlDebugLogger)
val trackRecordsByMangaId =
TrackRecordTable.select { TrackRecordTable.mangaId inList ids }
TrackRecordTable
.select { TrackRecordTable.mangaId inList ids }
.map { TrackRecordType(it) }
.groupBy { it.mangaId }
ids.map { (trackRecordsByMangaId[it] ?: emptyList()).toNodeList() }
@@ -107,7 +108,8 @@ class DisplayScoreForTrackRecordDataLoader : KotlinDataLoader<Int, String> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val trackRecords =
TrackRecordTable.select { TrackRecordTable.id inList ids }
TrackRecordTable
.select { TrackRecordTable.id inList ids }
.toList()
.map { it.toTrack() }
.associateBy { it.id!! }
@@ -128,7 +130,8 @@ class TrackRecordsForTrackerIdDataLoader : KotlinDataLoader<Int, TrackRecordNode
transaction {
addLogger(Slf4jSqlDebugLogger)
val trackRecordsBySyncId =
TrackRecordTable.select { TrackRecordTable.trackerId inList ids }
TrackRecordTable
.select { TrackRecordTable.trackerId inList ids }
.map { TrackRecordType(it) }
.groupBy { it.mangaId }
ids.map { (trackRecordsBySyncId[it] ?: emptyList()).toNodeList() }
@@ -146,7 +149,8 @@ class TrackRecordDataLoader : KotlinDataLoader<Int, TrackRecordType> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val trackRecordsId =
TrackRecordTable.select { TrackRecordTable.id inList ids }
TrackRecordTable
.select { TrackRecordTable.id inList ids }
.map { TrackRecordType(it) }
.associateBy { it.id }
ids.map { trackRecordsId[it] }
@@ -38,15 +38,14 @@ class CategoryMutation {
val meta: CategoryMetaType,
)
fun setCategoryMeta(input: SetCategoryMetaInput): DataFetcherResult<SetCategoryMetaPayload?> {
return asDataFetcherResult {
fun setCategoryMeta(input: SetCategoryMetaInput): DataFetcherResult<SetCategoryMetaPayload?> =
asDataFetcherResult {
val (clientMutationId, meta) = input
Category.modifyMeta(meta.categoryId, meta.key, meta.value)
SetCategoryMetaPayload(clientMutationId, meta)
}
}
data class DeleteCategoryMetaInput(
val clientMutationId: String? = null,
@@ -60,14 +59,15 @@ class CategoryMutation {
val category: CategoryType,
)
fun deleteCategoryMeta(input: DeleteCategoryMetaInput): DataFetcherResult<DeleteCategoryMetaPayload?> {
return asDataFetcherResult {
fun deleteCategoryMeta(input: DeleteCategoryMetaInput): DataFetcherResult<DeleteCategoryMetaPayload?> =
asDataFetcherResult {
val (clientMutationId, categoryId, key) = input
val (meta, category) =
transaction {
val meta =
CategoryMetaTable.select { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) }
CategoryMetaTable
.select { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) }
.firstOrNull()
CategoryMetaTable.deleteWhere { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) }
@@ -86,7 +86,6 @@ class CategoryMutation {
DeleteCategoryMetaPayload(clientMutationId, meta, category)
}
}
data class UpdateCategoryPatch(
val name: String? = null,
@@ -153,8 +152,8 @@ class CategoryMutation {
}
}
fun updateCategory(input: UpdateCategoryInput): DataFetcherResult<UpdateCategoryPayload?> {
return asDataFetcherResult {
fun updateCategory(input: UpdateCategoryInput): DataFetcherResult<UpdateCategoryPayload?> =
asDataFetcherResult {
val (clientMutationId, id, patch) = input
updateCategories(listOf(id), patch)
@@ -169,10 +168,9 @@ class CategoryMutation {
category = category,
)
}
}
fun updateCategories(input: UpdateCategoriesInput): DataFetcherResult<UpdateCategoriesPayload?> {
return asDataFetcherResult {
fun updateCategories(input: UpdateCategoriesInput): DataFetcherResult<UpdateCategoriesPayload?> =
asDataFetcherResult {
val (clientMutationId, ids, patch) = input
updateCategories(ids, patch)
@@ -187,7 +185,6 @@ class CategoryMutation {
categories = categories,
)
}
}
data class UpdateCategoryOrderPayload(
val clientMutationId: String?,
@@ -200,8 +197,8 @@ class CategoryMutation {
val position: Int,
)
fun updateCategoryOrder(input: UpdateCategoryOrderInput): DataFetcherResult<UpdateCategoryOrderPayload?> {
return asDataFetcherResult {
fun updateCategoryOrder(input: UpdateCategoryOrderInput): DataFetcherResult<UpdateCategoryOrderPayload?> =
asDataFetcherResult {
val (clientMutationId, categoryId, position) = input
require(position > 0) {
"'order' must not be <= 0"
@@ -242,7 +239,6 @@ class CategoryMutation {
categories = categories,
)
}
}
data class CreateCategoryInput(
val clientMutationId: String? = null,
@@ -258,8 +254,8 @@ class CategoryMutation {
val category: CategoryType,
)
fun createCategory(input: CreateCategoryInput): DataFetcherResult<CreateCategoryPayload?> {
return asDataFetcherResult {
fun createCategory(input: CreateCategoryInput): DataFetcherResult<CreateCategoryPayload?> =
asDataFetcherResult {
val (clientMutationId, name, order, default, includeInUpdate, includeInDownload) = input
transaction {
require(CategoryTable.select { CategoryTable.name eq input.name }.isEmpty()) {
@@ -305,7 +301,6 @@ class CategoryMutation {
CreateCategoryPayload(clientMutationId, category)
}
}
data class DeleteCategoryInput(
val clientMutationId: String? = null,
@@ -332,12 +327,14 @@ class CategoryMutation {
val (category, mangas) =
transaction {
val category =
CategoryTable.select { CategoryTable.id eq categoryId }
CategoryTable
.select { CategoryTable.id eq categoryId }
.firstOrNull()
val mangas =
transaction {
MangaTable.innerJoin(CategoryMangaTable)
MangaTable
.innerJoin(CategoryMangaTable)
.select { CategoryMangaTable.category eq categoryId }
.map { MangaType(it) }
}
@@ -403,9 +400,10 @@ class CategoryMutation {
ids.filter { it != DEFAULT_CATEGORY_ID }.forEach { mangaId ->
patch.addToCategories.forEach { categoryId ->
val existingMapping =
CategoryMangaTable.select {
(CategoryMangaTable.manga eq mangaId) and (CategoryMangaTable.category eq categoryId)
}.isNotEmpty()
CategoryMangaTable
.select {
(CategoryMangaTable.manga eq mangaId) and (CategoryMangaTable.category eq categoryId)
}.isNotEmpty()
if (!existingMapping) {
add(mangaId to categoryId)
@@ -422,8 +420,8 @@ class CategoryMutation {
}
}
fun updateMangaCategories(input: UpdateMangaCategoriesInput): DataFetcherResult<UpdateMangaCategoriesPayload?> {
return asDataFetcherResult {
fun updateMangaCategories(input: UpdateMangaCategoriesInput): DataFetcherResult<UpdateMangaCategoriesPayload?> =
asDataFetcherResult {
val (clientMutationId, id, patch) = input
updateMangas(listOf(id), patch)
@@ -438,10 +436,9 @@ class CategoryMutation {
manga = manga,
)
}
}
fun updateMangasCategories(input: UpdateMangasCategoriesInput): DataFetcherResult<UpdateMangasCategoriesPayload?> {
return asDataFetcherResult {
fun updateMangasCategories(input: UpdateMangasCategoriesInput): DataFetcherResult<UpdateMangasCategoriesPayload?> =
asDataFetcherResult {
val (clientMutationId, ids, patch) = input
updateMangas(ids, patch)
@@ -456,5 +453,4 @@ class CategoryMutation {
mangas = mangas,
)
}
}
}
@@ -95,8 +95,8 @@ class ChapterMutation {
}
}
fun updateChapter(input: UpdateChapterInput): DataFetcherResult<UpdateChapterPayload?> {
return asDataFetcherResult {
fun updateChapter(input: UpdateChapterInput): DataFetcherResult<UpdateChapterPayload?> =
asDataFetcherResult {
val (clientMutationId, id, patch) = input
updateChapters(listOf(id), patch)
@@ -111,10 +111,9 @@ class ChapterMutation {
chapter = chapter,
)
}
}
fun updateChapters(input: UpdateChaptersInput): DataFetcherResult<UpdateChaptersPayload?> {
return asDataFetcherResult {
fun updateChapters(input: UpdateChaptersInput): DataFetcherResult<UpdateChaptersPayload?> =
asDataFetcherResult {
val (clientMutationId, ids, patch) = input
updateChapters(ids, patch)
@@ -129,7 +128,6 @@ class ChapterMutation {
chapters = chapters,
)
}
}
data class FetchChaptersInput(
val clientMutationId: String? = null,
@@ -150,7 +148,8 @@ class ChapterMutation {
val chapters =
transaction {
ChapterTable.select { ChapterTable.manga eq mangaId }
ChapterTable
.select { ChapterTable.manga eq mangaId }
.orderBy(ChapterTable.sourceOrder)
.map { ChapterType(it) }
}
@@ -173,15 +172,14 @@ class ChapterMutation {
val meta: ChapterMetaType,
)
fun setChapterMeta(input: SetChapterMetaInput): DataFetcherResult<SetChapterMetaPayload?> {
return asDataFetcherResult {
fun setChapterMeta(input: SetChapterMetaInput): DataFetcherResult<SetChapterMetaPayload?> =
asDataFetcherResult {
val (clientMutationId, meta) = input
Chapter.modifyChapterMeta(meta.chapterId, meta.key, meta.value)
SetChapterMetaPayload(clientMutationId, meta)
}
}
data class DeleteChapterMetaInput(
val clientMutationId: String? = null,
@@ -195,14 +193,15 @@ class ChapterMutation {
val chapter: ChapterType,
)
fun deleteChapterMeta(input: DeleteChapterMetaInput): DataFetcherResult<DeleteChapterMetaPayload?> {
return asDataFetcherResult {
fun deleteChapterMeta(input: DeleteChapterMetaInput): DataFetcherResult<DeleteChapterMetaPayload?> =
asDataFetcherResult {
val (clientMutationId, chapterId, key) = input
val (meta, chapter) =
transaction {
val meta =
ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
ChapterMetaTable
.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
.firstOrNull()
ChapterMetaTable.deleteWhere { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
@@ -221,7 +220,6 @@ class ChapterMutation {
DeleteChapterMetaPayload(clientMutationId, meta, chapter)
}
}
data class FetchChapterPagesInput(
val clientMutationId: String? = null,
@@ -37,7 +37,8 @@ class DownloadMutation {
clientMutationId = clientMutationId,
chapters =
transaction {
ChapterTable.select { ChapterTable.id inList chapters }
ChapterTable
.select { ChapterTable.id inList chapters }
.map { ChapterType(it) }
},
)
@@ -195,8 +196,8 @@ class DownloadMutation {
val downloadStatus: DownloadStatus,
)
fun startDownloader(input: StartDownloaderInput): CompletableFuture<DataFetcherResult<StartDownloaderPayload?>> {
return future {
fun startDownloader(input: StartDownloaderInput): CompletableFuture<DataFetcherResult<StartDownloaderPayload?>> =
future {
asDataFetcherResult {
DownloadManager.start()
@@ -211,7 +212,6 @@ class DownloadMutation {
)
}
}
}
data class StopDownloaderInput(
val clientMutationId: String? = null,
@@ -222,8 +222,8 @@ class DownloadMutation {
val downloadStatus: DownloadStatus,
)
fun stopDownloader(input: StopDownloaderInput): CompletableFuture<DataFetcherResult<StopDownloaderPayload?>> {
return future {
fun stopDownloader(input: StopDownloaderInput): CompletableFuture<DataFetcherResult<StopDownloaderPayload?>> =
future {
asDataFetcherResult {
DownloadManager.stop()
@@ -238,7 +238,6 @@ class DownloadMutation {
)
}
}
}
data class ClearDownloaderInput(
val clientMutationId: String? = null,
@@ -249,8 +248,8 @@ class DownloadMutation {
val downloadStatus: DownloadStatus,
)
fun clearDownloader(input: ClearDownloaderInput): CompletableFuture<DataFetcherResult<ClearDownloaderPayload?>> {
return future {
fun clearDownloader(input: ClearDownloaderInput): CompletableFuture<DataFetcherResult<ClearDownloaderPayload?>> =
future {
asDataFetcherResult {
DownloadManager.clear()
@@ -265,7 +264,6 @@ class DownloadMutation {
)
}
}
}
data class ReorderChapterDownloadInput(
val clientMutationId: String? = null,
@@ -48,7 +48,8 @@ class ExtensionMutation {
) {
val extensions =
transaction {
ExtensionTable.select { ExtensionTable.pkgName inList ids }
ExtensionTable
.select { ExtensionTable.pkgName inList ids }
.map { ExtensionType(it) }
}
@@ -80,7 +81,9 @@ class ExtensionMutation {
val extension =
transaction {
ExtensionTable.select { ExtensionTable.pkgName eq id }.firstOrNull()
ExtensionTable
.select { ExtensionTable.pkgName eq id }
.firstOrNull()
?.let { ExtensionType(it) }
}
@@ -101,7 +104,8 @@ class ExtensionMutation {
val extensions =
transaction {
ExtensionTable.select { ExtensionTable.pkgName inList ids }
ExtensionTable
.select { ExtensionTable.pkgName inList ids }
.map { ExtensionType(it) }
}
@@ -131,7 +135,8 @@ class ExtensionMutation {
val extensions =
transaction {
ExtensionTable.select { ExtensionTable.name neq LocalSource.EXTENSION_NAME }
ExtensionTable
.select { ExtensionTable.name neq LocalSource.EXTENSION_NAME }
.map { ExtensionType(it) }
}
@@ -59,8 +59,8 @@ class InfoMutation {
}
}
fun resetWebUIUpdateStatus(): CompletableFuture<DataFetcherResult<WebUIUpdateStatus?>> {
return future {
fun resetWebUIUpdateStatus(): CompletableFuture<DataFetcherResult<WebUIUpdateStatus?>> =
future {
asDataFetcherResult {
withTimeout(30.seconds) {
val isUpdateFinished = WebInterfaceManager.status.value.state != DOWNLOADING
@@ -74,5 +74,4 @@ class InfoMutation {
}
}
}
}
}
@@ -76,7 +76,8 @@ class MangaMutation {
// try to initialize uninitialized in library manga to ensure that the expected data is available (chapter list, metadata, ...)
val mangas =
transaction {
MangaTable.select { (MangaTable.id inList ids) and (MangaTable.initialized eq false) }
MangaTable
.select { (MangaTable.id inList ids) and (MangaTable.initialized eq false) }
.map { MangaTable.toDataClass(it) }
}
@@ -198,7 +199,8 @@ class MangaMutation {
val (meta, manga) =
transaction {
val meta =
MangaMetaTable.select { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) }
MangaMetaTable
.select { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) }
.firstOrNull()
MangaMetaTable.deleteWhere { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) }
@@ -48,7 +48,8 @@ class MetaMutation {
val meta =
transaction {
val meta =
GlobalMetaTable.select { GlobalMetaTable.key eq key }
GlobalMetaTable
.select { GlobalMetaTable.key eq key }
.firstOrNull()
GlobalMetaTable.deleteWhere { GlobalMetaTable.key eq key }
@@ -138,7 +138,9 @@ class SettingsMutation {
return SetSettingsPayload(clientMutationId, SettingsType())
}
data class ResetSettingsInput(val clientMutationId: String? = null)
data class ResetSettingsInput(
val clientMutationId: String? = null,
)
data class ResetSettingsPayload(
val clientMutationId: String?,
@@ -68,14 +68,17 @@ class SourceMutation {
val (meta, source) =
transaction {
val meta =
SourceMetaTable.select { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) }
SourceMetaTable
.select { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) }
.firstOrNull()
SourceMetaTable.deleteWhere { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) }
val source =
transaction {
SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()
SourceTable
.select { SourceTable.id eq sourceId }
.firstOrNull()
?.let { SourceType(it) }
}
@@ -139,7 +142,8 @@ class SourceMutation {
val mangas =
transaction {
MangaTable.select { MangaTable.id inList mangaIds }
MangaTable
.select { MangaTable.id inList mangaIds }
.map { MangaType(it) }
}.sortedBy {
mangaIds.indexOf(it.id)
@@ -126,9 +126,10 @@ class TrackMutation {
)
val trackRecord =
transaction {
TrackRecordTable.select {
TrackRecordTable.mangaId eq mangaId and (TrackRecordTable.trackerId eq trackerId)
}.first()
TrackRecordTable
.select {
TrackRecordTable.mangaId eq mangaId and (TrackRecordTable.trackerId eq trackerId)
}.first()
}
BindTrackPayload(
clientMutationId,
@@ -154,9 +155,10 @@ class TrackMutation {
Track.refresh(recordId)
val trackRecord =
transaction {
TrackRecordTable.select {
TrackRecordTable.id eq recordId
}.first()
TrackRecordTable
.select {
TrackRecordTable.id eq recordId
}.first()
}
FetchTrackPayload(
clientMutationId,
@@ -184,9 +186,10 @@ class TrackMutation {
Track.unbind(recordId, deleteRemoteTrack)
val trackRecord =
transaction {
TrackRecordTable.select {
TrackRecordTable.id eq recordId
}.firstOrNull()
TrackRecordTable
.select {
TrackRecordTable.id eq recordId
}.firstOrNull()
}
UnbindTrackPayload(
clientMutationId,
@@ -213,7 +216,8 @@ class TrackMutation {
Track.trackChapter(mangaId)
val trackRecords =
transaction {
TrackRecordTable.select { TrackRecordTable.mangaId eq mangaId }
TrackRecordTable
.select { TrackRecordTable.mangaId eq mangaId }
.toList()
}
TrackProgressPayload(
@@ -241,8 +245,8 @@ class TrackMutation {
val trackRecord: TrackRecordType?,
)
fun updateTrack(input: UpdateTrackInput): CompletableFuture<UpdateTrackPayload> {
return future {
fun updateTrack(input: UpdateTrackInput): CompletableFuture<UpdateTrackPayload> =
future {
Track.update(
Track.UpdateInput(
input.recordId,
@@ -257,14 +261,14 @@ class TrackMutation {
val trackRecord =
transaction {
TrackRecordTable.select {
TrackRecordTable.id eq input.recordId
}.firstOrNull()
TrackRecordTable
.select {
TrackRecordTable.id eq input.recordId
}.firstOrNull()
}
UpdateTrackPayload(
input.clientMutationId,
trackRecord?.let { TrackRecordType(it) },
)
}
}
}
@@ -33,7 +33,5 @@ class BackupQuery {
)
}
fun restoreStatus(id: String): BackupRestoreStatus? {
return ProtoBackupImport.getRestoreState(id)?.toStatus()
}
fun restoreStatus(id: String): BackupRestoreStatus? = ProtoBackupImport.getRestoreState(id)?.toStatus()
}
@@ -45,31 +45,29 @@ class CategoryQuery {
fun category(
dataFetchingEnvironment: DataFetchingEnvironment,
id: Int,
): CompletableFuture<CategoryType> {
return dataFetchingEnvironment.getValueFromDataLoader("CategoryDataLoader", id)
}
): CompletableFuture<CategoryType> = dataFetchingEnvironment.getValueFromDataLoader("CategoryDataLoader", id)
enum class CategoryOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<CategoryType> {
enum class CategoryOrderBy(
override val column: Column<out Comparable<*>>,
) : OrderBy<CategoryType> {
ID(CategoryTable.id),
NAME(CategoryTable.name),
ORDER(CategoryTable.order),
;
override fun greater(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun greater(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> CategoryTable.id greater cursor.value.toInt()
NAME -> greaterNotUnique(CategoryTable.name, CategoryTable.id, cursor, String::toString)
ORDER -> greaterNotUnique(CategoryTable.order, CategoryTable.id, cursor, String::toInt)
}
}
override fun less(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun less(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> CategoryTable.id less cursor.value.toInt()
NAME -> lessNotUnique(CategoryTable.name, CategoryTable.id, cursor, String::toString)
ORDER -> lessNotUnique(CategoryTable.order, CategoryTable.id, cursor, String::toInt)
}
}
override fun asCursor(type: CategoryType): Cursor {
val value =
@@ -113,14 +111,13 @@ class CategoryQuery {
override val or: List<CategoryFilter>? = null,
override val not: CategoryFilter? = null,
) : Filter<CategoryFilter> {
override fun getOpList(): List<Op<Boolean>> {
return listOfNotNull(
override fun getOpList(): List<Op<Boolean>> =
listOfNotNull(
andFilterWithCompareEntity(CategoryTable.id, id),
andFilterWithCompare(CategoryTable.order, order),
andFilterWithCompareString(CategoryTable.name, name),
andFilterWithCompare(CategoryTable.isDefault, default),
)
}
}
fun categories(
@@ -54,11 +54,11 @@ class ChapterQuery {
fun chapter(
dataFetchingEnvironment: DataFetchingEnvironment,
id: Int,
): CompletableFuture<ChapterType> {
return dataFetchingEnvironment.getValueFromDataLoader("ChapterDataLoader", id)
}
): CompletableFuture<ChapterType> = dataFetchingEnvironment.getValueFromDataLoader("ChapterDataLoader", id)
enum class ChapterOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<ChapterType> {
enum class ChapterOrderBy(
override val column: Column<out Comparable<*>>,
) : OrderBy<ChapterType> {
ID(ChapterTable.id),
SOURCE_ORDER(ChapterTable.sourceOrder),
NAME(ChapterTable.name),
@@ -68,8 +68,8 @@ class ChapterQuery {
FETCHED_AT(ChapterTable.fetchedAt),
;
override fun greater(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun greater(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> ChapterTable.id greater cursor.value.toInt()
SOURCE_ORDER -> greaterNotUnique(ChapterTable.sourceOrder, ChapterTable.id, cursor, String::toInt)
NAME -> greaterNotUnique(ChapterTable.name, ChapterTable.id, cursor, String::toString)
@@ -78,10 +78,9 @@ class ChapterQuery {
LAST_READ_AT -> greaterNotUnique(ChapterTable.lastReadAt, ChapterTable.id, cursor, String::toLong)
FETCHED_AT -> greaterNotUnique(ChapterTable.fetchedAt, ChapterTable.id, cursor, String::toLong)
}
}
override fun less(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun less(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> ChapterTable.id less cursor.value.toInt()
SOURCE_ORDER -> lessNotUnique(ChapterTable.sourceOrder, ChapterTable.id, cursor, String::toInt)
NAME -> lessNotUnique(ChapterTable.name, ChapterTable.id, cursor, String::toString)
@@ -90,7 +89,6 @@ class ChapterQuery {
LAST_READ_AT -> lessNotUnique(ChapterTable.lastReadAt, ChapterTable.id, cursor, String::toLong)
FETCHED_AT -> lessNotUnique(ChapterTable.fetchedAt, ChapterTable.id, cursor, String::toLong)
}
}
override fun asCursor(type: ChapterType): Cursor {
val value =
@@ -175,8 +173,8 @@ class ChapterQuery {
override val or: List<ChapterFilter>? = null,
override val not: ChapterFilter? = null,
) : Filter<ChapterFilter> {
override fun getOpList(): List<Op<Boolean>> {
return listOfNotNull(
override fun getOpList(): List<Op<Boolean>> =
listOfNotNull(
andFilterWithCompareEntity(ChapterTable.id, id),
andFilterWithCompareString(ChapterTable.url, url),
andFilterWithCompareString(ChapterTable.name, name),
@@ -194,7 +192,6 @@ class ChapterQuery {
andFilterWithCompare(ChapterTable.isDownloaded, isDownloaded),
andFilterWithCompare(ChapterTable.pageCount, pageCount),
)
}
fun getLibraryOp() = andFilterWithCompare(MangaTable.inLibrary, inLibrary)
}
@@ -7,9 +7,8 @@ import suwayomi.tachidesk.server.JavalinSetup.future
import java.util.concurrent.CompletableFuture
class DownloadQuery {
fun downloadStatus(): CompletableFuture<DownloadStatus> {
return future {
fun downloadStatus(): CompletableFuture<DownloadStatus> =
future {
DownloadStatus(DownloadManager.status.first())
}
}
}
@@ -46,31 +46,29 @@ class ExtensionQuery {
fun extension(
dataFetchingEnvironment: DataFetchingEnvironment,
pkgName: String,
): CompletableFuture<ExtensionType> {
return dataFetchingEnvironment.getValueFromDataLoader("ExtensionDataLoader", pkgName)
}
): CompletableFuture<ExtensionType> = dataFetchingEnvironment.getValueFromDataLoader("ExtensionDataLoader", pkgName)
enum class ExtensionOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<ExtensionType> {
enum class ExtensionOrderBy(
override val column: Column<out Comparable<*>>,
) : OrderBy<ExtensionType> {
PKG_NAME(ExtensionTable.pkgName),
NAME(ExtensionTable.name),
APK_NAME(ExtensionTable.apkName),
;
override fun greater(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun greater(cursor: Cursor): Op<Boolean> =
when (this) {
PKG_NAME -> ExtensionTable.pkgName greater cursor.value
NAME -> greaterNotUnique(ExtensionTable.name, ExtensionTable.pkgName, cursor, String::toString)
APK_NAME -> greaterNotUnique(ExtensionTable.apkName, ExtensionTable.pkgName, cursor, String::toString)
}
}
override fun less(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun less(cursor: Cursor): Op<Boolean> =
when (this) {
PKG_NAME -> ExtensionTable.pkgName less cursor.value
NAME -> lessNotUnique(ExtensionTable.name, ExtensionTable.pkgName, cursor, String::toString)
APK_NAME -> lessNotUnique(ExtensionTable.apkName, ExtensionTable.pkgName, cursor, String::toString)
}
}
override fun asCursor(type: ExtensionType): Cursor {
val value =
@@ -137,8 +135,8 @@ class ExtensionQuery {
override val or: List<ExtensionFilter>? = null,
override val not: ExtensionFilter? = null,
) : Filter<ExtensionFilter> {
override fun getOpList(): List<Op<Boolean>> {
return listOfNotNull(
override fun getOpList(): List<Op<Boolean>> =
listOfNotNull(
andFilterWithCompareString(ExtensionTable.repo, repo),
andFilterWithCompareString(ExtensionTable.apkName, apkName),
andFilterWithCompareString(ExtensionTable.iconUrl, iconUrl),
@@ -152,7 +150,6 @@ class ExtensionQuery {
andFilterWithCompare(ExtensionTable.hasUpdate, hasUpdate),
andFilterWithCompare(ExtensionTable.isObsolete, isObsolete),
)
}
}
fun extensions(
@@ -22,8 +22,8 @@ class InfoQuery {
val discord: String,
)
fun aboutServer(): AboutServerPayload {
return AboutServerPayload(
fun aboutServer(): AboutServerPayload =
AboutServerPayload(
BuildConfig.NAME,
BuildConfig.VERSION,
BuildConfig.REVISION,
@@ -32,7 +32,6 @@ class InfoQuery {
BuildConfig.GITHUB,
BuildConfig.DISCORD,
)
}
data class CheckForServerUpdatesPayload(
/** [channel] mirrors [suwayomi.tachidesk.server.BuildConfig.BUILD_TYPE] */
@@ -41,8 +40,8 @@ class InfoQuery {
val url: String,
)
fun checkForServerUpdates(): CompletableFuture<List<CheckForServerUpdatesPayload>> {
return future {
fun checkForServerUpdates(): CompletableFuture<List<CheckForServerUpdatesPayload>> =
future {
AppUpdate.checkUpdate().map {
CheckForServerUpdatesPayload(
channel = it.channel,
@@ -51,16 +50,14 @@ class InfoQuery {
)
}
}
}
fun aboutWebUI(): CompletableFuture<AboutWebUI> {
return future {
fun aboutWebUI(): CompletableFuture<AboutWebUI> =
future {
WebInterfaceManager.getAboutInfo()
}
}
fun checkForWebUIUpdate(): CompletableFuture<WebUIUpdateCheck> {
return future {
fun checkForWebUIUpdate(): CompletableFuture<WebUIUpdateCheck> =
future {
val (version, updateAvailable) = WebInterfaceManager.isUpdateAvailable(WebUIFlavor.current, raiseError = true)
WebUIUpdateCheck(
channel = serverConfig.webUIChannel.value,
@@ -68,9 +65,6 @@ class InfoQuery {
updateAvailable,
)
}
}
fun getWebUIUpdateStatus(): WebUIUpdateStatus {
return WebInterfaceManager.status.value
}
fun getWebUIUpdateStatus(): WebUIUpdateStatus = WebInterfaceManager.status.value
}
@@ -50,34 +50,32 @@ class MangaQuery {
fun manga(
dataFetchingEnvironment: DataFetchingEnvironment,
id: Int,
): CompletableFuture<MangaType> {
return dataFetchingEnvironment.getValueFromDataLoader("MangaDataLoader", id)
}
): CompletableFuture<MangaType> = dataFetchingEnvironment.getValueFromDataLoader("MangaDataLoader", id)
enum class MangaOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<MangaType> {
enum class MangaOrderBy(
override val column: Column<out Comparable<*>>,
) : OrderBy<MangaType> {
ID(MangaTable.id),
TITLE(MangaTable.title),
IN_LIBRARY_AT(MangaTable.inLibraryAt),
LAST_FETCHED_AT(MangaTable.lastFetchedAt),
;
override fun greater(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun greater(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> MangaTable.id greater cursor.value.toInt()
TITLE -> greaterNotUnique(MangaTable.title, MangaTable.id, cursor, String::toString)
IN_LIBRARY_AT -> greaterNotUnique(MangaTable.inLibraryAt, MangaTable.id, cursor, String::toLong)
LAST_FETCHED_AT -> greaterNotUnique(MangaTable.lastFetchedAt, MangaTable.id, cursor, String::toLong)
}
}
override fun less(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun less(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> MangaTable.id less cursor.value.toInt()
TITLE -> lessNotUnique(MangaTable.title, MangaTable.id, cursor, String::toString)
IN_LIBRARY_AT -> lessNotUnique(MangaTable.inLibraryAt, MangaTable.id, cursor, String::toLong)
LAST_FETCHED_AT -> lessNotUnique(MangaTable.lastFetchedAt, MangaTable.id, cursor, String::toLong)
}
}
override fun asCursor(type: MangaType): Cursor {
val value =
@@ -197,8 +195,8 @@ class MangaQuery {
override val or: List<MangaFilter>? = null,
override val not: MangaFilter? = null,
) : Filter<MangaFilter> {
override fun getOpList(): List<Op<Boolean>> {
return listOfNotNull(
override fun getOpList(): List<Op<Boolean>> =
listOfNotNull(
andFilterWithCompareEntity(MangaTable.id, id),
andFilterWithCompare(MangaTable.sourceReference, sourceId),
andFilterWithCompareString(MangaTable.url, url),
@@ -217,7 +215,6 @@ class MangaQuery {
andFilterWithCompare(MangaTable.chaptersLastFetchedAt, chaptersLastFetchedAt),
andFilterWithCompareEntity(CategoryMangaTable.category, categoryId),
)
}
}
fun mangas(
@@ -243,11 +240,13 @@ class MangaQuery {
val queryResults =
transaction {
val res =
MangaTable.leftJoin(CategoryMangaTable).slice(
distinctOn(MangaTable.id),
*(MangaTable.columns).toTypedArray(),
*(CategoryMangaTable.columns).toTypedArray(),
).selectAll()
MangaTable
.leftJoin(CategoryMangaTable)
.slice(
distinctOn(MangaTable.id),
*(MangaTable.columns).toTypedArray(),
*(CategoryMangaTable.columns).toTypedArray(),
).selectAll()
res.applyOps(condition, filter)
@@ -41,28 +41,26 @@ class MetaQuery {
fun meta(
dataFetchingEnvironment: DataFetchingEnvironment,
key: String,
): CompletableFuture<GlobalMetaType> {
return dataFetchingEnvironment.getValueFromDataLoader("GlobalMetaDataLoader", key)
}
): CompletableFuture<GlobalMetaType> = dataFetchingEnvironment.getValueFromDataLoader("GlobalMetaDataLoader", key)
enum class MetaOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<GlobalMetaType> {
enum class MetaOrderBy(
override val column: Column<out Comparable<*>>,
) : OrderBy<GlobalMetaType> {
KEY(GlobalMetaTable.key),
VALUE(GlobalMetaTable.value),
;
override fun greater(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun greater(cursor: Cursor): Op<Boolean> =
when (this) {
KEY -> GlobalMetaTable.key greater cursor.value
VALUE -> greaterNotUnique(GlobalMetaTable.value, GlobalMetaTable.key, cursor, String::toString)
}
}
override fun less(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun less(cursor: Cursor): Op<Boolean> =
when (this) {
KEY -> GlobalMetaTable.key less cursor.value
VALUE -> lessNotUnique(GlobalMetaTable.value, GlobalMetaTable.key, cursor, String::toString)
}
}
override fun asCursor(type: GlobalMetaType): Cursor {
val value =
@@ -99,12 +97,11 @@ class MetaQuery {
override val or: List<MetaFilter>? = null,
override val not: MetaFilter? = null,
) : Filter<MetaFilter> {
override fun getOpList(): List<Op<Boolean>> {
return listOfNotNull(
override fun getOpList(): List<Op<Boolean>> =
listOfNotNull(
andFilterWithCompareString(GlobalMetaTable.key, key),
andFilterWithCompareString(GlobalMetaTable.value, value),
)
}
}
fun metas(
@@ -3,7 +3,5 @@ package suwayomi.tachidesk.graphql.queries
import suwayomi.tachidesk.graphql.types.SettingsType
class SettingsQuery {
fun settings(): SettingsType {
return SettingsType()
}
fun settings(): SettingsType = SettingsType()
}
@@ -45,31 +45,29 @@ class SourceQuery {
fun source(
dataFetchingEnvironment: DataFetchingEnvironment,
id: Long,
): CompletableFuture<SourceType> {
return dataFetchingEnvironment.getValueFromDataLoader("SourceDataLoader", id)
}
): CompletableFuture<SourceType> = dataFetchingEnvironment.getValueFromDataLoader("SourceDataLoader", id)
enum class SourceOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<SourceType> {
enum class SourceOrderBy(
override val column: Column<out Comparable<*>>,
) : OrderBy<SourceType> {
ID(SourceTable.id),
NAME(SourceTable.name),
LANG(SourceTable.lang),
;
override fun greater(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun greater(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> SourceTable.id greater cursor.value.toLong()
NAME -> greaterNotUnique(SourceTable.name, SourceTable.id, cursor, String::toString)
LANG -> greaterNotUnique(SourceTable.lang, SourceTable.id, cursor, String::toString)
}
}
override fun less(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun less(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> SourceTable.id less cursor.value.toLong()
NAME -> lessNotUnique(SourceTable.name, SourceTable.id, cursor, String::toString)
LANG -> lessNotUnique(SourceTable.lang, SourceTable.id, cursor, String::toString)
}
}
override fun asCursor(type: SourceType): Cursor {
val value =
@@ -113,14 +111,13 @@ class SourceQuery {
override val or: List<SourceFilter>? = null,
override val not: SourceFilter? = null,
) : Filter<SourceFilter> {
override fun getOpList(): List<Op<Boolean>> {
return listOfNotNull(
override fun getOpList(): List<Op<Boolean>> =
listOfNotNull(
andFilterWithCompareEntity(SourceTable.id, id),
andFilterWithCompareString(SourceTable.name, name),
andFilterWithCompareString(SourceTable.lang, lang),
andFilterWithCompare(SourceTable.isNsfw, isNsfw),
)
}
}
fun sources(
@@ -46,9 +46,7 @@ class TrackQuery {
fun tracker(
dataFetchingEnvironment: DataFetchingEnvironment,
id: Int,
): CompletableFuture<TrackerType> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, TrackerType>("TrackerDataLoader", id)
}
): CompletableFuture<TrackerType> = dataFetchingEnvironment.getValueFromDataLoader<Int, TrackerType>("TrackerDataLoader", id)
enum class TrackerOrderBy {
ID,
@@ -59,8 +57,8 @@ class TrackQuery {
fun greater(
tracker: TrackerType,
cursor: Cursor,
): Boolean {
return when (this) {
): Boolean =
when (this) {
ID -> tracker.id > cursor.value.toInt()
NAME -> tracker.name > cursor.value
IS_LOGGED_IN -> {
@@ -68,13 +66,12 @@ class TrackQuery {
!value || tracker.isLoggedIn
}
}
}
fun less(
tracker: TrackerType,
cursor: Cursor,
): Boolean {
return when (this) {
): Boolean =
when (this) {
ID -> tracker.id < cursor.value.toInt()
NAME -> tracker.name < cursor.value
IS_LOGGED_IN -> {
@@ -82,7 +79,6 @@ class TrackQuery {
value || !tracker.isLoggedIn
}
}
}
fun asCursor(type: TrackerType): Cursor {
val value =
@@ -244,11 +240,12 @@ class TrackQuery {
fun trackRecord(
dataFetchingEnvironment: DataFetchingEnvironment,
id: Int,
): CompletableFuture<TrackRecordType> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, TrackRecordType>("TrackRecordDataLoader", id)
}
): CompletableFuture<TrackRecordType> =
dataFetchingEnvironment.getValueFromDataLoader<Int, TrackRecordType>("TrackRecordDataLoader", id)
enum class TrackRecordOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<TrackRecordType> {
enum class TrackRecordOrderBy(
override val column: Column<out Comparable<*>>,
) : OrderBy<TrackRecordType> {
ID(TrackRecordTable.id),
MANGA_ID(TrackRecordTable.mangaId),
TRACKER_ID(TrackRecordTable.trackerId),
@@ -261,8 +258,8 @@ class TrackQuery {
FINISH_DATE(TrackRecordTable.finishDate),
;
override fun greater(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun greater(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> TrackRecordTable.id greater cursor.value.toInt()
MANGA_ID -> greaterNotUnique(TrackRecordTable.mangaId, TrackRecordTable.id, cursor)
TRACKER_ID -> greaterNotUnique(TrackRecordTable.trackerId, TrackRecordTable.id, cursor, String::toInt)
@@ -274,10 +271,9 @@ class TrackQuery {
START_DATE -> greaterNotUnique(TrackRecordTable.startDate, TrackRecordTable.id, cursor, String::toLong)
FINISH_DATE -> greaterNotUnique(TrackRecordTable.finishDate, TrackRecordTable.id, cursor, String::toLong)
}
}
override fun less(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun less(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> TrackRecordTable.id less cursor.value.toInt()
MANGA_ID -> lessNotUnique(TrackRecordTable.mangaId, TrackRecordTable.id, cursor)
TRACKER_ID -> lessNotUnique(TrackRecordTable.trackerId, TrackRecordTable.id, cursor, String::toInt)
@@ -289,7 +285,6 @@ class TrackQuery {
START_DATE -> lessNotUnique(TrackRecordTable.startDate, TrackRecordTable.id, cursor, String::toLong)
FINISH_DATE -> lessNotUnique(TrackRecordTable.finishDate, TrackRecordTable.id, cursor, String::toLong)
}
}
override fun asCursor(type: TrackRecordType): Cursor {
val value =
@@ -367,8 +362,8 @@ class TrackQuery {
override val or: List<TrackRecordFilter>? = null,
override val not: TrackRecordFilter? = null,
) : Filter<TrackRecordFilter> {
override fun getOpList(): List<Op<Boolean>> {
return listOfNotNull(
override fun getOpList(): List<Op<Boolean>> =
listOfNotNull(
andFilterWithCompareEntity(TrackRecordTable.id, id),
andFilterWithCompareEntity(TrackRecordTable.mangaId, mangaId),
andFilterWithCompare(TrackRecordTable.trackerId, trackerId),
@@ -383,7 +378,6 @@ class TrackQuery {
andFilterWithCompare(TrackRecordTable.startDate, startDate),
andFilterWithCompare(TrackRecordTable.finishDate, finishDate),
)
}
}
fun trackRecords(
@@ -484,10 +478,12 @@ class TrackQuery {
val query: String,
)
data class SearchTrackerPayload(val trackSearches: List<TrackSearchType>)
data class SearchTrackerPayload(
val trackSearches: List<TrackSearchType>,
)
fun searchTracker(input: SearchTrackerInput): CompletableFuture<SearchTrackerPayload> {
return future {
fun searchTracker(input: SearchTrackerInput): CompletableFuture<SearchTrackerPayload> =
future {
val tracker =
requireNotNull(TrackerManager.getTracker(input.trackerId)) {
"Tracker not found"
@@ -501,5 +497,4 @@ class TrackQuery {
},
)
}
}
}
@@ -12,13 +12,11 @@ import java.util.concurrent.CompletableFuture
class UpdateQuery {
private val updater by DI.global.instance<IUpdater>()
fun updateStatus(): CompletableFuture<UpdateStatus> {
return future { UpdateStatus(updater.status.first()) }
}
fun updateStatus(): CompletableFuture<UpdateStatus> = future { UpdateStatus(updater.status.first()) }
data class LastUpdateTimestampPayload(val timestamp: Long)
data class LastUpdateTimestampPayload(
val timestamp: Long,
)
fun lastUpdateTimestamp(): LastUpdateTimestampPayload {
return LastUpdateTimestampPayload(updater.getLastUpdateTimestamp())
}
fun lastUpdateTimestamp(): LastUpdateTimestampPayload = LastUpdateTimestampPayload(updater.getLastUpdateTimestamp())
}
@@ -17,11 +17,16 @@ import org.jetbrains.exposed.sql.or
import org.jetbrains.exposed.sql.stringParam
import org.jetbrains.exposed.sql.upperCase
class ILikeEscapeOp(expr1: Expression<*>, expr2: Expression<*>, like: Boolean, val escapeChar: Char?) : ComparisonOp(
expr1,
expr2,
if (like) "ILIKE" else "NOT ILIKE",
) {
class ILikeEscapeOp(
expr1: Expression<*>,
expr2: Expression<*>,
like: Boolean,
val escapeChar: Char?,
) : ComparisonOp(
expr1,
expr2,
if (like) "ILIKE" else "NOT ILIKE",
) {
override fun toQueryBuilder(queryBuilder: QueryBuilder) {
super.toQueryBuilder(queryBuilder)
if (escapeChar != null) {
@@ -67,11 +72,15 @@ class ILikeEscapeOp(expr1: Expression<*>, expr2: Expression<*>, like: Boolean, v
}
}
class DistinctFromOp(expr1: Expression<*>, expr2: Expression<*>, not: Boolean) : ComparisonOp(
expr1,
expr2,
if (not) "IS NOT DISTINCT FROM" else "IS DISTINCT FROM",
) {
class DistinctFromOp(
expr1: Expression<*>,
expr2: Expression<*>,
not: Boolean,
) : ComparisonOp(
expr1,
expr2,
if (not) "IS NOT DISTINCT FROM" else "IS DISTINCT FROM",
) {
companion object {
fun <T> distinctFrom(
expression: ExpressionWithColumnType<T>,
@@ -472,7 +481,9 @@ fun <T : String, S : T?> andFilterWithCompareString(
return opAnd.op
}
class OpAnd(var op: Op<Boolean>? = null) {
class OpAnd(
var op: Op<Boolean>? = null,
) {
fun <T> andWhere(
value: T?,
andPart: SqlExpressionBuilder.(T & Any) -> Op<Boolean>,
@@ -38,26 +38,29 @@ class JavalinGraphQLRequestParser : GraphQLRequestParser<Context> {
GraphQLServerRequest::class.java,
)
val map =
context.formParam("map")?.let {
context.jsonMapper().fromJsonString(
it,
Map::class.java as Class<Map<String, List<String>>>,
)
}.orEmpty()
context
.formParam("map")
?.let {
context.jsonMapper().fromJsonString(
it,
Map::class.java as Class<Map<String, List<String>>>,
)
}.orEmpty()
val mapItems =
map.flatMap { (key, variables) ->
val file = context.uploadedFile(key)
variables.map { fullVariable ->
val variable = fullVariable.removePrefix("variables.").substringBefore('.')
val listIndex = fullVariable.substringAfterLast('.').toIntOrNull()
MapItem(
variable,
listIndex,
file,
)
}
}.groupBy { it.variable }
map
.flatMap { (key, variables) ->
val file = context.uploadedFile(key)
variables.map { fullVariable ->
val variable = fullVariable.removePrefix("variables.").substringBefore('.')
val listIndex = fullVariable.substringAfterLast('.').toIntOrNull()
MapItem(
variable,
listIndex,
file,
)
}
}.groupBy { it.variable }
when (request) {
is GraphQLRequest -> {
@@ -91,8 +94,8 @@ class JavalinGraphQLRequestParser : GraphQLRequestParser<Context> {
* Example map "{ "0": ["variables.file"] }"
* TODO nested objects
*/
private fun Map<String, Any?>.modifyFiles(map: Map<String, List<MapItem>>): Map<String, Any?> {
return mapValues { (name, value) ->
private fun Map<String, Any?>.modifyFiles(map: Map<String, List<MapItem>>): Map<String, Any?> =
mapValues { (name, value) ->
if (map.containsKey(name)) {
val items = map[name].orEmpty()
if (items.size > 1) {
@@ -110,5 +113,4 @@ class JavalinGraphQLRequestParser : GraphQLRequestParser<Context> {
value
}
}
}
}
@@ -46,8 +46,8 @@ import suwayomi.tachidesk.graphql.dataLoaders.UnreadChapterCountForMangaDataLoad
class TachideskDataLoaderRegistryFactory {
companion object {
fun create(): KotlinDataLoaderRegistryFactory {
return KotlinDataLoaderRegistryFactory(
fun create(): KotlinDataLoaderRegistryFactory =
KotlinDataLoaderRegistryFactory(
MangaDataLoader(),
ChapterDataLoader(),
ChaptersForMangaDataLoader(),
@@ -84,6 +84,5 @@ class TachideskDataLoaderRegistryFactory {
TrackRecordsForTrackerIdDataLoader(),
TrackRecordDataLoader(),
)
}
}
}
@@ -34,7 +34,8 @@ class TachideskGraphQLServer(
@OptIn(DelicateCoroutinesApi::class)
fun handleSubscriptionMessage(context: WsMessageContext) {
subscriptionProtocolHandler.handleMessage(context)
subscriptionProtocolHandler
.handleMessage(context)
.map { objectMapper.writeValueAsString(it) }
.map { context.send(it) }
.launchIn(GlobalScope)
@@ -46,7 +47,8 @@ class TachideskGraphQLServer(
companion object {
private fun getGraphQLObject(): GraphQL =
GraphQL.newGraphQL(schema)
GraphQL
.newGraphQL(schema)
.subscriptionExecutionStrategy(FlowSubscriptionExecutionStrategy())
.mutationExecutionStrategy(AsyncExecutionStrategy())
.build()
@@ -43,7 +43,5 @@ object TemporaryFileStorage {
}
}
fun retrieveFile(name: String): Path {
return folder.resolve(name)
}
fun retrieveFile(name: String): Path = folder.resolve(name)
}
@@ -12,18 +12,21 @@ import graphql.schema.CoercingSerializeException
import graphql.schema.GraphQLScalarType
import java.util.Locale
data class Cursor(val value: String)
data class Cursor(
val value: String,
)
val GraphQLCursor: GraphQLScalarType =
GraphQLScalarType.newScalar()
GraphQLScalarType
.newScalar()
.name(
"Cursor",
).description("A location in a connection that can be used for resuming pagination.").coercing(GraphqlCursorCoercing()).build()
).description("A location in a connection that can be used for resuming pagination.")
.coercing(GraphqlCursorCoercing())
.build()
private class GraphqlCursorCoercing : Coercing<Cursor, String> {
private fun toStringImpl(input: Any): String? {
return (input as? Cursor)?.value
}
private fun toStringImpl(input: Any): String? = (input as? Cursor)?.value
private fun parseValueImpl(
input: Any,
@@ -58,54 +61,44 @@ private class GraphqlCursorCoercing : Coercing<Cursor, String> {
return Cursor(input.value)
}
private fun valueToLiteralImpl(input: Any): StringValue {
return StringValue.newStringValue(input.toString()).build()
}
private fun valueToLiteralImpl(input: Any): StringValue = StringValue.newStringValue(input.toString()).build()
@Deprecated("")
override fun serialize(dataFetcherResult: Any): String {
return toStringImpl(dataFetcherResult) ?: throw CoercingSerializeException(
override fun serialize(dataFetcherResult: Any): String =
toStringImpl(dataFetcherResult) ?: throw CoercingSerializeException(
CoercingUtil.i18nMsg(
Locale.getDefault(),
"String.unexpectedRawValueType",
CoercingUtil.typeName(dataFetcherResult),
),
)
}
@Throws(CoercingSerializeException::class)
override fun serialize(
dataFetcherResult: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): String {
return toStringImpl(dataFetcherResult) ?: throw CoercingSerializeException(
): String =
toStringImpl(dataFetcherResult) ?: throw CoercingSerializeException(
CoercingUtil.i18nMsg(
locale,
"String.unexpectedRawValueType",
CoercingUtil.typeName(dataFetcherResult),
),
)
}
@Deprecated("")
override fun parseValue(input: Any): Cursor {
return parseValueImpl(input, Locale.getDefault())
}
override fun parseValue(input: Any): Cursor = parseValueImpl(input, Locale.getDefault())
@Throws(CoercingParseValueException::class)
override fun parseValue(
input: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): Cursor {
return parseValueImpl(input, locale)
}
): Cursor = parseValueImpl(input, locale)
@Deprecated("")
override fun parseLiteral(input: Any): Cursor {
return parseLiteralImpl(input, Locale.getDefault())
}
override fun parseLiteral(input: Any): Cursor = parseLiteralImpl(input, Locale.getDefault())
@Throws(CoercingParseLiteralException::class)
override fun parseLiteral(
@@ -113,20 +106,14 @@ private class GraphqlCursorCoercing : Coercing<Cursor, String> {
variables: CoercedVariables,
graphQLContext: GraphQLContext,
locale: Locale,
): Cursor {
return parseLiteralImpl(input, locale)
}
): Cursor = parseLiteralImpl(input, locale)
@Deprecated("")
override fun valueToLiteral(input: Any): Value<*> {
return valueToLiteralImpl(input)
}
override fun valueToLiteral(input: Any): Value<*> = valueToLiteralImpl(input)
override fun valueToLiteral(
input: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): Value<*> {
return valueToLiteralImpl(input)
}
): Value<*> = valueToLiteralImpl(input)
}
@@ -13,13 +13,15 @@ import graphql.schema.GraphQLScalarType
import java.util.Locale
val GraphQLLongAsString: GraphQLScalarType =
GraphQLScalarType.newScalar()
.name("LongString").description("A 64-bit signed integer as a String").coercing(GraphqlLongAsStringCoercing()).build()
GraphQLScalarType
.newScalar()
.name("LongString")
.description("A 64-bit signed integer as a String")
.coercing(GraphqlLongAsStringCoercing())
.build()
private class GraphqlLongAsStringCoercing : Coercing<Long, String> {
private fun toStringImpl(input: Any): String {
return input.toString()
}
private fun toStringImpl(input: Any): String = input.toString()
private fun parseValueImpl(
input: Any,
@@ -54,42 +56,30 @@ private class GraphqlLongAsStringCoercing : Coercing<Long, String> {
return input.value.toLong()
}
private fun valueToLiteralImpl(input: Any): StringValue {
return StringValue.newStringValue(input.toString()).build()
}
private fun valueToLiteralImpl(input: Any): StringValue = StringValue.newStringValue(input.toString()).build()
@Deprecated("")
override fun serialize(dataFetcherResult: Any): String {
return toStringImpl(dataFetcherResult)
}
override fun serialize(dataFetcherResult: Any): String = toStringImpl(dataFetcherResult)
@Throws(CoercingSerializeException::class)
override fun serialize(
dataFetcherResult: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): String {
return toStringImpl(dataFetcherResult)
}
): String = toStringImpl(dataFetcherResult)
@Deprecated("")
override fun parseValue(input: Any): Long {
return parseValueImpl(input, Locale.getDefault())
}
override fun parseValue(input: Any): Long = parseValueImpl(input, Locale.getDefault())
@Throws(CoercingParseValueException::class)
override fun parseValue(
input: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): Long {
return parseValueImpl(input, locale)
}
): Long = parseValueImpl(input, locale)
@Deprecated("")
override fun parseLiteral(input: Any): Long {
return parseLiteralImpl(input, Locale.getDefault())
}
override fun parseLiteral(input: Any): Long = parseLiteralImpl(input, Locale.getDefault())
@Throws(CoercingParseLiteralException::class)
override fun parseLiteral(
@@ -97,20 +87,14 @@ private class GraphqlLongAsStringCoercing : Coercing<Long, String> {
variables: CoercedVariables,
graphQLContext: GraphQLContext,
locale: Locale,
): Long {
return parseLiteralImpl(input, locale)
}
): Long = parseLiteralImpl(input, locale)
@Deprecated("")
override fun valueToLiteral(input: Any): Value<*> {
return valueToLiteralImpl(input)
}
override fun valueToLiteral(input: Any): Value<*> = valueToLiteralImpl(input)
override fun valueToLiteral(
input: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): Value<*> {
return valueToLiteralImpl(input)
}
): Value<*> = valueToLiteralImpl(input)
}
@@ -27,8 +27,8 @@ interface Order<By : OrderBy<*>> {
val byType: SortOrder?
}
fun SortOrder?.maybeSwap(value: Any?): SortOrder {
return if (value != null) {
fun SortOrder?.maybeSwap(value: Any?): SortOrder =
if (value != null) {
when (this) {
SortOrder.ASC -> SortOrder.DESC
SortOrder.DESC -> SortOrder.ASC
@@ -41,7 +41,6 @@ fun SortOrder?.maybeSwap(value: Any?): SortOrder {
} else {
this ?: SortOrder.ASC
}
}
fun <T> Query.applyBeforeAfter(
before: Cursor?,
@@ -72,9 +71,7 @@ fun <T : Comparable<T>> greaterNotUnique(
idColumn: Column<EntityID<Int>>,
cursor: Cursor,
toValue: (String) -> T,
): Op<Boolean> {
return greaterNotUniqueImpl(column, idColumn, cursor, String::toInt, toValue)
}
): Op<Boolean> = greaterNotUniqueImpl(column, idColumn, cursor, String::toInt, toValue)
@JvmName("greaterNotUniqueLongKey")
fun <T : Comparable<T>> greaterNotUnique(
@@ -82,18 +79,14 @@ fun <T : Comparable<T>> greaterNotUnique(
idColumn: Column<EntityID<Long>>,
cursor: Cursor,
toValue: (String) -> T,
): Op<Boolean> {
return greaterNotUniqueImpl(column, idColumn, cursor, String::toLong, toValue)
}
): Op<Boolean> = greaterNotUniqueImpl(column, idColumn, cursor, String::toLong, toValue)
@JvmName("greaterNotUniqueIntKeyIntValue")
fun greaterNotUnique(
column: Column<EntityID<Int>>,
idColumn: Column<EntityID<Int>>,
cursor: Cursor,
): Op<Boolean> {
return greaterNotUniqueImpl(column, idColumn, cursor, String::toInt, String::toInt)
}
): Op<Boolean> = greaterNotUniqueImpl(column, idColumn, cursor, String::toInt, String::toInt)
private fun <K : Comparable<K>, V : Comparable<V>> greaterNotUniqueImpl(
column: Column<V>,
@@ -138,9 +131,7 @@ fun <T : Comparable<T>> lessNotUnique(
idColumn: Column<EntityID<Int>>,
cursor: Cursor,
toValue: (String) -> T,
): Op<Boolean> {
return lessNotUniqueImpl(column, idColumn, cursor, String::toInt, toValue)
}
): Op<Boolean> = lessNotUniqueImpl(column, idColumn, cursor, String::toInt, toValue)
@JvmName("lessNotUniqueLongKey")
fun <T : Comparable<T>> lessNotUnique(
@@ -148,18 +139,14 @@ fun <T : Comparable<T>> lessNotUnique(
idColumn: Column<EntityID<Long>>,
cursor: Cursor,
toValue: (String) -> T,
): Op<Boolean> {
return lessNotUniqueImpl(column, idColumn, cursor, String::toLong, toValue)
}
): Op<Boolean> = lessNotUniqueImpl(column, idColumn, cursor, String::toLong, toValue)
@JvmName("lessNotUniqueIntKeyIntValue")
fun lessNotUnique(
column: Column<EntityID<Int>>,
idColumn: Column<EntityID<Int>>,
cursor: Cursor,
): Op<Boolean> {
return lessNotUniqueImpl(column, idColumn, cursor, String::toInt, String::toInt)
}
): Op<Boolean> = lessNotUniqueImpl(column, idColumn, cursor, String::toInt, String::toInt)
private fun <K : Comparable<K>, V : Comparable<V>> lessNotUniqueImpl(
column: Column<V>,
@@ -2,4 +2,9 @@ package suwayomi.tachidesk.graphql.server.primitives
import org.jetbrains.exposed.sql.ResultRow
data class QueryResults<T>(val total: Long, val firstKey: T, val lastKey: T, val results: List<ResultRow>)
data class QueryResults<T>(
val total: Long,
val firstKey: T,
val lastKey: T,
val results: List<ResultRow>,
)
@@ -10,7 +10,8 @@ import io.javalin.http.UploadedFile
import java.util.Locale
val GraphQLUpload =
GraphQLScalarType.newScalar()
GraphQLScalarType
.newScalar()
.name("Upload")
.description("A file part in a multipart request")
.coercing(GraphqlUploadCoercing())
@@ -34,35 +35,25 @@ private class GraphqlUploadCoercing : Coercing<UploadedFile, Void?> {
}
@Deprecated("")
override fun serialize(dataFetcherResult: Any): Void? {
throw CoercingSerializeException("Upload is an input-only type")
}
override fun serialize(dataFetcherResult: Any): Void? = throw CoercingSerializeException("Upload is an input-only type")
@Throws(CoercingSerializeException::class)
override fun serialize(
dataFetcherResult: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): Void? {
throw CoercingSerializeException("Upload is an input-only type")
}
): Void? = throw CoercingSerializeException("Upload is an input-only type")
@Deprecated("")
override fun parseValue(input: Any): UploadedFile {
return parseValueImpl(input, Locale.getDefault())
}
override fun parseValue(input: Any): UploadedFile = parseValueImpl(input, Locale.getDefault())
@Throws(CoercingParseValueException::class)
override fun parseValue(
input: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): UploadedFile {
return parseValueImpl(input, locale)
}
): UploadedFile = parseValueImpl(input, locale)
@Deprecated("")
override fun parseLiteral(input: Any): UploadedFile {
return parseValueImpl(input, Locale.getDefault())
}
override fun parseLiteral(input: Any): UploadedFile = parseValueImpl(input, Locale.getDefault())
}
@@ -99,14 +99,13 @@ class ApolloSubscriptionProtocolHandler(
onDisconnect(context)
}
private fun convertToMessageOrNull(payload: String): SubscriptionOperationMessage? {
return try {
private fun convertToMessageOrNull(payload: String): SubscriptionOperationMessage? =
try {
objectMapper.readValue(payload)
} catch (exception: Exception) {
logger.error("Error parsing the subscription message", exception)
null
}
}
private fun startSubscription(
operationMessage: SubscriptionOperationMessage,
@@ -133,15 +132,15 @@ class ApolloSubscriptionProtocolHandler(
try {
val request = objectMapper.convertValue<GraphQLRequest>(payload)
return subscriptionHandler.executeSubscription(request, graphQLContext)
return subscriptionHandler
.executeSubscription(request, graphQLContext)
.map {
if (it.errors?.isNotEmpty() == true) {
SubscriptionOperationMessage(type = GQL_ERROR.type, id = operationMessage.id, payload = it.errors)
} else {
SubscriptionOperationMessage(type = GQL_NEXT.type, id = operationMessage.id, payload = it)
}
}
.onCompletion { if (it == null) emitAll(onComplete(operationMessage)) }
}.onCompletion { if (it == null) emitAll(onComplete(operationMessage)) }
.onStart { sessionState.saveOperation(context, operationMessage, currentCoroutineContext().job) }
} catch (exception: Exception) {
logger.error("Error running graphql subscription", exception)
@@ -169,13 +168,10 @@ class ApolloSubscriptionProtocolHandler(
/**
* Called with the publisher has completed on its own.
*/
private fun onComplete(operationMessage: SubscriptionOperationMessage): Flow<SubscriptionOperationMessage> {
return sessionState.completeOperation(operationMessage)
}
private fun onComplete(operationMessage: SubscriptionOperationMessage): Flow<SubscriptionOperationMessage> =
sessionState.completeOperation(operationMessage)
private fun onPing(): Flow<SubscriptionOperationMessage> {
return flowOf(pongMessage)
}
private fun onPing(): Flow<SubscriptionOperationMessage> = flowOf(pongMessage)
private fun onDisconnect(context: WsContext): Flow<SubscriptionOperationMessage> {
logger.debug("Session \"${context.sessionId}\" disconnected")

Some files were not shown because too many files have changed in this diff Show More