apk installation

This commit is contained in:
Aria Moradi
2020-12-25 02:39:30 +03:30
parent c97be075cd
commit f16bead363
5 changed files with 162 additions and 51 deletions
@@ -7,6 +7,7 @@ import com.github.salomonbrys.kotson.int
import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.LoadResult
import eu.kanade.tachiyomi.extension.util.ExtensionLoader
import ir.armor.tachidesk.database.model.ExtensionDataClass
//import kotlinx.coroutines.Dispatchers
//import kotlinx.coroutines.withContext
import kotlinx.serialization.json.JsonArray
@@ -75,6 +76,10 @@ internal class ExtensionGithubApi {
return "$REPO_URL_PREFIX/apk/${extension.apkName}"
}
fun getApkUrl(extension: ExtensionDataClass): String {
return "$REPO_URL_PREFIX/apk/${extension.apkName}"
}
companion object {
const val BASE_URL = "https://raw.githubusercontent.com/"
const val REPO_URL_PREFIX = "${BASE_URL}inorichi/tachiyomi-extensions/repo"
@@ -4,4 +4,5 @@ import net.harawata.appdirs.AppDirsFactory
object Config {
val dataRoot = AppDirsFactory.getInstance().getUserDataDir("Tachidesk",null, null)
val extensionsRoot = "$dataRoot/extensions"
}
+138 -36
View File
@@ -2,32 +2,31 @@ 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.model.MangasPage
import eu.kanade.tachiyomi.source.online.HttpSource
import io.javalin.Javalin
import io.javalin.http.Context
import ir.armor.tachidesk.database.DBMangaer
import ir.armor.tachidesk.database.makeDataBaseTables
import ir.armor.tachidesk.database.model.ExtensionDataClass
import ir.armor.tachidesk.database.model.ExtensionsTable
import ir.armor.tachidesk.database.model.SourcesTable
import kotlinx.coroutines.runBlocking
import net.harawata.appdirs.AppDirsFactory
import okhttp3.Request
import okio.buffer
import okio.sink
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.*
import java.io.File
import java.net.URL
import java.net.URLClassLoader
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.transactions.transaction
class Main {
companion object {
var lastExtensionCheck: Long = 0
@JvmStatic
fun downloadAPK(url: String, apkPath: String){
fun downloadAPKFile(url: String, apkPath: String) {
val request = Request.Builder().url(url).build()
val response = NetworkHelper().client.newCall(request).execute();
@@ -38,9 +37,8 @@ class Main {
}
@JvmStatic
fun testExtensionExecution(){
val contentRoot = Config.dataRoot + "/extensions"
File(contentRoot).mkdirs()
fun testExtensionExecution() {
File(Config.extensionsRoot).mkdirs()
var sourcePkg = ""
// get list of extensions
@@ -55,13 +53,13 @@ class Main {
}
val apkFileName = apkToDownload.split("/").last()
val apkFilePath = "$contentRoot/$apkFileName"
val apkFilePath = "${Config.extensionsRoot}/$apkFileName"
val zipDirPath = apkFilePath.substringBefore(".apk")
val jarFilePath = "$zipDirPath.jar"
val dexFilePath = "$zipDirPath.dex"
// download apk file
downloadAPK(apkToDownload, apkFilePath)
downloadAPKFile(apkToDownload, apkFilePath)
val className = APKExtractor.extract_dex_and_read_className(apkFilePath, dexFilePath)
@@ -76,40 +74,144 @@ class Main {
mangasPage.mangas.forEach {
println(it.title)
}
// exitProcess(0)
}
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 extension = getExtensionList(true).first { it.apkName == apkName }
val fileNameWithoutType = apkName.substringBefore(".apk")
val dirPathWithoutType = "${Config.extensionsRoot}/$apkName"
// 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(extension)
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)
// dex -> jar
Dex2jarCmd.main(dexFilePath, "-o", jarFilePath, "--force")
File(apkFilePath).delete()
transaction {
ExtensionsTable.update({ ExtensionsTable.name eq extension.name }) {
it[installed] = true
it[classFQName] = className
}
}
}
return 201 // we downloaded successfully
}
else {
return 302
}
}
@JvmStatic
fun main(args: Array<String>) {
// make sure data everything we need exists
// make sure everything we need exists
File(Config.dataRoot).mkdirs()
File(Config.extensionsRoot).mkdirs()
makeDataBaseTables()
// val app = Javalin.create().start(4567)
//
// app.before() { ctx ->
// ctx.header("Access-Control-Allow-Origin", "*")
// }
//
// app.get("/api/v1/extensions") { ctx ->
// runBlocking {
// val api = ExtensionGithubApi()
// val sources = api.findExtensions()
// ctx.json(sources)
// }
// }
//
// app.get("/api/v1/extensions/install/:extensionURL") { ctx ->
// ctx.pathParam("extensionURL")
// })
val app = Javalin.create().start(4567)
app.before() { ctx ->
ctx.header("Access-Control-Allow-Origin", "*") // allow the client which is running on another port
}
app.get("/api/v1/extensions") { ctx ->
ctx.json(getExtensionList())
}
// ExtensionTable.new {
// name = "khar"
// pkgName = "eu.khar"
// }
app.get("/api/v1/extensions/install/:apkName") { ctx ->
val apkName = ctx.pathParam("apkName")
ctx.status(
downloadApk(apkName)
)
}
}
}
}
@@ -15,16 +15,23 @@ object ExtensionsTable : IntIdTable() {
val versionName = varchar("version_name", 16)
val versionCode = integer("version_code")
val lang = varchar("lang", 5)
val isNsfw = bool("is_nsfw")
val isNsfw = bool("is_nsfw")
val apkName = varchar("apk_name", 1024)
val iconUrl = varchar("icon_url", 2048)
val installed = bool("installed").default(false)
val classFQName = varchar("class_name", 256).default("") // fully qualified name
}
//class Extension(id: EntityID<Int>) : IntEntity(id) {
// companion object : IntEntityClass<Extension>(ExtensionsTable)
//
// val name by ExtensionsTable.name
// val pkgName by ExtensionsTable.pkgName
// val versionName by ExtensionsTable.versionName
// val versionCode by ExtensionsTable.versionCode
// val lang by ExtensionsTable.lang
// val isNsfw by ExtensionsTable.isNsfw
//}
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,
)
@@ -1,4 +0,0 @@
package ir.armor.tachidesk.extension
class Extension {
}