mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-04 11:24:35 -05:00
Improve source handling, fix errors with uninitialized mangas in broken sources (#319)
This commit is contained in:
@@ -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` */
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>) {
|
||||||
|
|||||||
@@ -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,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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}" })
|
||||||
|
|||||||
Reference in New Issue
Block a user