Improve Extensions List (#753)

* Use new extension icon path

* Improve Extension list performance
This commit is contained in:
Mitchell Syer
2023-11-04 18:09:55 -04:00
committed by GitHub
parent 0785f4d0f5
commit 442a290966
2 changed files with 92 additions and 58 deletions

View File

@@ -9,11 +9,13 @@ package suwayomi.tachidesk.manga.impl.extension
import eu.kanade.tachiyomi.source.local.LocalSource import eu.kanade.tachiyomi.source.local.LocalSource
import mu.KotlinLogging import mu.KotlinLogging
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
import org.jetbrains.exposed.sql.batchInsert
import org.jetbrains.exposed.sql.deleteWhere 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.selectAll
import org.jetbrains.exposed.sql.statements.BatchUpdateStatement
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIconUrl import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIconUrl
@@ -69,67 +71,99 @@ object ExtensionsList {
private fun updateExtensionDatabase(foundExtensions: List<OnlineExtension>) { private fun updateExtensionDatabase(foundExtensions: List<OnlineExtension>) {
transaction { transaction {
foundExtensions.forEach { foundExtension -> val installedExtensions =
val extensionRecord = ExtensionTable.select { ExtensionTable.pkgName eq foundExtension.pkgName }.firstOrNull() ExtensionTable.selectAll().toList()
if (extensionRecord != null) { .associateBy { it[ExtensionTable.pkgName] }
if (extensionRecord[ExtensionTable.isInstalled]) { val extensionsToUpdate = mutableListOf<Pair<OnlineExtension, ResultRow>>()
val extensionsToInsert = mutableListOf<OnlineExtension>()
val extensionsToDelete =
installedExtensions.mapNotNull { (pkgName, extension) ->
extension.takeUnless { foundExtensions.any { it.pkgName == pkgName } }
}
foundExtensions.forEach {
val extension = installedExtensions[it.pkgName]
if (extension != null) {
extensionsToUpdate.add(it to extension)
} else {
extensionsToInsert.add(it)
}
}
if (extensionsToUpdate.isNotEmpty()) {
val extensionsInstalled =
extensionsToUpdate
.groupBy { it.second[ExtensionTable.isInstalled] }
val installedExtensionsToUpdate = extensionsInstalled[true].orEmpty()
if (installedExtensionsToUpdate.isNotEmpty()) {
BatchUpdateStatement(ExtensionTable).apply {
installedExtensionsToUpdate.forEach { (foundExtension, extensionRecord) ->
addBatch(EntityID(extensionRecord[ExtensionTable.id].value, ExtensionTable))
// Always update icon url
this[ExtensionTable.iconUrl] = foundExtension.iconUrl
// add these because batch updates need matching columns
this[ExtensionTable.hasUpdate] = extensionRecord[ExtensionTable.hasUpdate]
this[ExtensionTable.isObsolete] = extensionRecord[ExtensionTable.isObsolete]
when { when {
foundExtension.versionCode > extensionRecord[ExtensionTable.versionCode] -> { foundExtension.versionCode > extensionRecord[ExtensionTable.versionCode] -> {
// there is an update // there is an update
ExtensionTable.update({ ExtensionTable.pkgName eq foundExtension.pkgName }) { this[ExtensionTable.hasUpdate] = true
it[hasUpdate] = true
}
updateMap.putIfAbsent(foundExtension.pkgName, foundExtension) updateMap.putIfAbsent(foundExtension.pkgName, foundExtension)
} }
foundExtension.versionCode < extensionRecord[ExtensionTable.versionCode] -> { foundExtension.versionCode < extensionRecord[ExtensionTable.versionCode] -> {
// somehow the user installed an invalid version // somehow the user installed an invalid version
ExtensionTable.update({ ExtensionTable.pkgName eq foundExtension.pkgName }) { this[ExtensionTable.isObsolete] = true
it[isObsolete] = true
} }
} }
} }
} else { execute(this@transaction)
}
}
val extensionsToFullyUpdate = extensionsInstalled[false].orEmpty()
if (extensionsToFullyUpdate.isNotEmpty()) {
BatchUpdateStatement(ExtensionTable).apply {
extensionsToFullyUpdate.forEach { (foundExtension, extensionRecord) ->
addBatch(EntityID(extensionRecord[ExtensionTable.id].value, ExtensionTable))
// extension is not installed, so we can overwrite the data without a care // extension is not installed, so we can overwrite the data without a care
ExtensionTable.update({ ExtensionTable.pkgName eq foundExtension.pkgName }) { this[ExtensionTable.name] = foundExtension.name
it[name] = foundExtension.name this[ExtensionTable.versionName] = foundExtension.versionName
it[versionName] = foundExtension.versionName this[ExtensionTable.versionCode] = foundExtension.versionCode
it[versionCode] = foundExtension.versionCode this[ExtensionTable.lang] = foundExtension.lang
it[lang] = foundExtension.lang this[ExtensionTable.isNsfw] = foundExtension.isNsfw
it[isNsfw] = foundExtension.isNsfw this[ExtensionTable.apkName] = foundExtension.apkName
it[apkName] = foundExtension.apkName this[ExtensionTable.iconUrl] = foundExtension.iconUrl
it[iconUrl] = foundExtension.iconUrl }
execute(this@transaction)
} }
} }
} else {
// insert new record
ExtensionTable.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
} }
if (extensionsToInsert.isNotEmpty()) {
ExtensionTable.batchInsert(extensionsToInsert) { foundExtension ->
this[ExtensionTable.name] = foundExtension.name
this[ExtensionTable.pkgName] = foundExtension.pkgName
this[ExtensionTable.versionName] = foundExtension.versionName
this[ExtensionTable.versionCode] = foundExtension.versionCode
this[ExtensionTable.lang] = foundExtension.lang
this[ExtensionTable.isNsfw] = foundExtension.isNsfw
this[ExtensionTable.apkName] = foundExtension.apkName
this[ExtensionTable.iconUrl] = foundExtension.iconUrl
} }
} }
// deal with obsolete extensions // deal with obsolete extensions
ExtensionTable.selectAll().forEach { extensionRecord -> val extensionsToRemove =
val foundExtension = foundExtensions.find { it.pkgName == extensionRecord[ExtensionTable.pkgName] } extensionsToDelete.groupBy { it[ExtensionTable.isInstalled] }
if (foundExtension == null) { .mapValues { (_, extensions) -> extensions.map { it[ExtensionTable.pkgName] } }
// not in the repo, so these extensions are obsolete // not in the repo, so these extensions are obsolete
if (extensionRecord[ExtensionTable.isInstalled]) { val obsoleteExtensions = extensionsToRemove[true].orEmpty()
// is installed so we should mark it as obsolete if (obsoleteExtensions.isNotEmpty()) {
ExtensionTable.update({ ExtensionTable.pkgName eq extensionRecord[ExtensionTable.pkgName] }) { ExtensionTable.update({ ExtensionTable.pkgName inList obsoleteExtensions }) {
it[isObsolete] = true it[isObsolete] = true
} }
} else { }
// is not installed, so we can remove the record without a care // is not installed, so we can remove the record without a care
ExtensionTable.deleteWhere { ExtensionTable.pkgName eq extensionRecord[ExtensionTable.pkgName] } val removeExtensions = extensionsToRemove[false].orEmpty()
} if (removeExtensions.isNotEmpty()) {
} ExtensionTable.deleteWhere { ExtensionTable.pkgName inList removeExtensions }
} }
} }
} }

View File

@@ -109,7 +109,7 @@ object ExtensionGithubApi {
hasChangelog = it.hasChangelog == 1, hasChangelog = it.hasChangelog == 1,
sources = it.sources?.toExtensionSources() ?: emptyList(), sources = it.sources?.toExtensionSources() ?: emptyList(),
apkName = it.apk, apkName = it.apk,
iconUrl = "${REPO_URL_PREFIX}icon/${it.apk.replace(".apk", ".png")}", iconUrl = "${REPO_URL_PREFIX}icon/${it.pkg}.png",
) )
} }
} }