diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Source.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Source.kt index cfd02a61..20b7b03f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Source.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Source.kt @@ -12,7 +12,6 @@ import android.content.Context import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.getPreferenceKey -import eu.kanade.tachiyomi.source.online.HttpSource import mu.KotlinLogging import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.selectAll @@ -33,34 +32,32 @@ import xyz.nulldev.androidcompat.androidimpl.CustomContext object Source { private val logger = KotlinLogging.logger {} - val HttpSource.isNsfw: Boolean - get() = this::class.annotations.any { it.toString() == "@eu.kanade.tachiyomi.annotations.Nsfw()" } - fun getSourceList(): List { return transaction { SourceTable.selectAll().map { val httpSource = getHttpSource(it[SourceTable.id].value) + val sourceExtension = ExtensionTable.select { ExtensionTable.id eq it[SourceTable.extension] }.first() SourceDataClass( it[SourceTable.id].value.toString(), it[SourceTable.name], it[SourceTable.lang], - getExtensionIconUrl( - ExtensionTable.select { ExtensionTable.id eq it[SourceTable.extension] } - .first()[ExtensionTable.apkName] - ), + getExtensionIconUrl(sourceExtension[ExtensionTable.apkName]), httpSource.supportsLatest, httpSource is ConfigurableSource, - httpSource.isNsfw + it[SourceTable.isNsfw] ) } } } - fun getSource(sourceId: Long): SourceDataClass { + fun getSource(sourceId: Long): SourceDataClass { // all the data extracted fresh form the source instance return transaction { val source = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull() val httpSource = source?.let { getHttpSource(sourceId) } + val extension = source?.let { + ExtensionTable.select { ExtensionTable.id eq source[SourceTable.extension] }.first() + } SourceDataClass( sourceId.toString(), @@ -68,13 +65,12 @@ object Source { source?.get(SourceTable.lang), source?.let { getExtensionIconUrl( - ExtensionTable.select { ExtensionTable.id eq source[SourceTable.extension] } - .first()[ExtensionTable.apkName] + extension!![ExtensionTable.apkName] ) }, httpSource?.supportsLatest, httpSource?.let { it is ConfigurableSource }, - httpSource?.isNsfw + source?.get(SourceTable.isNsfw) ) } } @@ -82,7 +78,7 @@ object Source { private val context by DI.global.instance() /** - * Clients should support these types for extensions to work properly (in order of importance) + * Clients should support these types for extensions to work properly * - EditTextPreference * - SwitchPreferenceCompat * - ListPreference diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/Extension.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/Extension.kt index 253285f3..18cdfd4a 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/Extension.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/Extension.kt @@ -50,10 +50,8 @@ object Extension { private val logger = KotlinLogging.logger {} private val applicationDirs by DI.global.instance() - data class InstallableAPK( - val apkFilePath: String, - val pkgName: String - ) + private fun Any.isNsfw(): Boolean = + this::class.annotations.any { it.toString() == "@eu.kanade.tachiyomi.annotations.Nsfw()" } suspend fun installExtension(pkgName: String): Int { logger.debug("Installing $pkgName") @@ -99,7 +97,7 @@ object Extension { if (libVersion < LIB_VERSION_MIN || libVersion > LIB_VERSION_MAX) { throw Exception( "Lib version is $libVersion, while only versions " + - "$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed" + "$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed" ) } @@ -114,7 +112,8 @@ object Extension { val isNsfw = packageInfo.applicationInfo.metaData.getString(METADATA_NSFW) == "1" - val className = packageInfo.packageName + packageInfo.applicationInfo.metaData.getString(METADATA_SOURCE_CLASS) + val className = + packageInfo.packageName + packageInfo.applicationInfo.metaData.getString(METADATA_SOURCE_CLASS) logger.debug("Main class for extension is $className") @@ -125,10 +124,11 @@ object Extension { File(dexFilePath).delete() // collect sources from the extension - val sources: List = when (val instance = loadExtensionSources(jarFilePath, className)) { - is Source -> listOf(instance) - is SourceFactory -> instance.createSources() - else -> throw RuntimeException("Unknown source class type! ${instance.javaClass}") + val extensionMainClassInstance = loadExtensionSources(jarFilePath, className) + val sources: List = when (extensionMainClassInstance) { + is Source -> listOf(extensionMainClassInstance) + is SourceFactory -> extensionMainClassInstance.createSources() + else -> throw RuntimeException("Unknown source class type! ${extensionMainClassInstance.javaClass}") }.map { it as CatalogueSource } val langs = sources.map { it.lang }.toSet() @@ -159,7 +159,8 @@ object Extension { it[this.classFQName] = className } - val extensionId = ExtensionTable.select { ExtensionTable.pkgName eq pkgName }.first()[ExtensionTable.id].value + val extensionId = + ExtensionTable.select { ExtensionTable.pkgName eq pkgName }.first()[ExtensionTable.id].value sources.forEach { httpSource -> SourceTable.insert { @@ -167,8 +168,9 @@ object Extension { it[name] = httpSource.name it[lang] = httpSource.lang it[extension] = extensionId + it[SourceTable.isNsfw] = isNsfw || extensionMainClassInstance.isNsfw() } - logger.debug("Installed source ${httpSource.name} (${httpSource.lang}) with id:${httpSource.id}") + logger.debug { "Installed source ${httpSource.name} (${httpSource.lang}) with id:${httpSource.id}" } } } return 201 // we installed successfully @@ -234,7 +236,8 @@ object Extension { } suspend fun getExtensionIcon(apkName: String): Pair { - val iconUrl = transaction { ExtensionTable.select { ExtensionTable.apkName eq apkName }.first() }[ExtensionTable.iconUrl] + val iconUrl = + transaction { ExtensionTable.select { ExtensionTable.apkName eq apkName }.first() }[ExtensionTable.iconUrl] val saveDir = "${applicationDirs.extensionsRoot}/icon" diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/PackageTools.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/PackageTools.kt index f1685e07..486def72 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/PackageTools.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/PackageTools.kt @@ -137,7 +137,7 @@ object PackageTools { } /** - * loads the extension main class called $className from the jar located at $jarPath + * loads the extension main class called [className] from the jar located at [jarPath] * It may return an instance of HttpSource or SourceFactory depending on the extension. */ fun loadExtensionSources(jarPath: String, className: String): Any { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/SourceTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/SourceTable.kt index fb66f3b9..39d2476d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/SourceTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/SourceTable.kt @@ -15,4 +15,5 @@ object SourceTable : IdTable() { val lang = varchar("lang", 10) val extension = reference("extension", ExtensionTable) val partOfFactorySource = bool("part_of_factory_source").default(false) + val isNsfw = bool("is_nsfw").default(false) } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0011_SourceIsNsfw.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0011_SourceIsNsfw.kt new file mode 100644 index 00000000..6faed982 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0011_SourceIsNsfw.kt @@ -0,0 +1,18 @@ +package suwayomi.tachidesk.server.database.migration + +/* + * 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 de.neonew.exposed.migrations.helpers.AddColumnMigration + +@Suppress("ClassName", "unused") +class M0011_SourceIsNsfw : AddColumnMigration( + "Source", + "is_nsfw", + "BOOLEAN", + "FALSE" +)