add kotlinter

This commit is contained in:
Aria Moradi
2021-01-22 12:34:03 +03:30
parent b854fdeadb
commit 9a61f58043
38 changed files with 262 additions and 290 deletions

View File

@@ -3,6 +3,6 @@ package ir.armor.tachidesk
import net.harawata.appdirs.AppDirsFactory
object Config {
val dataRoot = AppDirsFactory.getInstance().getUserDataDir("Tachidesk",null, null)
val dataRoot = AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null)
val extensionsRoot = "$dataRoot/extensions"
}
}

View File

@@ -2,7 +2,17 @@ package ir.armor.tachidesk
import eu.kanade.tachiyomi.App
import io.javalin.Javalin
import ir.armor.tachidesk.util.*
import ir.armor.tachidesk.util.applicationSetup
import ir.armor.tachidesk.util.getChapterList
import ir.armor.tachidesk.util.getExtensionList
import ir.armor.tachidesk.util.getManga
import ir.armor.tachidesk.util.getMangaList
import ir.armor.tachidesk.util.getPages
import ir.armor.tachidesk.util.getSourceList
import ir.armor.tachidesk.util.installAPK
import ir.armor.tachidesk.util.sourceFilters
import ir.armor.tachidesk.util.sourceGlobalSearch
import ir.armor.tachidesk.util.sourceSearch
import org.kodein.di.DI
import org.kodein.di.conf.global
import xyz.nulldev.androidcompat.AndroidCompat
@@ -28,26 +38,23 @@ class Main {
registerConfigModules()
//Load config API
// Load config API
DI.global.addImport(ConfigKodeinModule().create())
//Load Android compatibility dependencies
// Load Android compatibility dependencies
AndroidCompatInitializer().init()
// start app
androidCompat.startApp(App())
val app = Javalin.create { config ->
try {
this::class.java.classLoader.getResource("/react/index.html")
config.addStaticFiles("/react")
config.addSinglePageRoot("/","/react/index.html")
config.addSinglePageRoot("/", "/react/index.html")
} catch (e: RuntimeException) {
println("Warning: react build files are missing.")
}
}.start(4567)
app.before() { ctx ->
// allow the client which is running on another port
ctx.header("Access-Control-Allow-Origin", "*")
@@ -57,12 +64,11 @@ class Main {
ctx.json(getExtensionList())
}
app.get("/api/v1/extension/install/:apkName") { ctx ->
val apkName = ctx.pathParam("apkName")
println(apkName)
ctx.status(
installAPK(apkName)
installAPK(apkName)
)
}
app.get("/api/v1/source/list") { ctx ->
@@ -114,11 +120,6 @@ class Main {
val sourceId = ctx.pathParam("sourceId").toLong()
ctx.json(sourceFilters(sourceId))
}
}
}
}

View File

@@ -25,4 +25,4 @@ fun makeDataBaseTables() {
SchemaUtils.create(MangaTable)
SchemaUtils.create(ChapterTable)
}
}
}

View File

@@ -1,11 +1,11 @@
package ir.armor.tachidesk.database.dataclass
data class ChapterDataClass(
val id: Int,
val url: String,
val name: String,
val date_upload: Long,
val chapter_number: Float,
val scanlator: String?,
val mangaId: Int,
)
val id: Int,
val url: String,
val name: String,
val date_upload: Long,
val chapter_number: Float,
val scanlator: String?,
val mangaId: Int,
)

View File

@@ -1,14 +1,14 @@
package ir.armor.tachidesk.database.dataclass
data class ExtensionDataClass(
val name: String,
val pkgName: String,
val versionName: String,
val versionCode: Int,
val lang: String,
val isNsfw: Boolean,
val apkName: String,
val iconUrl: String,
val installed: Boolean,
val classFQName: String,
)
val name: String,
val pkgName: String,
val versionName: String,
val versionCode: Int,
val lang: String,
val isNsfw: Boolean,
val apkName: String,
val iconUrl: String,
val installed: Boolean,
val classFQName: String,
)

View File

@@ -3,18 +3,18 @@ package ir.armor.tachidesk.database.dataclass
import ir.armor.tachidesk.database.table.MangaStatus
data class MangaDataClass(
val id: Int,
val sourceId: Long,
val id: Int,
val sourceId: Long,
val url: String,
val title: String,
val thumbnail_url: String? = null,
val url: String,
val title: String,
val thumbnail_url: String? = null,
val initialized: Boolean = false,
val initialized: Boolean = false,
val artist: String? = null,
val author: String? = null,
val description: String? = null,
val genre: String? = null,
val status: String = MangaStatus.UNKNOWN.name
)
val artist: String? = null,
val author: String? = null,
val description: String? = null,
val genre: String? = null,
val status: String = MangaStatus.UNKNOWN.name
)

View File

@@ -1,6 +1,6 @@
package ir.armor.tachidesk.database.dataclass
data class PageDataClass(
val index: Int,
var imageUrl: String,
)
val index: Int,
var imageUrl: String,
)

View File

@@ -1,9 +1,9 @@
package ir.armor.tachidesk.database.dataclass
data class SourceDataClass(
val id: String,
val name: String,
val lang: String,
val iconUrl: String,
val supportsLatest: Boolean
)
val id: String,
val name: String,
val lang: String,
val iconUrl: String,
val supportsLatest: Boolean
)

View File

@@ -18,4 +18,4 @@ class ExtensionEntity(id: EntityID<Int>) : IntEntity(id) {
var iconUrl by ExtensionsTable.iconUrl
var installed by ExtensionsTable.installed
var classFQName by ExtensionsTable.classFQName
}
}

View File

@@ -20,4 +20,4 @@ class MangaEntity(id: EntityID<Int>) : IntEntity(id) {
var thumbnail_url by MangaTable.thumbnail_url
var sourceReference by MangaEntity referencedOn MangaTable.sourceReference
}
}

View File

@@ -1,7 +1,8 @@
package ir.armor.tachidesk.database.entity
import ir.armor.tachidesk.database.table.SourceTable
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.EntityClass
import org.jetbrains.exposed.dao.LongEntity
import org.jetbrains.exposed.dao.id.EntityID
class SourceEntity(id: EntityID<Long>) : LongEntity(id) {
@@ -13,4 +14,4 @@ class SourceEntity(id: EntityID<Long>) : LongEntity(id) {
var extension by ExtensionEntity referencedOn SourceTable.extension
var partOfFactorySource by SourceTable.partOfFactorySource
var positionInFactorySource by SourceTable.positionInFactorySource
}
}

View File

@@ -1,6 +1,5 @@
package ir.armor.tachidesk.database.table
import eu.kanade.tachiyomi.source.model.SManga
import org.jetbrains.exposed.dao.id.IntIdTable
object ChapterTable : IntIdTable() {
@@ -8,7 +7,7 @@ object ChapterTable : IntIdTable() {
val name = varchar("name", 512)
val date_upload = long("date_upload").default(0)
val chapter_number = float("chapter_number").default(-1f)
val scanlator = varchar("scanlator",128).nullable()
val scanlator = varchar("scanlator", 128).nullable()
val manga = reference("manga", MangaTable)
}
}

View File

@@ -2,7 +2,6 @@ package ir.armor.tachidesk.database.table
import org.jetbrains.exposed.dao.id.IntIdTable
object ExtensionsTable : IntIdTable() {
val name = varchar("name", 128)
val pkgName = varchar("pkg_name", 128)
@@ -15,4 +14,4 @@ object ExtensionsTable : IntIdTable() {
val installed = bool("installed").default(false)
val classFQName = varchar("class_name", 256).default("") // fully qualified name
}
}

View File

@@ -30,4 +30,4 @@ enum class MangaStatus(val status: Int) {
companion object {
fun valueOf(value: Int): MangaStatus = values().find { it.status == value } ?: UNKNOWN
}
}
}

View File

@@ -4,9 +4,9 @@ import org.jetbrains.exposed.dao.id.IdTable
object SourceTable : IdTable<Long>() {
override val id = long("id").entityId()
val name= varchar("name", 128)
val name = varchar("name", 128)
val lang = varchar("lang", 10)
val extension = reference("extension", ExtensionsTable)
val partOfFactorySource = bool("part_of_factory_source").default(false)
val positionInFactorySource = integer("position_in_factory_source").nullable()
}
}

View File

@@ -41,7 +41,6 @@ fun installAPK(apkName: String): Int {
// download apk file
downloadAPKFile(apkToDownload, apkFilePath)
val className: String = APKExtractor.extract_dex_and_read_className(apkFilePath, dexFilePath)
println(className)
// dex -> jar
@@ -60,7 +59,7 @@ fun installAPK(apkName: String): Int {
return@transaction ExtensionsTable.select { ExtensionsTable.name eq extensionRecord.name }.first()[ExtensionsTable.id]
}
if (instance is HttpSource) {// single source
if (instance is HttpSource) { // single source
val httpSource = instance as HttpSource
transaction {
// SourceEntity.new {
@@ -80,7 +79,6 @@ fun installAPK(apkName: String): Int {
// println(httpSource.name)
// println()
}
} else { // multi source
val sourceFactory = instance as SourceFactory
transaction {
@@ -110,7 +108,6 @@ fun installAPK(apkName: String): Int {
it[classFQName] = className
}
}
}
return 201 // we downloaded successfully
} else {
@@ -122,7 +119,7 @@ val networkHelper: NetworkHelper by injectLazy()
private fun downloadAPKFile(url: String, apkPath: String) {
val request = Request.Builder().url(url).build()
val response = networkHelper.client.newCall(request).execute()
val response = networkHelper.client.newCall(request).execute()
val downloadedFile = File(apkPath)
val sink = downloadedFile.sink().buffer()

View File

@@ -6,23 +6,21 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import ir.armor.tachidesk.database.dataclass.ChapterDataClass
import ir.armor.tachidesk.database.dataclass.PageDataClass
import ir.armor.tachidesk.database.entity.MangaEntity
import ir.armor.tachidesk.database.table.ChapterTable
import ir.armor.tachidesk.database.table.MangaTable
import org.jetbrains.exposed.sql.insertAndGetId
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
fun getChapterList(mangaId: Int): List<ChapterDataClass> {
val mangaDetails = getManga(mangaId)
val source = getHttpSource(mangaDetails.sourceId)
val chapterList = source.fetchChapterList(
SManga.create().apply {
title = mangaDetails.title
url = mangaDetails.url
}
SManga.create().apply {
title = mangaDetails.title
url = mangaDetails.url
}
).toBlocking().first()
return transaction {
@@ -41,16 +39,15 @@ fun getChapterList(mangaId: Int): List<ChapterDataClass> {
}
}
return@transaction chapterList.map {
ChapterDataClass(
ChapterTable.select { ChapterTable.url eq it.url }.firstOrNull()!![ChapterTable.id].value,
it.url,
it.name,
it.date_upload,
it.chapter_number,
it.scanlator,
mangaId
ChapterTable.select { ChapterTable.url eq it.url }.firstOrNull()!![ChapterTable.id].value,
it.url,
it.name,
it.date_upload,
it.chapter_number,
it.scanlator,
mangaId
)
}
}
@@ -64,24 +61,23 @@ fun getPages(chapterId: Int, mangaId: Int): List<PageDataClass> {
val source = getHttpSource(mangaEntry[MangaTable.sourceReference].value)
val pagesList = source.fetchPageList(
SChapter.create().apply {
url = chapterEntry[ChapterTable.url]
name = chapterEntry[ChapterTable.name]
}
SChapter.create().apply {
url = chapterEntry[ChapterTable.url]
name = chapterEntry[ChapterTable.name]
}
).toBlocking().first()
return@transaction pagesList.map {
PageDataClass(
it.index,
getTrueImageUrl(it,source)
it.index,
getTrueImageUrl(it, source)
)
}
}
}
fun getTrueImageUrl(page: Page, source: HttpSource): String {
return if ( page.imageUrl == null){
return if (page.imageUrl == null) {
source.fetchImageUrl(page).toBlocking().first()!!
} else page.imageUrl!!
}

View File

@@ -67,18 +67,17 @@ fun getExtensionList(offline: Boolean = false): List<ExtensionDataClass> {
return transaction {
return@transaction ExtensionsTable.selectAll().map {
ExtensionDataClass(
it[ExtensionsTable.name],
it[ExtensionsTable.pkgName],
it[ExtensionsTable.versionName],
it[ExtensionsTable.versionCode],
it[ExtensionsTable.lang],
it[ExtensionsTable.isNsfw],
it[ExtensionsTable.apkName],
it[ExtensionsTable.iconUrl],
it[ExtensionsTable.installed],
it[ExtensionsTable.classFQName]
it[ExtensionsTable.name],
it[ExtensionsTable.pkgName],
it[ExtensionsTable.versionName],
it[ExtensionsTable.versionCode],
it[ExtensionsTable.lang],
it[ExtensionsTable.isNsfw],
it[ExtensionsTable.apkName],
it[ExtensionsTable.iconUrl],
it[ExtensionsTable.installed],
it[ExtensionsTable.classFQName]
)
}
}
}

View File

@@ -14,28 +14,28 @@ fun getManga(mangaId: Int): MangaDataClass {
return@transaction if (mangaEntry[MangaTable.initialized]) {
MangaDataClass(
mangaId,
mangaEntry[MangaTable.sourceReference].value,
mangaId,
mangaEntry[MangaTable.sourceReference].value,
mangaEntry[MangaTable.url],
mangaEntry[MangaTable.title],
mangaEntry[MangaTable.thumbnail_url],
mangaEntry[MangaTable.url],
mangaEntry[MangaTable.title],
mangaEntry[MangaTable.thumbnail_url],
true,
true,
mangaEntry[MangaTable.artist],
mangaEntry[MangaTable.author],
mangaEntry[MangaTable.description],
mangaEntry[MangaTable.genre],
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
mangaEntry[MangaTable.artist],
mangaEntry[MangaTable.author],
mangaEntry[MangaTable.description],
mangaEntry[MangaTable.genre],
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
)
} else { // initialize manga
val source = getHttpSource(mangaEntry[MangaTable.sourceReference].value)
val fetchedManga = source.fetchMangaDetails(
SManga.create().apply {
url = mangaEntry[MangaTable.url]
title = mangaEntry[MangaTable.title]
}
SManga.create().apply {
url = mangaEntry[MangaTable.url]
title = mangaEntry[MangaTable.title]
}
).toBlocking().first()
// update database
@@ -56,23 +56,21 @@ fun getManga(mangaId: Int): MangaDataClass {
mangaEntry = MangaTable.select { MangaTable.id eq mangaId }.firstOrNull()!!
MangaDataClass(
mangaId,
mangaEntry[MangaTable.sourceReference].value,
mangaId,
mangaEntry[MangaTable.sourceReference].value,
mangaEntry[MangaTable.url],
mangaEntry[MangaTable.title],
mangaEntry[MangaTable.thumbnail_url],
mangaEntry[MangaTable.url],
mangaEntry[MangaTable.title],
mangaEntry[MangaTable.thumbnail_url],
true,
true,
mangaEntry[MangaTable.artist],
mangaEntry[MangaTable.author],
mangaEntry[MangaTable.description],
mangaEntry[MangaTable.genre],
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
mangaEntry[MangaTable.artist],
mangaEntry[MangaTable.author],
mangaEntry[MangaTable.description],
mangaEntry[MangaTable.genre],
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
)
}
}
}

View File

@@ -3,8 +3,6 @@ package ir.armor.tachidesk.util
import ir.armor.tachidesk.database.dataclass.MangaDataClass
import ir.armor.tachidesk.database.table.MangaStatus
import ir.armor.tachidesk.database.table.MangaTable
import ir.armor.tachidesk.database.table.SourceTable
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.insertAndGetId
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
@@ -41,21 +39,21 @@ fun getMangaList(sourceId: Long, pageNum: Int = 1, popular: Boolean): List<Manga
}
MangaDataClass(
mangaEntityId,
sourceId.toLong(),
mangaEntityId,
sourceId.toLong(),
manga.url,
manga.title,
manga.thumbnail_url,
manga.url,
manga.title,
manga.thumbnail_url,
manga.initialized,
manga.initialized,
manga.artist,
manga.author,
manga.description,
manga.genre,
MangaStatus.valueOf(manga.status).name,
manga.artist,
manga.author,
manga.description,
manga.genre,
MangaStatus.valueOf(manga.status).name,
)
}
}
}
}

View File

@@ -1,28 +1,24 @@
package ir.armor.tachidesk.util
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
fun sourceFilters(sourceId: Long) {
val source = getHttpSource(sourceId)
//source.getFilterList().toItems()
// source.getFilterList().toItems()
}
fun sourceSearch(sourceId: Long, searchTerm: String) {
val source = getHttpSource(sourceId)
//source.fetchSearchManga()
// source.fetchSearchManga()
}
fun sourceGlobalSearch(searchTerm: String) {
}
data class FilterWrapper(
val type: String,
val filter: Any
val type: String,
val filter: Any
)
//private fun FilterList.toItems(): List<FilterWrapper> {
// private fun FilterList.toItems(): List<FilterWrapper> {
// return mapNotNull { filter ->
// when (filter) {
// is Filter.Header -> FilterWrapper("Header",filter)
@@ -56,4 +52,4 @@ data class FilterWrapper(
// }
// }
// }
//}
// }

View File

@@ -13,7 +13,7 @@ import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import java.net.URL
import java.net.URLClassLoader
import java.util.*
import java.util.Locale
private val sourceCache = mutableListOf<Pair<Long, HttpSource>>()
private val extensionCache = mutableListOf<Pair<String, Any>>()
@@ -39,16 +39,16 @@ fun getHttpSource(sourceId: Long): HttpSource {
val cachedExtensionPair = extensionCache.firstOrNull { it.first == jarPath }
var usedCached = false
val instance =
if (cachedExtensionPair != null) {
usedCached = true
println("Used cached Extension")
cachedExtensionPair.second
} else {
println("No Extension cache")
val child = URLClassLoader(arrayOf<URL>(URL("file:$jarPath")), this::class.java.classLoader)
val classToLoad = Class.forName(className, true, child)
classToLoad.newInstance()
}
if (cachedExtensionPair != null) {
usedCached = true
println("Used cached Extension")
cachedExtensionPair.second
} else {
println("No Extension cache")
val child = URLClassLoader(arrayOf<URL>(URL("file:$jarPath")), this::class.java.classLoader)
val classToLoad = Class.forName(className, true, child)
classToLoad.newInstance()
}
if (sourceRecord.partOfFactorySource) {
return@transaction if (usedCached) {
(instance as List<HttpSource>)[sourceRecord.positionInFactorySource!!]
@@ -71,12 +71,12 @@ fun getSourceList(): List<SourceDataClass> {
return transaction {
return@transaction SourceTable.selectAll().map {
SourceDataClass(
it[SourceTable.id].value.toString(),
it[SourceTable.name],
Locale(it[SourceTable.lang]).getDisplayLanguage(Locale(it[SourceTable.lang])),
ExtensionsTable.select { ExtensionsTable.id eq it[SourceTable.extension] }.first()[ExtensionsTable.iconUrl],
getHttpSource(it[SourceTable.id].value).supportsLatest
it[SourceTable.id].value.toString(),
it[SourceTable.name],
Locale(it[SourceTable.lang]).getDisplayLanguage(Locale(it[SourceTable.lang])),
ExtensionsTable.select { ExtensionsTable.id eq it[SourceTable.extension] }.first()[ExtensionsTable.iconUrl],
getHttpSource(it[SourceTable.id].value).supportsLatest
)
}
}
}
}

View File

@@ -9,6 +9,5 @@ fun applicationSetup() {
File(Config.dataRoot).mkdirs()
File(Config.extensionsRoot).mkdirs()
makeDataBaseTables()
}
}