Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 819ceba17d | |||
| 0aa0d62e03 | |||
| b3e2a35880 | |||
| 15ec20c65d | |||
| d4d6d7e12f | |||
| 2e7a4f1421 | |||
| ab8a52faf3 | |||
| bd465559fb | |||
| 13ec45a95c | |||
| 13b034875b | |||
| bb701fb088 | |||
| b367414865 | |||
| 4b00eec608 | |||
| 5e11b51152 | |||
| 9fb43b996e |
@@ -14,7 +14,7 @@ const val CONFIG_PREFIX = "suwayomi.tachidesk.config"
|
|||||||
val ApplicationRootDir: String
|
val ApplicationRootDir: String
|
||||||
get(): String {
|
get(): String {
|
||||||
return System.getProperty(
|
return System.getProperty(
|
||||||
"$CONFIG_PREFIX.server.rootDir",
|
"$CONFIG_PREFIX.server.rootDir",
|
||||||
AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null)
|
AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import org.kodein.di.singleton
|
|||||||
|
|
||||||
class ConfigKodeinModule {
|
class ConfigKodeinModule {
|
||||||
fun create() = DI.Module("ConfigManager") {
|
fun create() = DI.Module("ConfigManager") {
|
||||||
//Config module
|
// Config module
|
||||||
bind<ConfigManager>() with singleton { GlobalConfigManager }
|
bind<ConfigManager>() with singleton { GlobalConfigManager }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ open class ConfigManager {
|
|||||||
private val generatedModules = mutableMapOf<Class<out ConfigModule>, ConfigModule>()
|
private val generatedModules = mutableMapOf<Class<out ConfigModule>, ConfigModule>()
|
||||||
val config by lazy { loadConfigs() }
|
val config by lazy { loadConfigs() }
|
||||||
|
|
||||||
//Public read-only view of modules
|
// Public read-only view of modules
|
||||||
val loadedModules: Map<Class<out ConfigModule>, ConfigModule>
|
val loadedModules: Map<Class<out ConfigModule>, ConfigModule>
|
||||||
get() = generatedModules
|
get() = generatedModules
|
||||||
|
|
||||||
@@ -42,29 +42,28 @@ open class ConfigManager {
|
|||||||
* Load configs
|
* Load configs
|
||||||
*/
|
*/
|
||||||
fun loadConfigs(): Config {
|
fun loadConfigs(): Config {
|
||||||
//Load reference configs
|
// Load reference configs
|
||||||
val compatConfig = ConfigFactory.parseResources("compat-reference.conf")
|
val compatConfig = ConfigFactory.parseResources("compat-reference.conf")
|
||||||
val serverConfig = ConfigFactory.parseResources("server-reference.conf")
|
val serverConfig = ConfigFactory.parseResources("server-reference.conf")
|
||||||
val baseConfig =
|
val baseConfig =
|
||||||
ConfigFactory.parseMap(
|
ConfigFactory.parseMap(
|
||||||
mapOf(
|
mapOf(
|
||||||
"androidcompat.rootDir" to "$ApplicationRootDir/android-compat" // override AndroidCompat's rootDir
|
"androidcompat.rootDir" to "$ApplicationRootDir/android-compat" // override AndroidCompat's rootDir
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
//Load user config
|
// Load user config
|
||||||
val userConfig =
|
val userConfig =
|
||||||
File(ApplicationRootDir, "server.conf").let {
|
File(ApplicationRootDir, "server.conf").let {
|
||||||
ConfigFactory.parseFile(it)
|
ConfigFactory.parseFile(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val config = ConfigFactory.empty()
|
val config = ConfigFactory.empty()
|
||||||
.withFallback(baseConfig)
|
.withFallback(baseConfig)
|
||||||
.withFallback(userConfig)
|
.withFallback(userConfig)
|
||||||
.withFallback(compatConfig)
|
.withFallback(compatConfig)
|
||||||
.withFallback(serverConfig)
|
.withFallback(serverConfig)
|
||||||
.resolve()
|
.resolve()
|
||||||
|
|
||||||
// set log level early
|
// set log level early
|
||||||
if (debugLogsEnabled(config)) {
|
if (debugLogsEnabled(config)) {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ abstract class ConfigModule(config: Config)
|
|||||||
/**
|
/**
|
||||||
* Abstract jvm-commandline-argument-overridable config module.
|
* Abstract jvm-commandline-argument-overridable config module.
|
||||||
*/
|
*/
|
||||||
abstract class SystemPropertyOverridableConfigModule(config: Config, moduleName: String): ConfigModule(config) {
|
abstract class SystemPropertyOverridableConfigModule(config: Config, moduleName: String) : ConfigModule(config) {
|
||||||
val overridableConfig = SystemPropertyOverrideDelegate(config, moduleName)
|
val overridableConfig = SystemPropertyOverrideDelegate(config, moduleName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ class SystemPropertyOverrideDelegate(val config: Config, val moduleName: String)
|
|||||||
configValue.toString()
|
configValue.toString()
|
||||||
)
|
)
|
||||||
|
|
||||||
return when(T::class.simpleName) {
|
return when (T::class.simpleName) {
|
||||||
"Int" -> combined.toInt()
|
"Int" -> combined.toInt()
|
||||||
"Boolean" -> combined.toBoolean()
|
"Boolean" -> combined.toBoolean()
|
||||||
// add more types as needed
|
// add more types as needed
|
||||||
|
|||||||
@@ -16,5 +16,5 @@ fun setLogLevel(level: Level) {
|
|||||||
(KotlinLogging.logger(Logger.ROOT_LOGGER_NAME).underlyingLogger as ch.qos.logback.classic.Logger).level = level
|
(KotlinLogging.logger(Logger.ROOT_LOGGER_NAME).underlyingLogger as ch.qos.logback.classic.Logger).level = level
|
||||||
}
|
}
|
||||||
|
|
||||||
fun debugLogsEnabled(config: Config)
|
fun debugLogsEnabled(config: Config) =
|
||||||
= System.getProperty("suwayomi.tachidesk.config.server.debugLogsEnabled", config.getString("server.debugLogsEnabled")).toBoolean()
|
System.getProperty("suwayomi.tachidesk.config.server.debugLogsEnabled", config.getString("server.debugLogsEnabled")).toBoolean()
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ package xyz.nulldev.ts.config.util
|
|||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
|
|
||||||
operator fun Config.get(key: String) = getString(key)
|
operator fun Config.get(key: String) = getString(key)
|
||||||
?: throw IllegalStateException("Could not find value for config entry: $key!")
|
?: throw IllegalStateException("Could not find value for config entry: $key!")
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ import android.content.Context
|
|||||||
class PreferenceManager {
|
class PreferenceManager {
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getDefaultSharedPreferences(context: Context)
|
fun getDefaultSharedPreferences(context: Context) =
|
||||||
= context.getSharedPreferences(context.applicationInfo.packageName,
|
context.getSharedPreferences(
|
||||||
Context.MODE_PRIVATE)!!
|
context.applicationInfo.packageName,
|
||||||
|
Context.MODE_PRIVATE
|
||||||
|
)!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -5,4 +5,4 @@ import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory
|
|||||||
|
|
||||||
class RequerySQLiteOpenHelperFactory {
|
class RequerySQLiteOpenHelperFactory {
|
||||||
fun create(configuration: SupportSQLiteOpenHelper.Configuration) = FrameworkSQLiteOpenHelperFactory().create(configuration)
|
fun create(configuration: SupportSQLiteOpenHelper.Configuration) = FrameworkSQLiteOpenHelperFactory().create(configuration)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,4 +14,4 @@ class AndroidCompat {
|
|||||||
application.attach(context)
|
application.attach(context)
|
||||||
application.onCreate()
|
application.onCreate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class AndroidCompatInitializer {
|
|||||||
fun init() {
|
fun init() {
|
||||||
DI.global.addImport(AndroidCompatModule().create())
|
DI.global.addImport(AndroidCompatModule().create())
|
||||||
|
|
||||||
//Register config modules
|
// Register config modules
|
||||||
GlobalConfigManager.registerModules(
|
GlobalConfigManager.registerModules(
|
||||||
FilesConfigModule.register(GlobalConfigManager.config),
|
FilesConfigModule.register(GlobalConfigManager.config),
|
||||||
ApplicationInfoConfigModule.register(GlobalConfigManager.config),
|
ApplicationInfoConfigModule.register(GlobalConfigManager.config),
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class AndroidCompatModule {
|
|||||||
|
|
||||||
bind<PackageController>() with singleton { PackageController() }
|
bind<PackageController>() with singleton { PackageController() }
|
||||||
|
|
||||||
//Context
|
// Context
|
||||||
bind<CustomContext>() with singleton { CustomContext() }
|
bind<CustomContext>() with singleton { CustomContext() }
|
||||||
bind<Context>() with singleton {
|
bind<Context>() with singleton {
|
||||||
val context: Context by DI.global.instance<CustomContext>()
|
val context: Context by DI.global.instance<CustomContext>()
|
||||||
|
|||||||
+2
-2
@@ -13,7 +13,7 @@ class ApplicationInfoConfigModule(config: Config) : ConfigModule(config) {
|
|||||||
val debug: Boolean by config
|
val debug: Boolean by config
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun register(config: Config)
|
fun register(config: Config) =
|
||||||
= ApplicationInfoConfigModule(config.getConfig("android.app"))
|
ApplicationInfoConfigModule(config.getConfig("android.app"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,26 +9,26 @@ import xyz.nulldev.ts.config.ConfigModule
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
class FilesConfigModule(config: Config) : ConfigModule(config) {
|
class FilesConfigModule(config: Config) : ConfigModule(config) {
|
||||||
val dataDir:String by config
|
val dataDir: String by config
|
||||||
val filesDir:String by config
|
val filesDir: String by config
|
||||||
val noBackupFilesDir:String by config
|
val noBackupFilesDir: String by config
|
||||||
val externalFilesDirs: MutableList<String> by config
|
val externalFilesDirs: MutableList<String> by config
|
||||||
val obbDirs: MutableList<String> by config
|
val obbDirs: MutableList<String> by config
|
||||||
val cacheDir:String by config
|
val cacheDir: String by config
|
||||||
val codeCacheDir:String by config
|
val codeCacheDir: String by config
|
||||||
val externalCacheDirs: MutableList<String> by config
|
val externalCacheDirs: MutableList<String> by config
|
||||||
val externalMediaDirs: MutableList<String> by config
|
val externalMediaDirs: MutableList<String> by config
|
||||||
val rootDir:String by config
|
val rootDir: String by config
|
||||||
val externalStorageDir:String by config
|
val externalStorageDir: String by config
|
||||||
val downloadCacheDir:String by config
|
val downloadCacheDir: String by config
|
||||||
val databasesDir:String by config
|
val databasesDir: String by config
|
||||||
|
|
||||||
val prefsDir:String by config
|
val prefsDir: String by config
|
||||||
|
|
||||||
val packageDir:String by config
|
val packageDir: String by config
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun register(config: Config)
|
fun register(config: Config) =
|
||||||
= FilesConfigModule(config.getConfig("android.files"))
|
FilesConfigModule(config.getConfig("android.files"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package xyz.nulldev.androidcompat.config
|
package xyz.nulldev.androidcompat.config
|
||||||
|
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import xyz.nulldev.ts.config.ConfigModule
|
|
||||||
import io.github.config4k.getValue
|
import io.github.config4k.getValue
|
||||||
|
import xyz.nulldev.ts.config.ConfigModule
|
||||||
|
|
||||||
class SystemConfigModule(val config: Config) : ConfigModule(config) {
|
class SystemConfigModule(val config: Config) : ConfigModule(config) {
|
||||||
val isDebuggable: Boolean by config
|
val isDebuggable: Boolean by config
|
||||||
@@ -16,7 +16,7 @@ class SystemConfigModule(val config: Config) : ConfigModule(config) {
|
|||||||
fun hasProperty(property: String) = config.hasPath("$propertyPrefix$property")
|
fun hasProperty(property: String) = config.hasPath("$propertyPrefix$property")
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun register(config: Config)
|
fun register(config: Config) =
|
||||||
= SystemConfigModule(config.getConfig("android.system"))
|
SystemConfigModule(config.getConfig("android.system"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
|
|
||||||
val parentMetadata = parent.metaData
|
val parentMetadata = parent.metaData
|
||||||
val columnCount = parentMetadata.columnCount
|
val columnCount = parentMetadata.columnCount
|
||||||
val columnLabels = (1 .. columnCount).map {
|
val columnLabels = (1..columnCount).map {
|
||||||
parentMetadata.getColumnLabel(it)
|
parentMetadata.getColumnLabel(it)
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
|
|
||||||
@@ -41,10 +41,10 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
// How can we optimize this?
|
// How can we optimize this?
|
||||||
// We need to fill the cache as the set is loaded
|
// We need to fill the cache as the set is loaded
|
||||||
|
|
||||||
//Fill cache
|
// Fill cache
|
||||||
while(parent.next()) {
|
while (parent.next()) {
|
||||||
cachedContent += ResultSetEntry().apply {
|
cachedContent += ResultSetEntry().apply {
|
||||||
for(i in 1 .. columnCount)
|
for (i in 1..columnCount)
|
||||||
data += parent.getObject(i)
|
data += parent.getObject(i)
|
||||||
}
|
}
|
||||||
resultSetLength++
|
resultSetLength++
|
||||||
@@ -60,8 +60,8 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun internalMove(row: Int) {
|
private fun internalMove(row: Int) {
|
||||||
if(cursor < 0) cursor = 0
|
if (cursor < 0) cursor = 0
|
||||||
else if(cursor > resultSetLength + 1) cursor = resultSetLength + 1
|
else if (cursor > resultSetLength + 1) cursor = resultSetLength + 1
|
||||||
else cursor = row
|
else cursor = row
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,10 +75,10 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
return obj(cachedFindColumn(column))
|
return obj(cachedFindColumn(column))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cachedFindColumn(column: String?)
|
private fun cachedFindColumn(column: String?) =
|
||||||
= columnCache.getOrPut(column!!, {
|
columnCache.getOrPut(column!!, {
|
||||||
findColumn(column)
|
findColumn(column)
|
||||||
})
|
})
|
||||||
|
|
||||||
override fun getNClob(columnIndex: Int): NClob {
|
override fun getNClob(columnIndex: Int): NClob {
|
||||||
return obj(columnIndex) as NClob
|
return obj(columnIndex) as NClob
|
||||||
@@ -157,27 +157,27 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getDate(columnIndex: Int): Date {
|
override fun getDate(columnIndex: Int): Date {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDate(columnLabel: String?): Date {
|
override fun getDate(columnLabel: String?): Date {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDate(columnIndex: Int, cal: Calendar?): Date {
|
override fun getDate(columnIndex: Int, cal: Calendar?): Date {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDate(columnLabel: String?, cal: Calendar?): Date {
|
override fun getDate(columnLabel: String?, cal: Calendar?): Date {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun beforeFirst() {
|
override fun beforeFirst() {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,12 +202,12 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getBigDecimal(columnIndex: Int, scale: Int): BigDecimal {
|
override fun getBigDecimal(columnIndex: Int, scale: Int): BigDecimal {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getBigDecimal(columnLabel: String?, scale: Int): BigDecimal {
|
override fun getBigDecimal(columnLabel: String?, scale: Int): BigDecimal {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,22 +236,22 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getTime(columnIndex: Int): Time {
|
override fun getTime(columnIndex: Int): Time {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTime(columnLabel: String?): Time {
|
override fun getTime(columnLabel: String?): Time {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTime(columnIndex: Int, cal: Calendar?): Time {
|
override fun getTime(columnIndex: Int, cal: Calendar?): Time {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTime(columnLabel: String?, cal: Calendar?): Time {
|
override fun getTime(columnLabel: String?, cal: Calendar?): Time {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,28 +272,28 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun absolute(row: Int): Boolean {
|
override fun absolute(row: Int): Boolean {
|
||||||
if(row > 0) {
|
if (row > 0) {
|
||||||
internalMove(row)
|
internalMove(row)
|
||||||
} else {
|
} else {
|
||||||
last()
|
last()
|
||||||
for(i in 1 .. row)
|
for (i in 1..row)
|
||||||
previous()
|
previous()
|
||||||
}
|
}
|
||||||
return cursorValid()
|
return cursorValid()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSQLXML(columnIndex: Int): SQLXML? {
|
override fun getSQLXML(columnIndex: Int): SQLXML? {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSQLXML(columnLabel: String?): SQLXML? {
|
override fun getSQLXML(columnLabel: String?): SQLXML? {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T : Any?> unwrap(iface: Class<T>?): T {
|
override fun <T : Any?> unwrap(iface: Class<T>?): T {
|
||||||
if(thisIsWrapperFor(iface))
|
if (thisIsWrapperFor(iface))
|
||||||
return this as T
|
return this as T
|
||||||
else
|
else
|
||||||
return parent.unwrap(iface)
|
return parent.unwrap(iface)
|
||||||
@@ -426,12 +426,12 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getBlob(columnIndex: Int): Blob {
|
override fun getBlob(columnIndex: Int): Blob {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getBlob(columnLabel: String?): Blob {
|
override fun getBlob(columnLabel: String?): Blob {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,12 +500,12 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getObject(columnIndex: Int, map: MutableMap<String, Class<*>>?): Any {
|
override fun getObject(columnIndex: Int, map: MutableMap<String, Class<*>>?): Any {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getObject(columnLabel: String?, map: MutableMap<String, Class<*>>?): Any {
|
override fun getObject(columnLabel: String?, map: MutableMap<String, Class<*>>?): Any {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,9 +531,9 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun castToLong(obj: Any?): Long {
|
private fun castToLong(obj: Any?): Long {
|
||||||
if(obj == null) return 0
|
if (obj == null) return 0
|
||||||
else if(obj is Long) return obj
|
else if (obj is Long) return obj
|
||||||
else if(obj is Number) return obj.toLong()
|
else if (obj is Number) return obj.toLong()
|
||||||
else throw IllegalStateException("Object is not a long!")
|
else throw IllegalStateException("Object is not a long!")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,12 +546,12 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getClob(columnIndex: Int): Clob {
|
override fun getClob(columnIndex: Int): Clob {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getClob(columnLabel: String?): Clob {
|
override fun getClob(columnLabel: String?): Clob {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -604,12 +604,12 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getArray(columnIndex: Int): Array {
|
override fun getArray(columnIndex: Int): Array {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getArray(columnLabel: String?): Array {
|
override fun getArray(columnLabel: String?): Array {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,32 +688,32 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getTimestamp(columnIndex: Int): Timestamp {
|
override fun getTimestamp(columnIndex: Int): Timestamp {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTimestamp(columnLabel: String?): Timestamp {
|
override fun getTimestamp(columnLabel: String?): Timestamp {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTimestamp(columnIndex: Int, cal: Calendar?): Timestamp {
|
override fun getTimestamp(columnIndex: Int, cal: Calendar?): Timestamp {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTimestamp(columnLabel: String?, cal: Calendar?): Timestamp {
|
override fun getTimestamp(columnLabel: String?, cal: Calendar?): Timestamp {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRef(columnIndex: Int): Ref {
|
override fun getRef(columnIndex: Int): Ref {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRef(columnLabel: String?): Ref {
|
override fun getRef(columnLabel: String?): Ref {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -792,12 +792,12 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getRowId(columnIndex: Int): RowId {
|
override fun getRowId(columnIndex: Int): RowId {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRowId(columnLabel: String?): RowId {
|
override fun getRowId(columnLabel: String?): RowId {
|
||||||
//TODO Maybe?
|
// TODO Maybe?
|
||||||
notImplemented()
|
notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -848,4 +848,4 @@ class ScrollableResultSet(val parent: ResultSet) : ResultSet by parent {
|
|||||||
class ResultSetEntry {
|
class ResultSetEntry {
|
||||||
val data = mutableListOf<Any?>()
|
val data = mutableListOf<Any?>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -174,4 +174,4 @@ class JavaSharedPreferences(key: String) : SharedPreferences {
|
|||||||
javaPreferences.removeNode()
|
javaPreferences.removeNode()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ import java.io.File
|
|||||||
import javax.imageio.ImageIO
|
import javax.imageio.ImageIO
|
||||||
import javax.xml.parsers.DocumentBuilderFactory
|
import javax.xml.parsers.DocumentBuilderFactory
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
data class InstalledPackage(val root: File) {
|
data class InstalledPackage(val root: File) {
|
||||||
val apk = File(root, "package.apk")
|
val apk = File(root, "package.apk")
|
||||||
val jar = File(root, "translated.jar")
|
val jar = File(root, "translated.jar")
|
||||||
@@ -40,20 +38,24 @@ data class InstalledPackage(val root: File) {
|
|||||||
}?.filter {
|
}?.filter {
|
||||||
it.tagName == "meta-data"
|
it.tagName == "meta-data"
|
||||||
}?.map {
|
}?.map {
|
||||||
putString(it.attributes.getNamedItem("android:name").nodeValue,
|
putString(
|
||||||
it.attributes.getNamedItem("android:value").nodeValue)
|
it.attributes.getNamedItem("android:name").nodeValue,
|
||||||
|
it.attributes.getNamedItem("android:value").nodeValue
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
it.signatures = (parsed.apkSingers.flatMap { it.certificateMetas }
|
it.signatures = (
|
||||||
/*+ parsed.apkV2Singers.flatMap { it.certificateMetas }*/) // Blocked by: https://github.com/hsiafan/apk-parser/issues/72
|
parsed.apkSingers.flatMap { it.certificateMetas }
|
||||||
.map { Signature(it.data) }.toTypedArray()
|
/*+ parsed.apkV2Singers.flatMap { it.certificateMetas }*/
|
||||||
|
) // Blocked by: https://github.com/hsiafan/apk-parser/issues/72
|
||||||
|
.map { Signature(it.data) }.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun verify(): Boolean {
|
fun verify(): Boolean {
|
||||||
val res = ApkVerifier.Builder(apk)
|
val res = ApkVerifier.Builder(apk)
|
||||||
.build()
|
.build()
|
||||||
.verify()
|
.verify()
|
||||||
|
|
||||||
return res.isVerified
|
return res.isVerified
|
||||||
}
|
}
|
||||||
@@ -69,7 +71,7 @@ data class InstalledPackage(val root: File) {
|
|||||||
}.sortedByDescending { it.width * it.height }.firstOrNull() ?: return
|
}.sortedByDescending { it.width * it.height }.firstOrNull() ?: return
|
||||||
|
|
||||||
ImageIO.write(read, "png", icon)
|
ImageIO.write(read, "png", icon)
|
||||||
} catch(e: Exception) {
|
} catch (e: Exception) {
|
||||||
icon.delete()
|
icon.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,7 +79,7 @@ data class InstalledPackage(val root: File) {
|
|||||||
fun writeJar() {
|
fun writeJar() {
|
||||||
try {
|
try {
|
||||||
Dex2jar.from(apk).to(jar.toPath())
|
Dex2jar.from(apk).to(jar.toPath())
|
||||||
} catch(e: Exception) {
|
} catch (e: Exception) {
|
||||||
jar.delete()
|
jar.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,4 +94,4 @@ data class InstalledPackage(val root: File) {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class PackageController {
|
|||||||
if (!installed.jar.exists()) {
|
if (!installed.jar.exists()) {
|
||||||
throw IllegalStateException("Failed to translate APK dex!")
|
throw IllegalStateException("Failed to translate APK dex!")
|
||||||
}
|
}
|
||||||
} catch(t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
root.deleteRecursively()
|
root.deleteRecursively()
|
||||||
throw t
|
throw t
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,7 @@ class PackageController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun deletePackage(pack: InstalledPackage) {
|
fun deletePackage(pack: InstalledPackage) {
|
||||||
if(!pack.root.exists()) error("Package was never installed!")
|
if (!pack.root.exists()) error("Package was never installed!")
|
||||||
|
|
||||||
val packageName = pack.info.packageName
|
val packageName = pack.info.packageName
|
||||||
pack.root.deleteRecursively()
|
pack.root.deleteRecursively()
|
||||||
@@ -74,7 +74,7 @@ class PackageController {
|
|||||||
|
|
||||||
fun findPackage(packageName: String): InstalledPackage? {
|
fun findPackage(packageName: String): InstalledPackage? {
|
||||||
val file = File(androidFiles.packagesDir, packageName)
|
val file = File(androidFiles.packagesDir, packageName)
|
||||||
return if(file.exists())
|
return if (file.exists())
|
||||||
InstalledPackage(file)
|
InstalledPackage(file)
|
||||||
else
|
else
|
||||||
null
|
null
|
||||||
@@ -84,4 +84,4 @@ class PackageController {
|
|||||||
val pkgName = ApkParsers.getMetaInfo(apkFile).packageName
|
val pkgName = ApkParsers.getMetaInfo(apkFile).packageName
|
||||||
return findPackage(pkgName)?.jar
|
return findPackage(pkgName)?.jar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,4 +24,4 @@ fun ApkMeta.toPackageInfo(apk: File): PackageInfo {
|
|||||||
sourceDir = apk.absolutePath
|
sourceDir = apk.absolutePath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,4 +24,4 @@ interface Resource {
|
|||||||
fun getType(): Class<out Resource>
|
fun getType(): Class<out Resource>
|
||||||
|
|
||||||
fun getValue(): Any?
|
fun getValue(): Any?
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ class ServiceSupport {
|
|||||||
|
|
||||||
runningServices[name] = service
|
runningServices[name] = service
|
||||||
|
|
||||||
//Setup service
|
// Setup service
|
||||||
thread {
|
thread {
|
||||||
callOnCreate(service)
|
callOnCreate(service)
|
||||||
//TODO Handle more complex cases
|
// TODO Handle more complex cases
|
||||||
service.onStartCommand(intent, 0, 0)
|
service.onStartCommand(intent, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ class ServiceSupport {
|
|||||||
fun stopService(name: String) {
|
fun stopService(name: String) {
|
||||||
logger.debug { "Stopping service: $name" }
|
logger.debug { "Stopping service: $name" }
|
||||||
val service = runningServices.remove(name)
|
val service = runningServices.remove(name)
|
||||||
if(service == null) {
|
if (service == null) {
|
||||||
logger.warn { "An attempt was made to stop a service that is not running: $name" }
|
logger.warn { "An attempt was made to stop a service that is not running: $name" }
|
||||||
} else {
|
} else {
|
||||||
thread {
|
thread {
|
||||||
@@ -63,6 +63,6 @@ class ServiceSupport {
|
|||||||
fun serviceInstanceFromClass(className: String): Service {
|
fun serviceInstanceFromClass(className: String): Service {
|
||||||
val clazzObj = Class.forName(className)
|
val clazzObj = Class.forName(className)
|
||||||
return clazzObj.getDeclaredConstructor().newInstance() as? Service
|
return clazzObj.getDeclaredConstructor().newInstance() as? Service
|
||||||
?: throw IllegalArgumentException("$className is not a Service!")
|
?: throw IllegalArgumentException("$className is not a Service!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ object KodeinGlobalHelper {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun <T : Any> instance(type: Class<T>, kodein: DI? = null): T {
|
fun <T : Any> instance(type: Class<T>, kodein: DI? = null): T {
|
||||||
return when(type) {
|
return when (type) {
|
||||||
AndroidFiles::class.java -> {
|
AndroidFiles::class.java -> {
|
||||||
val instance: AndroidFiles by (kodein ?: kodein()).instance()
|
val instance: AndroidFiles by (kodein ?: kodein()).instance()
|
||||||
instance as T
|
instance as T
|
||||||
@@ -64,5 +64,4 @@ object KodeinGlobalHelper {
|
|||||||
fun <T : Any> instance(type: Class<T>): T {
|
fun <T : Any> instance(type: Class<T>): T {
|
||||||
return instance(type, null)
|
return instance(type, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-20
@@ -2,27 +2,9 @@
|
|||||||
## TL;DR
|
## TL;DR
|
||||||
<!-- TODO: fill before release -->
|
<!-- TODO: fill before release -->
|
||||||
|
|
||||||
## Tachidesk-Server
|
## Tachidesk-Server Changelog
|
||||||
### Public API
|
|
||||||
#### Non-breaking changes
|
|
||||||
- N/A
|
|
||||||
|
|
||||||
#### Breaking changes
|
|
||||||
- N/A
|
|
||||||
|
|
||||||
#### Bug fixes
|
|
||||||
- N/A
|
|
||||||
|
|
||||||
### Private API
|
|
||||||
- N/A
|
- N/A
|
||||||
|
|
||||||
|
|
||||||
## Tachidesk-WebUI
|
## Tachidesk-WebUI Changelog
|
||||||
#### Visible changes
|
|
||||||
- N/A
|
|
||||||
|
|
||||||
#### Bug fixes
|
|
||||||
- N/A
|
|
||||||
|
|
||||||
#### Internal changes
|
|
||||||
- N/A
|
- N/A
|
||||||
|
|||||||
+20
-3
@@ -1,3 +1,19 @@
|
|||||||
|
# Server: v0.5.3 + WebUI: r809
|
||||||
|
## TL;DR
|
||||||
|
<!-- TODO: fill before release -->
|
||||||
|
|
||||||
|
## Tachidesk-Server Changelog
|
||||||
|
- (r956) fix macOS-arm64 bundle launchers not working
|
||||||
|
- (r957) Workaround StdLib issue and add KtLint to all modules ([#206](https://github.com/Suwayomi/Tachidesk-Server/pull/206) by @Syer10)
|
||||||
|
- (r960-r963) Add recently updated chapters(Updates) endpoint
|
||||||
|
|
||||||
|
|
||||||
|
## Tachidesk-WebUI Changelog
|
||||||
|
- (r808) fix chapter list not calling onlineFetch=true
|
||||||
|
- (r809) add support for Updates
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Server: v0.5.2 + WebUI: r807
|
# Server: v0.5.2 + WebUI: r807
|
||||||
## TL;DR
|
## TL;DR
|
||||||
- Fixed Local source not working on Windows
|
- Fixed Local source not working on Windows
|
||||||
@@ -12,11 +28,12 @@
|
|||||||
- N/A
|
- N/A
|
||||||
|
|
||||||
#### Bug fixes
|
#### Bug fixes
|
||||||
- N/A
|
- (r948) Fix ManaToki (KO) and NewToki (KO) (issue [#202](https://github.com/Suwayomi/Tachidesk-Server/issue/202))
|
||||||
|
- (r949) Local source: fix windows paths
|
||||||
|
|
||||||
### Private API
|
### Private API
|
||||||
- (r942) Gradle Updates ([#199](https://github.com/Suwayomi/Tachidesk-WebUI/pull/199) by @Syer10)
|
- (r941) Update BytecodeEditor to use Java NIO Paths ([#200](https://github.com/Suwayomi/Tachidesk-Server/pull/200) by @Syer10)
|
||||||
- (r941) Update BytecodeEditor to use Java NIO Paths ([#200](https://github.com/Suwayomi/Tachidesk-WebUI/pull/200) by @Syer10)
|
- (r942) Gradle Updates ([#199](https://github.com/Suwayomi/Tachidesk-Server/pull/199) by @Syer10)
|
||||||
|
|
||||||
|
|
||||||
## Tachidesk-WebUI
|
## Tachidesk-WebUI
|
||||||
|
|||||||
@@ -3,16 +3,7 @@
|
|||||||
|-------|----------|---------|---------|
|
|-------|----------|---------|---------|
|
||||||
|  | [](https://github.com/Suwayomi/Tachidesk/releases) | [](https://github.com/Suwayomi/Tachidesk-preview/releases/latest) | [](https://discord.gg/DDZdqZWaHA) |
|
|  | [](https://github.com/Suwayomi/Tachidesk/releases) | [](https://github.com/Suwayomi/Tachidesk-preview/releases/latest) | [](https://discord.gg/DDZdqZWaHA) |
|
||||||
|
|
||||||
# Tachidesk-Server is a server app! You may not want to Download Tachidesk-Server directly.
|
# What is Tachidesk?
|
||||||
Yes, you need a client/user interface app as a front-end for Tachidesk-Server, if you Directly Download Tachidesk-Server you'll get a bundled version of [Tachidesk-WebUI](https://github.com/Suwayomi/Tachidesk-WebUI) with it.
|
|
||||||
|
|
||||||
Here's a list of known clients/user interfaces for Tachidesk-Server:
|
|
||||||
- [Tachidesk-JUI](https://github.com/Suwayomi/Tachidesk-JUI): The "official" front-end for Tachidesk-Server, A native desktop Application.
|
|
||||||
- [Tachidesk-WebUI](https://github.com/Suwayomi/Tachidesk-WebUI): The web/electrion front-end that Tachidesk-Server is traditionally shipped with.
|
|
||||||
- [Tachidesk-qtui](https://github.com/Suwayomi/Tachidesk-qtui): A C++/Qt front-end for mobile devices(Android/linux), in super early stage of development.
|
|
||||||
- [Equinox](https://github.com/Suwayomi/Equinox): A web user interface made with Vue.js, in super early stage of development.
|
|
||||||
|
|
||||||
# What is Tachidesk then?
|
|
||||||
<img src="https://github.com/Suwayomi/Tachidesk/raw/master/server/src/main/resources/icon/faviconlogo.png" alt="drawing" width="200"/>
|
<img src="https://github.com/Suwayomi/Tachidesk/raw/master/server/src/main/resources/icon/faviconlogo.png" alt="drawing" width="200"/>
|
||||||
|
|
||||||
A free and open source manga reader server that runs extensions built for [Tachiyomi](https://tachiyomi.org/).
|
A free and open source manga reader server that runs extensions built for [Tachiyomi](https://tachiyomi.org/).
|
||||||
@@ -23,6 +14,15 @@ Tachidesk-Server is as multi-platform as you can get. Any platform that runs jav
|
|||||||
|
|
||||||
Ability to read and write Tachiyomi compatible backups and syncing is a planned feature.
|
Ability to read and write Tachiyomi compatible backups and syncing is a planned feature.
|
||||||
|
|
||||||
|
# Tachidesk-Server is a server app! You may not want to Download Tachidesk-Server directly.
|
||||||
|
Yes, you need a client/user interface app as a front-end for Tachidesk-Server, if you Directly Download Tachidesk-Server you'll get a bundled version of [Tachidesk-WebUI](https://github.com/Suwayomi/Tachidesk-WebUI) with it.
|
||||||
|
|
||||||
|
Here's a list of known clients/user interfaces for Tachidesk-Server:
|
||||||
|
- [Tachidesk-JUI](https://github.com/Suwayomi/Tachidesk-JUI): The "official" front-end for Tachidesk-Server, A native desktop Application.
|
||||||
|
- [Tachidesk-WebUI](https://github.com/Suwayomi/Tachidesk-WebUI): The web/electrion front-end that Tachidesk-Server is traditionally shipped with.
|
||||||
|
- [Tachidesk-qtui](https://github.com/Suwayomi/Tachidesk-qtui): A C++/Qt front-end for mobile devices(Android/linux), in super early stage of development.
|
||||||
|
- [Equinox](https://github.com/Suwayomi/Equinox): A web user interface made with Vue.js, in super early stage of development.
|
||||||
|
|
||||||
## Is this application usable? Should I test it?
|
## Is this application usable? Should I test it?
|
||||||
Here is a list of current features:
|
Here is a list of current features:
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ plugins {
|
|||||||
kotlin("jvm") version kotlinVersion
|
kotlin("jvm") version kotlinVersion
|
||||||
kotlin("plugin.serialization") version kotlinVersion
|
kotlin("plugin.serialization") version kotlinVersion
|
||||||
id("org.jmailen.kotlinter") version "3.6.0"
|
id("org.jmailen.kotlinter") version "3.6.0"
|
||||||
|
id("com.github.gmazzo.buildconfig") version "3.0.3" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
@@ -29,6 +30,7 @@ val projects = listOf(
|
|||||||
configure(projects) {
|
configure(projects) {
|
||||||
apply(plugin = "org.jetbrains.kotlin.jvm")
|
apply(plugin = "org.jetbrains.kotlin.jvm")
|
||||||
apply(plugin = "org.jetbrains.kotlin.plugin.serialization")
|
apply(plugin = "org.jetbrains.kotlin.plugin.serialization")
|
||||||
|
apply(plugin = "org.jmailen.kotlinter")
|
||||||
|
|
||||||
java {
|
java {
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
@@ -37,6 +39,7 @@ configure(projects) {
|
|||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
withType<KotlinCompile> {
|
withType<KotlinCompile> {
|
||||||
|
dependsOn(formatKotlin)
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||||
freeCompilerArgs = listOf(
|
freeCompilerArgs = listOf(
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ const val kotlinVersion = "1.5.30"
|
|||||||
const val MainClass = "suwayomi.tachidesk.MainKt"
|
const val MainClass = "suwayomi.tachidesk.MainKt"
|
||||||
|
|
||||||
// should be bumped with each stable release
|
// should be bumped with each stable release
|
||||||
val tachideskVersion = System.getenv("ProductVersion") ?: "v0.5.2"
|
val tachideskVersion = System.getenv("ProductVersion") ?: "v0.5.3"
|
||||||
|
|
||||||
val webUIRevisionTag = System.getenv("WebUIRevision") ?: "r807"
|
val webUIRevisionTag = System.getenv("WebUIRevision") ?: "r809"
|
||||||
|
|
||||||
// counts commits on the master branch
|
// counts commits on the master branch
|
||||||
val tachideskRevision = runCatching {
|
val tachideskRevision = runCatching {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#/bin/bash
|
||||||
|
|
||||||
# Copyright (C) Contributors to the Suwayomi project
|
# Copyright (C) Contributors to the Suwayomi project
|
||||||
#
|
#
|
||||||
@@ -24,7 +24,7 @@ elif [ $1 = "macOS-arm64" ]; then
|
|||||||
jre="zulu8.56.0.23-ca-jre8.0.302-macosx_aarch64.tar.gz"
|
jre="zulu8.56.0.23-ca-jre8.0.302-macosx_aarch64.tar.gz"
|
||||||
jre_release="zulu8.56.0.23-ca-jre8.0.302-macosx_aarch64"
|
jre_release="zulu8.56.0.23-ca-jre8.0.302-macosx_aarch64"
|
||||||
jre_url="https://cdn.azul.com/zulu/bin/$jre"
|
jre_url="https://cdn.azul.com/zulu/bin/$jre"
|
||||||
jre_dir="$jre_release"
|
jre_dir="$jre_release/zulu-8.jre"
|
||||||
electron="electron-$electron_version-darwin-arm64.zip"
|
electron="electron-$electron_version-darwin-arm64.zip"
|
||||||
else
|
else
|
||||||
echo "Unsupported arch value: $1"
|
echo "Unsupported arch value: $1"
|
||||||
|
|||||||
+3
-10
@@ -1,11 +1,10 @@
|
|||||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
|
||||||
import de.undercouch.gradle.tasks.download.Download
|
import de.undercouch.gradle.tasks.download.Download
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
application
|
application
|
||||||
id("com.github.johnrengelman.shadow") version "7.0.0"
|
id("com.github.johnrengelman.shadow") version "7.0.0"
|
||||||
id("com.github.gmazzo.buildconfig") version "3.0.3"
|
id("com.github.gmazzo.buildconfig")
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -70,6 +69,7 @@ dependencies {
|
|||||||
|
|
||||||
// uncomment to test extensions directly
|
// uncomment to test extensions directly
|
||||||
// implementation(fileTree("lib/"))
|
// implementation(fileTree("lib/"))
|
||||||
|
implementation(kotlin("script-runtime"))
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
@@ -127,20 +127,13 @@ tasks {
|
|||||||
archiveBaseName.set(rootProject.name)
|
archiveBaseName.set(rootProject.name)
|
||||||
archiveVersion.set(tachideskVersion)
|
archiveVersion.set(tachideskVersion)
|
||||||
archiveClassifier.set(tachideskRevision)
|
archiveClassifier.set(tachideskRevision)
|
||||||
|
destinationDirectory.set(File("$rootDir/server/build"))
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
useJUnit()
|
useJUnit()
|
||||||
}
|
}
|
||||||
|
|
||||||
withType<ShadowJar> {
|
|
||||||
destinationDirectory.set(File("$rootDir/server/build"))
|
|
||||||
}
|
|
||||||
|
|
||||||
named("run") {
|
|
||||||
dependsOn(":formatKotlin", ":lintKotlin")
|
|
||||||
}
|
|
||||||
|
|
||||||
named<Copy>("processResources") {
|
named<Copy>("processResources") {
|
||||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||||
mustRunAfter("downloadWebUI")
|
mustRunAfter("downloadWebUI")
|
||||||
|
|||||||
@@ -440,7 +440,6 @@ class LocalSource : HttpSource() {
|
|||||||
private object FileSystemInterceptor : Interceptor {
|
private object FileSystemInterceptor : Interceptor {
|
||||||
fun fakeUrlFrom(path: String): String = "http://$path"
|
fun fakeUrlFrom(path: String): String = "http://$path"
|
||||||
|
|
||||||
|
|
||||||
private fun restoreFilePath(url: String): String {
|
private fun restoreFilePath(url: String): String {
|
||||||
val path = URLDecoder.decode(url.replaceFirst("http://", ""), "UTF-8")
|
val path = URLDecoder.decode(url.replaceFirst("http://", ""), "UTF-8")
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import suwayomi.tachidesk.manga.controller.DownloadController
|
|||||||
import suwayomi.tachidesk.manga.controller.ExtensionController
|
import suwayomi.tachidesk.manga.controller.ExtensionController
|
||||||
import suwayomi.tachidesk.manga.controller.MangaController
|
import suwayomi.tachidesk.manga.controller.MangaController
|
||||||
import suwayomi.tachidesk.manga.controller.SourceController
|
import suwayomi.tachidesk.manga.controller.SourceController
|
||||||
|
import suwayomi.tachidesk.manga.controller.UpdateController
|
||||||
|
|
||||||
object MangaAPI {
|
object MangaAPI {
|
||||||
fun defineEndpoints() {
|
fun defineEndpoints() {
|
||||||
@@ -106,5 +107,9 @@ object MangaAPI {
|
|||||||
get("{mangaId}/chapter/{chapterIndex}", DownloadController::queueChapter)
|
get("{mangaId}/chapter/{chapterIndex}", DownloadController::queueChapter)
|
||||||
delete("{mangaId}/chapter/{chapterIndex}", DownloadController::unqueueChapter)
|
delete("{mangaId}/chapter/{chapterIndex}", DownloadController::unqueueChapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path("update") {
|
||||||
|
get("recentChapters", UpdateController::recentChapters)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package suwayomi.tachidesk.manga.controller
|
||||||
|
|
||||||
|
import io.javalin.http.Context
|
||||||
|
import suwayomi.tachidesk.manga.impl.Chapter
|
||||||
|
import suwayomi.tachidesk.server.JavalinSetup.future
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
object UpdateController {
|
||||||
|
/** get recently updated manga chapters */
|
||||||
|
fun recentChapters(ctx: Context) {
|
||||||
|
ctx.future(
|
||||||
|
future {
|
||||||
|
Chapter.getRecentChapters()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.source.model.SChapter
|
|||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.util.chapter.ChapterRecognition
|
import eu.kanade.tachiyomi.util.chapter.ChapterRecognition
|
||||||
import org.jetbrains.exposed.dao.id.EntityID
|
import org.jetbrains.exposed.dao.id.EntityID
|
||||||
import org.jetbrains.exposed.sql.SortOrder.DESC
|
import org.jetbrains.exposed.sql.SortOrder
|
||||||
import org.jetbrains.exposed.sql.and
|
import org.jetbrains.exposed.sql.and
|
||||||
import org.jetbrains.exposed.sql.deleteWhere
|
import org.jetbrains.exposed.sql.deleteWhere
|
||||||
import org.jetbrains.exposed.sql.insert
|
import org.jetbrains.exposed.sql.insert
|
||||||
@@ -25,6 +25,7 @@ import suwayomi.tachidesk.manga.impl.util.getChapterDir
|
|||||||
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
|
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
|
||||||
import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse
|
import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass
|
import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass
|
||||||
|
import suwayomi.tachidesk.manga.model.dataclass.MangaChapterDataClass
|
||||||
import suwayomi.tachidesk.manga.model.table.ChapterMetaTable
|
import suwayomi.tachidesk.manga.model.table.ChapterMetaTable
|
||||||
import suwayomi.tachidesk.manga.model.table.ChapterTable
|
import suwayomi.tachidesk.manga.model.table.ChapterTable
|
||||||
import suwayomi.tachidesk.manga.model.table.MangaTable
|
import suwayomi.tachidesk.manga.model.table.MangaTable
|
||||||
@@ -40,7 +41,7 @@ object Chapter {
|
|||||||
getSourceChapters(mangaId)
|
getSourceChapters(mangaId)
|
||||||
} else {
|
} else {
|
||||||
transaction {
|
transaction {
|
||||||
ChapterTable.select { ChapterTable.manga eq mangaId }.orderBy(ChapterTable.chapterIndex to DESC)
|
ChapterTable.select { ChapterTable.manga eq mangaId }.orderBy(ChapterTable.sourceOrder to SortOrder.DESC)
|
||||||
.map {
|
.map {
|
||||||
ChapterTable.toDataClass(it)
|
ChapterTable.toDataClass(it)
|
||||||
}
|
}
|
||||||
@@ -68,6 +69,7 @@ object Chapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val chapterCount = chapterList.count()
|
val chapterCount = chapterList.count()
|
||||||
|
var now = Instant.now().epochSecond
|
||||||
|
|
||||||
transaction {
|
transaction {
|
||||||
chapterList.reversed().forEachIndexed { index, fetchedChapter ->
|
chapterList.reversed().forEachIndexed { index, fetchedChapter ->
|
||||||
@@ -80,7 +82,8 @@ object Chapter {
|
|||||||
it[chapter_number] = fetchedChapter.chapter_number
|
it[chapter_number] = fetchedChapter.chapter_number
|
||||||
it[scanlator] = fetchedChapter.scanlator
|
it[scanlator] = fetchedChapter.scanlator
|
||||||
|
|
||||||
it[chapterIndex] = index + 1
|
it[sourceOrder] = index + 1
|
||||||
|
it[fetchedAt] = now++
|
||||||
it[ChapterTable.manga] = mangaId
|
it[ChapterTable.manga] = mangaId
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -90,7 +93,7 @@ object Chapter {
|
|||||||
it[chapter_number] = fetchedChapter.chapter_number
|
it[chapter_number] = fetchedChapter.chapter_number
|
||||||
it[scanlator] = fetchedChapter.scanlator
|
it[scanlator] = fetchedChapter.scanlator
|
||||||
|
|
||||||
it[chapterIndex] = index + 1
|
it[sourceOrder] = index + 1
|
||||||
it[ChapterTable.manga] = mangaId
|
it[ChapterTable.manga] = mangaId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,8 +106,8 @@ object Chapter {
|
|||||||
val dbChapterList = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.toList() }
|
val dbChapterList = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.toList() }
|
||||||
|
|
||||||
dbChapterList.forEach {
|
dbChapterList.forEach {
|
||||||
if (it[ChapterTable.chapterIndex] >= chapterList.size ||
|
if (it[ChapterTable.sourceOrder] >= chapterList.size ||
|
||||||
chapterList[it[ChapterTable.chapterIndex] - 1].url != it[ChapterTable.url]
|
chapterList[it[ChapterTable.sourceOrder] - 1].url != it[ChapterTable.url]
|
||||||
) {
|
) {
|
||||||
transaction {
|
transaction {
|
||||||
PageTable.deleteWhere { PageTable.chapter eq it[ChapterTable.id] }
|
PageTable.deleteWhere { PageTable.chapter eq it[ChapterTable.id] }
|
||||||
@@ -137,6 +140,7 @@ object Chapter {
|
|||||||
dbChapter[ChapterTable.lastReadAt],
|
dbChapter[ChapterTable.lastReadAt],
|
||||||
|
|
||||||
chapterCount - index,
|
chapterCount - index,
|
||||||
|
dbChapter[ChapterTable.fetchedAt],
|
||||||
dbChapter[ChapterTable.isDownloaded],
|
dbChapter[ChapterTable.isDownloaded],
|
||||||
|
|
||||||
dbChapter[ChapterTable.pageCount],
|
dbChapter[ChapterTable.pageCount],
|
||||||
@@ -151,7 +155,7 @@ object Chapter {
|
|||||||
suspend fun getChapter(chapterIndex: Int, mangaId: Int): ChapterDataClass {
|
suspend fun getChapter(chapterIndex: Int, mangaId: Int): ChapterDataClass {
|
||||||
val chapterEntry = transaction {
|
val chapterEntry = transaction {
|
||||||
ChapterTable.select {
|
ChapterTable.select {
|
||||||
(ChapterTable.chapterIndex eq chapterIndex) and (ChapterTable.manga eq mangaId)
|
(ChapterTable.sourceOrder eq chapterIndex) and (ChapterTable.manga eq mangaId)
|
||||||
}.first()
|
}.first()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +163,7 @@ object Chapter {
|
|||||||
chapterEntry[ChapterTable.isDownloaded] && firstPageExists(mangaId, chapterEntry[ChapterTable.id].value)
|
chapterEntry[ChapterTable.isDownloaded] && firstPageExists(mangaId, chapterEntry[ChapterTable.id].value)
|
||||||
return if (!isReallyDownloaded) {
|
return if (!isReallyDownloaded) {
|
||||||
transaction {
|
transaction {
|
||||||
ChapterTable.update({ (ChapterTable.chapterIndex eq chapterIndex) and (ChapterTable.manga eq mangaId) }) {
|
ChapterTable.update({ (ChapterTable.sourceOrder eq chapterIndex) and (ChapterTable.manga eq mangaId) }) {
|
||||||
it[isDownloaded] = false
|
it[isDownloaded] = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,7 +207,7 @@ object Chapter {
|
|||||||
val pageCount = pageList.count()
|
val pageCount = pageList.count()
|
||||||
|
|
||||||
transaction {
|
transaction {
|
||||||
ChapterTable.update({ (ChapterTable.manga eq mangaId) and (ChapterTable.chapterIndex eq chapterIndex) }) {
|
ChapterTable.update({ (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }) {
|
||||||
it[ChapterTable.pageCount] = pageCount
|
it[ChapterTable.pageCount] = pageCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,7 +223,8 @@ object Chapter {
|
|||||||
chapterEntry[ChapterTable.lastPageRead],
|
chapterEntry[ChapterTable.lastPageRead],
|
||||||
chapterEntry[ChapterTable.lastReadAt],
|
chapterEntry[ChapterTable.lastReadAt],
|
||||||
|
|
||||||
chapterEntry[ChapterTable.chapterIndex],
|
chapterEntry[ChapterTable.sourceOrder],
|
||||||
|
chapterEntry[ChapterTable.fetchedAt],
|
||||||
chapterEntry[ChapterTable.isDownloaded],
|
chapterEntry[ChapterTable.isDownloaded],
|
||||||
pageCount,
|
pageCount,
|
||||||
chapterCount.toInt(),
|
chapterCount.toInt(),
|
||||||
@@ -249,7 +254,7 @@ object Chapter {
|
|||||||
) {
|
) {
|
||||||
transaction {
|
transaction {
|
||||||
if (listOf(isRead, isBookmarked, lastPageRead).any { it != null }) {
|
if (listOf(isRead, isBookmarked, lastPageRead).any { it != null }) {
|
||||||
ChapterTable.update({ (ChapterTable.manga eq mangaId) and (ChapterTable.chapterIndex eq chapterIndex) }) { update ->
|
ChapterTable.update({ (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }) { update ->
|
||||||
isRead?.also {
|
isRead?.also {
|
||||||
update[ChapterTable.isRead] = it
|
update[ChapterTable.isRead] = it
|
||||||
}
|
}
|
||||||
@@ -264,7 +269,7 @@ object Chapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
markPrevRead?.let {
|
markPrevRead?.let {
|
||||||
ChapterTable.update({ (ChapterTable.manga eq mangaId) and (ChapterTable.chapterIndex less chapterIndex) }) {
|
ChapterTable.update({ (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder less chapterIndex) }) {
|
||||||
it[ChapterTable.isRead] = markPrevRead
|
it[ChapterTable.isRead] = markPrevRead
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -281,7 +286,7 @@ object Chapter {
|
|||||||
fun modifyChapterMeta(mangaId: Int, chapterIndex: Int, key: String, value: String) {
|
fun modifyChapterMeta(mangaId: Int, chapterIndex: Int, key: String, value: String) {
|
||||||
transaction {
|
transaction {
|
||||||
val chapterId =
|
val chapterId =
|
||||||
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.chapterIndex eq chapterIndex) }
|
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }
|
||||||
.first()[ChapterTable.id].value
|
.first()[ChapterTable.id].value
|
||||||
val meta =
|
val meta =
|
||||||
transaction { ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } }.firstOrNull()
|
transaction { ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) } }.firstOrNull()
|
||||||
@@ -302,16 +307,30 @@ object Chapter {
|
|||||||
fun deleteChapter(mangaId: Int, chapterIndex: Int) {
|
fun deleteChapter(mangaId: Int, chapterIndex: Int) {
|
||||||
transaction {
|
transaction {
|
||||||
val chapterId =
|
val chapterId =
|
||||||
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.chapterIndex eq chapterIndex) }
|
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }
|
||||||
.first()[ChapterTable.id].value
|
.first()[ChapterTable.id].value
|
||||||
|
|
||||||
val chapterDir = getChapterDir(mangaId, chapterId)
|
val chapterDir = getChapterDir(mangaId, chapterId)
|
||||||
|
|
||||||
File(chapterDir).deleteRecursively()
|
File(chapterDir).deleteRecursively()
|
||||||
|
|
||||||
ChapterTable.update({ (ChapterTable.manga eq mangaId) and (ChapterTable.chapterIndex eq chapterIndex) }) {
|
ChapterTable.update({ (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }) {
|
||||||
it[isDownloaded] = false
|
it[isDownloaded] = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getRecentChapters(): List<MangaChapterDataClass> {
|
||||||
|
return transaction {
|
||||||
|
(ChapterTable innerJoin MangaTable)
|
||||||
|
.select { (MangaTable.inLibrary eq true) and (ChapterTable.fetchedAt greater MangaTable.inLibraryAt) }
|
||||||
|
.orderBy(ChapterTable.fetchedAt to SortOrder.DESC)
|
||||||
|
.map {
|
||||||
|
MangaChapterDataClass(
|
||||||
|
MangaTable.toDataClass(it),
|
||||||
|
ChapterTable.toDataClass(it)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import suwayomi.tachidesk.manga.impl.Manga.getManga
|
|||||||
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
|
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
|
||||||
import suwayomi.tachidesk.manga.model.table.CategoryTable
|
import suwayomi.tachidesk.manga.model.table.CategoryTable
|
||||||
import suwayomi.tachidesk.manga.model.table.MangaTable
|
import suwayomi.tachidesk.manga.model.table.MangaTable
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
object Library {
|
object Library {
|
||||||
suspend fun addMangaToLibrary(mangaId: Int) {
|
suspend fun addMangaToLibrary(mangaId: Int) {
|
||||||
@@ -25,8 +26,9 @@ object Library {
|
|||||||
val defaultCategories = CategoryTable.select { CategoryTable.isDefault eq true }.toList()
|
val defaultCategories = CategoryTable.select { CategoryTable.isDefault eq true }.toList()
|
||||||
|
|
||||||
MangaTable.update({ MangaTable.id eq manga.id }) {
|
MangaTable.update({ MangaTable.id eq manga.id }) {
|
||||||
it[MangaTable.inLibrary] = true
|
it[inLibrary] = true
|
||||||
it[MangaTable.defaultCategory] = defaultCategories.isEmpty()
|
it[inLibraryAt] = Instant.now().epochSecond
|
||||||
|
it[defaultCategory] = defaultCategories.isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultCategories.forEach { category ->
|
defaultCategories.forEach { category ->
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ object Manga {
|
|||||||
mangaEntry[MangaTable.genre].toGenreList(),
|
mangaEntry[MangaTable.genre].toGenreList(),
|
||||||
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
|
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
|
||||||
mangaEntry[MangaTable.inLibrary],
|
mangaEntry[MangaTable.inLibrary],
|
||||||
|
mangaEntry[MangaTable.inLibraryAt],
|
||||||
getSource(mangaEntry[MangaTable.sourceReference]),
|
getSource(mangaEntry[MangaTable.sourceReference]),
|
||||||
getMangaMetaMap(mangaId),
|
getMangaMetaMap(mangaId),
|
||||||
mangaEntry[MangaTable.realUrl],
|
mangaEntry[MangaTable.realUrl],
|
||||||
@@ -121,6 +122,7 @@ object Manga {
|
|||||||
fetchedManga.genre.toGenreList(),
|
fetchedManga.genre.toGenreList(),
|
||||||
MangaStatus.valueOf(fetchedManga.status).name,
|
MangaStatus.valueOf(fetchedManga.status).name,
|
||||||
mangaEntry[MangaTable.inLibrary],
|
mangaEntry[MangaTable.inLibrary],
|
||||||
|
mangaEntry[MangaTable.inLibraryAt],
|
||||||
getSource(mangaEntry[MangaTable.sourceReference]),
|
getSource(mangaEntry[MangaTable.sourceReference]),
|
||||||
getMangaMetaMap(mangaId),
|
getMangaMetaMap(mangaId),
|
||||||
mangaEntry[MangaTable.realUrl],
|
mangaEntry[MangaTable.realUrl],
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ object MangaList {
|
|||||||
manga.genre.toGenreList(),
|
manga.genre.toGenreList(),
|
||||||
MangaStatus.valueOf(manga.status).name,
|
MangaStatus.valueOf(manga.status).name,
|
||||||
false, // It's a new manga entry
|
false, // It's a new manga entry
|
||||||
|
0,
|
||||||
meta = getMangaMetaMap(mangaId),
|
meta = getMangaMetaMap(mangaId),
|
||||||
realUrl = mangaEntry[MangaTable.realUrl],
|
realUrl = mangaEntry[MangaTable.realUrl],
|
||||||
freshData = true
|
freshData = true
|
||||||
@@ -103,6 +104,7 @@ object MangaList {
|
|||||||
mangaEntry[MangaTable.genre].toGenreList(),
|
mangaEntry[MangaTable.genre].toGenreList(),
|
||||||
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
|
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
|
||||||
mangaEntry[MangaTable.inLibrary],
|
mangaEntry[MangaTable.inLibrary],
|
||||||
|
mangaEntry[MangaTable.inLibraryAt],
|
||||||
meta = getMangaMetaMap(mangaId),
|
meta = getMangaMetaMap(mangaId),
|
||||||
realUrl = mangaEntry[MangaTable.realUrl],
|
realUrl = mangaEntry[MangaTable.realUrl],
|
||||||
freshData = false
|
freshData = false
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ object Page {
|
|||||||
val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
|
val source = getHttpSource(mangaEntry[MangaTable.sourceReference])
|
||||||
val chapterEntry = transaction {
|
val chapterEntry = transaction {
|
||||||
ChapterTable.select {
|
ChapterTable.select {
|
||||||
(ChapterTable.chapterIndex eq chapterIndex) and (ChapterTable.manga eq mangaId)
|
(ChapterTable.sourceOrder eq chapterIndex) and (ChapterTable.manga eq mangaId)
|
||||||
}.first()
|
}.first()
|
||||||
}
|
}
|
||||||
val chapterId = chapterEntry[ChapterTable.id].value
|
val chapterId = chapterEntry[ChapterTable.id].value
|
||||||
|
|||||||
+2
-2
@@ -160,7 +160,7 @@ object ProtoBackupImport : ProtoBackupBase() {
|
|||||||
it[chapter_number] = chapter.chapter_number
|
it[chapter_number] = chapter.chapter_number
|
||||||
it[scanlator] = chapter.scanlator
|
it[scanlator] = chapter.scanlator
|
||||||
|
|
||||||
it[chapterIndex] = chaptersLength - chapter.source_order
|
it[sourceOrder] = chaptersLength - chapter.source_order
|
||||||
it[ChapterTable.manga] = mangaId
|
it[ChapterTable.manga] = mangaId
|
||||||
|
|
||||||
it[isRead] = chapter.read
|
it[isRead] = chapter.read
|
||||||
@@ -207,7 +207,7 @@ object ProtoBackupImport : ProtoBackupBase() {
|
|||||||
it[chapter_number] = chapter.chapter_number
|
it[chapter_number] = chapter.chapter_number
|
||||||
it[scanlator] = chapter.scanlator
|
it[scanlator] = chapter.scanlator
|
||||||
|
|
||||||
it[chapterIndex] = chaptersLength - chapter.source_order
|
it[sourceOrder] = chaptersLength - chapter.source_order
|
||||||
it[ChapterTable.manga] = mangaId
|
it[ChapterTable.manga] = mangaId
|
||||||
|
|
||||||
it[isRead] = chapter.read
|
it[isRead] = chapter.read
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ object DownloadManager {
|
|||||||
mangaId,
|
mangaId,
|
||||||
chapter = ChapterTable.toDataClass(
|
chapter = ChapterTable.toDataClass(
|
||||||
transaction {
|
transaction {
|
||||||
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.chapterIndex eq chapterIndex) }
|
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }
|
||||||
.first()
|
.first()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class Downloader(private val downloadQueue: CopyOnWriteArrayList<DownloadChapter
|
|||||||
}
|
}
|
||||||
download.state = Finished
|
download.state = Finished
|
||||||
transaction {
|
transaction {
|
||||||
ChapterTable.update({ (ChapterTable.manga eq download.mangaId) and (ChapterTable.chapterIndex eq download.chapterIndex) }) {
|
ChapterTable.update({ (ChapterTable.manga eq download.mangaId) and (ChapterTable.sourceOrder eq download.chapterIndex) }) {
|
||||||
it[isDownloaded] = true
|
it[isDownloaded] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,13 @@ data class ChapterDataClass(
|
|||||||
/** last read page, zero means not read/no data */
|
/** last read page, zero means not read/no data */
|
||||||
val lastReadAt: Long,
|
val lastReadAt: Long,
|
||||||
|
|
||||||
|
// TODO(v0.6.0): rename to sourceOrder
|
||||||
/** this chapter's index, starts with 1 */
|
/** this chapter's index, starts with 1 */
|
||||||
val index: Int,
|
val index: Int,
|
||||||
|
|
||||||
|
/** the date we fist saw this chapter*/
|
||||||
|
val fetchedAt: Long,
|
||||||
|
|
||||||
/** is chapter downloaded */
|
/** is chapter downloaded */
|
||||||
val downloaded: Boolean,
|
val downloaded: Boolean,
|
||||||
|
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package suwayomi.tachidesk.manga.model.dataclass
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
data class MangaChapterDataClass(
|
||||||
|
val manga: MangaDataClass,
|
||||||
|
val chapter: ChapterDataClass,
|
||||||
|
)
|
||||||
@@ -26,6 +26,7 @@ data class MangaDataClass(
|
|||||||
val genre: List<String> = emptyList(),
|
val genre: List<String> = emptyList(),
|
||||||
val status: String = MangaStatus.UNKNOWN.name,
|
val status: String = MangaStatus.UNKNOWN.name,
|
||||||
val inLibrary: Boolean = false,
|
val inLibrary: Boolean = false,
|
||||||
|
val inLibraryAt: Long = 0,
|
||||||
val source: SourceDataClass? = null,
|
val source: SourceDataClass? = null,
|
||||||
|
|
||||||
/** meta data for clients */
|
/** meta data for clients */
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ object ChapterTable : IntIdTable() {
|
|||||||
val isBookmarked = bool("bookmark").default(false)
|
val isBookmarked = bool("bookmark").default(false)
|
||||||
val lastPageRead = integer("last_page_read").default(0)
|
val lastPageRead = integer("last_page_read").default(0)
|
||||||
val lastReadAt = long("last_read_at").default(0)
|
val lastReadAt = long("last_read_at").default(0)
|
||||||
|
val fetchedAt = long("fetched_at").default(0)
|
||||||
|
|
||||||
// index is reserved by a function
|
val sourceOrder = integer("source_order")
|
||||||
val chapterIndex = integer("index")
|
|
||||||
|
|
||||||
val isDownloaded = bool("is_downloaded").default(false)
|
val isDownloaded = bool("is_downloaded").default(false)
|
||||||
|
|
||||||
@@ -48,7 +48,8 @@ fun ChapterTable.toDataClass(chapterEntry: ResultRow) =
|
|||||||
chapterEntry[isBookmarked],
|
chapterEntry[isBookmarked],
|
||||||
chapterEntry[lastPageRead],
|
chapterEntry[lastPageRead],
|
||||||
chapterEntry[lastReadAt],
|
chapterEntry[lastReadAt],
|
||||||
chapterEntry[chapterIndex],
|
chapterEntry[sourceOrder],
|
||||||
|
chapterEntry[fetchedAt],
|
||||||
chapterEntry[isDownloaded],
|
chapterEntry[isDownloaded],
|
||||||
chapterEntry[pageCount],
|
chapterEntry[pageCount],
|
||||||
transaction { ChapterTable.select { manga eq chapterEntry[manga].value }.count().toInt() },
|
transaction { ChapterTable.select { manga eq chapterEntry[manga].value }.count().toInt() },
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ object MangaTable : IntIdTable() {
|
|||||||
|
|
||||||
val inLibrary = bool("in_library").default(false)
|
val inLibrary = bool("in_library").default(false)
|
||||||
val defaultCategory = bool("default_category").default(true)
|
val defaultCategory = bool("default_category").default(true)
|
||||||
|
val inLibraryAt = long("in_library_at").default(0)
|
||||||
|
|
||||||
// the [source] field name is used by some ancestor of IntIdTable
|
// the [source] field name is used by some ancestor of IntIdTable
|
||||||
val sourceReference = long("source")
|
val sourceReference = long("source")
|
||||||
@@ -56,6 +57,7 @@ fun MangaTable.toDataClass(mangaEntry: ResultRow) =
|
|||||||
mangaEntry[genre].toGenreList(),
|
mangaEntry[genre].toGenreList(),
|
||||||
Companion.valueOf(mangaEntry[status]).name,
|
Companion.valueOf(mangaEntry[status]).name,
|
||||||
mangaEntry[inLibrary],
|
mangaEntry[inLibrary],
|
||||||
|
mangaEntry[inLibraryAt],
|
||||||
meta = getMangaMetaMap(mangaEntry[id].value),
|
meta = getMangaMetaMap(mangaEntry[id].value),
|
||||||
realUrl = mangaEntry[realUrl],
|
realUrl = mangaEntry[realUrl],
|
||||||
)
|
)
|
||||||
|
|||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package suwayomi.tachidesk.server.database.migration
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import de.neonew.exposed.migrations.helpers.SQLMigration
|
||||||
|
|
||||||
|
@Suppress("ClassName", "unused")
|
||||||
|
class M0016_ChapterIndexRenameToSourceOrder : SQLMigration() {
|
||||||
|
override val sql = """
|
||||||
|
ALTER TABLE CHAPTER ALTER COLUMN INDEX RENAME TO SOURCE_ORDER;
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package suwayomi.tachidesk.server.database.migration
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import de.neonew.exposed.migrations.helpers.AddColumnMigration
|
||||||
|
|
||||||
|
@Suppress("ClassName", "unused")
|
||||||
|
class M0017_ChapterFetchedAt : AddColumnMigration(
|
||||||
|
"Chapter",
|
||||||
|
"fetched_at",
|
||||||
|
"BIGINT",
|
||||||
|
"0"
|
||||||
|
)
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package suwayomi.tachidesk.server.database.migration
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import de.neonew.exposed.migrations.helpers.AddColumnMigration
|
||||||
|
|
||||||
|
@Suppress("ClassName", "unused")
|
||||||
|
class M0018_MangaInLibraryAt : AddColumnMigration(
|
||||||
|
"Manga",
|
||||||
|
"in_library_at",
|
||||||
|
"BIGINT",
|
||||||
|
"0"
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user