mirror of
https://github.com/tachiyomiorg/tachiyomi-extensions-inspector.git
synced 2024-12-25 00:01:49 +01:00
refactor & support for extension update: Backend
This commit is contained in:
parent
b1b1abad1d
commit
077bbc3c38
@ -11,13 +11,14 @@ package eu.kanade.tachiyomi.extension.api
|
||||
// import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.extension.util.ExtensionLoader
|
||||
import ir.armor.tachidesk.database.dataclass.ExtensionDataClass
|
||||
import ir.armor.tachidesk.model.dataclass.ExtensionDataClass
|
||||
// import kotlinx.coroutines.Dispatchers
|
||||
// import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.int
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
|
||||
// import uy.kohesive.injekt.injectLazy
|
||||
|
||||
internal class ExtensionGithubApi {
|
||||
@ -84,7 +85,9 @@ internal class ExtensionGithubApi {
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val BASE_URL = "https://raw.githubusercontent.com/"
|
||||
const val REPO_URL_PREFIX = "${BASE_URL}inorichi/tachiyomi-extensions/repo"
|
||||
// const val BASE_URL = "https://raw.githubusercontent.com"
|
||||
// const val REPO_URL_PREFIX = "${BASE_URL}/tachiyomiorg/tachiyomi-extensions/repo"
|
||||
const val BASE_URL = "http://127.0.0.1:8000"
|
||||
const val REPO_URL_PREFIX = "$BASE_URL/repo"
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ interface ExtensionGithubService {
|
||||
.addNetworkInterceptor { chain ->
|
||||
val originalResponse = chain.proceed(chain.request())
|
||||
originalResponse.newBuilder()
|
||||
.header("Content-Encoding", "gzip")
|
||||
// .header("Content-Encoding", "gzip")
|
||||
.header("Content-Type", "application/json")
|
||||
.build()
|
||||
}
|
||||
@ -41,6 +41,7 @@ interface ExtensionGithubService {
|
||||
}
|
||||
}
|
||||
|
||||
@GET("${ExtensionGithubApi.REPO_URL_PREFIX}/index.json.gz")
|
||||
// @GET("${ExtensionGithubApi.REPO_URL_PREFIX}/index.json.gz")
|
||||
@GET("${ExtensionGithubApi.REPO_URL_PREFIX}/index.json")
|
||||
suspend fun getRepo(): JsonArray
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
package ir.armor.tachidesk.database.entity
|
||||
|
||||
/*
|
||||
* 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 ir.armor.tachidesk.database.table.ExtensionTable
|
||||
import org.jetbrains.exposed.dao.IntEntity
|
||||
import org.jetbrains.exposed.dao.IntEntityClass
|
||||
import org.jetbrains.exposed.dao.id.EntityID
|
||||
|
||||
class ExtensionEntity(id: EntityID<Int>) : IntEntity(id) {
|
||||
companion object : IntEntityClass<ExtensionEntity>(ExtensionTable)
|
||||
|
||||
var name by ExtensionTable.name
|
||||
var pkgName by ExtensionTable.pkgName
|
||||
var versionName by ExtensionTable.versionName
|
||||
var versionCode by ExtensionTable.versionCode
|
||||
var lang by ExtensionTable.lang
|
||||
var isNsfw by ExtensionTable.isNsfw
|
||||
var apkName by ExtensionTable.apkName
|
||||
var iconUrl by ExtensionTable.iconUrl
|
||||
var installed by ExtensionTable.installed
|
||||
var classFQName by ExtensionTable.classFQName
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package ir.armor.tachidesk.database.entity
|
||||
|
||||
/*
|
||||
* 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 ir.armor.tachidesk.database.table.MangaTable
|
||||
import org.jetbrains.exposed.dao.IntEntity
|
||||
import org.jetbrains.exposed.dao.IntEntityClass
|
||||
import org.jetbrains.exposed.dao.id.EntityID
|
||||
|
||||
class MangaEntity(id: EntityID<Int>) : IntEntity(id) {
|
||||
companion object : IntEntityClass<MangaEntity>(MangaTable)
|
||||
|
||||
var url by MangaTable.url
|
||||
var title by MangaTable.title
|
||||
var initialized by MangaTable.initialized
|
||||
|
||||
var artist by MangaTable.artist
|
||||
var author by MangaTable.author
|
||||
var description by MangaTable.description
|
||||
var genre by MangaTable.genre
|
||||
var status by MangaTable.status
|
||||
var thumbnail_url by MangaTable.thumbnail_url
|
||||
|
||||
var sourceReference by MangaEntity referencedOn MangaTable.sourceReference
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package ir.armor.tachidesk.database.entity
|
||||
|
||||
/*
|
||||
* 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 ir.armor.tachidesk.database.table.SourceTable
|
||||
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) {
|
||||
companion object : EntityClass<Long, SourceEntity>(SourceTable, null)
|
||||
|
||||
var sourceId by SourceTable.id
|
||||
var name by SourceTable.name
|
||||
var lang by SourceTable.lang
|
||||
var extension by ExtensionEntity referencedOn SourceTable.extension
|
||||
var partOfFactorySource by SourceTable.partOfFactorySource
|
||||
var positionInFactorySource by SourceTable.positionInFactorySource
|
||||
}
|
@ -7,10 +7,10 @@ package ir.armor.tachidesk.impl
|
||||
* 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 ir.armor.tachidesk.database.dataclass.CategoryDataClass
|
||||
import ir.armor.tachidesk.database.table.CategoryMangaTable
|
||||
import ir.armor.tachidesk.database.table.CategoryTable
|
||||
import ir.armor.tachidesk.database.table.toDataClass
|
||||
import ir.armor.tachidesk.model.database.CategoryMangaTable
|
||||
import ir.armor.tachidesk.model.database.CategoryTable
|
||||
import ir.armor.tachidesk.model.database.toDataClass
|
||||
import ir.armor.tachidesk.model.dataclass.CategoryDataClass
|
||||
import org.jetbrains.exposed.sql.SortOrder
|
||||
import org.jetbrains.exposed.sql.deleteWhere
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
|
@ -7,12 +7,12 @@ package ir.armor.tachidesk.impl
|
||||
* 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 ir.armor.tachidesk.database.dataclass.CategoryDataClass
|
||||
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
||||
import ir.armor.tachidesk.database.table.CategoryMangaTable
|
||||
import ir.armor.tachidesk.database.table.CategoryTable
|
||||
import ir.armor.tachidesk.database.table.MangaTable
|
||||
import ir.armor.tachidesk.database.table.toDataClass
|
||||
import ir.armor.tachidesk.model.database.CategoryMangaTable
|
||||
import ir.armor.tachidesk.model.database.CategoryTable
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.toDataClass
|
||||
import ir.armor.tachidesk.model.dataclass.CategoryDataClass
|
||||
import ir.armor.tachidesk.model.dataclass.MangaDataClass
|
||||
import org.jetbrains.exposed.sql.SortOrder
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.deleteWhere
|
||||
|
@ -9,10 +9,10 @@ package ir.armor.tachidesk.impl
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import ir.armor.tachidesk.database.dataclass.ChapterDataClass
|
||||
import ir.armor.tachidesk.database.table.ChapterTable
|
||||
import ir.armor.tachidesk.database.table.MangaTable
|
||||
import ir.armor.tachidesk.database.table.PageTable
|
||||
import ir.armor.tachidesk.model.database.ChapterTable
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.PageTable
|
||||
import ir.armor.tachidesk.model.dataclass.ChapterDataClass
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
import org.jetbrains.exposed.sql.insertAndGetId
|
||||
|
@ -15,9 +15,9 @@ import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.source.SourceFactory
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import ir.armor.tachidesk.database.table.ExtensionTable
|
||||
import ir.armor.tachidesk.database.table.SourceTable
|
||||
import ir.armor.tachidesk.impl.util.APKExtractor
|
||||
import ir.armor.tachidesk.model.database.ExtensionTable
|
||||
import ir.armor.tachidesk.model.database.SourceTable
|
||||
import ir.armor.tachidesk.server.applicationDirs
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import mu.KotlinLogging
|
||||
@ -71,10 +71,10 @@ private fun dex2jar(dexFile: String, jarFile: String, fileNameWithoutType: Strin
|
||||
}
|
||||
}
|
||||
|
||||
fun installAPK(apkName: String): Int {
|
||||
logger.debug("Installing $apkName")
|
||||
val extensionRecord = getExtensionList(true).first { it.apkName == apkName }
|
||||
val fileNameWithoutType = apkName.substringBefore(".apk")
|
||||
fun installExtension(pkgName: String): Int {
|
||||
logger.debug("Installing $pkgName")
|
||||
val extensionRecord = extensionTableAsDataClass().first { it.pkgName == pkgName }
|
||||
val fileNameWithoutType = extensionRecord.apkName.substringBefore(".apk")
|
||||
val dirPathWithoutType = "${applicationDirs.extensionsRoot}/$fileNameWithoutType"
|
||||
|
||||
// check if we don't have the dex file already downloaded
|
||||
@ -145,7 +145,7 @@ fun installAPK(apkName: String): Int {
|
||||
// update extension info
|
||||
transaction {
|
||||
ExtensionTable.update({ ExtensionTable.name eq extensionRecord.name }) {
|
||||
it[installed] = true
|
||||
it[isInstalled] = true
|
||||
it[classFQName] = className
|
||||
}
|
||||
}
|
||||
@ -168,18 +168,18 @@ private fun downloadAPKFile(url: String, apkPath: String) {
|
||||
sink.close()
|
||||
}
|
||||
|
||||
fun removeExtension(apkName: String) {
|
||||
logger.debug("Uninstalling $apkName")
|
||||
fun removeExtension(pkgName: String) {
|
||||
logger.debug("Uninstalling $pkgName")
|
||||
|
||||
val extensionRecord = getExtensionList(true).first { it.apkName == apkName }
|
||||
val fileNameWithoutType = apkName.substringBefore(".apk")
|
||||
val extensionRecord = extensionTableAsDataClass().first { it.pkgName == pkgName }
|
||||
val fileNameWithoutType = extensionRecord.apkName.substringBefore(".apk")
|
||||
val jarPath = "${applicationDirs.extensionsRoot}/$fileNameWithoutType.jar"
|
||||
transaction {
|
||||
val extensionId = ExtensionTable.select { ExtensionTable.name eq extensionRecord.name }.first()[ExtensionTable.id]
|
||||
|
||||
SourceTable.deleteWhere { SourceTable.extension eq extensionId }
|
||||
ExtensionTable.update({ ExtensionTable.name eq extensionRecord.name }) {
|
||||
it[ExtensionTable.installed] = false
|
||||
it[isInstalled] = false
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,6 +188,24 @@ fun removeExtension(apkName: String) {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateExtension(pkgName: String): Int {
|
||||
val targetExtension = ExtensionListData.updateMap.remove(pkgName)!!
|
||||
removeExtension(pkgName)
|
||||
transaction {
|
||||
ExtensionTable.update({ ExtensionTable.pkgName eq pkgName }) {
|
||||
it[name] = targetExtension.name
|
||||
it[versionName] = targetExtension.versionName
|
||||
it[versionCode] = targetExtension.versionCode
|
||||
it[lang] = targetExtension.lang
|
||||
it[isNsfw] = targetExtension.isNsfw
|
||||
it[apkName] = targetExtension.apkName
|
||||
it[iconUrl] = targetExtension.iconUrl
|
||||
it[hasUpdate] = false
|
||||
}
|
||||
}
|
||||
return installExtension(pkgName)
|
||||
}
|
||||
|
||||
val network: NetworkHelper by injectLazy()
|
||||
|
||||
fun getExtensionIcon(apkName: String): Pair<InputStream, String> {
|
||||
|
@ -9,45 +9,84 @@ package ir.armor.tachidesk.impl
|
||||
|
||||
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.ExtensionTable
|
||||
import ir.armor.tachidesk.model.database.ExtensionTable
|
||||
import ir.armor.tachidesk.model.dataclass.ExtensionDataClass
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import mu.KotlinLogging
|
||||
import org.jetbrains.exposed.sql.deleteWhere
|
||||
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
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
private object Data {
|
||||
var lastExtensionCheck: Long = 0
|
||||
object ExtensionListData {
|
||||
var lastUpdateCheck: Long = 0
|
||||
var updateMap = ConcurrentHashMap<String, Extension.Available>()
|
||||
}
|
||||
|
||||
private fun extensionDatabaseIsEmtpy(): Boolean {
|
||||
return transaction {
|
||||
return@transaction ExtensionTable.selectAll().count() == 0L
|
||||
}
|
||||
}
|
||||
|
||||
fun getExtensionList(offline: Boolean = false): List<ExtensionDataClass> {
|
||||
fun getExtensionList(): List<ExtensionDataClass> {
|
||||
// update if 60 seconds has passed or requested offline and database is empty
|
||||
if (Data.lastExtensionCheck + 60 * 1000 < System.currentTimeMillis() || (offline && extensionDatabaseIsEmtpy())) {
|
||||
if (ExtensionListData.lastUpdateCheck + 60 * 1000 < System.currentTimeMillis()) {
|
||||
logger.debug("Getting extensions list from the internet")
|
||||
Data.lastExtensionCheck = System.currentTimeMillis()
|
||||
var foundExtensions: List<Extension.Available>
|
||||
ExtensionListData.lastUpdateCheck = System.currentTimeMillis()
|
||||
runBlocking {
|
||||
val api = ExtensionGithubApi()
|
||||
foundExtensions = api.findExtensions()
|
||||
val foundExtensions = ExtensionGithubApi().findExtensions()
|
||||
updateExtensionDatabase(foundExtensions)
|
||||
}
|
||||
} else {
|
||||
logger.debug("used cached extension list")
|
||||
}
|
||||
|
||||
return extensionTableAsDataClass()
|
||||
}
|
||||
|
||||
fun extensionTableAsDataClass() = transaction {
|
||||
return@transaction ExtensionTable.selectAll().map {
|
||||
ExtensionDataClass(
|
||||
it[ExtensionTable.name],
|
||||
it[ExtensionTable.pkgName],
|
||||
it[ExtensionTable.versionName],
|
||||
it[ExtensionTable.versionCode],
|
||||
it[ExtensionTable.lang],
|
||||
it[ExtensionTable.isNsfw],
|
||||
it[ExtensionTable.apkName],
|
||||
getExtensionIconUrl(it[ExtensionTable.apkName]),
|
||||
it[ExtensionTable.isInstalled],
|
||||
it[ExtensionTable.hasUpdate],
|
||||
it[ExtensionTable.isObsolete],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateExtensionDatabase(foundExtensions: List<Extension.Available>) {
|
||||
transaction {
|
||||
foundExtensions.forEach { foundExtension ->
|
||||
val extensionRecord = ExtensionTable.select { ExtensionTable.name eq foundExtension.name }.firstOrNull()
|
||||
val extensionRecord = ExtensionTable.select { ExtensionTable.pkgName eq foundExtension.pkgName }.firstOrNull()
|
||||
if (extensionRecord != null) {
|
||||
// update the record
|
||||
ExtensionTable.update({ ExtensionTable.name eq foundExtension.name }) {
|
||||
if (extensionRecord[ExtensionTable.isInstalled]) {
|
||||
if (foundExtension.versionCode > extensionRecord[ExtensionTable.versionCode]) {
|
||||
// there is an update
|
||||
ExtensionTable.update({ ExtensionTable.pkgName eq foundExtension.pkgName }) {
|
||||
it[hasUpdate] = true
|
||||
}
|
||||
ExtensionListData.updateMap.putIfAbsent(foundExtension.pkgName, foundExtension)
|
||||
} else if (foundExtension.versionCode < extensionRecord[ExtensionTable.versionCode]) {
|
||||
// some how the user installed an invalid version
|
||||
ExtensionTable.update({ ExtensionTable.pkgName eq foundExtension.pkgName }) {
|
||||
it[isObsolete] = true
|
||||
}
|
||||
} else {
|
||||
// the two are equal
|
||||
// NOOP
|
||||
}
|
||||
} else {
|
||||
// extension is not installed so we can overwrite the data without a care
|
||||
ExtensionTable.update({ ExtensionTable.pkgName eq foundExtension.pkgName }) {
|
||||
it[name] = foundExtension.name
|
||||
it[pkgName] = foundExtension.pkgName
|
||||
it[versionName] = foundExtension.versionName
|
||||
it[versionCode] = foundExtension.versionCode
|
||||
it[lang] = foundExtension.lang
|
||||
@ -55,6 +94,7 @@ fun getExtensionList(offline: Boolean = false): List<ExtensionDataClass> {
|
||||
it[apkName] = foundExtension.apkName
|
||||
it[iconUrl] = foundExtension.iconUrl
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// insert new record
|
||||
ExtensionTable.insert {
|
||||
@ -69,26 +109,22 @@ fun getExtensionList(offline: Boolean = false): List<ExtensionDataClass> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deal with obsolete extensions
|
||||
ExtensionTable.selectAll().forEach { extensionRecord ->
|
||||
val foundExtension = foundExtensions.find { it.pkgName == extensionRecord[ExtensionTable.pkgName] }
|
||||
if (foundExtension == null) {
|
||||
// this extensions is obsolete
|
||||
if (extensionRecord[ExtensionTable.isInstalled]) {
|
||||
// is installed so we should mark it as obsolete
|
||||
ExtensionTable.update({ ExtensionTable.pkgName eq extensionRecord[ExtensionTable.pkgName] }) {
|
||||
it[isObsolete] = true
|
||||
}
|
||||
} else {
|
||||
logger.debug("used cached extension list")
|
||||
}
|
||||
|
||||
return transaction {
|
||||
return@transaction ExtensionTable.selectAll().map {
|
||||
ExtensionDataClass(
|
||||
it[ExtensionTable.name],
|
||||
it[ExtensionTable.pkgName],
|
||||
it[ExtensionTable.versionName],
|
||||
it[ExtensionTable.versionCode],
|
||||
it[ExtensionTable.lang],
|
||||
it[ExtensionTable.isNsfw],
|
||||
it[ExtensionTable.apkName],
|
||||
getExtensionIconUrl(it[ExtensionTable.apkName]),
|
||||
it[ExtensionTable.installed],
|
||||
it[ExtensionTable.classFQName]
|
||||
)
|
||||
// is not installed so we can remove the record without a care
|
||||
ExtensionTable.deleteWhere { ExtensionTable.pkgName eq extensionRecord[ExtensionTable.pkgName] }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,10 @@ package ir.armor.tachidesk.impl
|
||||
* 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 ir.armor.tachidesk.database.dataclass.MangaDataClass
|
||||
import ir.armor.tachidesk.database.table.CategoryMangaTable
|
||||
import ir.armor.tachidesk.database.table.MangaTable
|
||||
import ir.armor.tachidesk.database.table.toDataClass
|
||||
import ir.armor.tachidesk.model.database.CategoryMangaTable
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.toDataClass
|
||||
import ir.armor.tachidesk.model.dataclass.MangaDataClass
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.deleteWhere
|
||||
import org.jetbrains.exposed.sql.select
|
||||
|
@ -9,9 +9,9 @@ package ir.armor.tachidesk.impl
|
||||
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
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.model.database.MangaStatus
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.dataclass.MangaDataClass
|
||||
import ir.armor.tachidesk.server.applicationDirs
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
@ -8,10 +8,10 @@ package ir.armor.tachidesk.impl
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
||||
import ir.armor.tachidesk.database.dataclass.PagedMangaListDataClass
|
||||
import ir.armor.tachidesk.database.table.MangaStatus
|
||||
import ir.armor.tachidesk.database.table.MangaTable
|
||||
import ir.armor.tachidesk.model.database.MangaStatus
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.dataclass.MangaDataClass
|
||||
import ir.armor.tachidesk.model.dataclass.PagedMangaListDataClass
|
||||
import org.jetbrains.exposed.sql.insertAndGetId
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
|
@ -9,10 +9,10 @@ package ir.armor.tachidesk.impl
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import ir.armor.tachidesk.database.table.ChapterTable
|
||||
import ir.armor.tachidesk.database.table.MangaTable
|
||||
import ir.armor.tachidesk.database.table.PageTable
|
||||
import ir.armor.tachidesk.database.table.SourceTable
|
||||
import ir.armor.tachidesk.model.database.ChapterTable
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.PageTable
|
||||
import ir.armor.tachidesk.model.database.SourceTable
|
||||
import ir.armor.tachidesk.server.applicationDirs
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.select
|
||||
|
@ -7,7 +7,7 @@ package ir.armor.tachidesk.impl
|
||||
* 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 ir.armor.tachidesk.database.dataclass.PagedMangaListDataClass
|
||||
import ir.armor.tachidesk.model.dataclass.PagedMangaListDataClass
|
||||
|
||||
fun sourceFilters(sourceId: Long) {
|
||||
val source = getHttpSource(sourceId)
|
||||
|
@ -9,17 +9,14 @@ package ir.armor.tachidesk.impl
|
||||
|
||||
import eu.kanade.tachiyomi.source.SourceFactory
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
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.ExtensionTable
|
||||
import ir.armor.tachidesk.database.table.SourceTable
|
||||
import ir.armor.tachidesk.model.database.ExtensionTable
|
||||
import ir.armor.tachidesk.model.database.SourceTable
|
||||
import ir.armor.tachidesk.model.dataclass.SourceDataClass
|
||||
import ir.armor.tachidesk.server.applicationDirs
|
||||
import mu.KotlinLogging
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.jetbrains.exposed.sql.selectAll
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import java.lang.NullPointerException
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
|
||||
@ -30,8 +27,8 @@ private val extensionCache = mutableListOf<Pair<String, Any>>()
|
||||
|
||||
fun getHttpSource(sourceId: Long): HttpSource {
|
||||
val sourceRecord = transaction {
|
||||
SourceEntity.findById(sourceId)
|
||||
} ?: throw NullPointerException("Source with id $sourceId is not installed")
|
||||
SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()!!
|
||||
}
|
||||
|
||||
val cachedResult: Pair<Long, HttpSource>? = sourceCache.firstOrNull { it.first == sourceId }
|
||||
if (cachedResult != null) {
|
||||
@ -40,10 +37,10 @@ fun getHttpSource(sourceId: Long): HttpSource {
|
||||
}
|
||||
|
||||
val result: HttpSource = transaction {
|
||||
val extensionId = sourceRecord.extension.id.value
|
||||
val extensionRecord = ExtensionEntity.findById(extensionId)!!
|
||||
val apkName = extensionRecord.apkName
|
||||
val className = extensionRecord.classFQName
|
||||
val extensionId = sourceRecord[SourceTable.extension]
|
||||
val extensionRecord = ExtensionTable.select { ExtensionTable.id eq extensionId }.firstOrNull()!!
|
||||
val apkName = extensionRecord[ExtensionTable.apkName]
|
||||
val className = extensionRecord[ExtensionTable.classFQName]
|
||||
val jarName = apkName.substringBefore(".apk") + ".jar"
|
||||
val jarPath = "${applicationDirs.extensionsRoot}/$jarName"
|
||||
|
||||
@ -60,13 +57,15 @@ fun getHttpSource(sourceId: Long): HttpSource {
|
||||
val classToLoad = Class.forName(className, true, child)
|
||||
classToLoad.newInstance()
|
||||
}
|
||||
if (sourceRecord.partOfFactorySource) {
|
||||
if (sourceRecord[SourceTable.partOfFactorySource]) {
|
||||
val positionInFactorySource = sourceRecord[SourceTable.positionInFactorySource]!!
|
||||
return@transaction if (usedCached) {
|
||||
(instance as List<HttpSource>)[sourceRecord.positionInFactorySource!!]
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(instance as List<HttpSource>)[positionInFactorySource]
|
||||
} else {
|
||||
val list = (instance as SourceFactory).createSources()
|
||||
extensionCache.add(Pair(jarPath, list))
|
||||
list[sourceRecord.positionInFactorySource!!] as HttpSource
|
||||
list[positionInFactorySource] as HttpSource
|
||||
}
|
||||
} else {
|
||||
if (!usedCached)
|
||||
|
@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.database.table
|
||||
package ir.armor.tachidesk.model.database
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.database.table
|
||||
package ir.armor.tachidesk.model.database
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
@ -7,7 +7,7 @@ package ir.armor.tachidesk.database.table
|
||||
* 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 ir.armor.tachidesk.database.dataclass.CategoryDataClass
|
||||
import ir.armor.tachidesk.model.dataclass.CategoryDataClass
|
||||
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
|
@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.database.table
|
||||
package ir.armor.tachidesk.model.database
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.database.table
|
||||
package ir.armor.tachidesk.model.database
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
@ -19,6 +19,8 @@ object ExtensionTable : IntIdTable() {
|
||||
val apkName = varchar("apk_name", 1024)
|
||||
val iconUrl = varchar("icon_url", 2048)
|
||||
|
||||
val installed = bool("installed").default(false)
|
||||
val isInstalled = bool("is_installed").default(false)
|
||||
val hasUpdate = bool("has_update").default(false)
|
||||
val isObsolete = bool("is_obsolete").default(false)
|
||||
val classFQName = varchar("class_name", 256).default("") // fully qualified name
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.database.table
|
||||
package ir.armor.tachidesk.model.database
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
@ -8,8 +8,8 @@ package ir.armor.tachidesk.database.table
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import ir.armor.tachidesk.database.dataclass.MangaDataClass
|
||||
import ir.armor.tachidesk.impl.proxyThumbnailUrl
|
||||
import ir.armor.tachidesk.model.dataclass.MangaDataClass
|
||||
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
|
@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.database.table
|
||||
package ir.armor.tachidesk.model.database
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.database.table
|
||||
package ir.armor.tachidesk.model.database
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.database.dataclass
|
||||
package ir.armor.tachidesk.model.dataclass
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.database.dataclass
|
||||
package ir.armor.tachidesk.model.dataclass
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.database
|
||||
package ir.armor.tachidesk.model.dataclass
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
@ -7,13 +7,13 @@ package ir.armor.tachidesk.database
|
||||
* 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 ir.armor.tachidesk.database.table.CategoryMangaTable
|
||||
import ir.armor.tachidesk.database.table.CategoryTable
|
||||
import ir.armor.tachidesk.database.table.ChapterTable
|
||||
import ir.armor.tachidesk.database.table.ExtensionTable
|
||||
import ir.armor.tachidesk.database.table.MangaTable
|
||||
import ir.armor.tachidesk.database.table.PageTable
|
||||
import ir.armor.tachidesk.database.table.SourceTable
|
||||
import ir.armor.tachidesk.model.database.CategoryMangaTable
|
||||
import ir.armor.tachidesk.model.database.CategoryTable
|
||||
import ir.armor.tachidesk.model.database.ChapterTable
|
||||
import ir.armor.tachidesk.model.database.ExtensionTable
|
||||
import ir.armor.tachidesk.model.database.MangaTable
|
||||
import ir.armor.tachidesk.model.database.PageTable
|
||||
import ir.armor.tachidesk.model.database.SourceTable
|
||||
import ir.armor.tachidesk.server.applicationDirs
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.SchemaUtils
|
@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.database.dataclass
|
||||
package ir.armor.tachidesk.model.dataclass
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
@ -16,6 +16,7 @@ data class ExtensionDataClass(
|
||||
val isNsfw: Boolean,
|
||||
val apkName: String,
|
||||
val iconUrl: String,
|
||||
val installed: Boolean,
|
||||
val classFQName: String,
|
||||
val isInstalled: Boolean,
|
||||
val hasUpdate: Boolean,
|
||||
val isObsolete: Boolean,
|
||||
)
|
@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.database.dataclass
|
||||
package ir.armor.tachidesk.model.dataclass
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
@ -7,7 +7,7 @@ package ir.armor.tachidesk.database.dataclass
|
||||
* 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 ir.armor.tachidesk.database.table.MangaStatus
|
||||
import ir.armor.tachidesk.model.database.MangaStatus
|
||||
|
||||
data class MangaDataClass(
|
||||
val id: Int,
|
@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.database.dataclass
|
||||
package ir.armor.tachidesk.model.dataclass
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
@ -1,4 +1,4 @@
|
||||
package ir.armor.tachidesk.database.dataclass
|
||||
package ir.armor.tachidesk.model.dataclass
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
@ -19,7 +19,7 @@ import ir.armor.tachidesk.impl.getPageImage
|
||||
import ir.armor.tachidesk.impl.getSource
|
||||
import ir.armor.tachidesk.impl.getSourceList
|
||||
import ir.armor.tachidesk.impl.getThumbnail
|
||||
import ir.armor.tachidesk.impl.installAPK
|
||||
import ir.armor.tachidesk.impl.installExtension
|
||||
import ir.armor.tachidesk.impl.removeCategory
|
||||
import ir.armor.tachidesk.impl.removeExtension
|
||||
import ir.armor.tachidesk.impl.removeMangaFromCategory
|
||||
@ -29,6 +29,7 @@ import ir.armor.tachidesk.impl.sourceFilters
|
||||
import ir.armor.tachidesk.impl.sourceGlobalSearch
|
||||
import ir.armor.tachidesk.impl.sourceSearch
|
||||
import ir.armor.tachidesk.impl.updateCategory
|
||||
import ir.armor.tachidesk.impl.updateExtension
|
||||
import ir.armor.tachidesk.server.util.openInBrowser
|
||||
import mu.KotlinLogging
|
||||
|
||||
@ -69,18 +70,26 @@ fun javalinSetup() {
|
||||
ctx.json(getExtensionList())
|
||||
}
|
||||
|
||||
app.get("/api/v1/extension/install/:apkName") { ctx ->
|
||||
val apkName = ctx.pathParam("apkName")
|
||||
app.get("/api/v1/extension/install/:pkgName") { ctx ->
|
||||
val pkgName = ctx.pathParam("pkgName")
|
||||
|
||||
ctx.status(
|
||||
installAPK(apkName)
|
||||
installExtension(pkgName)
|
||||
)
|
||||
}
|
||||
|
||||
app.get("/api/v1/extension/uninstall/:apkName") { ctx ->
|
||||
val apkName = ctx.pathParam("apkName")
|
||||
app.get("/api/v1/extension/update/:pkgName") { ctx ->
|
||||
val pkgName = ctx.pathParam("pkgName")
|
||||
|
||||
removeExtension(apkName)
|
||||
ctx.status(
|
||||
updateExtension(pkgName)
|
||||
)
|
||||
}
|
||||
|
||||
app.get("/api/v1/extension/uninstall/:pkgName") { ctx ->
|
||||
val pkgName = ctx.pathParam("pkgName")
|
||||
|
||||
removeExtension(pkgName)
|
||||
ctx.status(200)
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ package ir.armor.tachidesk.server
|
||||
import ch.qos.logback.classic.Level
|
||||
import eu.kanade.tachiyomi.App
|
||||
import ir.armor.tachidesk.Main
|
||||
import ir.armor.tachidesk.database.makeDataBaseTables
|
||||
import ir.armor.tachidesk.model.dataclass.makeDataBaseTables
|
||||
import ir.armor.tachidesk.server.util.systemTray
|
||||
import mu.KotlinLogging
|
||||
import net.harawata.appdirs.AppDirsFactory
|
||||
@ -99,5 +99,6 @@ fun applicationSetup() {
|
||||
System.getProperties()["proxySet"] = "true"
|
||||
System.getProperties()["socksProxyHost"] = serverConfig.socksProxyHost
|
||||
System.getProperties()["socksProxyPort"] = serverConfig.socksProxyPort
|
||||
logger.info("Socks Proxy is enabled to ${serverConfig.socksProxyHost}:${serverConfig.socksProxyPort}")
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ interface IProps {
|
||||
export default function ExtensionCard(props: IProps) {
|
||||
const {
|
||||
extension: {
|
||||
name, lang, versionName, installed, apkName, iconUrl,
|
||||
name, lang, versionName, installed, pkgName, iconUrl,
|
||||
},
|
||||
notifyInstall,
|
||||
} = props;
|
||||
@ -62,7 +62,7 @@ export default function ExtensionCard(props: IProps) {
|
||||
|
||||
function install() {
|
||||
setInstalledState('installing');
|
||||
client.get(`/api/v1/extension/install/${apkName}`)
|
||||
client.get(`/api/v1/extension/install/${pkgName}`)
|
||||
.then(() => {
|
||||
setInstalledState('uninstall');
|
||||
notifyInstall();
|
||||
@ -71,7 +71,7 @@ export default function ExtensionCard(props: IProps) {
|
||||
|
||||
function uninstall() {
|
||||
setInstalledState('uninstalling');
|
||||
client.get(`/api/v1/extension/uninstall/${apkName}`)
|
||||
client.get(`/api/v1/extension/uninstall/${pkgName}`)
|
||||
.then(() => {
|
||||
// setInstalledState('install');
|
||||
notifyInstall();
|
||||
|
14
webUI/react/src/typings.d.ts
vendored
14
webUI/react/src/typings.d.ts
vendored
@ -7,12 +7,16 @@
|
||||
|
||||
interface IExtension {
|
||||
name: string
|
||||
lang: string
|
||||
versionName: string
|
||||
iconUrl: string
|
||||
installed: boolean
|
||||
apkName: string
|
||||
pkgName: string
|
||||
versionName: string
|
||||
versionCode: number
|
||||
lang: string
|
||||
isNsfw: boolean
|
||||
apkName: string
|
||||
iconUrl: string
|
||||
isInstalled: boolean
|
||||
hasUpdate: boolean
|
||||
isObsolete: boolean
|
||||
}
|
||||
|
||||
interface ISource {
|
||||
|
Loading…
Reference in New Issue
Block a user