refactor & support for extension update: Backend

This commit is contained in:
Aria Moradi 2021-03-29 00:35:21 +04:30
parent b1b1abad1d
commit 077bbc3c38
34 changed files with 244 additions and 252 deletions

View File

@ -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"
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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> {

View File

@ -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(): List<ExtensionDataClass> {
// update if 60 seconds has passed or requested offline and database is empty
if (ExtensionListData.lastUpdateCheck + 60 * 1000 < System.currentTimeMillis()) {
logger.debug("Getting extensions list from the internet")
ExtensionListData.lastUpdateCheck = System.currentTimeMillis()
runBlocking {
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],
)
}
}
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())) {
logger.debug("Getting extensions list from the internet")
Data.lastExtensionCheck = System.currentTimeMillis()
var foundExtensions: List<Extension.Available>
runBlocking {
val api = ExtensionGithubApi()
foundExtensions = api.findExtensions()
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")
// is not installed so we can remove the record without a care
ExtensionTable.deleteWhere { ExtensionTable.pkgName eq extensionRecord[ExtensionTable.pkgName] }
}
}
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]
)
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -1,4 +1,4 @@
package ir.armor.tachidesk.database.table
package ir.armor.tachidesk.model.database
/*
* Copyright (C) Contributors to the Suwayomi project

View File

@ -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

View File

@ -1,4 +1,4 @@
package ir.armor.tachidesk.database.table
package ir.armor.tachidesk.model.database
/*
* Copyright (C) Contributors to the Suwayomi project

View File

@ -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
}

View File

@ -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

View File

@ -1,4 +1,4 @@
package ir.armor.tachidesk.database.table
package ir.armor.tachidesk.model.database
/*
* Copyright (C) Contributors to the Suwayomi project

View File

@ -1,4 +1,4 @@
package ir.armor.tachidesk.database.table
package ir.armor.tachidesk.model.database
/*
* Copyright (C) Contributors to the Suwayomi project

View File

@ -1,4 +1,4 @@
package ir.armor.tachidesk.database.dataclass
package ir.armor.tachidesk.model.dataclass
/*
* Copyright (C) Contributors to the Suwayomi project

View File

@ -1,4 +1,4 @@
package ir.armor.tachidesk.database.dataclass
package ir.armor.tachidesk.model.dataclass
/*
* Copyright (C) Contributors to the Suwayomi project

View File

@ -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

View File

@ -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,
)

View File

@ -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,

View File

@ -1,4 +1,4 @@
package ir.armor.tachidesk.database.dataclass
package ir.armor.tachidesk.model.dataclass
/*
* Copyright (C) Contributors to the Suwayomi project

View File

@ -1,4 +1,4 @@
package ir.armor.tachidesk.database.dataclass
package ir.armor.tachidesk.model.dataclass
/*
* Copyright (C) Contributors to the Suwayomi project

View File

@ -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)
}

View File

@ -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}")
}
}

View File

@ -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();

View File

@ -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 {