package eu.kanade.tachiyomi.source import android.content.Context import com.elvishew.xlog.XLog import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.all.EHentai import eu.kanade.tachiyomi.source.online.all.Hitomi import eu.kanade.tachiyomi.source.online.all.MergedSource import eu.kanade.tachiyomi.source.online.all.NHentai import eu.kanade.tachiyomi.source.online.all.PervEden import eu.kanade.tachiyomi.source.online.english.EightMuses import eu.kanade.tachiyomi.source.online.english.HBrowse import eu.kanade.tachiyomi.source.online.english.HentaiCafe import eu.kanade.tachiyomi.source.online.english.Pururin import eu.kanade.tachiyomi.source.online.english.Tsumino import exh.EH_SOURCE_ID import exh.EXH_SOURCE_ID import exh.PERV_EDEN_EN_SOURCE_ID import exh.PERV_EDEN_IT_SOURCE_ID import exh.metadata.metadata.PervEdenLang import exh.source.BlacklistedSources import exh.source.DelegatedHttpSource import exh.source.EnhancedHttpSource import kotlin.reflect.KClass import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import rx.Observable import uy.kohesive.injekt.injectLazy open class SourceManager(private val context: Context) { private val sourcesMap = mutableMapOf() private val stubSourcesMap = mutableMapOf() // SY --> private val prefs: PreferencesHelper by injectLazy() private val scope = CoroutineScope(Job() + Dispatchers.Main) // SY <-- init { createInternalSources().forEach { registerSource(it) } // SY --> // Recreate sources when they change prefs.enableExhentai().asFlow().onEach { createEHSources().forEach { registerSource(it) } }.launchIn(scope) registerSource(MergedSource()) // SY <-- } open fun get(sourceKey: Long): Source? { return sourcesMap[sourceKey] } fun getOrStub(sourceKey: Long): Source { return sourcesMap[sourceKey] ?: stubSourcesMap.getOrPut(sourceKey) { StubSource(sourceKey) } } fun getOnlineSources() = sourcesMap.values.filterIsInstance() fun getCatalogueSources() = sourcesMap.values.filterIsInstance() // SY --> fun getVisibleCatalogueSources() = sourcesMap.values.filterIsInstance().filter { it.id !in BlacklistedSources.HIDDEN_SOURCES } fun getDelegatedCatalogueSources() = sourcesMap.values.filterIsInstance().mapNotNull { enhancedHttpSource -> enhancedHttpSource.enchancedSource as? DelegatedHttpSource } // SY <-- internal fun registerSource(source: Source, overwrite: Boolean = false) { // EXH --> val sourceQName = source::class.qualifiedName val delegate = DELEGATED_SOURCES[sourceQName] val newSource = if (source is HttpSource && delegate != null) { XLog.d("[EXH] Delegating source: %s -> %s!", sourceQName, delegate.newSourceClass.qualifiedName) EnhancedHttpSource( source, delegate.newSourceClass.constructors.find { it.parameters.size == 1 }!!.call(source) ) } else source if (source.id in BlacklistedSources.BLACKLISTED_EXT_SOURCES) { XLog.d("[EXH] Removing blacklisted source: (id: %s, name: %s, lang: %s)!", source.id, source.name, (source as? CatalogueSource)?.lang) return } // EXH <-- if (overwrite || !sourcesMap.containsKey(source.id)) { sourcesMap[source.id] = newSource } } internal fun unregisterSource(source: Source) { sourcesMap.remove(source.id) } private fun createInternalSources(): List = listOf( LocalSource(context) ) // SY --> private fun createEHSources(): List { val exSrcs = mutableListOf( EHentai(EH_SOURCE_ID, false, context) ) if (prefs.enableExhentai().get()) { exSrcs += EHentai(EXH_SOURCE_ID, true, context) } exSrcs += PervEden(PERV_EDEN_EN_SOURCE_ID, PervEdenLang.en) exSrcs += PervEden(PERV_EDEN_IT_SOURCE_ID, PervEdenLang.it) exSrcs += NHentai(context) exSrcs += Hitomi() exSrcs += EightMuses() exSrcs += HBrowse() return exSrcs } // SY <-- private inner class StubSource(override val id: Long) : Source { override val name: String get() = id.toString() override fun fetchMangaDetails(manga: SManga): Observable { return Observable.error(getSourceNotInstalledException()) } override fun fetchChapterList(manga: SManga): Observable> { return Observable.error(getSourceNotInstalledException()) } override fun fetchPageList(chapter: SChapter): Observable> { return Observable.error(getSourceNotInstalledException()) } override fun toString(): String { return name } private fun getSourceNotInstalledException(): Exception { return Exception(context.getString(R.string.source_not_installed, id.toString())) } } // SY --> companion object { val DELEGATED_SOURCES = listOf( DelegatedSource( "Hentai Cafe", 260868874183818481, "eu.kanade.tachiyomi.extension.all.foolslide.HentaiCafe", HentaiCafe::class ), DelegatedSource( "Pururin", 2221515250486218861, "eu.kanade.tachiyomi.extension.en.pururin.Pururin", Pururin::class ), DelegatedSource( "Tsumino", 6707338697138388238, "eu.kanade.tachiyomi.extension.en.tsumino.Tsumino", Tsumino::class ) ).associateBy { it.originalSourceQualifiedClassName } data class DelegatedSource( val sourceName: String, val sourceId: Long, val originalSourceQualifiedClassName: String, val newSourceClass: KClass ) } // SY <-- }