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,68 +71,100 @@ 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>>()
when { val extensionsToInsert = mutableListOf<OnlineExtension>()
foundExtension.versionCode > extensionRecord[ExtensionTable.versionCode] -> { val extensionsToDelete =
// there is an update installedExtensions.mapNotNull { (pkgName, extension) ->
ExtensionTable.update({ ExtensionTable.pkgName eq foundExtension.pkgName }) { extension.takeUnless { foundExtensions.any { it.pkgName == pkgName } }
it[hasUpdate] = true }
} foundExtensions.forEach {
updateMap.putIfAbsent(foundExtension.pkgName, foundExtension) val extension = installedExtensions[it.pkgName]
} if (extension != null) {
foundExtension.versionCode < extensionRecord[ExtensionTable.versionCode] -> { extensionsToUpdate.add(it to extension)
// somehow the user installed an invalid version
ExtensionTable.update({ ExtensionTable.pkgName eq foundExtension.pkgName }) {
it[isObsolete] = true
}
}
}
} 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[versionName] = foundExtension.versionName
it[versionCode] = foundExtension.versionCode
it[lang] = foundExtension.lang
it[isNsfw] = foundExtension.isNsfw
it[apkName] = foundExtension.apkName
it[iconUrl] = foundExtension.iconUrl
}
}
} else { } else {
// insert new record extensionsToInsert.add(it)
ExtensionTable.insert { }
it[name] = foundExtension.name }
it[pkgName] = foundExtension.pkgName if (extensionsToUpdate.isNotEmpty()) {
it[versionName] = foundExtension.versionName val extensionsInstalled =
it[versionCode] = foundExtension.versionCode extensionsToUpdate
it[lang] = foundExtension.lang .groupBy { it.second[ExtensionTable.isInstalled] }
it[isNsfw] = foundExtension.isNsfw val installedExtensionsToUpdate = extensionsInstalled[true].orEmpty()
it[apkName] = foundExtension.apkName if (installedExtensionsToUpdate.isNotEmpty()) {
it[iconUrl] = foundExtension.iconUrl 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 {
foundExtension.versionCode > extensionRecord[ExtensionTable.versionCode] -> {
// there is an update
this[ExtensionTable.hasUpdate] = true
updateMap.putIfAbsent(foundExtension.pkgName, foundExtension)
}
foundExtension.versionCode < extensionRecord[ExtensionTable.versionCode] -> {
// somehow the user installed an invalid version
this[ExtensionTable.isObsolete] = true
}
}
}
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
this[ExtensionTable.name] = foundExtension.name
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
}
execute(this@transaction)
}
}
}
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
ExtensionTable.deleteWhere { ExtensionTable.pkgName eq extensionRecord[ExtensionTable.pkgName] }
}
} }
} }
// is not installed, so we can remove the record without a care
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",
) )
} }
} }