Improve source handling, fix errors with uninitialized mangas in broken sources (#319)

This commit is contained in:
Mitchell Syer
2022-03-22 07:21:07 -04:00
committed by GitHub
parent a27af0b642
commit 152b193ad5
6 changed files with 59 additions and 56 deletions

View File

@@ -29,7 +29,7 @@ object SourceController {
/** fetch source with id `sourceId` */ /** fetch source with id `sourceId` */
fun retrieve(ctx: Context) { fun retrieve(ctx: Context) {
val sourceId = ctx.pathParam("sourceId").toLong() val sourceId = ctx.pathParam("sourceId").toLong()
ctx.json(Source.getSource(sourceId)) ctx.json(Source.getSource(sourceId)!!)
} }
/** popular mangas from source with id `sourceId` */ /** popular mangas from source with id `sourceId` */

View File

@@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.local.LocalSource import eu.kanade.tachiyomi.source.local.LocalSource
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
@@ -23,6 +24,7 @@ import suwayomi.tachidesk.manga.impl.MangaList.proxyThumbnailUrl
import suwayomi.tachidesk.manga.impl.Source.getSource import suwayomi.tachidesk.manga.impl.Source.getSource
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.manga.impl.util.network.await import suwayomi.tachidesk.manga.impl.util.network.await
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrNull
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub
import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse.clearCachedImage import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse.clearCachedImage
import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse.getImageResponse import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse.getImageResponse
@@ -50,30 +52,10 @@ object Manga {
var mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() } var mangaEntry = transaction { MangaTable.select { MangaTable.id eq mangaId }.first() }
return if (mangaEntry[MangaTable.initialized] && !onlineFetch) { return if (mangaEntry[MangaTable.initialized] && !onlineFetch) {
MangaDataClass( getMangaDataClass(mangaId, mangaEntry)
mangaId,
mangaEntry[MangaTable.sourceReference].toString(),
mangaEntry[MangaTable.url],
mangaEntry[MangaTable.title],
proxyThumbnailUrl(mangaId),
true,
mangaEntry[MangaTable.artist],
mangaEntry[MangaTable.author],
mangaEntry[MangaTable.description],
mangaEntry[MangaTable.genre].toGenreList(),
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
mangaEntry[MangaTable.inLibrary],
mangaEntry[MangaTable.inLibraryAt],
getSource(mangaEntry[MangaTable.sourceReference]),
getMangaMetaMap(mangaId),
mangaEntry[MangaTable.realUrl],
false
)
} else { // initialize manga } else { // initialize manga
val source = getCatalogueSourceOrStub(mangaEntry[MangaTable.sourceReference]) val source = getCatalogueSourceOrNull(mangaEntry[MangaTable.sourceReference])
?: return getMangaDataClass(mangaId, mangaEntry)
val sManga = SManga.create().apply { val sManga = SManga.create().apply {
url = mangaEntry[MangaTable.url] url = mangaEntry[MangaTable.url]
title = mangaEntry[MangaTable.title] title = mangaEntry[MangaTable.title]
@@ -135,6 +117,29 @@ object Manga {
} }
} }
private fun getMangaDataClass(mangaId: Int, mangaEntry: ResultRow) = MangaDataClass(
mangaId,
mangaEntry[MangaTable.sourceReference].toString(),
mangaEntry[MangaTable.url],
mangaEntry[MangaTable.title],
proxyThumbnailUrl(mangaId),
true,
mangaEntry[MangaTable.artist],
mangaEntry[MangaTable.author],
mangaEntry[MangaTable.description],
mangaEntry[MangaTable.genre].toGenreList(),
MangaStatus.valueOf(mangaEntry[MangaTable.status]).name,
mangaEntry[MangaTable.inLibrary],
mangaEntry[MangaTable.inLibraryAt],
getSource(mangaEntry[MangaTable.sourceReference]),
getMangaMetaMap(mangaId),
mangaEntry[MangaTable.realUrl],
false
)
fun getMangaMetaMap(manga: Int): Map<String, String> { fun getMangaMetaMap(manga: Int): Map<String, String> {
return transaction { return transaction {
MangaMetaTable.select { MangaMetaTable.ref eq manga } MangaMetaTable.select { MangaMetaTable.ref eq manga }

View File

@@ -21,10 +21,9 @@ import org.kodein.di.DI
import org.kodein.di.conf.global import org.kodein.di.conf.global
import org.kodein.di.instance import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIconUrl import suwayomi.tachidesk.manga.impl.extension.Extension.getExtensionIconUrl
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSource import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrNull
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.unregisterCatalogueSource import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.unregisterCatalogueSource
import suwayomi.tachidesk.manga.impl.util.source.StubSource
import suwayomi.tachidesk.manga.model.dataclass.SourceDataClass import suwayomi.tachidesk.manga.model.dataclass.SourceDataClass
import suwayomi.tachidesk.manga.model.table.ExtensionTable import suwayomi.tachidesk.manga.model.table.ExtensionTable
import suwayomi.tachidesk.manga.model.table.SourceTable import suwayomi.tachidesk.manga.model.table.SourceTable
@@ -38,8 +37,7 @@ object Source {
fun getSourceList(): List<SourceDataClass> { fun getSourceList(): List<SourceDataClass> {
return transaction { return transaction {
SourceTable.selectAll().mapNotNull { SourceTable.selectAll().mapNotNull {
val catalogueSource = getCatalogueSourceOrStub(it[SourceTable.id].value) val catalogueSource = getCatalogueSourceOrNull(it[SourceTable.id].value) ?: return@mapNotNull null
if (catalogueSource is StubSource) return@mapNotNull null
val sourceExtension = ExtensionTable.select { ExtensionTable.id eq it[SourceTable.extension] }.first() val sourceExtension = ExtensionTable.select { ExtensionTable.id eq it[SourceTable.extension] }.first()
SourceDataClass( SourceDataClass(
@@ -56,27 +54,23 @@ object Source {
} }
} }
fun getSource(sourceId: Long): SourceDataClass { // all the data extracted fresh form the source instance fun getSource(sourceId: Long): SourceDataClass? { // all the data extracted fresh form the source instance
return transaction { return transaction {
val source = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull() val source = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull() ?: return@transaction null
val catalogueSource = source?.let { getCatalogueSource(sourceId) } val catalogueSource = getCatalogueSourceOrNull(sourceId) ?: return@transaction null
val extension = source?.let { val extension = ExtensionTable.select { ExtensionTable.id eq source[SourceTable.extension] }.first()
ExtensionTable.select { ExtensionTable.id eq source[SourceTable.extension] }.first()
}
SourceDataClass( SourceDataClass(
sourceId.toString(), sourceId.toString(),
source?.get(SourceTable.name), source[SourceTable.name],
source?.get(SourceTable.lang), source[SourceTable.lang],
source?.let { getExtensionIconUrl(
getExtensionIconUrl( extension[ExtensionTable.apkName]
extension!![ExtensionTable.apkName] ),
) catalogueSource.supportsLatest,
}, catalogueSource is ConfigurableSource,
catalogueSource?.supportsLatest, source[SourceTable.isNsfw],
catalogueSource?.let { it is ConfigurableSource }, catalogueSource.toString()
source?.get(SourceTable.isNsfw),
catalogueSource?.toString()
) )
} }
} }

View File

@@ -26,7 +26,7 @@ object GetCatalogueSource {
private val sourceCache = ConcurrentHashMap<Long, CatalogueSource>() private val sourceCache = ConcurrentHashMap<Long, CatalogueSource>()
private val applicationDirs by DI.global.instance<ApplicationDirs>() private val applicationDirs by DI.global.instance<ApplicationDirs>()
fun getCatalogueSource(sourceId: Long): CatalogueSource? { private fun getCatalogueSource(sourceId: Long): CatalogueSource? {
val cachedResult: CatalogueSource? = sourceCache[sourceId] val cachedResult: CatalogueSource? = sourceCache[sourceId]
if (cachedResult != null) { if (cachedResult != null) {
return cachedResult return cachedResult
@@ -56,8 +56,12 @@ object GetCatalogueSource {
return sourceCache[sourceId]!! return sourceCache[sourceId]!!
} }
fun getCatalogueSourceOrNull(sourceId: Long): CatalogueSource? {
return runCatching { getCatalogueSource(sourceId) }.getOrNull()
}
fun getCatalogueSourceOrStub(sourceId: Long): CatalogueSource { fun getCatalogueSourceOrStub(sourceId: Long): CatalogueSource {
return runCatching { getCatalogueSource(sourceId) }.getOrNull() ?: StubSource(sourceId) return getCatalogueSourceOrNull(sourceId) ?: StubSource(sourceId)
} }
fun registerCatalogueSource(sourcePair: Pair<Long, CatalogueSource>) { fun registerCatalogueSource(sourcePair: Pair<Long, CatalogueSource>) {

View File

@@ -11,19 +11,19 @@ import eu.kanade.tachiyomi.source.ConfigurableSource
data class SourceDataClass( data class SourceDataClass(
val id: String, val id: String,
val name: String?, val name: String,
val lang: String?, val lang: String,
val iconUrl: String?, val iconUrl: String,
/** The Source provides a latest listing */ /** The Source provides a latest listing */
val supportsLatest: Boolean?, val supportsLatest: Boolean,
/** The Source implements [ConfigurableSource] */ /** The Source implements [ConfigurableSource] */
val isConfigurable: Boolean?, val isConfigurable: Boolean,
/** The Source class has a @Nsfw annotation */ /** The Source class has a @Nsfw annotation */
val isNsfw: Boolean?, val isNsfw: Boolean,
/** A nicer version of [name] */ /** A nicer version of [name] */
val displayName: String?, val displayName: String,
) )

View File

@@ -27,7 +27,7 @@ import suwayomi.tachidesk.manga.impl.extension.Extension.uninstallExtension
import suwayomi.tachidesk.manga.impl.extension.Extension.updateExtension import suwayomi.tachidesk.manga.impl.extension.Extension.updateExtension
import suwayomi.tachidesk.manga.impl.extension.ExtensionsList.getExtensionList import suwayomi.tachidesk.manga.impl.extension.ExtensionsList.getExtensionList
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSource import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrNull
import suwayomi.tachidesk.manga.model.dataclass.ExtensionDataClass import suwayomi.tachidesk.manga.model.dataclass.ExtensionDataClass
import suwayomi.tachidesk.server.applicationSetup import suwayomi.tachidesk.server.applicationSetup
import suwayomi.tachidesk.test.BASE_PATH import suwayomi.tachidesk.test.BASE_PATH
@@ -72,7 +72,7 @@ class TestExtensionCompatibility {
} }
} }
} }
sources = getSourceList().map { getCatalogueSource(it.id.toLong())!! as HttpSource } sources = getSourceList().map { getCatalogueSourceOrNull(it.id.toLong())!! as HttpSource }
} }
setLoggingEnabled(true) setLoggingEnabled(true)
File("$BASE_PATH/sources.txt").writeText(sources.joinToString("\n") { "${it.name} - ${it.lang.uppercase()} - ${it.id}" }) File("$BASE_PATH/sources.txt").writeText(sources.joinToString("\n") { "${it.name} - ${it.lang.uppercase()} - ${it.id}" })