mirror of
https://github.com/tachiyomiorg/tachiyomi-extensions-inspector.git
synced 2024-12-25 08:11:50 +01:00
refactor Main
This commit is contained in:
parent
bb94e638cb
commit
5597ad66b4
@ -14,7 +14,7 @@ import java.util.zip.ZipEntry;
|
|||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
class APKExtractor {
|
public class APKExtractor {
|
||||||
// decompressXML -- Parse the 'compressed' binary form of Android XML docs
|
// decompressXML -- Parse the 'compressed' binary form of Android XML docs
|
||||||
// such as for AndroidManifest.xml in .apk files
|
// such as for AndroidManifest.xml in .apk files
|
||||||
public static int endDocTag = 0x00100101;
|
public static int endDocTag = 0x00100101;
|
||||||
|
@ -1,293 +1,17 @@
|
|||||||
package ir.armor.tachidesk
|
package ir.armor.tachidesk
|
||||||
|
|
||||||
import com.googlecode.dex2jar.tools.Dex2jarCmd
|
|
||||||
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
|
||||||
import eu.kanade.tachiyomi.source.SourceFactory
|
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
|
||||||
import io.javalin.Javalin
|
import io.javalin.Javalin
|
||||||
import ir.armor.tachidesk.database.dataclass.ExtensionDataClass
|
import ir.armor.tachidesk.util.applicationSetup
|
||||||
import ir.armor.tachidesk.database.dataclass.SourceDataClass
|
import ir.armor.tachidesk.util.installAPK
|
||||||
import ir.armor.tachidesk.database.entity.ExtensionEntity
|
import ir.armor.tachidesk.util.getExtensionList
|
||||||
import ir.armor.tachidesk.database.entity.SourceEntity
|
import ir.armor.tachidesk.util.getSourceList
|
||||||
import ir.armor.tachidesk.database.makeDataBaseTables
|
|
||||||
import ir.armor.tachidesk.database.table.*
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import okhttp3.Request
|
|
||||||
import okio.buffer
|
|
||||||
import okio.sink
|
|
||||||
import org.jetbrains.exposed.sql.*
|
|
||||||
import java.io.File
|
|
||||||
import java.net.URL
|
|
||||||
import java.net.URLClassLoader
|
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
|
||||||
|
|
||||||
class Main {
|
class Main {
|
||||||
companion object {
|
companion object {
|
||||||
var lastExtensionCheck: Long = 0
|
|
||||||
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun downloadAPKFile(url: String, apkPath: String) {
|
|
||||||
val request = Request.Builder().url(url).build()
|
|
||||||
val response = NetworkHelper().client.newCall(request).execute();
|
|
||||||
|
|
||||||
val downloadedFile = File(apkPath)
|
|
||||||
val sink = downloadedFile.sink().buffer()
|
|
||||||
sink.writeAll(response.body!!.source())
|
|
||||||
sink.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun testExtensionExecution() {
|
|
||||||
File(Config.extensionsRoot).mkdirs()
|
|
||||||
var sourcePkg = ""
|
|
||||||
|
|
||||||
// get list of extensions
|
|
||||||
var apkToDownload: String = ""
|
|
||||||
runBlocking {
|
|
||||||
val api = ExtensionGithubApi()
|
|
||||||
val source = api.findExtensions().first {
|
|
||||||
api.getApkUrl(it).endsWith("killsixbilliondemons-v1.2.3.apk")
|
|
||||||
}
|
|
||||||
apkToDownload = api.getApkUrl(source)
|
|
||||||
sourcePkg = source.pkgName
|
|
||||||
}
|
|
||||||
|
|
||||||
val apkFileName = apkToDownload.split("/").last()
|
|
||||||
val apkFilePath = "${Config.extensionsRoot}/$apkFileName"
|
|
||||||
val zipDirPath = apkFilePath.substringBefore(".apk")
|
|
||||||
val jarFilePath = "$zipDirPath.jar"
|
|
||||||
val dexFilePath = "$zipDirPath.dex"
|
|
||||||
|
|
||||||
// download apk file
|
|
||||||
downloadAPKFile(apkToDownload, apkFilePath)
|
|
||||||
|
|
||||||
|
|
||||||
val className = APKExtractor.extract_dex_and_read_className(apkFilePath, dexFilePath)
|
|
||||||
// dex -> jar
|
|
||||||
Dex2jarCmd.main(dexFilePath, "-o", jarFilePath, "--force")
|
|
||||||
|
|
||||||
val child = URLClassLoader(arrayOf<URL>(URL("file:$jarFilePath")), this::class.java.classLoader)
|
|
||||||
val classToLoad = Class.forName(className, true, child)
|
|
||||||
val instance = classToLoad.newInstance() as HttpSource
|
|
||||||
val result = instance.fetchPopularManga(1)
|
|
||||||
val mangasPage = result.toBlocking().first() as MangasPage
|
|
||||||
mangasPage.mangas.forEach {
|
|
||||||
println(it.title)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun extensionDatabaseIsEmtpy(): Boolean {
|
|
||||||
return transaction {
|
|
||||||
return@transaction ExtensionsTable.selectAll().count() == 0L
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getExtensionList(offline: Boolean = false): List<ExtensionDataClass> {
|
|
||||||
// update if 60 seconds has passed or requested offline and database is empty
|
|
||||||
if (lastExtensionCheck + 60 * 1000 < System.currentTimeMillis() || (offline && extensionDatabaseIsEmtpy())) {
|
|
||||||
println("Getting extensions list from the internet")
|
|
||||||
lastExtensionCheck = System.currentTimeMillis()
|
|
||||||
var foundExtensions: List<Extension.Available>
|
|
||||||
runBlocking {
|
|
||||||
val api = ExtensionGithubApi()
|
|
||||||
foundExtensions = api.findExtensions()
|
|
||||||
transaction {
|
|
||||||
foundExtensions.forEach { foundExtension ->
|
|
||||||
val extensionRecord = ExtensionsTable.select { ExtensionsTable.name eq foundExtension.name }.firstOrNull()
|
|
||||||
if (extensionRecord != null) {
|
|
||||||
// update the record
|
|
||||||
ExtensionsTable.update({ ExtensionsTable.name eq foundExtension.name }) {
|
|
||||||
it[name] = foundExtension.name
|
|
||||||
it[pkgName] = foundExtension.pkgName
|
|
||||||
it[versionName] = foundExtension.versionName
|
|
||||||
it[versionCode] = foundExtension.versionCode
|
|
||||||
it[lang] = foundExtension.lang
|
|
||||||
it[isNsfw] = foundExtension.isNsfw
|
|
||||||
it[apkName] = foundExtension.apkName
|
|
||||||
it[iconUrl] = foundExtension.iconUrl
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// insert new record
|
|
||||||
ExtensionsTable.insert {
|
|
||||||
it[name] = foundExtension.name
|
|
||||||
it[pkgName] = foundExtension.pkgName
|
|
||||||
it[versionName] = foundExtension.versionName
|
|
||||||
it[versionCode] = foundExtension.versionCode
|
|
||||||
it[lang] = foundExtension.lang
|
|
||||||
it[isNsfw] = foundExtension.isNsfw
|
|
||||||
it[apkName] = foundExtension.apkName
|
|
||||||
it[iconUrl] = foundExtension.iconUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun downloadApk(apkName: String): Int {
|
|
||||||
val extensionRecord = getExtensionList(true).first { it.apkName == apkName }
|
|
||||||
val fileNameWithoutType = apkName.substringBefore(".apk")
|
|
||||||
val dirPathWithoutType = "${Config.extensionsRoot}/$fileNameWithoutType"
|
|
||||||
|
|
||||||
// check if we don't have the dex file already downloaded
|
|
||||||
val dexPath = "${Config.extensionsRoot}/$fileNameWithoutType.jar"
|
|
||||||
if (!File(dexPath).exists()) {
|
|
||||||
runBlocking {
|
|
||||||
val api = ExtensionGithubApi()
|
|
||||||
val apkToDownload = api.getApkUrl(extensionRecord)
|
|
||||||
|
|
||||||
val apkFilePath = "$dirPathWithoutType.apk"
|
|
||||||
val jarFilePath = "$dirPathWithoutType.jar"
|
|
||||||
val dexFilePath = "$dirPathWithoutType.dex"
|
|
||||||
|
|
||||||
// download apk file
|
|
||||||
downloadAPKFile(apkToDownload, apkFilePath)
|
|
||||||
|
|
||||||
|
|
||||||
val className: String = APKExtractor.extract_dex_and_read_className(apkFilePath, dexFilePath)
|
|
||||||
println(className)
|
|
||||||
// dex -> jar
|
|
||||||
Dex2jarCmd.main(dexFilePath, "-o", jarFilePath, "--force")
|
|
||||||
|
|
||||||
// clean up
|
|
||||||
File(apkFilePath).delete()
|
|
||||||
File(dexFilePath).delete()
|
|
||||||
|
|
||||||
// update sources of the extension
|
|
||||||
val child = URLClassLoader(arrayOf<URL>(URL("file:$jarFilePath")), this::class.java.classLoader)
|
|
||||||
val classToLoad = Class.forName(className, true, child)
|
|
||||||
val instance = classToLoad.newInstance()
|
|
||||||
|
|
||||||
val extensionId = transaction {
|
|
||||||
return@transaction ExtensionsTable.select { ExtensionsTable.name eq extensionRecord.name }.first()[ExtensionsTable.id]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (instance is HttpSource) {// single source
|
|
||||||
val httpSource = instance as HttpSource
|
|
||||||
transaction {
|
|
||||||
// SourceEntity.new {
|
|
||||||
// sourceId = httpSource.id
|
|
||||||
// name = httpSource.name
|
|
||||||
// this.extension = ExtensionEntity.find { ExtensionsTable.name eq extension.name }.first().id
|
|
||||||
// }
|
|
||||||
if (SourcesTable.select { SourcesTable.sourceId eq httpSource.id }.count() == 0L) {
|
|
||||||
SourcesTable.insert {
|
|
||||||
it[this.sourceId] = httpSource.id
|
|
||||||
it[name] = httpSource.name
|
|
||||||
it[this.lang] = httpSource.lang
|
|
||||||
it[extension] = extensionId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// println(httpSource.id)
|
|
||||||
// println(httpSource.name)
|
|
||||||
// println()
|
|
||||||
}
|
|
||||||
|
|
||||||
} else { // multi source
|
|
||||||
val sourceFactory = instance as SourceFactory
|
|
||||||
transaction {
|
|
||||||
sourceFactory.createSources().forEachIndexed { index, source ->
|
|
||||||
val httpSource = source as HttpSource
|
|
||||||
if (SourcesTable.select { SourcesTable.sourceId eq httpSource.id }.count() == 0L) {
|
|
||||||
SourcesTable.insert {
|
|
||||||
it[this.sourceId] = httpSource.id
|
|
||||||
it[name] = httpSource.name
|
|
||||||
it[this.lang] = httpSource.lang
|
|
||||||
it[extension] = extensionId
|
|
||||||
it[partOfFactorySource] = true
|
|
||||||
it[positionInFactorySource] = index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// println(httpSource.id)
|
|
||||||
// println(httpSource.name)
|
|
||||||
// println()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update extension info
|
|
||||||
transaction {
|
|
||||||
ExtensionsTable.update({ ExtensionsTable.name eq extensionRecord.name }) {
|
|
||||||
it[installed] = true
|
|
||||||
it[classFQName] = className
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return 201 // we downloaded successfully
|
|
||||||
} else {
|
|
||||||
return 302
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getHttpSource(sourceId: Long): HttpSource {
|
|
||||||
return transaction {
|
|
||||||
val sourceRecord = SourceEntity.find { SourcesTable.sourceId eq sourceId }.first()
|
|
||||||
val extensionId = sourceRecord.extension.id.value
|
|
||||||
val extensionRecord = ExtensionEntity.get(extensionId)
|
|
||||||
val apkName = extensionRecord.apkName
|
|
||||||
val className = extensionRecord.classFQName
|
|
||||||
val jarName = apkName.substringBefore(".apk") + ".jar"
|
|
||||||
val jarPath = "${Config.extensionsRoot}/$jarName"
|
|
||||||
|
|
||||||
println(jarPath)
|
|
||||||
|
|
||||||
val child = URLClassLoader(arrayOf<URL>(URL("file:$jarPath")), this::class.java.classLoader)
|
|
||||||
val classToLoad = Class.forName(className, true, child)
|
|
||||||
val instance = classToLoad.newInstance()
|
|
||||||
|
|
||||||
if (sourceRecord.partOfFactorySource) {
|
|
||||||
return@transaction (instance as SourceFactory).createSources()[sourceRecord.positionInFactorySource!!] as HttpSource
|
|
||||||
} else {
|
|
||||||
return@transaction instance as HttpSource
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSourceList(): List<SourceDataClass> {
|
|
||||||
return transaction {
|
|
||||||
return@transaction SourcesTable.selectAll().map {
|
|
||||||
SourceDataClass(
|
|
||||||
it[SourcesTable.sourceId],
|
|
||||||
it[SourcesTable.name],
|
|
||||||
it[SourcesTable.lang],
|
|
||||||
ExtensionsTable.select { ExtensionsTable.id eq it[SourcesTable.extension] }.first()[ExtensionsTable.iconUrl],
|
|
||||||
getHttpSource(it[SourcesTable.sourceId]).supportsLatest
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
// make sure everything we need exists
|
// make sure everything we need exists
|
||||||
File(Config.dataRoot).mkdirs()
|
applicationSetup()
|
||||||
File(Config.extensionsRoot).mkdirs()
|
|
||||||
makeDataBaseTables()
|
|
||||||
|
|
||||||
|
|
||||||
val app = Javalin.create().start(4567)
|
val app = Javalin.create().start(4567)
|
||||||
@ -305,14 +29,12 @@ class Main {
|
|||||||
val apkName = ctx.pathParam("apkName")
|
val apkName = ctx.pathParam("apkName")
|
||||||
println(apkName)
|
println(apkName)
|
||||||
ctx.status(
|
ctx.status(
|
||||||
downloadApk(apkName)
|
installAPK(apkName)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
app.get("/api/v1/sources/") { ctx ->
|
app.get("/api/v1/sources/") { ctx ->
|
||||||
ctx.json(getSourceList())
|
ctx.json(getSourceList())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
129
server/src/main/kotlin/ir/armor/tachidesk/util/APK.kt
Normal file
129
server/src/main/kotlin/ir/armor/tachidesk/util/APK.kt
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
|
import com.googlecode.dex2jar.tools.Dex2jarCmd
|
||||||
|
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
||||||
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
|
import eu.kanade.tachiyomi.source.SourceFactory
|
||||||
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import ir.armor.tachidesk.APKExtractor
|
||||||
|
import ir.armor.tachidesk.Config
|
||||||
|
import ir.armor.tachidesk.database.table.ExtensionsTable
|
||||||
|
import ir.armor.tachidesk.database.table.SourcesTable
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import okhttp3.Request
|
||||||
|
import okio.buffer
|
||||||
|
import okio.sink
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
import org.jetbrains.exposed.sql.insert
|
||||||
|
import org.jetbrains.exposed.sql.select
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import org.jetbrains.exposed.sql.update
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URL
|
||||||
|
import java.net.URLClassLoader
|
||||||
|
|
||||||
|
fun installAPK(apkName: String): Int {
|
||||||
|
val extensionRecord = getExtensionList(true).first { it.apkName == apkName }
|
||||||
|
val fileNameWithoutType = apkName.substringBefore(".apk")
|
||||||
|
val dirPathWithoutType = "${Config.extensionsRoot}/$fileNameWithoutType"
|
||||||
|
|
||||||
|
// check if we don't have the dex file already downloaded
|
||||||
|
val dexPath = "${Config.extensionsRoot}/$fileNameWithoutType.jar"
|
||||||
|
if (!File(dexPath).exists()) {
|
||||||
|
runBlocking {
|
||||||
|
val api = ExtensionGithubApi()
|
||||||
|
val apkToDownload = api.getApkUrl(extensionRecord)
|
||||||
|
|
||||||
|
val apkFilePath = "$dirPathWithoutType.apk"
|
||||||
|
val jarFilePath = "$dirPathWithoutType.jar"
|
||||||
|
val dexFilePath = "$dirPathWithoutType.dex"
|
||||||
|
|
||||||
|
// download apk file
|
||||||
|
downloadAPKFile(apkToDownload, apkFilePath)
|
||||||
|
|
||||||
|
|
||||||
|
val className: String = APKExtractor.extract_dex_and_read_className(apkFilePath, dexFilePath)
|
||||||
|
println(className)
|
||||||
|
// dex -> jar
|
||||||
|
Dex2jarCmd.main(dexFilePath, "-o", jarFilePath, "--force")
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
File(apkFilePath).delete()
|
||||||
|
File(dexFilePath).delete()
|
||||||
|
|
||||||
|
// update sources of the extension
|
||||||
|
val child = URLClassLoader(arrayOf<URL>(URL("file:$jarFilePath")), this::class.java.classLoader)
|
||||||
|
val classToLoad = Class.forName(className, true, child)
|
||||||
|
val instance = classToLoad.newInstance()
|
||||||
|
|
||||||
|
val extensionId = transaction {
|
||||||
|
return@transaction ExtensionsTable.select { ExtensionsTable.name eq extensionRecord.name }.first()[ExtensionsTable.id]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance is HttpSource) {// single source
|
||||||
|
val httpSource = instance as HttpSource
|
||||||
|
transaction {
|
||||||
|
// SourceEntity.new {
|
||||||
|
// sourceId = httpSource.id
|
||||||
|
// name = httpSource.name
|
||||||
|
// this.extension = ExtensionEntity.find { ExtensionsTable.name eq extension.name }.first().id
|
||||||
|
// }
|
||||||
|
if (SourcesTable.select { SourcesTable.sourceId eq httpSource.id }.count() == 0L) {
|
||||||
|
SourcesTable.insert {
|
||||||
|
it[this.sourceId] = httpSource.id
|
||||||
|
it[name] = httpSource.name
|
||||||
|
it[this.lang] = httpSource.lang
|
||||||
|
it[extension] = extensionId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// println(httpSource.id)
|
||||||
|
// println(httpSource.name)
|
||||||
|
// println()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else { // multi source
|
||||||
|
val sourceFactory = instance as SourceFactory
|
||||||
|
transaction {
|
||||||
|
sourceFactory.createSources().forEachIndexed { index, source ->
|
||||||
|
val httpSource = source as HttpSource
|
||||||
|
if (SourcesTable.select { SourcesTable.sourceId eq httpSource.id }.count() == 0L) {
|
||||||
|
SourcesTable.insert {
|
||||||
|
it[this.sourceId] = httpSource.id
|
||||||
|
it[name] = httpSource.name
|
||||||
|
it[this.lang] = httpSource.lang
|
||||||
|
it[extension] = extensionId
|
||||||
|
it[partOfFactorySource] = true
|
||||||
|
it[positionInFactorySource] = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// println(httpSource.id)
|
||||||
|
// println(httpSource.name)
|
||||||
|
// println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update extension info
|
||||||
|
transaction {
|
||||||
|
ExtensionsTable.update({ ExtensionsTable.name eq extensionRecord.name }) {
|
||||||
|
it[installed] = true
|
||||||
|
it[classFQName] = className
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return 201 // we downloaded successfully
|
||||||
|
} else {
|
||||||
|
return 302
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun downloadAPKFile(url: String, apkPath: String) {
|
||||||
|
val request = Request.Builder().url(url).build()
|
||||||
|
val response = NetworkHelper().client.newCall(request).execute();
|
||||||
|
|
||||||
|
val downloadedFile = File(apkPath)
|
||||||
|
val sink = downloadedFile.sink().buffer()
|
||||||
|
sink.writeAll(response.body!!.source())
|
||||||
|
sink.close()
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
||||||
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
|
import ir.armor.tachidesk.database.dataclass.ExtensionDataClass
|
||||||
|
import ir.armor.tachidesk.database.table.ExtensionsTable
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
import org.jetbrains.exposed.sql.insert
|
||||||
|
import org.jetbrains.exposed.sql.select
|
||||||
|
import org.jetbrains.exposed.sql.selectAll
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import org.jetbrains.exposed.sql.update
|
||||||
|
|
||||||
|
private object Data {
|
||||||
|
var lastExtensionCheck: Long = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extensionDatabaseIsEmtpy(): Boolean {
|
||||||
|
return transaction {
|
||||||
|
return@transaction ExtensionsTable.selectAll().count() == 0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getExtensionList(offline: Boolean = false): List<ExtensionDataClass> {
|
||||||
|
// update if 60 seconds has passed or requested offline and database is empty
|
||||||
|
if (Data.lastExtensionCheck + 60 * 1000 < System.currentTimeMillis() || (offline && extensionDatabaseIsEmtpy())) {
|
||||||
|
println("Getting extensions list from the internet")
|
||||||
|
Data.lastExtensionCheck = System.currentTimeMillis()
|
||||||
|
var foundExtensions: List<Extension.Available>
|
||||||
|
runBlocking {
|
||||||
|
val api = ExtensionGithubApi()
|
||||||
|
foundExtensions = api.findExtensions()
|
||||||
|
transaction {
|
||||||
|
foundExtensions.forEach { foundExtension ->
|
||||||
|
val extensionRecord = ExtensionsTable.select { ExtensionsTable.name eq foundExtension.name }.firstOrNull()
|
||||||
|
if (extensionRecord != null) {
|
||||||
|
// update the record
|
||||||
|
ExtensionsTable.update({ ExtensionsTable.name eq foundExtension.name }) {
|
||||||
|
it[name] = foundExtension.name
|
||||||
|
it[pkgName] = foundExtension.pkgName
|
||||||
|
it[versionName] = foundExtension.versionName
|
||||||
|
it[versionCode] = foundExtension.versionCode
|
||||||
|
it[lang] = foundExtension.lang
|
||||||
|
it[isNsfw] = foundExtension.isNsfw
|
||||||
|
it[apkName] = foundExtension.apkName
|
||||||
|
it[iconUrl] = foundExtension.iconUrl
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// insert new record
|
||||||
|
ExtensionsTable.insert {
|
||||||
|
it[name] = foundExtension.name
|
||||||
|
it[pkgName] = foundExtension.pkgName
|
||||||
|
it[versionName] = foundExtension.versionName
|
||||||
|
it[versionCode] = foundExtension.versionCode
|
||||||
|
it[lang] = foundExtension.lang
|
||||||
|
it[isNsfw] = foundExtension.isNsfw
|
||||||
|
it[apkName] = foundExtension.apkName
|
||||||
|
it[iconUrl] = foundExtension.iconUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
55
server/src/main/kotlin/ir/armor/tachidesk/util/SourceList.kt
Normal file
55
server/src/main/kotlin/ir/armor/tachidesk/util/SourceList.kt
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.SourceFactory
|
||||||
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import ir.armor.tachidesk.Config
|
||||||
|
import ir.armor.tachidesk.Main
|
||||||
|
import ir.armor.tachidesk.database.dataclass.SourceDataClass
|
||||||
|
import ir.armor.tachidesk.database.entity.ExtensionEntity
|
||||||
|
import ir.armor.tachidesk.database.entity.SourceEntity
|
||||||
|
import ir.armor.tachidesk.database.table.ExtensionsTable
|
||||||
|
import ir.armor.tachidesk.database.table.SourcesTable
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
import org.jetbrains.exposed.sql.select
|
||||||
|
import org.jetbrains.exposed.sql.selectAll
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import java.net.URL
|
||||||
|
import java.net.URLClassLoader
|
||||||
|
|
||||||
|
fun getHttpSource(sourceId: Long): HttpSource {
|
||||||
|
return transaction {
|
||||||
|
val sourceRecord = SourceEntity.find { SourcesTable.sourceId eq sourceId }.first()
|
||||||
|
val extensionId = sourceRecord.extension.id.value
|
||||||
|
val extensionRecord = ExtensionEntity.get(extensionId)
|
||||||
|
val apkName = extensionRecord.apkName
|
||||||
|
val className = extensionRecord.classFQName
|
||||||
|
val jarName = apkName.substringBefore(".apk") + ".jar"
|
||||||
|
val jarPath = "${Config.extensionsRoot}/$jarName"
|
||||||
|
|
||||||
|
println(jarPath)
|
||||||
|
|
||||||
|
val child = URLClassLoader(arrayOf<URL>(URL("file:$jarPath")), this::class.java.classLoader)
|
||||||
|
val classToLoad = Class.forName(className, true, child)
|
||||||
|
val instance = classToLoad.newInstance()
|
||||||
|
|
||||||
|
if (sourceRecord.partOfFactorySource) {
|
||||||
|
return@transaction (instance as SourceFactory).createSources()[sourceRecord.positionInFactorySource!!] as HttpSource
|
||||||
|
} else {
|
||||||
|
return@transaction instance as HttpSource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSourceList(): List<SourceDataClass> {
|
||||||
|
return transaction {
|
||||||
|
return@transaction SourcesTable.selectAll().map {
|
||||||
|
SourceDataClass(
|
||||||
|
it[SourcesTable.sourceId],
|
||||||
|
it[SourcesTable.name],
|
||||||
|
it[SourcesTable.lang],
|
||||||
|
ExtensionsTable.select { ExtensionsTable.id eq it[SourcesTable.extension] }.first()[ExtensionsTable.iconUrl],
|
||||||
|
getHttpSource(it[SourcesTable.sourceId]).supportsLatest
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
server/src/main/kotlin/ir/armor/tachidesk/util/Util.kt
Normal file
14
server/src/main/kotlin/ir/armor/tachidesk/util/Util.kt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package ir.armor.tachidesk.util
|
||||||
|
|
||||||
|
import ir.armor.tachidesk.Config
|
||||||
|
import ir.armor.tachidesk.database.makeDataBaseTables
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
fun applicationSetup() {
|
||||||
|
// make dirs we need
|
||||||
|
File(Config.dataRoot).mkdirs()
|
||||||
|
File(Config.extensionsRoot).mkdirs()
|
||||||
|
|
||||||
|
|
||||||
|
makeDataBaseTables()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user