better method of detemining if a source is Nsfw

This commit is contained in:
Aria Moradi
2021-08-24 02:44:13 +04:30
parent ff4e818e4c
commit 9c007483d4
5 changed files with 46 additions and 28 deletions
@@ -12,7 +12,6 @@ import android.content.Context
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.getPreferenceKey import eu.kanade.tachiyomi.source.getPreferenceKey
import eu.kanade.tachiyomi.source.online.HttpSource
import mu.KotlinLogging import mu.KotlinLogging
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.selectAll
@@ -33,34 +32,32 @@ import xyz.nulldev.androidcompat.androidimpl.CustomContext
object Source { object Source {
private val logger = KotlinLogging.logger {} private val logger = KotlinLogging.logger {}
val HttpSource.isNsfw: Boolean
get() = this::class.annotations.any { it.toString() == "@eu.kanade.tachiyomi.annotations.Nsfw()" }
fun getSourceList(): List<SourceDataClass> { fun getSourceList(): List<SourceDataClass> {
return transaction { return transaction {
SourceTable.selectAll().map { SourceTable.selectAll().map {
val httpSource = getHttpSource(it[SourceTable.id].value) val httpSource = getHttpSource(it[SourceTable.id].value)
val sourceExtension = ExtensionTable.select { ExtensionTable.id eq it[SourceTable.extension] }.first()
SourceDataClass( SourceDataClass(
it[SourceTable.id].value.toString(), it[SourceTable.id].value.toString(),
it[SourceTable.name], it[SourceTable.name],
it[SourceTable.lang], it[SourceTable.lang],
getExtensionIconUrl( getExtensionIconUrl(sourceExtension[ExtensionTable.apkName]),
ExtensionTable.select { ExtensionTable.id eq it[SourceTable.extension] }
.first()[ExtensionTable.apkName]
),
httpSource.supportsLatest, httpSource.supportsLatest,
httpSource is ConfigurableSource, 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 { return transaction {
val source = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull() val source = SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()
val httpSource = source?.let { getHttpSource(sourceId) } val httpSource = source?.let { getHttpSource(sourceId) }
val extension = source?.let {
ExtensionTable.select { ExtensionTable.id eq source[SourceTable.extension] }.first()
}
SourceDataClass( SourceDataClass(
sourceId.toString(), sourceId.toString(),
@@ -68,13 +65,12 @@ object Source {
source?.get(SourceTable.lang), source?.get(SourceTable.lang),
source?.let { source?.let {
getExtensionIconUrl( getExtensionIconUrl(
ExtensionTable.select { ExtensionTable.id eq source[SourceTable.extension] } extension!![ExtensionTable.apkName]
.first()[ExtensionTable.apkName]
) )
}, },
httpSource?.supportsLatest, httpSource?.supportsLatest,
httpSource?.let { it is ConfigurableSource }, httpSource?.let { it is ConfigurableSource },
httpSource?.isNsfw source?.get(SourceTable.isNsfw)
) )
} }
} }
@@ -82,7 +78,7 @@ object Source {
private val context by DI.global.instance<CustomContext>() private val context by DI.global.instance<CustomContext>()
/** /**
* 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 * - EditTextPreference
* - SwitchPreferenceCompat * - SwitchPreferenceCompat
* - ListPreference * - ListPreference
@@ -50,10 +50,8 @@ object Extension {
private val logger = KotlinLogging.logger {} private val logger = KotlinLogging.logger {}
private val applicationDirs by DI.global.instance<ApplicationDirs>() private val applicationDirs by DI.global.instance<ApplicationDirs>()
data class InstallableAPK( private fun Any.isNsfw(): Boolean =
val apkFilePath: String, this::class.annotations.any { it.toString() == "@eu.kanade.tachiyomi.annotations.Nsfw()" }
val pkgName: String
)
suspend fun installExtension(pkgName: String): Int { suspend fun installExtension(pkgName: String): Int {
logger.debug("Installing $pkgName") logger.debug("Installing $pkgName")
@@ -99,7 +97,7 @@ object Extension {
if (libVersion < LIB_VERSION_MIN || libVersion > LIB_VERSION_MAX) { if (libVersion < LIB_VERSION_MIN || libVersion > LIB_VERSION_MAX) {
throw Exception( throw Exception(
"Lib version is $libVersion, while only versions " + "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 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") logger.debug("Main class for extension is $className")
@@ -125,10 +124,11 @@ object Extension {
File(dexFilePath).delete() File(dexFilePath).delete()
// collect sources from the extension // collect sources from the extension
val sources: List<CatalogueSource> = when (val instance = loadExtensionSources(jarFilePath, className)) { val extensionMainClassInstance = loadExtensionSources(jarFilePath, className)
is Source -> listOf(instance) val sources: List<CatalogueSource> = when (extensionMainClassInstance) {
is SourceFactory -> instance.createSources() is Source -> listOf(extensionMainClassInstance)
else -> throw RuntimeException("Unknown source class type! ${instance.javaClass}") is SourceFactory -> extensionMainClassInstance.createSources()
else -> throw RuntimeException("Unknown source class type! ${extensionMainClassInstance.javaClass}")
}.map { it as CatalogueSource } }.map { it as CatalogueSource }
val langs = sources.map { it.lang }.toSet() val langs = sources.map { it.lang }.toSet()
@@ -159,7 +159,8 @@ object Extension {
it[this.classFQName] = className 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 -> sources.forEach { httpSource ->
SourceTable.insert { SourceTable.insert {
@@ -167,8 +168,9 @@ object Extension {
it[name] = httpSource.name it[name] = httpSource.name
it[lang] = httpSource.lang it[lang] = httpSource.lang
it[extension] = extensionId 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 return 201 // we installed successfully
@@ -234,7 +236,8 @@ object Extension {
} }
suspend fun getExtensionIcon(apkName: String): Pair<InputStream, String> { suspend fun getExtensionIcon(apkName: String): Pair<InputStream, String> {
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" val saveDir = "${applicationDirs.extensionsRoot}/icon"
@@ -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. * It may return an instance of HttpSource or SourceFactory depending on the extension.
*/ */
fun loadExtensionSources(jarPath: String, className: String): Any { fun loadExtensionSources(jarPath: String, className: String): Any {
@@ -15,4 +15,5 @@ object SourceTable : IdTable<Long>() {
val lang = varchar("lang", 10) val lang = varchar("lang", 10)
val extension = reference("extension", ExtensionTable) val extension = reference("extension", ExtensionTable)
val partOfFactorySource = bool("part_of_factory_source").default(false) val partOfFactorySource = bool("part_of_factory_source").default(false)
val isNsfw = bool("is_nsfw").default(false)
} }
@@ -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"
)